UNPKG

@orchard9ai/error-handling

Version:

Federated error handling package with go-core-http-toolkit format support and logging integration

204 lines (173 loc) 5.63 kB
import { ErrorHandler } from '../core/ErrorHandler.js'; import { parseHttpError, parseFetchError, parseUnknownError } from '../utils/errorParsers.js'; import { createUserFriendlyMessage, createRetryAction, createSupportAction } from '../utils/displayHelpers.js'; import type { ApiError, ErrorContext, DisplayError, ErrorHandlerConfig } from '../types/index.js'; /** * Specialized error handler for API requests */ export class ApiErrorHandler extends ErrorHandler { constructor(config: Partial<ErrorHandlerConfig> = {}) { super({ loggerName: 'api-error-handler', ...config }); } /** * Handle fetch API errors */ async handleFetchError( response: Response, context: ErrorContext = {} ): Promise<DisplayError> { let responseText: string; try { responseText = await response.text(); } catch { responseText = ''; } const apiError = parseHttpError(response, responseText); // Add HTTP-specific context const enrichedContext: ErrorContext = { ...context, action: 'http_request', metadata: { url: response.url, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()) } }; return this.handleApiError(apiError, enrichedContext); } /** * Handle network errors (fetch failures) */ override handleNetworkError(error: Error, context: ErrorContext = {}): DisplayError { const apiError = parseFetchError(error); const enrichedContext: ErrorContext = { ...context, action: 'network_request', metadata: { errorName: error.name, errorMessage: error.message } }; return this.handleApiError(apiError, enrichedContext); } /** * Handle unknown errors */ handleUnknownError(error: unknown, context: ErrorContext = {}): DisplayError { const apiError = parseUnknownError(error); const enrichedContext: ErrorContext = { ...context, action: 'unknown_error', metadata: { errorType: typeof error } }; return this.handleApiError(apiError, enrichedContext); } /** * Create a wrapper for fetch that handles errors */ createFetchWrapper(baseUrl: string = ''): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> { return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => { try { const url = typeof input === 'string' ? `${baseUrl}${input}` : input; const response = await fetch(url, init); if (!response.ok) { const displayError = await this.handleFetchError(response, { component: 'fetch-wrapper', action: 'api-request' }); // Throw a specialized error that includes display information const error = new ApiRequestError( `HTTP ${response.status}: ${response.statusText}`, response.status, displayError ); throw error; } return response; } catch (error) { if (error instanceof ApiRequestError) { throw error; } const displayError = this.handleNetworkError(error as Error, { component: 'fetch-wrapper', action: 'network-request' }); throw new ApiRequestError( (error as Error).message, 0, displayError ); } }; } /** * Enhanced transform with API-specific logic */ override transformForDisplay(error: ApiError): DisplayError { const baseDisplay = super.transformForDisplay(error); // Add API-specific actions const actions = [...(baseDisplay.actions || [])]; // Add support action for server errors if (error.code === 'INTERNAL_ERROR' || error.code === 'SERVICE_UNAVAILABLE') { actions.push(createSupportAction()); } // Add retry action for retryable errors if (baseDisplay.retryable) { actions.unshift(createRetryAction(() => { // Apps should override this with their retry logic })); } return { ...baseDisplay, message: createUserFriendlyMessage(error), actions }; } } /** * Specialized error class for API requests */ export class ApiRequestError extends Error { public readonly statusCode: number; public readonly displayError: DisplayError; constructor(message: string, statusCode: number, displayError: DisplayError) { super(message); this.name = 'ApiRequestError'; this.statusCode = statusCode; this.displayError = displayError; } } /** * Create a global API error handler instance */ let globalApiErrorHandler: ApiErrorHandler | null = null; export function getGlobalApiErrorHandler(): ApiErrorHandler { if (!globalApiErrorHandler) { globalApiErrorHandler = new ApiErrorHandler(); } return globalApiErrorHandler; } /** * Configure the global API error handler */ export function configureGlobalApiErrorHandler(config: Partial<ErrorHandlerConfig>): void { globalApiErrorHandler = new ApiErrorHandler(config); } /** * Convenience function to handle API errors */ export function handleApiError(error: ApiError, context?: ErrorContext): DisplayError { return getGlobalApiErrorHandler().handleApiError(error, context); } /** * Convenience function to handle fetch errors */ export async function handleFetchError(response: Response, context?: ErrorContext): Promise<DisplayError> { return getGlobalApiErrorHandler().handleFetchError(response, context); }