UNPKG

novel-reader-sdk

Version:

SDK for Novel Reader API

184 lines (149 loc) 4.72 kB
/** * SDK Error Classes * * Domain-specific error classes for client-side error handling * Matches the service's error types for consistency */ import type { ErrorCode, ErrorType, ApiError } from './types'; export abstract class ScrapingServiceError extends Error { abstract readonly code: ErrorCode; abstract readonly type: ErrorType; constructor( message: string, public override readonly cause?: unknown ) { super(message); this.name = this.constructor.name; } } // ===== Client-Side Errors ===== export class NetworkError extends ScrapingServiceError { readonly code = 'INTERNAL_SERVER_ERROR' as const; readonly type = 'SCRAPING_ERROR' as const; constructor(message: string, cause?: unknown) { super(`Network error: ${message}`, cause); } } export class TimeoutError extends ScrapingServiceError { readonly code = 'TIMEOUT_ERROR' as const; readonly type = 'TIMEOUT_ERROR' as const; constructor(message: string, cause?: unknown) { super(`Request timeout: ${message}`, cause); } } export class ValidationError extends ScrapingServiceError { readonly code = 'BAD_REQUEST' as const; readonly type = 'VALIDATION_ERROR' as const; constructor(message: string, cause?: unknown) { super(`Validation error: ${message}`, cause); } } // ===== Service-Side Errors (from API responses) ===== export class ChapterNotFoundError extends ScrapingServiceError { readonly code = 'NOT_FOUND' as const; readonly type = 'CHAPTER_NOT_FOUND' as const; constructor(url: string) { super(`Chapter not found at URL: ${url}`); } } export class ContentNotFoundError extends ScrapingServiceError { readonly code = 'NOT_FOUND' as const; readonly type = 'CONTENT_NOT_FOUND' as const; constructor(message: string) { super(`Content not found: ${message}`); } } export class UnsupportedSiteError extends ScrapingServiceError { readonly code = 'BAD_REQUEST' as const; readonly type = 'UNSUPPORTED_SITE' as const; constructor(hostname: string) { super(`Unsupported site: ${hostname}`); } } export class BrowserError extends ScrapingServiceError { readonly code = 'INTERNAL_SERVER_ERROR' as const; readonly type = 'BROWSER_ERROR' as const; constructor(message: string) { super(`Browser error: ${message}`); } } export class RateLimitError extends ScrapingServiceError { readonly code = 'RATE_LIMIT_EXCEEDED' as const; readonly type = 'RATE_LIMIT_ERROR' as const; constructor(message: string) { super(`Rate limit exceeded: ${message}`); } } export class ServiceError extends ScrapingServiceError { readonly code = 'INTERNAL_SERVER_ERROR' as const; readonly type = 'SCRAPING_ERROR' as const; constructor(message: string, cause?: unknown) { super(`Service error: ${message}`, cause); } } // ===== Error Mapping and Utilities ===== const errorTypeToClass = { SCRAPING_ERROR: ServiceError, CHAPTER_NOT_FOUND: ChapterNotFoundError, CONTENT_NOT_FOUND: ContentNotFoundError, UNSUPPORTED_SITE: UnsupportedSiteError, BROWSER_ERROR: BrowserError, TIMEOUT_ERROR: TimeoutError, VALIDATION_ERROR: ValidationError, RATE_LIMIT_ERROR: RateLimitError, } as const; /** * Creates appropriate error instance from API error response */ export function createErrorFromApiResponse(apiError: ApiError): ScrapingServiceError { const { code, message } = apiError.error; const ErrorClass = errorTypeToClass[code as ErrorType]; if (ErrorClass) { return new ErrorClass(message); } // Fallback to generic service error return new ServiceError(`API Error [${code}]: ${message}`); } /** * Type guard to check if error is a scraping service error */ export function isScrapingServiceError(error: unknown): error is ScrapingServiceError { return error instanceof ScrapingServiceError; } /** * Type guard to check if error is a specific error type */ export function isErrorOfType<T extends ScrapingServiceError>( error: unknown, ErrorClass: new (...args: unknown[]) => T ): error is T { return error instanceof ErrorClass; } /** * Extract error message from various error types */ export function getErrorMessage(error: unknown): string { if (isScrapingServiceError(error)) { return error.message; } if (error instanceof Error) { return error.message; } if (typeof error === 'string') { return error; } return 'Unknown error occurred'; } /** * HTTP status code mapping for errors */ export const httpStatusMap: Record<ErrorCode, number> = { INTERNAL_SERVER_ERROR: 500, NOT_FOUND: 404, BAD_REQUEST: 400, UNSUPPORTED_SITE: 400, CONTENT_NOT_FOUND: 404, BROWSER_ERROR: 500, TIMEOUT_ERROR: 408, RATE_LIMIT_EXCEEDED: 429, };