export type ErrorCatalog = Record; type ErrorLike = { code?: unknown; status?: unknown; message?: unknown; rawMessage?: unknown; }; export type ResolveErrorMessageOptions = { code?: string | null; status?: number | null; context?: string; fallbackMessage?: string | null; }; export type CreateErrorResolverConfig = { catalog: ErrorCatalog; fallbackCode?: string; defaultContext?: string; contextOverrides?: Record>>; inferCodeFromStatus?: (status?: number | null) => string | undefined; inferCodeFromLegacyMessage?: (message?: string | null) => string | undefined; }; export function createErrorResolver(config: CreateErrorResolverConfig) { const { catalog, fallbackCode, defaultContext = 'default', contextOverrides = {}, inferCodeFromStatus, inferCodeFromLegacyMessage, } = config; const knownCodes = new Set(Object.keys(catalog)); function isKnownErrorCode(value: string): boolean { return knownCodes.has(value); } function normalizeErrorCode(code?: string | null): string | undefined { if (!code) { return undefined; } return isKnownErrorCode(code) ? code : undefined; } function inferErrorCodeFromStatus(status?: number | null): string | undefined { return inferCodeFromStatus?.(status); } function resolveErrorMessage(options: ResolveErrorMessageOptions): string { const { code, status, context = defaultContext, fallbackMessage } = options; const resolvedCode = normalizeErrorCode(code) ?? inferCodeFromLegacyMessage?.(fallbackMessage) ?? inferErrorCodeFromStatus(status); if (resolvedCode) { const contextMessage = contextOverrides[context]?.[resolvedCode]; if (contextMessage) { return contextMessage; } const catalogMessage = catalog[resolvedCode]; if (catalogMessage) { return catalogMessage; } } const statusCode = inferErrorCodeFromStatus(status); if (statusCode) { const contextMessage = contextOverrides[context]?.[statusCode]; if (contextMessage) { return contextMessage; } const catalogMessage = catalog[statusCode]; if (catalogMessage) { return catalogMessage; } } if (fallbackCode && catalog[fallbackCode]) { return catalog[fallbackCode]; } if (fallbackMessage) { return fallbackMessage; } return 'Request failed. Please try again.'; } function resolveOptionalErrorMessage( code?: string | null, context: string = defaultContext, ): string | undefined { if (!code) { return undefined; } return resolveErrorMessage({ code, context }); } function toErrorMessage(err: unknown, context: string = defaultContext): string { if (err && typeof err === 'object') { const errorLike = err as ErrorLike; const code = typeof errorLike.code === 'string' ? errorLike.code : undefined; const status = typeof errorLike.status === 'number' ? errorLike.status : undefined; const rawMessage = typeof errorLike.rawMessage === 'string' ? errorLike.rawMessage : undefined; const message = typeof errorLike.message === 'string' ? errorLike.message : undefined; return resolveErrorMessage({ code, status, context, fallbackMessage: rawMessage ?? message, }); } return resolveErrorMessage({ context }); } return { isKnownErrorCode, inferErrorCodeFromStatus, resolveErrorMessage, resolveOptionalErrorMessage, toErrorMessage, }; }