export type WidgetErrorCode = | "access_denied" | "configuration_unavailable" | "invalid_response" | "rate_limited" | "service_unavailable" | "stream_error" | "stream_interrupted"; export class WidgetError extends Error { readonly code: WidgetErrorCode; readonly status?: number; constructor(code: WidgetErrorCode, options: { status?: number } = {}) { super(code); this.name = "WidgetError"; this.code = code; if (options.status !== undefined) this.status = options.status; } } /** * Maps internal failures to bounded visitor-safe copy. Provider payloads and * raw exception messages never cross this presentation boundary. */ export function getWidgetErrorMessage(error: unknown): string { if (isAbortError(error)) return "Richiesta interrotta."; if (!(error instanceof WidgetError)) { return "La connessione si è interrotta. Riprova tra poco."; } switch (error.code) { case "access_denied": return "Akintu non è disponibile su questo sito. Verifica l’installazione."; case "rate_limited": return "Sono arrivate troppe richieste. Attendi un momento e riprova."; case "service_unavailable": return "Il collega digitale è temporaneamente non disponibile. Riprova tra poco."; case "stream_error": case "stream_interrupted": return "La risposta si è interrotta. Puoi inviare di nuovo la domanda."; case "configuration_unavailable": case "invalid_response": return "Akintu non è disponibile in questo momento. Riprova più tardi."; } } /** Normalizes only HTTP states that have distinct visitor recovery actions. */ export function mapHttpError(status: number): WidgetError { if (status === 401 || status === 403) { return new WidgetError("access_denied", { status }); } if (status === 429) { return new WidgetError("rate_limited", { status }); } if (status === 503) { return new WidgetError("service_unavailable", { status }); } return new WidgetError("invalid_response", { status }); } export function isAbortError(error: unknown): boolean { return ( error instanceof DOMException && (error.name === "AbortError" || error.code === DOMException.ABORT_ERR) ); }