UNPKG

@ministryofjustice/hmpps-digital-prison-reporting-frontend

Version:

The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.

126 lines (123 loc) 4.36 kB
import { ZodError } from 'zod'; import logger from '../logger.js'; import { AggregatedValidationError } from './AggregatedValidationError.js'; class ErrorHandler { error; constructor(error) { this.error = error; logger.error(`Error: ${JSON.stringify(error)}`); } formatError() { return this.handleError(); } handleError() { const err = this.error; // 1. Zod if (err instanceof ZodError) { const issues = err.issues.map(i => i.message).join('. '); return { status: 500, userMessage: `Schema validation error: ${issues}`, }; } // 2. AggregatedValidationError if (err instanceof AggregatedValidationError) { return { status: 500, userMessage: [ err.message, ...err.details.map(detail => { const issues = detail.issues.map(i => i.message).join('. '); let prefix = ''; if (detail.type) prefix += `Type: '${detail.type}'. `; if (detail.id) prefix += `ID: '${detail.id}'. `; return `${prefix}Issues: ${issues}`; }), ], }; } // 3. SuperAgent / HTTP errors if (this.isSuperAgentError(err)) { const transportStatus = typeof err.status === 'number' ? err.status : (err.response?.status ?? 500); // 1. Prefer response.body if present let payload = err.response?.body; // 2. Otherwise try to parse text as JSON if (payload === undefined && 'text' in err) { payload = this.tryParseJson(err.text); } // 3. Extract actual payload (handles nested `data`) const extracted = this.extractPayload(payload); if (extracted) { const { userMessage, developerMessage, moreInfo, status } = extracted; return { status: status ?? transportStatus, ...(userMessage !== undefined && { userMessage }), ...(developerMessage !== undefined && { developerMessage }), ...(moreInfo !== undefined && { moreInfo }), }; } // 4. Transport-level fallback return { status: transportStatus, userMessage: err.message, }; } // 4. Generic JS error if (typeof err === 'object' && err !== null && 'message' in err && typeof err.message === 'string') { return { status: 500, userMessage: err.message, }; } // 5. Unknown return { status: 500, developerMessage: 'Unknown error', }; } /** * Extracts error payload from: * - direct payload * - nested payload.data */ extractPayload(payload) { if (this.isErrorPayload(payload)) return payload; if (typeof payload === 'object' && payload !== null && 'data' in payload) { const maybeData = payload.data; if (this.isErrorPayload(maybeData)) { return maybeData; } } return undefined; } isSuperAgentError(error) { if (typeof error !== 'object' || error === null) return false; const e = error; return (typeof e['message'] === 'string' && (typeof e['status'] === 'number' || typeof e['text'] !== 'undefined' || typeof e['response'] === 'object')); } isErrorPayload(value) { return (typeof value === 'object' && value !== null && ('userMessage' in value || 'developerMessage' in value || 'moreInfo' in value || 'status' in value)); } tryParseJson(value) { if (typeof value !== 'string') return undefined; try { return JSON.parse(value); } catch { return undefined; } } } export { ErrorHandler, ErrorHandler as default }; //# sourceMappingURL=ErrorHandler.js.map