import {z} from 'zod/mini' const StatusCodes: Record = { 400: 'Bad Request', 401: 'Unauthorized', 403: 'Forbidden', 404: 'Not Found', 408: 'Request Timeout', 409: 'Conflict', 429: 'Too Many Requests', 499: 'Client Closed Request', 500: 'Internal Server Error', } /** base error options interface */ export interface BaseErrorOpts { /** the cause of the error */ cause?: Error } /** * common base class for skypod errors * only difference is that we explicitly type cause to be Error */ export class BaseError extends Error { /** the cause of the error */ declare cause?: Error constructor(message: string, options?: BaseErrorOpts) { super(message, options) if (options?.cause) this.cause = normalizeError(options.cause) } } /** common base class for websocket errors */ export class ProtocolError extends BaseError { /** the HTTP status code representing this error */ status: number constructor(message: string, status: number, options?: BaseErrorOpts) { const statusText = StatusCodes[status] || 'Unknown' super(`${status} ${statusText}: ${message}`, options) this.name = this.constructor.name this.status = status } } /** check if an error is a protocol error with optional status check */ export function isProtocolError(e: Error, status?: number): e is ProtocolError { return e instanceof ProtocolError && (status === undefined || e.status == status) } /** * normalizes the given error into a protocol error * passes through input that is already protocol errors. */ export function normalizeProtocolError(cause: unknown, status = 500): ProtocolError { if (cause instanceof ProtocolError) return cause if (cause instanceof z.core.$ZodError) return new ProtocolError(z.prettifyError(cause), 400, {cause}) if (cause instanceof Error || cause instanceof DOMException) { if (cause.name === 'TimeoutError') return new ProtocolError('operation timed out', 408, {cause}) if (cause.name === 'AbortError') return new ProtocolError('operation was aborted', 499, {cause}) return new ProtocolError(cause.message, status, {cause}) } // fallback, unknown const options = cause == undefined ? undefined : {cause: normalizeError(cause)} return new ProtocolError(`Error! ${cause}`, status, options) } /** error wrapper for unknown errors (not an Error?) */ export class NormalizedError extends BaseError {} /** * wrap the given failure error into an error * passes through input that is already an Error object. */ export function normalizeError(failure: unknown): Error { if (failure instanceof Error) return failure return new NormalizedError(`unnormalized failure ${failure}`) }