Files
XCDesktop/shared/errors/index.ts
2026-03-08 01:34:54 +08:00

172 lines
5.0 KiB
TypeScript

import type { ErrorCode } from '../constants/errors.js'
export type { ErrorCode } from '../constants/errors.js'
export interface ErrorDetails {
[key: string]: unknown
}
export class AppError extends Error {
public readonly statusCode: number
public readonly details?: ErrorDetails
constructor(
public readonly code: ErrorCode,
message: string,
statusCode: number = 500,
details?: ErrorDetails
) {
super(message)
this.name = 'AppError'
this.statusCode = statusCode
this.details = details
}
toJSON() {
return {
name: this.name,
code: this.code,
message: this.message,
statusCode: this.statusCode,
details: this.details
}
}
}
export class ValidationError extends AppError {
constructor(message: string, details?: ErrorDetails) {
super('VALIDATION_ERROR', message, 400, details)
this.name = 'ValidationError'
}
}
export class NotFoundError extends AppError {
constructor(message: string = 'Resource not found', details?: ErrorDetails) {
super('NOT_FOUND', message, 404, details)
this.name = 'NotFoundError'
}
}
export class AccessDeniedError extends AppError {
constructor(message: string = 'Access denied', details?: ErrorDetails) {
super('ACCESS_DENIED', message, 403, details)
this.name = 'AccessDeniedError'
}
}
export class BadRequestError extends AppError {
constructor(message: string, details?: ErrorDetails) {
super('BAD_REQUEST', message, 400, details)
this.name = 'BadRequestError'
}
}
export class PathNotFoundError extends AppError {
constructor(message: string = 'Path not found', details?: ErrorDetails) {
super('PATH_NOT_FOUND', message, 404, details)
this.name = 'PathNotFoundError'
}
}
export class NotADirectoryError extends AppError {
constructor(message: string = '不是目录', details?: ErrorDetails) {
super('NOT_A_DIRECTORY', message, 400, details)
this.name = 'NotADirectoryError'
}
}
export class FileExistsError extends AppError {
constructor(message: string = 'File already exists', details?: ErrorDetails) {
super('FILE_EXISTS', message, 409, details)
this.name = 'FileExistsError'
}
}
export class InvalidPathError extends AppError {
constructor(message: string = '无效路径', details?: ErrorDetails) {
super('INVALID_PATH', message, 400, details)
this.name = 'InvalidPathError'
}
}
export class AlreadyExistsError extends AppError {
constructor(message: string = 'Resource already exists', details?: ErrorDetails) {
super('ALREADY_EXISTS', message, 409, details)
this.name = 'AlreadyExistsError'
}
}
export class ForbiddenError extends AppError {
constructor(message: string = '禁止访问', details?: ErrorDetails) {
super('FORBIDDEN', message, 403, details)
this.name = 'ForbiddenError'
}
}
export class UnsupportedMediaTypeError extends AppError {
constructor(message: string = '不支持的媒体类型', details?: ErrorDetails) {
super('UNSUPPORTED_MEDIA_TYPE', message, 415, details)
this.name = 'UnsupportedMediaTypeError'
}
}
export class PayloadTooLargeError extends AppError {
constructor(message: string = 'Payload too large', details?: ErrorDetails) {
super('PAYLOAD_TOO_LARGE', message, 413, details)
this.name = 'PayloadTooLargeError'
}
}
export class ResourceLockedError extends AppError {
constructor(message: string = '资源已锁定', details?: ErrorDetails) {
super('RESOURCE_LOCKED', message, 423, details)
this.name = 'ResourceLockedError'
}
}
export class InternalError extends AppError {
constructor(message: string = '服务器内部错误', details?: ErrorDetails) {
super('INTERNAL_ERROR', message, 500, details)
this.name = 'InternalError'
}
}
export function isAppError(error: unknown): error is AppError {
return error instanceof AppError
}
export function isNodeError(error: unknown): error is NodeJS.ErrnoException {
return error instanceof Error && 'code' in error
}
export function createErrorFromCode(code: ErrorCode, message: string, details?: ErrorDetails): AppError {
switch (code) {
case 'VALIDATION_ERROR':
return new ValidationError(message, details)
case 'NOT_FOUND':
case 'PATH_NOT_FOUND':
return new NotFoundError(message, details)
case 'ACCESS_DENIED':
case 'FORBIDDEN':
return new AccessDeniedError(message, details)
case 'BAD_REQUEST':
case 'NOT_A_DIRECTORY':
case 'INVALID_PATH':
case 'INVALID_NAME':
return new BadRequestError(message, details)
case 'FILE_EXISTS':
case 'ALREADY_EXISTS':
return new FileExistsError(message, details)
case 'UNSUPPORTED_MEDIA_TYPE':
return new UnsupportedMediaTypeError(message, details)
case 'PAYLOAD_TOO_LARGE':
return new PayloadTooLargeError(message, details)
case 'RESOURCE_LOCKED':
return new ResourceLockedError(message, details)
case 'INTERNAL_ERROR':
return new InternalError(message, details)
default:
return new AppError(code, message, 500, details)
}
}