UNPKG

@orchard9ai/error-handling

Version:

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

156 lines (140 loc) 3.75 kB
import type { ApiError } from '../types/index.js'; import { sanitizeErrorMessage } from './sanitize.js'; /** * Parse error from HTTP response */ export function parseHttpError(response: Response, responseText?: string): ApiError { try { // Try to parse as JSON first if (responseText) { const parsed = JSON.parse(responseText); // Check if it matches our API error format if (isApiError(parsed)) { return parsed; } // Transform other formats if (parsed.message) { return { error: parsed.message, code: parsed.code || getCodeFromStatus(response.status) }; } } } catch { // JSON parsing failed, fall back to status-based error } // Generate error from HTTP status return { error: getMessageFromStatus(response.status), code: getCodeFromStatus(response.status) }; } /** * Parse error from fetch error */ export function parseFetchError(error: Error): ApiError { if (error.name === 'AbortError') { return { error: 'Request was cancelled', code: 'REQUEST_CANCELLED' }; } if (error.name === 'TypeError' && error.message.includes('fetch')) { return { error: 'Network connection failed', code: 'NETWORK_ERROR', details: { originalMessage: error.message } }; } return { error: sanitizeErrorMessage(error.message || 'An unknown error occurred'), code: 'UNKNOWN_ERROR', details: { name: sanitizeErrorMessage(error.name) } }; } /** * Parse error from any thrown value */ export function parseUnknownError(error: unknown): ApiError { if (error instanceof Error) { return parseFetchError(error); } if (typeof error === 'string') { return { error, code: 'STRING_ERROR' }; } if (typeof error === 'object' && error !== null) { const obj = error as Record<string, unknown>; if (isApiError(obj)) { return obj; } return { error: (obj as any)['message'] || 'An error occurred', code: (obj as any)['code'] || 'OBJECT_ERROR', details: (obj as any)['details'] || {} }; } return { error: 'An unknown error occurred', code: 'UNKNOWN_ERROR', details: { type: typeof error, value: String(error) } }; } /** * Type guard for API error format */ export function isApiError(obj: unknown): obj is ApiError { return ( typeof obj === 'object' && obj !== null && 'error' in obj && typeof (obj as any).error === 'string' ); } /** * Get error code from HTTP status */ function getCodeFromStatus(status: number): string { switch (status) { case 400: return 'BAD_REQUEST'; case 401: return 'UNAUTHORIZED'; case 403: return 'FORBIDDEN'; case 404: return 'NOT_FOUND'; case 409: return 'CONFLICT'; case 422: return 'VALIDATION_ERROR'; case 429: return 'RATE_LIMIT_EXCEEDED'; case 500: return 'INTERNAL_ERROR'; case 502: return 'BAD_GATEWAY'; case 503: return 'SERVICE_UNAVAILABLE'; case 504: return 'TIMEOUT'; default: return 'HTTP_ERROR'; } } /** * Get error message from HTTP status */ function getMessageFromStatus(status: number): string { switch (status) { case 400: return 'Bad request'; case 401: return 'Authentication required'; case 403: return 'Access denied'; case 404: return 'Not found'; case 409: return 'Conflict'; case 422: return 'Validation failed'; case 429: return 'Too many requests'; case 500: return 'Internal server error'; case 502: return 'Bad gateway'; case 503: return 'Service unavailable'; case 504: return 'Request timeout'; default: return `HTTP error ${status}`; } }