/** * API Error Types and Utilities * * Centralized error types for API client layer. * Provides typed error handling across all API modules. * * @layer Infrastructure - API Client */ /** * API Error Code Types * * Categorizes errors by type for better error handling */ export enum ApiErrorCode { NETWORK_ERROR = 'NETWORK_ERROR', AUTH_ERROR = 'AUTH_ERROR', VALIDATION_ERROR = 'VALIDATION_ERROR', NOT_FOUND = 'NOT_FOUND', FORBIDDEN = 'FORBIDDEN', CONFLICT = 'CONFLICT', SERVER_ERROR = 'SERVER_ERROR', TIMEOUT = 'TIMEOUT', UNKNOWN = 'UNKNOWN', } /** * API Error * * Extended error class with status code, error code, and original error context */ export class ApiError extends Error { /** * HTTP status code (e.g., 404, 500) */ public readonly statusCode?: number; /** * Categorized error code for application-level handling */ public readonly code: ApiErrorCode; /** * Original error object from axios or other source */ public readonly originalError?: unknown; /** * Additional error details (validation errors, etc.) */ public readonly details?: unknown; constructor( message: string, code: ApiErrorCode = ApiErrorCode.UNKNOWN, statusCode?: number, originalError?: unknown, details?: unknown ) { super(message); this.name = 'ApiError'; this.code = code; this.statusCode = statusCode; this.originalError = originalError; this.details = details; // Maintains proper stack trace for where error was thrown if (Error.captureStackTrace) { Error.captureStackTrace(this, ApiError); } } /** * Check if error is an authentication error */ isAuthError(): boolean { return this.code === ApiErrorCode.AUTH_ERROR; } /** * Check if error is a validation error */ isValidationError(): boolean { return this.code === ApiErrorCode.VALIDATION_ERROR; } /** * Check if error is a network error */ isNetworkError(): boolean { return this.code === ApiErrorCode.NETWORK_ERROR; } /** * Check if error is a server error */ isServerError(): boolean { return this.code === ApiErrorCode.SERVER_ERROR; } /** * Convert error to JSON-serializable object */ toJSON(): Record { return { name: this.name, message: this.message, code: this.code, statusCode: this.statusCode, details: this.details, }; } } /** * Error Factory Functions * * Factory functions for creating typed ApiError instances */ /** * Create network error (no response from server) */ export function createNetworkError( message = 'Network error: Unable to connect to server', originalError?: unknown ): ApiError { return new ApiError( message, ApiErrorCode.NETWORK_ERROR, undefined, originalError ); } /** * Create authentication error (401, 403) */ export function createAuthError( message = 'Authentication failed', statusCode = 401, originalError?: unknown ): ApiError { return new ApiError(message, ApiErrorCode.AUTH_ERROR, statusCode, originalError); } /** * Create validation error (400) */ export function createValidationError( message = 'Validation failed', details?: unknown, originalError?: unknown ): ApiError { return new ApiError( message, ApiErrorCode.VALIDATION_ERROR, 400, originalError, details ); } /** * Create not found error (404) */ export function createNotFoundError( message = 'Resource not found', originalError?: unknown ): ApiError { return new ApiError(message, ApiErrorCode.NOT_FOUND, 404, originalError); } /** * Create forbidden error (403) */ export function createForbiddenError( message = 'Access forbidden', originalError?: unknown ): ApiError { return new ApiError(message, ApiErrorCode.FORBIDDEN, 403, originalError); } /** * Create conflict error (409) */ export function createConflictError( message = 'Resource conflict', originalError?: unknown ): ApiError { return new ApiError(message, ApiErrorCode.CONFLICT, 409, originalError); } /** * Create server error (500-599) */ export function createServerError( message = 'Internal server error', statusCode = 500, originalError?: unknown ): ApiError { return new ApiError( message, ApiErrorCode.SERVER_ERROR, statusCode, originalError ); } /** * Create timeout error */ export function createTimeoutError( message = 'Request timeout', originalError?: unknown ): ApiError { return new ApiError(message, ApiErrorCode.TIMEOUT, 408, originalError); } /** * Map HTTP status code to ApiErrorCode */ export function mapStatusToErrorCode(statusCode: number): ApiErrorCode { if (statusCode === 401 || statusCode === 403) { return ApiErrorCode.AUTH_ERROR; } if (statusCode === 400) { return ApiErrorCode.VALIDATION_ERROR; } if (statusCode === 404) { return ApiErrorCode.NOT_FOUND; } if (statusCode === 409) { return ApiErrorCode.CONFLICT; } if (statusCode === 408) { return ApiErrorCode.TIMEOUT; } if (statusCode >= 500) { return ApiErrorCode.SERVER_ERROR; } return ApiErrorCode.UNKNOWN; }