error-shield 1.2.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AppError: () => AppError,
24
+ Errors: () => Errors,
25
+ asyncHandler: () => asyncHandler,
26
+ createErrorResponse: () => createErrorResponse,
27
+ default: () => index_default,
28
+ expressErrorHandler: () => expressErrorHandler,
29
+ formatError: () => formatError,
30
+ handleError: () => handleError,
31
+ withRetry: () => withRetry
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+ var AppError = class _AppError extends Error {
35
+ code;
36
+ statusCode;
37
+ context;
38
+ isOperational;
39
+ constructor(message, statusCode = 500, code, context, cause) {
40
+ super(message, cause ? { cause } : void 0);
41
+ this.name = this.constructor.name;
42
+ this.statusCode = statusCode;
43
+ this.code = code;
44
+ this.context = context;
45
+ this.isOperational = true;
46
+ if (Error.captureStackTrace) {
47
+ Error.captureStackTrace(this, this.constructor);
48
+ }
49
+ }
50
+ /**
51
+ * Wraps an existing error as the cause of a new `AppError`.
52
+ *
53
+ * This is a convenience factory for error chaining — it creates a new
54
+ * `AppError` whose `.cause` is set to the original error.
55
+ *
56
+ * @param originalError - The caught error to wrap
57
+ * @param message - Human-readable description of the higher-level failure
58
+ * @param statusCode - HTTP status code (default: 500)
59
+ * @param code - Machine-readable error code
60
+ * @param context - Additional context metadata
61
+ * @returns A new `AppError` with `originalError` as its cause
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * try {
66
+ * await db.query('SELECT ...');
67
+ * } catch (err) {
68
+ * throw AppError.wrap(err, 'Database query failed', 500, 'DB_ERROR');
69
+ * }
70
+ * ```
71
+ */
72
+ static wrap(originalError, message, statusCode = 500, code, context) {
73
+ return new _AppError(message, statusCode, code, context, originalError);
74
+ }
75
+ };
76
+ function formatError(error, options = {}) {
77
+ const {
78
+ includeStack = false,
79
+ includeTimestamp = true,
80
+ context: optionsContext = {}
81
+ } = options;
82
+ const mergedContext = error instanceof AppError ? { ...error.context, ...optionsContext } : optionsContext;
83
+ const errorDetails = {
84
+ message: error.message,
85
+ ...includeTimestamp && { timestamp: (/* @__PURE__ */ new Date()).toISOString() },
86
+ ...includeStack && error.stack && { stack: error.stack },
87
+ ...error instanceof AppError && {
88
+ code: error.code,
89
+ statusCode: error.statusCode
90
+ },
91
+ ...Object.keys(mergedContext).length > 0 && { context: mergedContext }
92
+ };
93
+ if (error.cause && error.cause instanceof Error) {
94
+ errorDetails.cause = formatError(error.cause, {
95
+ includeStack,
96
+ includeTimestamp: false
97
+ // only top-level gets timestamp
98
+ });
99
+ }
100
+ return errorDetails;
101
+ }
102
+ function handleError(error, options = {}) {
103
+ const errorDetails = formatError(error, options);
104
+ if (options.logger) {
105
+ options.logger(errorDetails);
106
+ }
107
+ return errorDetails;
108
+ }
109
+ function asyncHandler(fn) {
110
+ return ((...args) => {
111
+ return Promise.resolve(fn(...args)).catch((error) => {
112
+ throw error;
113
+ });
114
+ });
115
+ }
116
+ function expressErrorHandler(options = {}) {
117
+ return (err, req, res, next) => {
118
+ const errorDetails = handleError(err, {
119
+ ...options,
120
+ context: {
121
+ ...options.context,
122
+ method: req.method,
123
+ path: req.path,
124
+ ip: req.ip
125
+ }
126
+ });
127
+ const statusCode = err instanceof AppError ? err.statusCode || 500 : 500;
128
+ if (options.format === "string") {
129
+ res.status(statusCode).send(errorDetails.message);
130
+ } else {
131
+ res.status(statusCode).json(errorDetails);
132
+ }
133
+ };
134
+ }
135
+ function createErrorResponse(message, statusCode = 500, code, context) {
136
+ return new AppError(message, statusCode, code, context);
137
+ }
138
+ function calculateDelay(attempt, options) {
139
+ const { backoff, initialDelay, maxDelay, jitter } = options;
140
+ let delay;
141
+ switch (backoff) {
142
+ case "exponential":
143
+ delay = initialDelay * Math.pow(2, attempt - 1);
144
+ break;
145
+ case "linear":
146
+ delay = initialDelay * attempt;
147
+ break;
148
+ case "fixed":
149
+ delay = initialDelay;
150
+ break;
151
+ default:
152
+ delay = initialDelay;
153
+ }
154
+ delay = Math.min(delay, maxDelay);
155
+ if (jitter) {
156
+ const jitterAmount = delay * 0.25;
157
+ delay = delay - jitterAmount + Math.random() * jitterAmount * 2;
158
+ }
159
+ return Math.round(delay);
160
+ }
161
+ async function withRetry(fn, options = {}) {
162
+ const {
163
+ maxRetries = 3,
164
+ backoff = "exponential",
165
+ initialDelay = 1e3,
166
+ maxDelay = 3e4,
167
+ retryIf,
168
+ onRetry,
169
+ jitter = true
170
+ } = options;
171
+ const errors = [];
172
+ let lastError;
173
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
174
+ try {
175
+ return await fn();
176
+ } catch (err) {
177
+ const error = err instanceof Error ? err : new Error(String(err));
178
+ lastError = error;
179
+ errors.push(error);
180
+ if (attempt === maxRetries) {
181
+ break;
182
+ }
183
+ if (retryIf && !retryIf(error)) {
184
+ break;
185
+ }
186
+ if (onRetry) {
187
+ onRetry(error, attempt + 1);
188
+ }
189
+ const delay = calculateDelay(attempt + 1, {
190
+ backoff,
191
+ initialDelay,
192
+ maxDelay,
193
+ jitter
194
+ });
195
+ await new Promise((resolve) => setTimeout(resolve, delay));
196
+ }
197
+ }
198
+ const finalError = lastError;
199
+ finalError.attempts = errors;
200
+ throw finalError;
201
+ }
202
+ var Errors = {
203
+ // 4xx Client Errors
204
+ badRequest: (message, context) => new AppError(message, 400, "BAD_REQUEST", context),
205
+ unauthorized: (message = "Unauthorized", context) => new AppError(message, 401, "UNAUTHORIZED", context),
206
+ paymentRequired: (message = "Payment Required", context) => new AppError(message, 402, "PAYMENT_REQUIRED", context),
207
+ forbidden: (message = "Forbidden", context) => new AppError(message, 403, "FORBIDDEN", context),
208
+ notFound: (message = "Not Found", context) => new AppError(message, 404, "NOT_FOUND", context),
209
+ methodNotAllowed: (message = "Method Not Allowed", context) => new AppError(message, 405, "METHOD_NOT_ALLOWED", context),
210
+ notAcceptable: (message = "Not Acceptable", context) => new AppError(message, 406, "NOT_ACCEPTABLE", context),
211
+ proxyAuthRequired: (message = "Proxy Authentication Required", context) => new AppError(message, 407, "PROXY_AUTH_REQUIRED", context),
212
+ requestTimeout: (message = "Request Timeout", context) => new AppError(message, 408, "REQUEST_TIMEOUT", context),
213
+ conflict: (message, context) => new AppError(message, 409, "CONFLICT", context),
214
+ gone: (message = "Gone", context) => new AppError(message, 410, "GONE", context),
215
+ lengthRequired: (message = "Length Required", context) => new AppError(message, 411, "LENGTH_REQUIRED", context),
216
+ preconditionFailed: (message = "Precondition Failed", context) => new AppError(message, 412, "PRECONDITION_FAILED", context),
217
+ payloadTooLarge: (message = "Payload Too Large", context) => new AppError(message, 413, "PAYLOAD_TOO_LARGE", context),
218
+ uriTooLong: (message = "URI Too Long", context) => new AppError(message, 414, "URI_TOO_LONG", context),
219
+ unsupportedMediaType: (message = "Unsupported Media Type", context) => new AppError(message, 415, "UNSUPPORTED_MEDIA_TYPE", context),
220
+ rangeNotSatisfiable: (message = "Range Not Satisfiable", context) => new AppError(message, 416, "RANGE_NOT_SATISFIABLE", context),
221
+ expectationFailed: (message = "Expectation Failed", context) => new AppError(message, 417, "EXPECTATION_FAILED", context),
222
+ imATeapot: (message = "I'm a Teapot", context) => new AppError(message, 418, "IM_A_TEAPOT", context),
223
+ misdirectedRequest: (message = "Misdirected Request", context) => new AppError(message, 421, "MISDIRECTED_REQUEST", context),
224
+ unprocessableEntity: (message = "Unprocessable Entity", context) => new AppError(message, 422, "UNPROCESSABLE_ENTITY", context),
225
+ validationError: (message, context) => new AppError(message, 422, "VALIDATION_ERROR", context),
226
+ locked: (message = "Locked", context) => new AppError(message, 423, "LOCKED", context),
227
+ failedDependency: (message = "Failed Dependency", context) => new AppError(message, 424, "FAILED_DEPENDENCY", context),
228
+ tooEarly: (message = "Too Early", context) => new AppError(message, 425, "TOO_EARLY", context),
229
+ upgradeRequired: (message = "Upgrade Required", context) => new AppError(message, 426, "UPGRADE_REQUIRED", context),
230
+ preconditionRequired: (message = "Precondition Required", context) => new AppError(message, 428, "PRECONDITION_REQUIRED", context),
231
+ tooManyRequests: (message = "Too Many Requests", context) => new AppError(message, 429, "TOO_MANY_REQUESTS", context),
232
+ requestHeaderFieldsTooLarge: (message = "Request Header Fields Too Large", context) => new AppError(message, 431, "REQUEST_HEADER_FIELDS_TOO_LARGE", context),
233
+ unavailableForLegalReasons: (message = "Unavailable For Legal Reasons", context) => new AppError(message, 451, "UNAVAILABLE_FOR_LEGAL_REASONS", context),
234
+ // 5xx Server Errors
235
+ internalServerError: (message = "Internal Server Error", context) => new AppError(message, 500, "INTERNAL_SERVER_ERROR", context),
236
+ notImplemented: (message = "Not Implemented", context) => new AppError(message, 501, "NOT_IMPLEMENTED", context),
237
+ badGateway: (message = "Bad Gateway", context) => new AppError(message, 502, "BAD_GATEWAY", context),
238
+ serviceUnavailable: (message = "Service Unavailable", context) => new AppError(message, 503, "SERVICE_UNAVAILABLE", context),
239
+ gatewayTimeout: (message = "Gateway Timeout", context) => new AppError(message, 504, "GATEWAY_TIMEOUT", context),
240
+ httpVersionNotSupported: (message = "HTTP Version Not Supported", context) => new AppError(message, 505, "HTTP_VERSION_NOT_SUPPORTED", context),
241
+ variantAlsoNegotiates: (message = "Variant Also Negotiates", context) => new AppError(message, 506, "VARIANT_ALSO_NEGOTIATES", context),
242
+ insufficientStorage: (message = "Insufficient Storage", context) => new AppError(message, 507, "INSUFFICIENT_STORAGE", context),
243
+ loopDetected: (message = "Loop Detected", context) => new AppError(message, 508, "LOOP_DETECTED", context),
244
+ bandwidthLimitExceeded: (message = "Bandwidth Limit Exceeded", context) => new AppError(message, 509, "BANDWIDTH_LIMIT_EXCEEDED", context),
245
+ notExtended: (message = "Not Extended", context) => new AppError(message, 510, "NOT_EXTENDED", context),
246
+ networkAuthenticationRequired: (message = "Network Authentication Required", context) => new AppError(message, 511, "NETWORK_AUTHENTICATION_REQUIRED", context),
247
+ networkConnectTimeout: (message = "Network Connect Timeout", context) => new AppError(message, 599, "NETWORK_CONNECT_TIMEOUT", context)
248
+ };
249
+ var index_default = {
250
+ AppError,
251
+ formatError,
252
+ handleError,
253
+ asyncHandler,
254
+ expressErrorHandler,
255
+ createErrorResponse,
256
+ withRetry,
257
+ Errors
258
+ };
259
+ // Annotate the CommonJS export names for ESM import in node:
260
+ 0 && (module.exports = {
261
+ AppError,
262
+ Errors,
263
+ asyncHandler,
264
+ createErrorResponse,
265
+ expressErrorHandler,
266
+ formatError,
267
+ handleError,
268
+ withRetry
269
+ });
270
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["/**\n * Error Shield - A comprehensive error handling utility for Node.js & Express.js\n *\n * Provides utilities for error handling, formatting, logging, retry logic,\n * and error chaining with full TypeScript support.\n *\n * @packageDocumentation\n */\n\n// ─── Types & Interfaces ─────────────────────────────────────────────────────\n\n/**\n * Structured representation of an error with optional metadata.\n */\nexport interface ErrorDetails {\n message: string;\n code?: string;\n statusCode?: number;\n stack?: string;\n timestamp?: string;\n context?: Record<string, any>;\n /** Cause chain information when error chaining is used */\n cause?: ErrorDetails;\n}\n\n/**\n * Options for configuring error handling behavior.\n */\nexport interface ErrorHandlerOptions {\n includeStack?: boolean;\n includeTimestamp?: boolean;\n format?: 'json' | 'string';\n logger?: (error: ErrorDetails) => void;\n context?: Record<string, any>;\n}\n\nexport type ErrorFn = (message: string, context?: Record<string, any>) => AppError;\n\nexport type ErrorMap = Record<string, ErrorFn>;\n\n/**\n * Configuration options for the retry utility.\n */\nexport interface RetryOptions {\n /** Maximum number of retry attempts (default: 3) */\n maxRetries?: number;\n /** Backoff strategy between retries (default: 'exponential') */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Initial delay in milliseconds before the first retry (default: 1000) */\n initialDelay?: number;\n /** Maximum delay in milliseconds between retries (default: 30000) */\n maxDelay?: number;\n /**\n * Optional predicate to determine whether a retry should be attempted.\n * If provided and returns `false`, the error is thrown immediately.\n */\n retryIf?: (error: Error) => boolean;\n /** Optional callback invoked before each retry attempt */\n onRetry?: (error: Error, attempt: number) => void;\n /** Whether to add random jitter to the delay to prevent thundering herd (default: true) */\n jitter?: boolean;\n}\n\n// ─── AppError Class ──────────────────────────────────────────────────────────\n\n/**\n * Custom Error class with additional properties for structured error handling.\n *\n * Supports error chaining via the native ES2022 `Error.cause` feature.\n *\n * @example\n * ```ts\n * const error = new AppError('Something broke', 500, 'INTERNAL', { userId: 123 });\n *\n * // With error chaining\n * try {\n * await fetchData();\n * } catch (err) {\n * throw new AppError('Failed to fetch data', 502, 'FETCH_FAILED', undefined, err);\n * }\n * ```\n */\nexport class AppError extends Error {\n public code?: string;\n public statusCode?: number;\n public context?: Record<string, any>;\n public isOperational: boolean;\n\n constructor(\n message: string,\n statusCode: number = 500,\n code?: string,\n context?: Record<string, any>,\n cause?: Error\n ) {\n super(message, cause ? { cause } : undefined);\n this.name = this.constructor.name;\n this.statusCode = statusCode;\n this.code = code;\n this.context = context;\n this.isOperational = true;\n if ((Error as any).captureStackTrace) {\n (Error as any).captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Wraps an existing error as the cause of a new `AppError`.\n *\n * This is a convenience factory for error chaining — it creates a new\n * `AppError` whose `.cause` is set to the original error.\n *\n * @param originalError - The caught error to wrap\n * @param message - Human-readable description of the higher-level failure\n * @param statusCode - HTTP status code (default: 500)\n * @param code - Machine-readable error code\n * @param context - Additional context metadata\n * @returns A new `AppError` with `originalError` as its cause\n *\n * @example\n * ```ts\n * try {\n * await db.query('SELECT ...');\n * } catch (err) {\n * throw AppError.wrap(err, 'Database query failed', 500, 'DB_ERROR');\n * }\n * ```\n */\n static wrap(\n originalError: Error,\n message: string,\n statusCode: number = 500,\n code?: string,\n context?: Record<string, any>\n ): AppError {\n return new AppError(message, statusCode, code, context, originalError);\n }\n}\n\n// ─── Core Utilities ──────────────────────────────────────────────────────────\n\n/**\n * Formats an error into a structured {@link ErrorDetails} object.\n *\n * When the error has a `.cause`, the cause chain is recursively formatted\n * and included in the output.\n */\nexport function formatError(\n error: Error | AppError,\n options: ErrorHandlerOptions = {}\n): ErrorDetails {\n const {\n includeStack = false,\n includeTimestamp = true,\n context: optionsContext = {},\n } = options;\n\n const mergedContext = error instanceof AppError\n ? { ...error.context, ...optionsContext }\n : optionsContext;\n\n const errorDetails: ErrorDetails = {\n message: error.message,\n ...(includeTimestamp && { timestamp: new Date().toISOString() }),\n ...(includeStack && error.stack && { stack: error.stack }),\n ...(error instanceof AppError && {\n code: error.code,\n statusCode: error.statusCode,\n }),\n ...(Object.keys(mergedContext).length > 0 && { context: mergedContext }),\n };\n\n // Recursively format the cause chain\n if (error.cause && error.cause instanceof Error) {\n errorDetails.cause = formatError(error.cause, {\n includeStack,\n includeTimestamp: false, // only top-level gets timestamp\n });\n }\n\n return errorDetails;\n}\n\n/**\n * Handles errors with optional logging and formatting.\n */\nexport function handleError(\n error: Error | AppError,\n options: ErrorHandlerOptions = {}\n): ErrorDetails {\n const errorDetails = formatError(error, options);\n\n if (options.logger) {\n options.logger(errorDetails);\n }\n\n return errorDetails;\n}\n\n/**\n * Async error wrapper — catches errors from async route handlers\n * and forwards them to Express's `next()` function.\n */\nexport function asyncHandler<T extends (...args: any[]) => Promise<any>>(\n fn: T\n): T {\n return ((...args: any[]) => {\n return Promise.resolve(fn(...args)).catch((error) => {\n throw error;\n });\n }) as T;\n}\n\n/**\n * Express.js error handler middleware factory.\n *\n * Returns a standard Express error-handling middleware that formats\n * the error and sends an appropriate JSON or string response.\n */\nexport function expressErrorHandler(\n options: ErrorHandlerOptions = {}\n) {\n return (\n err: Error | AppError,\n req: any,\n res: any,\n next: any\n ): void => {\n const errorDetails = handleError(err, {\n ...options,\n context: {\n ...options.context,\n method: req.method,\n path: req.path,\n ip: req.ip,\n },\n });\n\n const statusCode =\n err instanceof AppError ? err.statusCode || 500 : 500;\n\n if (options.format === 'string') {\n res.status(statusCode).send(errorDetails.message);\n } else {\n res.status(statusCode).json(errorDetails);\n }\n };\n}\n\n/**\n * Creates a standardized error response.\n */\nexport function createErrorResponse(\n message: string,\n statusCode: number = 500,\n code?: string,\n context?: Record<string, any>\n): AppError {\n return new AppError(message, statusCode, code, context);\n}\n\n// ─── Retry Utility ───────────────────────────────────────────────────────────\n\n/**\n * Calculates the delay for a given retry attempt based on the backoff strategy.\n * @internal\n */\nfunction calculateDelay(\n attempt: number,\n options: Required<Pick<RetryOptions, 'backoff' | 'initialDelay' | 'maxDelay' | 'jitter'>>\n): number {\n const { backoff, initialDelay, maxDelay, jitter } = options;\n\n let delay: number;\n\n switch (backoff) {\n case 'exponential':\n delay = initialDelay * Math.pow(2, attempt - 1);\n break;\n case 'linear':\n delay = initialDelay * attempt;\n break;\n case 'fixed':\n delay = initialDelay;\n break;\n default:\n delay = initialDelay;\n }\n\n // Cap at maxDelay\n delay = Math.min(delay, maxDelay);\n\n // Add jitter (±25% randomness)\n if (jitter) {\n const jitterAmount = delay * 0.25;\n delay = delay - jitterAmount + Math.random() * jitterAmount * 2;\n }\n\n return Math.round(delay);\n}\n\n/**\n * Executes an async function with automatic retries on failure.\n *\n * Supports exponential, linear, and fixed backoff strategies with\n * optional jitter, conditional retry predicates, and retry callbacks.\n *\n * If all retries are exhausted, the **last** error is thrown. The previous\n * attempt errors are accessible via the error's `.cause` chain and the\n * `attempts` property attached to the final error.\n *\n * @typeParam T - The return type of the async function\n * @param fn - The async function to execute with retries\n * @param options - Retry configuration options\n * @returns The result of the successful function execution\n *\n * @example\n * ```ts\n * const data = await withRetry(\n * () => fetch('https://api.example.com/data').then(r => r.json()),\n * {\n * maxRetries: 5,\n * backoff: 'exponential',\n * initialDelay: 500,\n * retryIf: (err) => err.message.includes('ECONNREFUSED'),\n * onRetry: (err, attempt) => console.log(`Retry ${attempt}: ${err.message}`),\n * }\n * );\n * ```\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<T> {\n const {\n maxRetries = 3,\n backoff = 'exponential',\n initialDelay = 1000,\n maxDelay = 30000,\n retryIf,\n onRetry,\n jitter = true,\n } = options;\n\n const errors: Error[] = [];\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n lastError = error;\n errors.push(error);\n\n // If this was the last attempt, break to throw\n if (attempt === maxRetries) {\n break;\n }\n\n // Check retryIf predicate\n if (retryIf && !retryIf(error)) {\n break;\n }\n\n // Invoke onRetry callback\n if (onRetry) {\n onRetry(error, attempt + 1);\n }\n\n // Wait before retrying\n const delay = calculateDelay(attempt + 1, {\n backoff,\n initialDelay,\n maxDelay,\n jitter,\n });\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n // Attach attempt history to the final error\n const finalError = lastError!;\n (finalError as any).attempts = errors;\n\n throw finalError;\n}\n\n// ─── Common Error Creators ───────────────────────────────────────────────────\n\n/**\n * Pre-built factory functions for common HTTP error responses.\n *\n * @example\n * ```ts\n * throw Errors.notFound('User not found', { userId: 42 });\n * throw Errors.unauthorized();\n * throw Errors.tooManyRequests('Rate limit exceeded');\n * ```\n */\nexport const Errors: ErrorMap = {\n // 4xx Client Errors\n badRequest: (message: string, context?: Record<string, any>) =>\n new AppError(message, 400, 'BAD_REQUEST', context),\n\n unauthorized: (message: string = 'Unauthorized', context?: Record<string, any>) =>\n new AppError(message, 401, 'UNAUTHORIZED', context),\n\n paymentRequired: (message: string = 'Payment Required', context?: Record<string, any>) =>\n new AppError(message, 402, 'PAYMENT_REQUIRED', context),\n\n forbidden: (message: string = 'Forbidden', context?: Record<string, any>) =>\n new AppError(message, 403, 'FORBIDDEN', context),\n\n notFound: (message: string = 'Not Found', context?: Record<string, any>) =>\n new AppError(message, 404, 'NOT_FOUND', context),\n\n methodNotAllowed: (message: string = 'Method Not Allowed', context?: Record<string, any>) =>\n new AppError(message, 405, 'METHOD_NOT_ALLOWED', context),\n\n notAcceptable: (message: string = 'Not Acceptable', context?: Record<string, any>) =>\n new AppError(message, 406, 'NOT_ACCEPTABLE', context),\n\n proxyAuthRequired: (message: string = 'Proxy Authentication Required', context?: Record<string, any>) =>\n new AppError(message, 407, 'PROXY_AUTH_REQUIRED', context),\n\n requestTimeout: (message: string = 'Request Timeout', context?: Record<string, any>) =>\n new AppError(message, 408, 'REQUEST_TIMEOUT', context),\n\n conflict: (message: string, context?: Record<string, any>) =>\n new AppError(message, 409, 'CONFLICT', context),\n\n gone: (message: string = 'Gone', context?: Record<string, any>) =>\n new AppError(message, 410, 'GONE', context),\n\n lengthRequired: (message: string = 'Length Required', context?: Record<string, any>) =>\n new AppError(message, 411, 'LENGTH_REQUIRED', context),\n\n preconditionFailed: (message: string = 'Precondition Failed', context?: Record<string, any>) =>\n new AppError(message, 412, 'PRECONDITION_FAILED', context),\n\n payloadTooLarge: (message: string = 'Payload Too Large', context?: Record<string, any>) =>\n new AppError(message, 413, 'PAYLOAD_TOO_LARGE', context),\n\n uriTooLong: (message: string = 'URI Too Long', context?: Record<string, any>) =>\n new AppError(message, 414, 'URI_TOO_LONG', context),\n\n unsupportedMediaType: (message: string = 'Unsupported Media Type', context?: Record<string, any>) =>\n new AppError(message, 415, 'UNSUPPORTED_MEDIA_TYPE', context),\n\n rangeNotSatisfiable: (message: string = 'Range Not Satisfiable', context?: Record<string, any>) =>\n new AppError(message, 416, 'RANGE_NOT_SATISFIABLE', context),\n\n expectationFailed: (message: string = 'Expectation Failed', context?: Record<string, any>) =>\n new AppError(message, 417, 'EXPECTATION_FAILED', context),\n\n imATeapot: (message: string = \"I'm a Teapot\", context?: Record<string, any>) =>\n new AppError(message, 418, 'IM_A_TEAPOT', context),\n\n misdirectedRequest: (message: string = 'Misdirected Request', context?: Record<string, any>) =>\n new AppError(message, 421, 'MISDIRECTED_REQUEST', context),\n\n unprocessableEntity: (message: string = 'Unprocessable Entity', context?: Record<string, any>) =>\n new AppError(message, 422, 'UNPROCESSABLE_ENTITY', context),\n\n validationError: (message: string, context?: Record<string, any>) =>\n new AppError(message, 422, 'VALIDATION_ERROR', context),\n\n locked: (message: string = 'Locked', context?: Record<string, any>) =>\n new AppError(message, 423, 'LOCKED', context),\n\n failedDependency: (message: string = 'Failed Dependency', context?: Record<string, any>) =>\n new AppError(message, 424, 'FAILED_DEPENDENCY', context),\n\n tooEarly: (message: string = 'Too Early', context?: Record<string, any>) =>\n new AppError(message, 425, 'TOO_EARLY', context),\n\n upgradeRequired: (message: string = 'Upgrade Required', context?: Record<string, any>) =>\n new AppError(message, 426, 'UPGRADE_REQUIRED', context),\n\n preconditionRequired: (message: string = 'Precondition Required', context?: Record<string, any>) =>\n new AppError(message, 428, 'PRECONDITION_REQUIRED', context),\n\n tooManyRequests: (message: string = 'Too Many Requests', context?: Record<string, any>) =>\n new AppError(message, 429, 'TOO_MANY_REQUESTS', context),\n\n requestHeaderFieldsTooLarge: (message: string = 'Request Header Fields Too Large', context?: Record<string, any>) =>\n new AppError(message, 431, 'REQUEST_HEADER_FIELDS_TOO_LARGE', context),\n\n unavailableForLegalReasons: (message: string = 'Unavailable For Legal Reasons', context?: Record<string, any>) =>\n new AppError(message, 451, 'UNAVAILABLE_FOR_LEGAL_REASONS', context),\n\n // 5xx Server Errors\n internalServerError: (message: string = 'Internal Server Error', context?: Record<string, any>) =>\n new AppError(message, 500, 'INTERNAL_SERVER_ERROR', context),\n\n notImplemented: (message: string = 'Not Implemented', context?: Record<string, any>) =>\n new AppError(message, 501, 'NOT_IMPLEMENTED', context),\n\n badGateway: (message: string = 'Bad Gateway', context?: Record<string, any>) =>\n new AppError(message, 502, 'BAD_GATEWAY', context),\n\n serviceUnavailable: (message: string = 'Service Unavailable', context?: Record<string, any>) =>\n new AppError(message, 503, 'SERVICE_UNAVAILABLE', context),\n\n gatewayTimeout: (message: string = 'Gateway Timeout', context?: Record<string, any>) =>\n new AppError(message, 504, 'GATEWAY_TIMEOUT', context),\n\n httpVersionNotSupported: (message: string = 'HTTP Version Not Supported', context?: Record<string, any>) =>\n new AppError(message, 505, 'HTTP_VERSION_NOT_SUPPORTED', context),\n\n variantAlsoNegotiates: (message: string = 'Variant Also Negotiates', context?: Record<string, any>) =>\n new AppError(message, 506, 'VARIANT_ALSO_NEGOTIATES', context),\n\n insufficientStorage: (message: string = 'Insufficient Storage', context?: Record<string, any>) =>\n new AppError(message, 507, 'INSUFFICIENT_STORAGE', context),\n\n loopDetected: (message: string = 'Loop Detected', context?: Record<string, any>) =>\n new AppError(message, 508, 'LOOP_DETECTED', context),\n\n bandwidthLimitExceeded: (message: string = 'Bandwidth Limit Exceeded', context?: Record<string, any>) =>\n new AppError(message, 509, 'BANDWIDTH_LIMIT_EXCEEDED', context),\n\n notExtended: (message: string = 'Not Extended', context?: Record<string, any>) =>\n new AppError(message, 510, 'NOT_EXTENDED', context),\n\n networkAuthenticationRequired: (message: string = 'Network Authentication Required', context?: Record<string, any>) =>\n new AppError(message, 511, 'NETWORK_AUTHENTICATION_REQUIRED', context),\n\n networkConnectTimeout: (message: string = 'Network Connect Timeout', context?: Record<string, any>) =>\n new AppError(message, 599, 'NETWORK_CONNECT_TIMEOUT', context),\n};\n\n// Default export\nexport default {\n AppError,\n formatError,\n handleError,\n asyncHandler,\n expressErrorHandler,\n createErrorResponse,\n withRetry,\n Errors,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkFO,IAAM,WAAN,MAAM,kBAAiB,MAAM;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEP,YACE,SACA,aAAqB,KACrB,MACA,SACA,OACA;AACA,UAAM,SAAS,QAAQ,EAAE,MAAM,IAAI,MAAS;AAC5C,SAAK,OAAO,KAAK,YAAY;AAC7B,SAAK,aAAa;AAClB,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,QAAK,MAAc,mBAAmB;AACpC,MAAC,MAAc,kBAAkB,MAAM,KAAK,WAAW;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,OAAO,KACL,eACA,SACA,aAAqB,KACrB,MACA,SACU;AACV,WAAO,IAAI,UAAS,SAAS,YAAY,MAAM,SAAS,aAAa;AAAA,EACvE;AACF;AAUO,SAAS,YACd,OACA,UAA+B,CAAC,GAClB;AACd,QAAM;AAAA,IACJ,eAAe;AAAA,IACf,mBAAmB;AAAA,IACnB,SAAS,iBAAiB,CAAC;AAAA,EAC7B,IAAI;AAEJ,QAAM,gBAAgB,iBAAiB,WACnC,EAAE,GAAG,MAAM,SAAS,GAAG,eAAe,IACtC;AAEJ,QAAM,eAA6B;AAAA,IACjC,SAAS,MAAM;AAAA,IACf,GAAI,oBAAoB,EAAE,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAAA,IAC9D,GAAI,gBAAgB,MAAM,SAAS,EAAE,OAAO,MAAM,MAAM;AAAA,IACxD,GAAI,iBAAiB,YAAY;AAAA,MAC/B,MAAM,MAAM;AAAA,MACZ,YAAY,MAAM;AAAA,IACpB;AAAA,IACA,GAAI,OAAO,KAAK,aAAa,EAAE,SAAS,KAAK,EAAE,SAAS,cAAc;AAAA,EACxE;AAGA,MAAI,MAAM,SAAS,MAAM,iBAAiB,OAAO;AAC/C,iBAAa,QAAQ,YAAY,MAAM,OAAO;AAAA,MAC5C;AAAA,MACA,kBAAkB;AAAA;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKO,SAAS,YACd,OACA,UAA+B,CAAC,GAClB;AACd,QAAM,eAAe,YAAY,OAAO,OAAO;AAE/C,MAAI,QAAQ,QAAQ;AAClB,YAAQ,OAAO,YAAY;AAAA,EAC7B;AAEA,SAAO;AACT;AAMO,SAAS,aACd,IACG;AACH,UAAQ,IAAI,SAAgB;AAC1B,WAAO,QAAQ,QAAQ,GAAG,GAAG,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU;AACnD,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAQO,SAAS,oBACd,UAA+B,CAAC,GAChC;AACA,SAAO,CACL,KACA,KACA,KACA,SACS;AACT,UAAM,eAAe,YAAY,KAAK;AAAA,MACpC,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAG,QAAQ;AAAA,QACX,QAAQ,IAAI;AAAA,QACZ,MAAM,IAAI;AAAA,QACV,IAAI,IAAI;AAAA,MACV;AAAA,IACF,CAAC;AAED,UAAM,aACJ,eAAe,WAAW,IAAI,cAAc,MAAM;AAEpD,QAAI,QAAQ,WAAW,UAAU;AAC/B,UAAI,OAAO,UAAU,EAAE,KAAK,aAAa,OAAO;AAAA,IAClD,OAAO;AACL,UAAI,OAAO,UAAU,EAAE,KAAK,YAAY;AAAA,IAC1C;AAAA,EACF;AACF;AAKO,SAAS,oBACd,SACA,aAAqB,KACrB,MACA,SACU;AACV,SAAO,IAAI,SAAS,SAAS,YAAY,MAAM,OAAO;AACxD;AAQA,SAAS,eACP,SACA,SACQ;AACR,QAAM,EAAE,SAAS,cAAc,UAAU,OAAO,IAAI;AAEpD,MAAI;AAEJ,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,cAAQ,eAAe,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9C;AAAA,IACF,KAAK;AACH,cAAQ,eAAe;AACvB;AAAA,IACF,KAAK;AACH,cAAQ;AACR;AAAA,IACF;AACE,cAAQ;AAAA,EACZ;AAGA,UAAQ,KAAK,IAAI,OAAO,QAAQ;AAGhC,MAAI,QAAQ;AACV,UAAM,eAAe,QAAQ;AAC7B,YAAQ,QAAQ,eAAe,KAAK,OAAO,IAAI,eAAe;AAAA,EAChE;AAEA,SAAO,KAAK,MAAM,KAAK;AACzB;AA+BA,eAAsB,UACpB,IACA,UAAwB,CAAC,GACb;AACZ,QAAM;AAAA,IACJ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,eAAe;AAAA,IACf,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,SAAkB,CAAC;AACzB,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,kBAAY;AACZ,aAAO,KAAK,KAAK;AAGjB,UAAI,YAAY,YAAY;AAC1B;AAAA,MACF;AAGA,UAAI,WAAW,CAAC,QAAQ,KAAK,GAAG;AAC9B;AAAA,MACF;AAGA,UAAI,SAAS;AACX,gBAAQ,OAAO,UAAU,CAAC;AAAA,MAC5B;AAGA,YAAM,QAAQ,eAAe,UAAU,GAAG;AAAA,QACxC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,IAC3D;AAAA,EACF;AAGA,QAAM,aAAa;AACnB,EAAC,WAAmB,WAAW;AAE/B,QAAM;AACR;AAcO,IAAM,SAAmB;AAAA;AAAA,EAE9B,YAAY,CAAC,SAAiB,YAC5B,IAAI,SAAS,SAAS,KAAK,eAAe,OAAO;AAAA,EAEnD,cAAc,CAAC,UAAkB,gBAAgB,YAC/C,IAAI,SAAS,SAAS,KAAK,gBAAgB,OAAO;AAAA,EAEpD,iBAAiB,CAAC,UAAkB,oBAAoB,YACtD,IAAI,SAAS,SAAS,KAAK,oBAAoB,OAAO;AAAA,EAExD,WAAW,CAAC,UAAkB,aAAa,YACzC,IAAI,SAAS,SAAS,KAAK,aAAa,OAAO;AAAA,EAEjD,UAAU,CAAC,UAAkB,aAAa,YACxC,IAAI,SAAS,SAAS,KAAK,aAAa,OAAO;AAAA,EAEjD,kBAAkB,CAAC,UAAkB,sBAAsB,YACzD,IAAI,SAAS,SAAS,KAAK,sBAAsB,OAAO;AAAA,EAE1D,eAAe,CAAC,UAAkB,kBAAkB,YAClD,IAAI,SAAS,SAAS,KAAK,kBAAkB,OAAO;AAAA,EAEtD,mBAAmB,CAAC,UAAkB,iCAAiC,YACrE,IAAI,SAAS,SAAS,KAAK,uBAAuB,OAAO;AAAA,EAE3D,gBAAgB,CAAC,UAAkB,mBAAmB,YACpD,IAAI,SAAS,SAAS,KAAK,mBAAmB,OAAO;AAAA,EAEvD,UAAU,CAAC,SAAiB,YAC1B,IAAI,SAAS,SAAS,KAAK,YAAY,OAAO;AAAA,EAEhD,MAAM,CAAC,UAAkB,QAAQ,YAC/B,IAAI,SAAS,SAAS,KAAK,QAAQ,OAAO;AAAA,EAE5C,gBAAgB,CAAC,UAAkB,mBAAmB,YACpD,IAAI,SAAS,SAAS,KAAK,mBAAmB,OAAO;AAAA,EAEvD,oBAAoB,CAAC,UAAkB,uBAAuB,YAC5D,IAAI,SAAS,SAAS,KAAK,uBAAuB,OAAO;AAAA,EAE3D,iBAAiB,CAAC,UAAkB,qBAAqB,YACvD,IAAI,SAAS,SAAS,KAAK,qBAAqB,OAAO;AAAA,EAEzD,YAAY,CAAC,UAAkB,gBAAgB,YAC7C,IAAI,SAAS,SAAS,KAAK,gBAAgB,OAAO;AAAA,EAEpD,sBAAsB,CAAC,UAAkB,0BAA0B,YACjE,IAAI,SAAS,SAAS,KAAK,0BAA0B,OAAO;AAAA,EAE9D,qBAAqB,CAAC,UAAkB,yBAAyB,YAC/D,IAAI,SAAS,SAAS,KAAK,yBAAyB,OAAO;AAAA,EAE7D,mBAAmB,CAAC,UAAkB,sBAAsB,YAC1D,IAAI,SAAS,SAAS,KAAK,sBAAsB,OAAO;AAAA,EAE1D,WAAW,CAAC,UAAkB,gBAAgB,YAC5C,IAAI,SAAS,SAAS,KAAK,eAAe,OAAO;AAAA,EAEnD,oBAAoB,CAAC,UAAkB,uBAAuB,YAC5D,IAAI,SAAS,SAAS,KAAK,uBAAuB,OAAO;AAAA,EAE3D,qBAAqB,CAAC,UAAkB,wBAAwB,YAC9D,IAAI,SAAS,SAAS,KAAK,wBAAwB,OAAO;AAAA,EAE5D,iBAAiB,CAAC,SAAiB,YACjC,IAAI,SAAS,SAAS,KAAK,oBAAoB,OAAO;AAAA,EAExD,QAAQ,CAAC,UAAkB,UAAU,YACnC,IAAI,SAAS,SAAS,KAAK,UAAU,OAAO;AAAA,EAE9C,kBAAkB,CAAC,UAAkB,qBAAqB,YACxD,IAAI,SAAS,SAAS,KAAK,qBAAqB,OAAO;AAAA,EAEzD,UAAU,CAAC,UAAkB,aAAa,YACxC,IAAI,SAAS,SAAS,KAAK,aAAa,OAAO;AAAA,EAEjD,iBAAiB,CAAC,UAAkB,oBAAoB,YACtD,IAAI,SAAS,SAAS,KAAK,oBAAoB,OAAO;AAAA,EAExD,sBAAsB,CAAC,UAAkB,yBAAyB,YAChE,IAAI,SAAS,SAAS,KAAK,yBAAyB,OAAO;AAAA,EAE7D,iBAAiB,CAAC,UAAkB,qBAAqB,YACvD,IAAI,SAAS,SAAS,KAAK,qBAAqB,OAAO;AAAA,EAEzD,6BAA6B,CAAC,UAAkB,mCAAmC,YACjF,IAAI,SAAS,SAAS,KAAK,mCAAmC,OAAO;AAAA,EAEvE,4BAA4B,CAAC,UAAkB,iCAAiC,YAC9E,IAAI,SAAS,SAAS,KAAK,iCAAiC,OAAO;AAAA;AAAA,EAGrE,qBAAqB,CAAC,UAAkB,yBAAyB,YAC/D,IAAI,SAAS,SAAS,KAAK,yBAAyB,OAAO;AAAA,EAE7D,gBAAgB,CAAC,UAAkB,mBAAmB,YACpD,IAAI,SAAS,SAAS,KAAK,mBAAmB,OAAO;AAAA,EAEvD,YAAY,CAAC,UAAkB,eAAe,YAC5C,IAAI,SAAS,SAAS,KAAK,eAAe,OAAO;AAAA,EAEnD,oBAAoB,CAAC,UAAkB,uBAAuB,YAC5D,IAAI,SAAS,SAAS,KAAK,uBAAuB,OAAO;AAAA,EAE3D,gBAAgB,CAAC,UAAkB,mBAAmB,YACpD,IAAI,SAAS,SAAS,KAAK,mBAAmB,OAAO;AAAA,EAEvD,yBAAyB,CAAC,UAAkB,8BAA8B,YACxE,IAAI,SAAS,SAAS,KAAK,8BAA8B,OAAO;AAAA,EAElE,uBAAuB,CAAC,UAAkB,2BAA2B,YACnE,IAAI,SAAS,SAAS,KAAK,2BAA2B,OAAO;AAAA,EAE/D,qBAAqB,CAAC,UAAkB,wBAAwB,YAC9D,IAAI,SAAS,SAAS,KAAK,wBAAwB,OAAO;AAAA,EAE5D,cAAc,CAAC,UAAkB,iBAAiB,YAChD,IAAI,SAAS,SAAS,KAAK,iBAAiB,OAAO;AAAA,EAErD,wBAAwB,CAAC,UAAkB,4BAA4B,YACrE,IAAI,SAAS,SAAS,KAAK,4BAA4B,OAAO;AAAA,EAEhE,aAAa,CAAC,UAAkB,gBAAgB,YAC9C,IAAI,SAAS,SAAS,KAAK,gBAAgB,OAAO;AAAA,EAEpD,+BAA+B,CAAC,UAAkB,mCAAmC,YACnF,IAAI,SAAS,SAAS,KAAK,mCAAmC,OAAO;AAAA,EAEvE,uBAAuB,CAAC,UAAkB,2BAA2B,YACnE,IAAI,SAAS,SAAS,KAAK,2BAA2B,OAAO;AACjE;AAGA,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
@@ -0,0 +1,238 @@
1
+ // src/index.ts
2
+ var AppError = class _AppError extends Error {
3
+ code;
4
+ statusCode;
5
+ context;
6
+ isOperational;
7
+ constructor(message, statusCode = 500, code, context, cause) {
8
+ super(message, cause ? { cause } : void 0);
9
+ this.name = this.constructor.name;
10
+ this.statusCode = statusCode;
11
+ this.code = code;
12
+ this.context = context;
13
+ this.isOperational = true;
14
+ if (Error.captureStackTrace) {
15
+ Error.captureStackTrace(this, this.constructor);
16
+ }
17
+ }
18
+ /**
19
+ * Wraps an existing error as the cause of a new `AppError`.
20
+ *
21
+ * This is a convenience factory for error chaining — it creates a new
22
+ * `AppError` whose `.cause` is set to the original error.
23
+ *
24
+ * @param originalError - The caught error to wrap
25
+ * @param message - Human-readable description of the higher-level failure
26
+ * @param statusCode - HTTP status code (default: 500)
27
+ * @param code - Machine-readable error code
28
+ * @param context - Additional context metadata
29
+ * @returns A new `AppError` with `originalError` as its cause
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * try {
34
+ * await db.query('SELECT ...');
35
+ * } catch (err) {
36
+ * throw AppError.wrap(err, 'Database query failed', 500, 'DB_ERROR');
37
+ * }
38
+ * ```
39
+ */
40
+ static wrap(originalError, message, statusCode = 500, code, context) {
41
+ return new _AppError(message, statusCode, code, context, originalError);
42
+ }
43
+ };
44
+ function formatError(error, options = {}) {
45
+ const {
46
+ includeStack = false,
47
+ includeTimestamp = true,
48
+ context: optionsContext = {}
49
+ } = options;
50
+ const mergedContext = error instanceof AppError ? { ...error.context, ...optionsContext } : optionsContext;
51
+ const errorDetails = {
52
+ message: error.message,
53
+ ...includeTimestamp && { timestamp: (/* @__PURE__ */ new Date()).toISOString() },
54
+ ...includeStack && error.stack && { stack: error.stack },
55
+ ...error instanceof AppError && {
56
+ code: error.code,
57
+ statusCode: error.statusCode
58
+ },
59
+ ...Object.keys(mergedContext).length > 0 && { context: mergedContext }
60
+ };
61
+ if (error.cause && error.cause instanceof Error) {
62
+ errorDetails.cause = formatError(error.cause, {
63
+ includeStack,
64
+ includeTimestamp: false
65
+ // only top-level gets timestamp
66
+ });
67
+ }
68
+ return errorDetails;
69
+ }
70
+ function handleError(error, options = {}) {
71
+ const errorDetails = formatError(error, options);
72
+ if (options.logger) {
73
+ options.logger(errorDetails);
74
+ }
75
+ return errorDetails;
76
+ }
77
+ function asyncHandler(fn) {
78
+ return ((...args) => {
79
+ return Promise.resolve(fn(...args)).catch((error) => {
80
+ throw error;
81
+ });
82
+ });
83
+ }
84
+ function expressErrorHandler(options = {}) {
85
+ return (err, req, res, next) => {
86
+ const errorDetails = handleError(err, {
87
+ ...options,
88
+ context: {
89
+ ...options.context,
90
+ method: req.method,
91
+ path: req.path,
92
+ ip: req.ip
93
+ }
94
+ });
95
+ const statusCode = err instanceof AppError ? err.statusCode || 500 : 500;
96
+ if (options.format === "string") {
97
+ res.status(statusCode).send(errorDetails.message);
98
+ } else {
99
+ res.status(statusCode).json(errorDetails);
100
+ }
101
+ };
102
+ }
103
+ function createErrorResponse(message, statusCode = 500, code, context) {
104
+ return new AppError(message, statusCode, code, context);
105
+ }
106
+ function calculateDelay(attempt, options) {
107
+ const { backoff, initialDelay, maxDelay, jitter } = options;
108
+ let delay;
109
+ switch (backoff) {
110
+ case "exponential":
111
+ delay = initialDelay * Math.pow(2, attempt - 1);
112
+ break;
113
+ case "linear":
114
+ delay = initialDelay * attempt;
115
+ break;
116
+ case "fixed":
117
+ delay = initialDelay;
118
+ break;
119
+ default:
120
+ delay = initialDelay;
121
+ }
122
+ delay = Math.min(delay, maxDelay);
123
+ if (jitter) {
124
+ const jitterAmount = delay * 0.25;
125
+ delay = delay - jitterAmount + Math.random() * jitterAmount * 2;
126
+ }
127
+ return Math.round(delay);
128
+ }
129
+ async function withRetry(fn, options = {}) {
130
+ const {
131
+ maxRetries = 3,
132
+ backoff = "exponential",
133
+ initialDelay = 1e3,
134
+ maxDelay = 3e4,
135
+ retryIf,
136
+ onRetry,
137
+ jitter = true
138
+ } = options;
139
+ const errors = [];
140
+ let lastError;
141
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
142
+ try {
143
+ return await fn();
144
+ } catch (err) {
145
+ const error = err instanceof Error ? err : new Error(String(err));
146
+ lastError = error;
147
+ errors.push(error);
148
+ if (attempt === maxRetries) {
149
+ break;
150
+ }
151
+ if (retryIf && !retryIf(error)) {
152
+ break;
153
+ }
154
+ if (onRetry) {
155
+ onRetry(error, attempt + 1);
156
+ }
157
+ const delay = calculateDelay(attempt + 1, {
158
+ backoff,
159
+ initialDelay,
160
+ maxDelay,
161
+ jitter
162
+ });
163
+ await new Promise((resolve) => setTimeout(resolve, delay));
164
+ }
165
+ }
166
+ const finalError = lastError;
167
+ finalError.attempts = errors;
168
+ throw finalError;
169
+ }
170
+ var Errors = {
171
+ // 4xx Client Errors
172
+ badRequest: (message, context) => new AppError(message, 400, "BAD_REQUEST", context),
173
+ unauthorized: (message = "Unauthorized", context) => new AppError(message, 401, "UNAUTHORIZED", context),
174
+ paymentRequired: (message = "Payment Required", context) => new AppError(message, 402, "PAYMENT_REQUIRED", context),
175
+ forbidden: (message = "Forbidden", context) => new AppError(message, 403, "FORBIDDEN", context),
176
+ notFound: (message = "Not Found", context) => new AppError(message, 404, "NOT_FOUND", context),
177
+ methodNotAllowed: (message = "Method Not Allowed", context) => new AppError(message, 405, "METHOD_NOT_ALLOWED", context),
178
+ notAcceptable: (message = "Not Acceptable", context) => new AppError(message, 406, "NOT_ACCEPTABLE", context),
179
+ proxyAuthRequired: (message = "Proxy Authentication Required", context) => new AppError(message, 407, "PROXY_AUTH_REQUIRED", context),
180
+ requestTimeout: (message = "Request Timeout", context) => new AppError(message, 408, "REQUEST_TIMEOUT", context),
181
+ conflict: (message, context) => new AppError(message, 409, "CONFLICT", context),
182
+ gone: (message = "Gone", context) => new AppError(message, 410, "GONE", context),
183
+ lengthRequired: (message = "Length Required", context) => new AppError(message, 411, "LENGTH_REQUIRED", context),
184
+ preconditionFailed: (message = "Precondition Failed", context) => new AppError(message, 412, "PRECONDITION_FAILED", context),
185
+ payloadTooLarge: (message = "Payload Too Large", context) => new AppError(message, 413, "PAYLOAD_TOO_LARGE", context),
186
+ uriTooLong: (message = "URI Too Long", context) => new AppError(message, 414, "URI_TOO_LONG", context),
187
+ unsupportedMediaType: (message = "Unsupported Media Type", context) => new AppError(message, 415, "UNSUPPORTED_MEDIA_TYPE", context),
188
+ rangeNotSatisfiable: (message = "Range Not Satisfiable", context) => new AppError(message, 416, "RANGE_NOT_SATISFIABLE", context),
189
+ expectationFailed: (message = "Expectation Failed", context) => new AppError(message, 417, "EXPECTATION_FAILED", context),
190
+ imATeapot: (message = "I'm a Teapot", context) => new AppError(message, 418, "IM_A_TEAPOT", context),
191
+ misdirectedRequest: (message = "Misdirected Request", context) => new AppError(message, 421, "MISDIRECTED_REQUEST", context),
192
+ unprocessableEntity: (message = "Unprocessable Entity", context) => new AppError(message, 422, "UNPROCESSABLE_ENTITY", context),
193
+ validationError: (message, context) => new AppError(message, 422, "VALIDATION_ERROR", context),
194
+ locked: (message = "Locked", context) => new AppError(message, 423, "LOCKED", context),
195
+ failedDependency: (message = "Failed Dependency", context) => new AppError(message, 424, "FAILED_DEPENDENCY", context),
196
+ tooEarly: (message = "Too Early", context) => new AppError(message, 425, "TOO_EARLY", context),
197
+ upgradeRequired: (message = "Upgrade Required", context) => new AppError(message, 426, "UPGRADE_REQUIRED", context),
198
+ preconditionRequired: (message = "Precondition Required", context) => new AppError(message, 428, "PRECONDITION_REQUIRED", context),
199
+ tooManyRequests: (message = "Too Many Requests", context) => new AppError(message, 429, "TOO_MANY_REQUESTS", context),
200
+ requestHeaderFieldsTooLarge: (message = "Request Header Fields Too Large", context) => new AppError(message, 431, "REQUEST_HEADER_FIELDS_TOO_LARGE", context),
201
+ unavailableForLegalReasons: (message = "Unavailable For Legal Reasons", context) => new AppError(message, 451, "UNAVAILABLE_FOR_LEGAL_REASONS", context),
202
+ // 5xx Server Errors
203
+ internalServerError: (message = "Internal Server Error", context) => new AppError(message, 500, "INTERNAL_SERVER_ERROR", context),
204
+ notImplemented: (message = "Not Implemented", context) => new AppError(message, 501, "NOT_IMPLEMENTED", context),
205
+ badGateway: (message = "Bad Gateway", context) => new AppError(message, 502, "BAD_GATEWAY", context),
206
+ serviceUnavailable: (message = "Service Unavailable", context) => new AppError(message, 503, "SERVICE_UNAVAILABLE", context),
207
+ gatewayTimeout: (message = "Gateway Timeout", context) => new AppError(message, 504, "GATEWAY_TIMEOUT", context),
208
+ httpVersionNotSupported: (message = "HTTP Version Not Supported", context) => new AppError(message, 505, "HTTP_VERSION_NOT_SUPPORTED", context),
209
+ variantAlsoNegotiates: (message = "Variant Also Negotiates", context) => new AppError(message, 506, "VARIANT_ALSO_NEGOTIATES", context),
210
+ insufficientStorage: (message = "Insufficient Storage", context) => new AppError(message, 507, "INSUFFICIENT_STORAGE", context),
211
+ loopDetected: (message = "Loop Detected", context) => new AppError(message, 508, "LOOP_DETECTED", context),
212
+ bandwidthLimitExceeded: (message = "Bandwidth Limit Exceeded", context) => new AppError(message, 509, "BANDWIDTH_LIMIT_EXCEEDED", context),
213
+ notExtended: (message = "Not Extended", context) => new AppError(message, 510, "NOT_EXTENDED", context),
214
+ networkAuthenticationRequired: (message = "Network Authentication Required", context) => new AppError(message, 511, "NETWORK_AUTHENTICATION_REQUIRED", context),
215
+ networkConnectTimeout: (message = "Network Connect Timeout", context) => new AppError(message, 599, "NETWORK_CONNECT_TIMEOUT", context)
216
+ };
217
+ var index_default = {
218
+ AppError,
219
+ formatError,
220
+ handleError,
221
+ asyncHandler,
222
+ expressErrorHandler,
223
+ createErrorResponse,
224
+ withRetry,
225
+ Errors
226
+ };
227
+ export {
228
+ AppError,
229
+ Errors,
230
+ asyncHandler,
231
+ createErrorResponse,
232
+ index_default as default,
233
+ expressErrorHandler,
234
+ formatError,
235
+ handleError,
236
+ withRetry
237
+ };
238
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["/**\n * Error Shield - A comprehensive error handling utility for Node.js & Express.js\n *\n * Provides utilities for error handling, formatting, logging, retry logic,\n * and error chaining with full TypeScript support.\n *\n * @packageDocumentation\n */\n\n// ─── Types & Interfaces ─────────────────────────────────────────────────────\n\n/**\n * Structured representation of an error with optional metadata.\n */\nexport interface ErrorDetails {\n message: string;\n code?: string;\n statusCode?: number;\n stack?: string;\n timestamp?: string;\n context?: Record<string, any>;\n /** Cause chain information when error chaining is used */\n cause?: ErrorDetails;\n}\n\n/**\n * Options for configuring error handling behavior.\n */\nexport interface ErrorHandlerOptions {\n includeStack?: boolean;\n includeTimestamp?: boolean;\n format?: 'json' | 'string';\n logger?: (error: ErrorDetails) => void;\n context?: Record<string, any>;\n}\n\nexport type ErrorFn = (message: string, context?: Record<string, any>) => AppError;\n\nexport type ErrorMap = Record<string, ErrorFn>;\n\n/**\n * Configuration options for the retry utility.\n */\nexport interface RetryOptions {\n /** Maximum number of retry attempts (default: 3) */\n maxRetries?: number;\n /** Backoff strategy between retries (default: 'exponential') */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Initial delay in milliseconds before the first retry (default: 1000) */\n initialDelay?: number;\n /** Maximum delay in milliseconds between retries (default: 30000) */\n maxDelay?: number;\n /**\n * Optional predicate to determine whether a retry should be attempted.\n * If provided and returns `false`, the error is thrown immediately.\n */\n retryIf?: (error: Error) => boolean;\n /** Optional callback invoked before each retry attempt */\n onRetry?: (error: Error, attempt: number) => void;\n /** Whether to add random jitter to the delay to prevent thundering herd (default: true) */\n jitter?: boolean;\n}\n\n// ─── AppError Class ──────────────────────────────────────────────────────────\n\n/**\n * Custom Error class with additional properties for structured error handling.\n *\n * Supports error chaining via the native ES2022 `Error.cause` feature.\n *\n * @example\n * ```ts\n * const error = new AppError('Something broke', 500, 'INTERNAL', { userId: 123 });\n *\n * // With error chaining\n * try {\n * await fetchData();\n * } catch (err) {\n * throw new AppError('Failed to fetch data', 502, 'FETCH_FAILED', undefined, err);\n * }\n * ```\n */\nexport class AppError extends Error {\n public code?: string;\n public statusCode?: number;\n public context?: Record<string, any>;\n public isOperational: boolean;\n\n constructor(\n message: string,\n statusCode: number = 500,\n code?: string,\n context?: Record<string, any>,\n cause?: Error\n ) {\n super(message, cause ? { cause } : undefined);\n this.name = this.constructor.name;\n this.statusCode = statusCode;\n this.code = code;\n this.context = context;\n this.isOperational = true;\n if ((Error as any).captureStackTrace) {\n (Error as any).captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Wraps an existing error as the cause of a new `AppError`.\n *\n * This is a convenience factory for error chaining — it creates a new\n * `AppError` whose `.cause` is set to the original error.\n *\n * @param originalError - The caught error to wrap\n * @param message - Human-readable description of the higher-level failure\n * @param statusCode - HTTP status code (default: 500)\n * @param code - Machine-readable error code\n * @param context - Additional context metadata\n * @returns A new `AppError` with `originalError` as its cause\n *\n * @example\n * ```ts\n * try {\n * await db.query('SELECT ...');\n * } catch (err) {\n * throw AppError.wrap(err, 'Database query failed', 500, 'DB_ERROR');\n * }\n * ```\n */\n static wrap(\n originalError: Error,\n message: string,\n statusCode: number = 500,\n code?: string,\n context?: Record<string, any>\n ): AppError {\n return new AppError(message, statusCode, code, context, originalError);\n }\n}\n\n// ─── Core Utilities ──────────────────────────────────────────────────────────\n\n/**\n * Formats an error into a structured {@link ErrorDetails} object.\n *\n * When the error has a `.cause`, the cause chain is recursively formatted\n * and included in the output.\n */\nexport function formatError(\n error: Error | AppError,\n options: ErrorHandlerOptions = {}\n): ErrorDetails {\n const {\n includeStack = false,\n includeTimestamp = true,\n context: optionsContext = {},\n } = options;\n\n const mergedContext = error instanceof AppError\n ? { ...error.context, ...optionsContext }\n : optionsContext;\n\n const errorDetails: ErrorDetails = {\n message: error.message,\n ...(includeTimestamp && { timestamp: new Date().toISOString() }),\n ...(includeStack && error.stack && { stack: error.stack }),\n ...(error instanceof AppError && {\n code: error.code,\n statusCode: error.statusCode,\n }),\n ...(Object.keys(mergedContext).length > 0 && { context: mergedContext }),\n };\n\n // Recursively format the cause chain\n if (error.cause && error.cause instanceof Error) {\n errorDetails.cause = formatError(error.cause, {\n includeStack,\n includeTimestamp: false, // only top-level gets timestamp\n });\n }\n\n return errorDetails;\n}\n\n/**\n * Handles errors with optional logging and formatting.\n */\nexport function handleError(\n error: Error | AppError,\n options: ErrorHandlerOptions = {}\n): ErrorDetails {\n const errorDetails = formatError(error, options);\n\n if (options.logger) {\n options.logger(errorDetails);\n }\n\n return errorDetails;\n}\n\n/**\n * Async error wrapper — catches errors from async route handlers\n * and forwards them to Express's `next()` function.\n */\nexport function asyncHandler<T extends (...args: any[]) => Promise<any>>(\n fn: T\n): T {\n return ((...args: any[]) => {\n return Promise.resolve(fn(...args)).catch((error) => {\n throw error;\n });\n }) as T;\n}\n\n/**\n * Express.js error handler middleware factory.\n *\n * Returns a standard Express error-handling middleware that formats\n * the error and sends an appropriate JSON or string response.\n */\nexport function expressErrorHandler(\n options: ErrorHandlerOptions = {}\n) {\n return (\n err: Error | AppError,\n req: any,\n res: any,\n next: any\n ): void => {\n const errorDetails = handleError(err, {\n ...options,\n context: {\n ...options.context,\n method: req.method,\n path: req.path,\n ip: req.ip,\n },\n });\n\n const statusCode =\n err instanceof AppError ? err.statusCode || 500 : 500;\n\n if (options.format === 'string') {\n res.status(statusCode).send(errorDetails.message);\n } else {\n res.status(statusCode).json(errorDetails);\n }\n };\n}\n\n/**\n * Creates a standardized error response.\n */\nexport function createErrorResponse(\n message: string,\n statusCode: number = 500,\n code?: string,\n context?: Record<string, any>\n): AppError {\n return new AppError(message, statusCode, code, context);\n}\n\n// ─── Retry Utility ───────────────────────────────────────────────────────────\n\n/**\n * Calculates the delay for a given retry attempt based on the backoff strategy.\n * @internal\n */\nfunction calculateDelay(\n attempt: number,\n options: Required<Pick<RetryOptions, 'backoff' | 'initialDelay' | 'maxDelay' | 'jitter'>>\n): number {\n const { backoff, initialDelay, maxDelay, jitter } = options;\n\n let delay: number;\n\n switch (backoff) {\n case 'exponential':\n delay = initialDelay * Math.pow(2, attempt - 1);\n break;\n case 'linear':\n delay = initialDelay * attempt;\n break;\n case 'fixed':\n delay = initialDelay;\n break;\n default:\n delay = initialDelay;\n }\n\n // Cap at maxDelay\n delay = Math.min(delay, maxDelay);\n\n // Add jitter (±25% randomness)\n if (jitter) {\n const jitterAmount = delay * 0.25;\n delay = delay - jitterAmount + Math.random() * jitterAmount * 2;\n }\n\n return Math.round(delay);\n}\n\n/**\n * Executes an async function with automatic retries on failure.\n *\n * Supports exponential, linear, and fixed backoff strategies with\n * optional jitter, conditional retry predicates, and retry callbacks.\n *\n * If all retries are exhausted, the **last** error is thrown. The previous\n * attempt errors are accessible via the error's `.cause` chain and the\n * `attempts` property attached to the final error.\n *\n * @typeParam T - The return type of the async function\n * @param fn - The async function to execute with retries\n * @param options - Retry configuration options\n * @returns The result of the successful function execution\n *\n * @example\n * ```ts\n * const data = await withRetry(\n * () => fetch('https://api.example.com/data').then(r => r.json()),\n * {\n * maxRetries: 5,\n * backoff: 'exponential',\n * initialDelay: 500,\n * retryIf: (err) => err.message.includes('ECONNREFUSED'),\n * onRetry: (err, attempt) => console.log(`Retry ${attempt}: ${err.message}`),\n * }\n * );\n * ```\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<T> {\n const {\n maxRetries = 3,\n backoff = 'exponential',\n initialDelay = 1000,\n maxDelay = 30000,\n retryIf,\n onRetry,\n jitter = true,\n } = options;\n\n const errors: Error[] = [];\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n lastError = error;\n errors.push(error);\n\n // If this was the last attempt, break to throw\n if (attempt === maxRetries) {\n break;\n }\n\n // Check retryIf predicate\n if (retryIf && !retryIf(error)) {\n break;\n }\n\n // Invoke onRetry callback\n if (onRetry) {\n onRetry(error, attempt + 1);\n }\n\n // Wait before retrying\n const delay = calculateDelay(attempt + 1, {\n backoff,\n initialDelay,\n maxDelay,\n jitter,\n });\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n // Attach attempt history to the final error\n const finalError = lastError!;\n (finalError as any).attempts = errors;\n\n throw finalError;\n}\n\n// ─── Common Error Creators ───────────────────────────────────────────────────\n\n/**\n * Pre-built factory functions for common HTTP error responses.\n *\n * @example\n * ```ts\n * throw Errors.notFound('User not found', { userId: 42 });\n * throw Errors.unauthorized();\n * throw Errors.tooManyRequests('Rate limit exceeded');\n * ```\n */\nexport const Errors: ErrorMap = {\n // 4xx Client Errors\n badRequest: (message: string, context?: Record<string, any>) =>\n new AppError(message, 400, 'BAD_REQUEST', context),\n\n unauthorized: (message: string = 'Unauthorized', context?: Record<string, any>) =>\n new AppError(message, 401, 'UNAUTHORIZED', context),\n\n paymentRequired: (message: string = 'Payment Required', context?: Record<string, any>) =>\n new AppError(message, 402, 'PAYMENT_REQUIRED', context),\n\n forbidden: (message: string = 'Forbidden', context?: Record<string, any>) =>\n new AppError(message, 403, 'FORBIDDEN', context),\n\n notFound: (message: string = 'Not Found', context?: Record<string, any>) =>\n new AppError(message, 404, 'NOT_FOUND', context),\n\n methodNotAllowed: (message: string = 'Method Not Allowed', context?: Record<string, any>) =>\n new AppError(message, 405, 'METHOD_NOT_ALLOWED', context),\n\n notAcceptable: (message: string = 'Not Acceptable', context?: Record<string, any>) =>\n new AppError(message, 406, 'NOT_ACCEPTABLE', context),\n\n proxyAuthRequired: (message: string = 'Proxy Authentication Required', context?: Record<string, any>) =>\n new AppError(message, 407, 'PROXY_AUTH_REQUIRED', context),\n\n requestTimeout: (message: string = 'Request Timeout', context?: Record<string, any>) =>\n new AppError(message, 408, 'REQUEST_TIMEOUT', context),\n\n conflict: (message: string, context?: Record<string, any>) =>\n new AppError(message, 409, 'CONFLICT', context),\n\n gone: (message: string = 'Gone', context?: Record<string, any>) =>\n new AppError(message, 410, 'GONE', context),\n\n lengthRequired: (message: string = 'Length Required', context?: Record<string, any>) =>\n new AppError(message, 411, 'LENGTH_REQUIRED', context),\n\n preconditionFailed: (message: string = 'Precondition Failed', context?: Record<string, any>) =>\n new AppError(message, 412, 'PRECONDITION_FAILED', context),\n\n payloadTooLarge: (message: string = 'Payload Too Large', context?: Record<string, any>) =>\n new AppError(message, 413, 'PAYLOAD_TOO_LARGE', context),\n\n uriTooLong: (message: string = 'URI Too Long', context?: Record<string, any>) =>\n new AppError(message, 414, 'URI_TOO_LONG', context),\n\n unsupportedMediaType: (message: string = 'Unsupported Media Type', context?: Record<string, any>) =>\n new AppError(message, 415, 'UNSUPPORTED_MEDIA_TYPE', context),\n\n rangeNotSatisfiable: (message: string = 'Range Not Satisfiable', context?: Record<string, any>) =>\n new AppError(message, 416, 'RANGE_NOT_SATISFIABLE', context),\n\n expectationFailed: (message: string = 'Expectation Failed', context?: Record<string, any>) =>\n new AppError(message, 417, 'EXPECTATION_FAILED', context),\n\n imATeapot: (message: string = \"I'm a Teapot\", context?: Record<string, any>) =>\n new AppError(message, 418, 'IM_A_TEAPOT', context),\n\n misdirectedRequest: (message: string = 'Misdirected Request', context?: Record<string, any>) =>\n new AppError(message, 421, 'MISDIRECTED_REQUEST', context),\n\n unprocessableEntity: (message: string = 'Unprocessable Entity', context?: Record<string, any>) =>\n new AppError(message, 422, 'UNPROCESSABLE_ENTITY', context),\n\n validationError: (message: string, context?: Record<string, any>) =>\n new AppError(message, 422, 'VALIDATION_ERROR', context),\n\n locked: (message: string = 'Locked', context?: Record<string, any>) =>\n new AppError(message, 423, 'LOCKED', context),\n\n failedDependency: (message: string = 'Failed Dependency', context?: Record<string, any>) =>\n new AppError(message, 424, 'FAILED_DEPENDENCY', context),\n\n tooEarly: (message: string = 'Too Early', context?: Record<string, any>) =>\n new AppError(message, 425, 'TOO_EARLY', context),\n\n upgradeRequired: (message: string = 'Upgrade Required', context?: Record<string, any>) =>\n new AppError(message, 426, 'UPGRADE_REQUIRED', context),\n\n preconditionRequired: (message: string = 'Precondition Required', context?: Record<string, any>) =>\n new AppError(message, 428, 'PRECONDITION_REQUIRED', context),\n\n tooManyRequests: (message: string = 'Too Many Requests', context?: Record<string, any>) =>\n new AppError(message, 429, 'TOO_MANY_REQUESTS', context),\n\n requestHeaderFieldsTooLarge: (message: string = 'Request Header Fields Too Large', context?: Record<string, any>) =>\n new AppError(message, 431, 'REQUEST_HEADER_FIELDS_TOO_LARGE', context),\n\n unavailableForLegalReasons: (message: string = 'Unavailable For Legal Reasons', context?: Record<string, any>) =>\n new AppError(message, 451, 'UNAVAILABLE_FOR_LEGAL_REASONS', context),\n\n // 5xx Server Errors\n internalServerError: (message: string = 'Internal Server Error', context?: Record<string, any>) =>\n new AppError(message, 500, 'INTERNAL_SERVER_ERROR', context),\n\n notImplemented: (message: string = 'Not Implemented', context?: Record<string, any>) =>\n new AppError(message, 501, 'NOT_IMPLEMENTED', context),\n\n badGateway: (message: string = 'Bad Gateway', context?: Record<string, any>) =>\n new AppError(message, 502, 'BAD_GATEWAY', context),\n\n serviceUnavailable: (message: string = 'Service Unavailable', context?: Record<string, any>) =>\n new AppError(message, 503, 'SERVICE_UNAVAILABLE', context),\n\n gatewayTimeout: (message: string = 'Gateway Timeout', context?: Record<string, any>) =>\n new AppError(message, 504, 'GATEWAY_TIMEOUT', context),\n\n httpVersionNotSupported: (message: string = 'HTTP Version Not Supported', context?: Record<string, any>) =>\n new AppError(message, 505, 'HTTP_VERSION_NOT_SUPPORTED', context),\n\n variantAlsoNegotiates: (message: string = 'Variant Also Negotiates', context?: Record<string, any>) =>\n new AppError(message, 506, 'VARIANT_ALSO_NEGOTIATES', context),\n\n insufficientStorage: (message: string = 'Insufficient Storage', context?: Record<string, any>) =>\n new AppError(message, 507, 'INSUFFICIENT_STORAGE', context),\n\n loopDetected: (message: string = 'Loop Detected', context?: Record<string, any>) =>\n new AppError(message, 508, 'LOOP_DETECTED', context),\n\n bandwidthLimitExceeded: (message: string = 'Bandwidth Limit Exceeded', context?: Record<string, any>) =>\n new AppError(message, 509, 'BANDWIDTH_LIMIT_EXCEEDED', context),\n\n notExtended: (message: string = 'Not Extended', context?: Record<string, any>) =>\n new AppError(message, 510, 'NOT_EXTENDED', context),\n\n networkAuthenticationRequired: (message: string = 'Network Authentication Required', context?: Record<string, any>) =>\n new AppError(message, 511, 'NETWORK_AUTHENTICATION_REQUIRED', context),\n\n networkConnectTimeout: (message: string = 'Network Connect Timeout', context?: Record<string, any>) =>\n new AppError(message, 599, 'NETWORK_CONNECT_TIMEOUT', context),\n};\n\n// Default export\nexport default {\n AppError,\n formatError,\n handleError,\n asyncHandler,\n expressErrorHandler,\n createErrorResponse,\n withRetry,\n Errors,\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Error Shield - A comprehensive error handling utility for Node.js & Express.js
3
+ *
4
+ * Provides utilities for error handling, formatting, logging, retry logic,
5
+ * and error chaining with full TypeScript support.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Structured representation of an error with optional metadata.
11
+ */
12
+ interface ErrorDetails {
13
+ message: string;
14
+ code?: string;
15
+ statusCode?: number;
16
+ stack?: string;
17
+ timestamp?: string;
18
+ context?: Record<string, any>;
19
+ /** Cause chain information when error chaining is used */
20
+ cause?: ErrorDetails;
21
+ }
22
+ /**
23
+ * Options for configuring error handling behavior.
24
+ */
25
+ interface ErrorHandlerOptions {
26
+ includeStack?: boolean;
27
+ includeTimestamp?: boolean;
28
+ format?: 'json' | 'string';
29
+ logger?: (error: ErrorDetails) => void;
30
+ context?: Record<string, any>;
31
+ }
32
+ type ErrorFn = (message: string, context?: Record<string, any>) => AppError;
33
+ type ErrorMap = Record<string, ErrorFn>;
34
+ /**
35
+ * Configuration options for the retry utility.
36
+ */
37
+ interface RetryOptions {
38
+ /** Maximum number of retry attempts (default: 3) */
39
+ maxRetries?: number;
40
+ /** Backoff strategy between retries (default: 'exponential') */
41
+ backoff?: 'exponential' | 'linear' | 'fixed';
42
+ /** Initial delay in milliseconds before the first retry (default: 1000) */
43
+ initialDelay?: number;
44
+ /** Maximum delay in milliseconds between retries (default: 30000) */
45
+ maxDelay?: number;
46
+ /**
47
+ * Optional predicate to determine whether a retry should be attempted.
48
+ * If provided and returns `false`, the error is thrown immediately.
49
+ */
50
+ retryIf?: (error: Error) => boolean;
51
+ /** Optional callback invoked before each retry attempt */
52
+ onRetry?: (error: Error, attempt: number) => void;
53
+ /** Whether to add random jitter to the delay to prevent thundering herd (default: true) */
54
+ jitter?: boolean;
55
+ }
56
+ /**
57
+ * Custom Error class with additional properties for structured error handling.
58
+ *
59
+ * Supports error chaining via the native ES2022 `Error.cause` feature.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const error = new AppError('Something broke', 500, 'INTERNAL', { userId: 123 });
64
+ *
65
+ * // With error chaining
66
+ * try {
67
+ * await fetchData();
68
+ * } catch (err) {
69
+ * throw new AppError('Failed to fetch data', 502, 'FETCH_FAILED', undefined, err);
70
+ * }
71
+ * ```
72
+ */
73
+ declare class AppError extends Error {
74
+ code?: string;
75
+ statusCode?: number;
76
+ context?: Record<string, any>;
77
+ isOperational: boolean;
78
+ constructor(message: string, statusCode?: number, code?: string, context?: Record<string, any>, cause?: Error);
79
+ /**
80
+ * Wraps an existing error as the cause of a new `AppError`.
81
+ *
82
+ * This is a convenience factory for error chaining — it creates a new
83
+ * `AppError` whose `.cause` is set to the original error.
84
+ *
85
+ * @param originalError - The caught error to wrap
86
+ * @param message - Human-readable description of the higher-level failure
87
+ * @param statusCode - HTTP status code (default: 500)
88
+ * @param code - Machine-readable error code
89
+ * @param context - Additional context metadata
90
+ * @returns A new `AppError` with `originalError` as its cause
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * try {
95
+ * await db.query('SELECT ...');
96
+ * } catch (err) {
97
+ * throw AppError.wrap(err, 'Database query failed', 500, 'DB_ERROR');
98
+ * }
99
+ * ```
100
+ */
101
+ static wrap(originalError: Error, message: string, statusCode?: number, code?: string, context?: Record<string, any>): AppError;
102
+ }
103
+ /**
104
+ * Formats an error into a structured {@link ErrorDetails} object.
105
+ *
106
+ * When the error has a `.cause`, the cause chain is recursively formatted
107
+ * and included in the output.
108
+ */
109
+ declare function formatError(error: Error | AppError, options?: ErrorHandlerOptions): ErrorDetails;
110
+ /**
111
+ * Handles errors with optional logging and formatting.
112
+ */
113
+ declare function handleError(error: Error | AppError, options?: ErrorHandlerOptions): ErrorDetails;
114
+ /**
115
+ * Async error wrapper — catches errors from async route handlers
116
+ * and forwards them to Express's `next()` function.
117
+ */
118
+ declare function asyncHandler<T extends (...args: any[]) => Promise<any>>(fn: T): T;
119
+ /**
120
+ * Express.js error handler middleware factory.
121
+ *
122
+ * Returns a standard Express error-handling middleware that formats
123
+ * the error and sends an appropriate JSON or string response.
124
+ */
125
+ declare function expressErrorHandler(options?: ErrorHandlerOptions): (err: Error | AppError, req: any, res: any, next: any) => void;
126
+ /**
127
+ * Creates a standardized error response.
128
+ */
129
+ declare function createErrorResponse(message: string, statusCode?: number, code?: string, context?: Record<string, any>): AppError;
130
+ /**
131
+ * Executes an async function with automatic retries on failure.
132
+ *
133
+ * Supports exponential, linear, and fixed backoff strategies with
134
+ * optional jitter, conditional retry predicates, and retry callbacks.
135
+ *
136
+ * If all retries are exhausted, the **last** error is thrown. The previous
137
+ * attempt errors are accessible via the error's `.cause` chain and the
138
+ * `attempts` property attached to the final error.
139
+ *
140
+ * @typeParam T - The return type of the async function
141
+ * @param fn - The async function to execute with retries
142
+ * @param options - Retry configuration options
143
+ * @returns The result of the successful function execution
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const data = await withRetry(
148
+ * () => fetch('https://api.example.com/data').then(r => r.json()),
149
+ * {
150
+ * maxRetries: 5,
151
+ * backoff: 'exponential',
152
+ * initialDelay: 500,
153
+ * retryIf: (err) => err.message.includes('ECONNREFUSED'),
154
+ * onRetry: (err, attempt) => console.log(`Retry ${attempt}: ${err.message}`),
155
+ * }
156
+ * );
157
+ * ```
158
+ */
159
+ declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
160
+ /**
161
+ * Pre-built factory functions for common HTTP error responses.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * throw Errors.notFound('User not found', { userId: 42 });
166
+ * throw Errors.unauthorized();
167
+ * throw Errors.tooManyRequests('Rate limit exceeded');
168
+ * ```
169
+ */
170
+ declare const Errors: ErrorMap;
171
+ declare const _default: {
172
+ AppError: typeof AppError;
173
+ formatError: typeof formatError;
174
+ handleError: typeof handleError;
175
+ asyncHandler: typeof asyncHandler;
176
+ expressErrorHandler: typeof expressErrorHandler;
177
+ createErrorResponse: typeof createErrorResponse;
178
+ withRetry: typeof withRetry;
179
+ Errors: ErrorMap;
180
+ };
181
+
182
+ export { AppError, type ErrorDetails, type ErrorFn, type ErrorHandlerOptions, type ErrorMap, Errors, type RetryOptions, asyncHandler, createErrorResponse, _default as default, expressErrorHandler, formatError, handleError, withRetry };
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "error-shield",
3
- "version": "1.2.3",
4
- "description": "Comprehensive error handling utility for Node.js & Express.js — custom error classes, async handler wrapper, Express middleware, HTTP error creators, and TypeScript support.",
3
+ "version": "2.0.1",
4
+ "description": "Comprehensive error handling utility for Node.js & Express.js — custom error classes, async handler wrapper, Express middleware, HTTP error creators, retry logic, error chaining, and TypeScript support.",
5
+ "type": "module",
5
6
  "keywords": [
6
7
  "error-handling",
7
8
  "error-handler",
@@ -26,7 +27,11 @@
26
27
  "validation-error",
27
28
  "app-error",
28
29
  "error-utility",
29
- "structured-errors"
30
+ "structured-errors",
31
+ "error-chaining",
32
+ "retry",
33
+ "retry-logic",
34
+ "exponential-backoff"
30
35
  ],
31
36
  "homepage": "https://github.com/Gopinathgopi13/error-shield",
32
37
  "repository": {
@@ -35,13 +40,38 @@
35
40
  },
36
41
  "author": "Gopinath Kathirvel",
37
42
  "license": "ISC",
38
- "main": "dist/index.js",
39
- "types": "dist/index.d.ts",
43
+ "main": "./dist/cjs/index.cjs",
44
+ "module": "./dist/esm/index.js",
45
+ "types": "./dist/types/index.d.ts",
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/types/index.d.ts",
49
+ "import": "./dist/esm/index.js",
50
+ "require": "./dist/cjs/index.cjs"
51
+ }
52
+ },
53
+ "files": [
54
+ "dist",
55
+ "README.md",
56
+ "LICENSE"
57
+ ],
40
58
  "scripts": {
41
- "build": "tsc",
42
- "start": "node dist/index.js"
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "test:coverage": "vitest run --coverage",
64
+ "lint": "tsc --noEmit",
65
+ "prepublishOnly": "npm run build"
43
66
  },
44
67
  "engines": {
45
- "node": ">=14.0.0"
68
+ "node": ">=16.0.0"
69
+ },
70
+ "devDependencies": {
71
+ "@types/node": "^20.0.0",
72
+ "tsup": "^8.0.0",
73
+ "typescript": "^5.3.0",
74
+ "vitest": "^2.0.0",
75
+ "@vitest/coverage-v8": "^2.0.0"
46
76
  }
47
77
  }