podcast manager
1import { prettifyError, ZodError } from "zod/v4";
2
3export const StatusCodes: Record<number, string> = {
4 400: "Bad Request",
5 403: "Forbidden",
6 404: "Not Found",
7 408: "Request Timeout",
8 409: "Conflict",
9 429: "Too Many Requests",
10 499: "Client Closed Request",
11 500: "Internal Server Error",
12};
13
14export class BaseError extends Error {
15 status: number;
16
17 constructor(message: string, status: number, cause?: Error) {
18 const statusText = StatusCodes[status] || "Unknown";
19 super(`${status} ${statusText}: ${message}`);
20
21 this.name = this.constructor.name;
22 this.status = status;
23 this.cause = cause;
24 }
25}
26
27export class CancellationError extends BaseError {
28 constructor(reason: string, cause?: Error) {
29 super(`${reason} was cancelled`, 499, cause);
30 }
31}
32
33export class AuthError extends BaseError {
34 constructor(reason: string, cause?: Error) {
35 super(`authentication failed: ${reason}`, 403, cause);
36 }
37}
38
39export class ValidationError extends BaseError {
40 constructor(zodError: ZodError, cause?: Error) {
41 super(`validation failed: ${prettifyError(zodError)}`, 400, cause);
42 }
43}
44
45export class NotFoundError extends BaseError {
46 constructor(resource: string, cause?: Error) {
47 super(`${resource} not found`, 404, cause);
48 }
49}
50
51export class ConflictError extends BaseError {
52 constructor(reason: string, cause?: Error) {
53 super(`conflict: ${reason}`, 409, cause);
54 }
55}
56
57export class RateLimitError extends BaseError {
58 constructor(reason: string, cause?: Error) {
59 super(`rate limit exceeded: ${reason}`, 429, cause);
60 }
61}
62
63export class NormalizedError extends Error {
64 value: unknown;
65 constructor(value: unknown, cause?: Error) {
66 super(cause?.message || String(value));
67 this.value = value;
68 }
69}
70
71export function normalizeError(value: unknown) {
72 if (value instanceof Error) {
73 return value;
74 } else {
75 return new NormalizedError(value);
76 }
77}