/ types / errors.ts
errors.ts
  1  // Error types for better error handling
  2  
  3  export enum ErrorType {
  4    NETWORK = 'NETWORK',
  5    VALIDATION = 'VALIDATION',
  6    CACHE = 'CACHE',
  7    PARSING = 'PARSING',
  8    AUTHENTICATION = 'AUTHENTICATION',
  9    PERMISSION = 'PERMISSION',
 10    NOT_FOUND = 'NOT_FOUND',
 11    RATE_LIMIT = 'RATE_LIMIT',
 12    SERVER = 'SERVER',
 13    UNKNOWN = 'UNKNOWN',
 14  }
 15  
 16  export interface AppError {
 17    type: ErrorType;
 18    message: string;
 19    code?: string | undefined;
 20    details?: any;
 21    timestamp: number;
 22    retryable: boolean;
 23  }
 24  
 25  export class MangaNetworkError extends Error {
 26    public readonly type = ErrorType.NETWORK;
 27    public readonly retryable = true;
 28    public readonly timestamp = Date.now();
 29  
 30    constructor(
 31      message: string,
 32      public readonly code?: string,
 33      public readonly details?: any
 34    ) {
 35      super(message);
 36      this.name = 'MangaNetworkError';
 37    }
 38  }
 39  
 40  export class MangaValidationError extends Error {
 41    public readonly type = ErrorType.VALIDATION;
 42    public readonly retryable = false;
 43    public readonly timestamp = Date.now();
 44  
 45    constructor(
 46      message: string,
 47      public readonly field?: string,
 48      public readonly details?: any
 49    ) {
 50      super(message);
 51      this.name = 'MangaValidationError';
 52    }
 53  }
 54  
 55  export class MangaCacheError extends Error {
 56    public readonly type = ErrorType.CACHE;
 57    public readonly retryable = true;
 58    public readonly timestamp = Date.now();
 59  
 60    constructor(
 61      message: string,
 62      public readonly operation?: string,
 63      public readonly details?: any
 64    ) {
 65      super(message);
 66      this.name = 'MangaCacheError';
 67    }
 68  }
 69  
 70  export class MangaParsingError extends Error {
 71    public readonly type = ErrorType.PARSING;
 72    public readonly retryable = false;
 73    public readonly timestamp = Date.now();
 74  
 75    constructor(
 76      message: string,
 77      public readonly source?: string,
 78      public readonly details?: any
 79    ) {
 80      super(message);
 81      this.name = 'MangaParsingError';
 82    }
 83  }
 84  
 85  export class MangaNotFoundError extends Error {
 86    public readonly type = ErrorType.NOT_FOUND;
 87    public readonly retryable = false;
 88    public readonly timestamp = Date.now();
 89  
 90    constructor(
 91      message: string,
 92      public readonly id?: string,
 93      public readonly details?: any
 94    ) {
 95      super(message);
 96      this.name = 'MangaNotFoundError';
 97    }
 98  }
 99  
100  export function createAppError(
101    type: ErrorType,
102    message: string,
103    options: {
104      code?: string;
105      details?: any;
106      retryable?: boolean;
107    } = {}
108  ): AppError {
109    return {
110      type,
111      message,
112      code: options.code,
113      details: options.details,
114      timestamp: Date.now(),
115      retryable: options.retryable ?? true,
116    };
117  }
118  
119  export function isRetryableError(error: Error | AppError): boolean {
120    if ('retryable' in error) {
121      return error.retryable;
122    }
123  
124    // Network errors are generally retryable
125    if (error.message.includes('network') || error.message.includes('timeout')) {
126      return true;
127    }
128  
129    // Validation errors are not retryable
130    if (
131      error.message.includes('validation') ||
132      error.message.includes('invalid')
133    ) {
134      return false;
135    }
136  
137    // Default to retryable for unknown errors
138    return true;
139  }
140  
141  export function getErrorType(error: Error): ErrorType {
142    if ('type' in error && typeof error.type === 'string') {
143      return error.type as ErrorType;
144    }
145  
146    const message = error.message.toLowerCase();
147  
148    if (
149      message.includes('network') ||
150      message.includes('timeout') ||
151      message.includes('connection')
152    ) {
153      return ErrorType.NETWORK;
154    }
155  
156    if (
157      message.includes('validation') ||
158      message.includes('invalid') ||
159      message.includes('required')
160    ) {
161      return ErrorType.VALIDATION;
162    }
163  
164    if (message.includes('cache')) {
165      return ErrorType.CACHE;
166    }
167  
168    if (message.includes('parse') || message.includes('parsing')) {
169      return ErrorType.PARSING;
170    }
171  
172    if (message.includes('not found') || message.includes('404')) {
173      return ErrorType.NOT_FOUND;
174    }
175  
176    if (message.includes('rate limit') || message.includes('429')) {
177      return ErrorType.RATE_LIMIT;
178    }
179  
180    if (message.includes('server') || message.includes('500')) {
181      return ErrorType.SERVER;
182    }
183  
184    return ErrorType.UNKNOWN;
185  }