export type AnyRequestError = { [key in keyof RequestErrorMap]: RequestError; }[keyof RequestErrorMap]; export type AnyRequestErrorInput = { [key in RequestErrorType]: RequestErrorInput; }[RequestErrorType]; export interface RequestErrorInput { type: TType; reason: RequestErrorReason; code: RequestErrorCode; } export class RequestError extends Error { public readonly type: TType; public readonly reason: RequestErrorReason; public readonly code: RequestErrorCode; constructor(input: AnyRequestErrorInput, options: { cause?: Error }) { super("Request failed", options); this.name = RequestError.name; this.type = input.type as TType; this.reason = input.reason as RequestErrorReason; this.code = input.code; } get cause(): Error | undefined { return super.cause as Error | undefined; } } export const requestErrorMap = { certificate: { expired: ["CERT_HAS_EXPIRED"], hostnameMismatch: ["ERR_TLS_CERT_ALTNAME_INVALID", "CERT_COMMON_NAME_INVALID"], notYetValid: ["CERT_NOT_YET_VALID"], untrusted: ["DEPTH_ZERO_SELF_SIGNED_CERT", "UNABLE_TO_VERIFY_LEAF_SIGNATURE", "UNABLE_TO_GET_ISSUER_CERT_LOCALLY"], }, connection: { hostUnreachable: "EHOSTUNREACH", networkUnreachable: "ENETUNREACH", refused: "ECONNREFUSED", reset: "ECONNRESET", }, dns: { notFound: "ENOTFOUND", timeout: "EAI_AGAIN", noAnswer: "ENODATA", }, timeout: { aborted: "ECONNABORTED", timeout: "ETIMEDOUT", }, } as const satisfies Record>; type RequestErrorMap = typeof requestErrorMap; export type RequestErrorType = keyof RequestErrorMap; export type RequestErrorReason = keyof RequestErrorMap[TType]; export type AnyRequestErrorReason = { [key in keyof RequestErrorMap]: RequestErrorReason; }[keyof RequestErrorMap]; type ExtractInnerValues = { [K in keyof T]: T[K][keyof T[K]]; }[keyof T]; type FlattenStringOrStringArray = T extends (infer U)[] ? U : T; export type RequestErrorCode = FlattenStringOrStringArray>;