@bombillazo/error-x 0.4.6 → 0.5.0
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.
- package/README.md +479 -195
- package/dist/index.cjs +563 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1067 -429
- package/dist/index.d.ts +1067 -429
- package/dist/index.js +561 -233
- package/dist/index.js.map +1 -1
- package/package.json +2 -15
package/dist/index.d.cts
CHANGED
|
@@ -64,12 +64,8 @@ type ErrorXOptions<TMetadata extends ErrorXMetadata = ErrorXMetadata> = {
|
|
|
64
64
|
cause?: ErrorXCause | Error | unknown;
|
|
65
65
|
/** Additional context and debugging information */
|
|
66
66
|
metadata?: TMetadata | undefined;
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
/** Documentation URL for this specific error */
|
|
70
|
-
docsUrl?: string | undefined;
|
|
71
|
-
/** Where the error originated (service name, module, component) */
|
|
72
|
-
source?: string | undefined;
|
|
67
|
+
/** HTTP status code associated with this error */
|
|
68
|
+
httpStatus?: number | undefined;
|
|
73
69
|
};
|
|
74
70
|
/**
|
|
75
71
|
* Simplified representation of an error cause for serialization.
|
|
@@ -85,13 +81,30 @@ type ErrorXCause = {
|
|
|
85
81
|
/** Stack trace (optional) */
|
|
86
82
|
stack?: string;
|
|
87
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Context passed to the transform function when creating errors via `.create()`.
|
|
86
|
+
* Contains information about the preset being used.
|
|
87
|
+
*
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
type ErrorXTransformContext = {
|
|
91
|
+
/** The preset key used (if any) */
|
|
92
|
+
presetKey: string | number | undefined;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Transform function signature for custom error classes.
|
|
96
|
+
* Transforms options after merge but before instantiation.
|
|
97
|
+
*
|
|
98
|
+
* @public
|
|
99
|
+
*/
|
|
100
|
+
type ErrorXTransform<TMetadata extends ErrorXMetadata = ErrorXMetadata> = (opts: ErrorXOptions<ErrorXMetadata>, ctx: ErrorXTransformContext) => ErrorXOptions<TMetadata>;
|
|
88
101
|
/**
|
|
89
102
|
* JSON-serializable representation of an ErrorX instance.
|
|
90
103
|
* Used for transmitting errors over network or storing in databases.
|
|
91
104
|
*
|
|
92
105
|
* @example
|
|
93
106
|
* ```typescript
|
|
94
|
-
* const serialized:
|
|
107
|
+
* const serialized: ErrorXSerialized = {
|
|
95
108
|
* name: 'AuthError',
|
|
96
109
|
* message: 'Authentication failed.',
|
|
97
110
|
* code: 'AUTH_FAILED',
|
|
@@ -99,13 +112,16 @@ type ErrorXCause = {
|
|
|
99
112
|
* stack: 'Error: Authentication failed.\n at login (auth.ts:42:15)',
|
|
100
113
|
* metadata: { userId: 123, loginAttempt: 3 },
|
|
101
114
|
* timestamp: 1705315845123,
|
|
102
|
-
*
|
|
115
|
+
* httpStatus: 401,
|
|
116
|
+
* original: {
|
|
103
117
|
* name: 'NetworkError',
|
|
104
118
|
* message: 'Request timeout.',
|
|
105
119
|
* stack: '...'
|
|
106
120
|
* },
|
|
107
|
-
*
|
|
108
|
-
*
|
|
121
|
+
* chain: [
|
|
122
|
+
* { name: 'AuthError', message: 'Authentication failed.' },
|
|
123
|
+
* { name: 'NetworkError', message: 'Request timeout.' }
|
|
124
|
+
* ]
|
|
109
125
|
* }
|
|
110
126
|
* ```
|
|
111
127
|
*
|
|
@@ -126,15 +142,20 @@ type ErrorXSerialized = {
|
|
|
126
142
|
metadata: ErrorXMetadata | undefined;
|
|
127
143
|
/** Unix epoch timestamp (milliseconds) when error was created */
|
|
128
144
|
timestamp: number;
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
/** Where the error originated */
|
|
136
|
-
source?: string;
|
|
145
|
+
/** HTTP status code associated with this error */
|
|
146
|
+
httpStatus?: number;
|
|
147
|
+
/** Serialized non-ErrorX entity this was wrapped from (if created via ErrorX.from()) */
|
|
148
|
+
original?: ErrorXCause;
|
|
149
|
+
/** Serialized error chain timeline (this error and all ancestors) */
|
|
150
|
+
chain?: ErrorXCause[];
|
|
137
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* Type representing valid preset keys for error creation.
|
|
154
|
+
* Allows both string and number keys while preventing accidental misuse.
|
|
155
|
+
*
|
|
156
|
+
* @public
|
|
157
|
+
*/
|
|
158
|
+
type ErrorXBasePresetKey = (number & {}) | (string & {});
|
|
138
159
|
|
|
139
160
|
/**
|
|
140
161
|
* Configuration interface for ErrorX global settings
|
|
@@ -142,12 +163,6 @@ type ErrorXSerialized = {
|
|
|
142
163
|
* @public
|
|
143
164
|
*/
|
|
144
165
|
interface ErrorXConfig {
|
|
145
|
-
/** Default source identifier for all errors (e.g., service name, module name) */
|
|
146
|
-
source?: string;
|
|
147
|
-
/** Base URL for error documentation */
|
|
148
|
-
docsBaseURL?: string;
|
|
149
|
-
/** Mapping of error codes to documentation paths */
|
|
150
|
-
docsMap?: Record<string, string>;
|
|
151
166
|
/**
|
|
152
167
|
* Control stack trace cleaning behavior
|
|
153
168
|
* - true: Enable automatic stack trace cleaning (default)
|
|
@@ -168,12 +183,6 @@ interface ErrorXConfig {
|
|
|
168
183
|
* ```typescript
|
|
169
184
|
* // Configure globally (optional)
|
|
170
185
|
* ErrorX.configure({
|
|
171
|
-
* source: 'my-service',
|
|
172
|
-
* docsBaseURL: 'https://docs.example.com',
|
|
173
|
-
* docsMap: {
|
|
174
|
-
* 'AUTH_FAILED': 'errors/authentication',
|
|
175
|
-
* 'DB_ERROR': 'errors/database'
|
|
176
|
-
* },
|
|
177
186
|
* cleanStackDelimiter: 'my-app-entry' // Clean stack traces after this line
|
|
178
187
|
* })
|
|
179
188
|
*
|
|
@@ -211,14 +220,27 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
211
220
|
metadata: TMetadata | undefined;
|
|
212
221
|
/** Unix epoch timestamp (milliseconds) when the error was created */
|
|
213
222
|
timestamp: number;
|
|
214
|
-
/**
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
|
|
223
|
+
/** HTTP status code associated with this error */
|
|
224
|
+
httpStatus: number | undefined;
|
|
225
|
+
/** Serialized non-ErrorX entity this was wrapped from (if created via ErrorX.from()) */
|
|
226
|
+
original: ErrorXCause | undefined;
|
|
227
|
+
/** Error chain timeline: [this, parent, grandparent, ...] - single source of truth */
|
|
228
|
+
private _chain;
|
|
229
|
+
/**
|
|
230
|
+
* Gets the immediate parent ErrorX in the chain (if any).
|
|
231
|
+
* @returns The ErrorX that caused this error, or undefined if this is the root
|
|
232
|
+
*/
|
|
233
|
+
get parent(): ErrorX | undefined;
|
|
234
|
+
/**
|
|
235
|
+
* Gets the deepest ErrorX in the chain (the original root cause).
|
|
236
|
+
* @returns The root cause ErrorX, or undefined if chain has only this error
|
|
237
|
+
*/
|
|
238
|
+
get root(): ErrorX | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* Gets the full error chain timeline.
|
|
241
|
+
* @returns Array of ErrorX instances: [this, parent, grandparent, ...]
|
|
242
|
+
*/
|
|
243
|
+
get chain(): readonly ErrorX[];
|
|
222
244
|
/**
|
|
223
245
|
* Creates a new ErrorX instance with enhanced error handling capabilities.
|
|
224
246
|
*
|
|
@@ -274,12 +296,7 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
274
296
|
* @example
|
|
275
297
|
* ```typescript
|
|
276
298
|
* ErrorX.configure({
|
|
277
|
-
*
|
|
278
|
-
* docsBaseURL: 'https://docs.example.com/errors',
|
|
279
|
-
* docsMap: {
|
|
280
|
-
* 'AUTH_FAILED': 'authentication-errors',
|
|
281
|
-
* 'DB_ERROR': 'database-errors'
|
|
282
|
-
* },
|
|
299
|
+
* cleanStack: true, // Enable stack trace cleaning
|
|
283
300
|
* cleanStackDelimiter: 'app-entry-point' // Trim stack traces after this line
|
|
284
301
|
* })
|
|
285
302
|
* ```
|
|
@@ -301,13 +318,6 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
301
318
|
* ```
|
|
302
319
|
*/
|
|
303
320
|
static resetConfig(): void;
|
|
304
|
-
/**
|
|
305
|
-
* Validates and normalizes the type field
|
|
306
|
-
*
|
|
307
|
-
* @param type - Type value to validate
|
|
308
|
-
* @returns Validated type string or undefined if invalid/empty
|
|
309
|
-
*/
|
|
310
|
-
private static validateType;
|
|
311
321
|
/**
|
|
312
322
|
* Validates if an object is a valid ErrorXOptions object.
|
|
313
323
|
* Checks that the object only contains accepted ErrorXOptions fields.
|
|
@@ -331,15 +341,6 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
331
341
|
* ```
|
|
332
342
|
*/
|
|
333
343
|
private static generateDefaultCode;
|
|
334
|
-
/**
|
|
335
|
-
* Preserves the original error's stack trace while updating the error message.
|
|
336
|
-
* Combines the new error's message with the original error's stack trace from ErrorXCause.
|
|
337
|
-
*
|
|
338
|
-
* @param cause - The ErrorXCause containing the original stack to preserve
|
|
339
|
-
* @param newError - The new error whose message to use
|
|
340
|
-
* @returns Combined stack trace with new error message and original stack
|
|
341
|
-
*/
|
|
342
|
-
private static preserveOriginalStackFromCause;
|
|
343
344
|
/**
|
|
344
345
|
* Cleans a stack trace by removing ErrorX internal method calls and optionally trimming after a delimiter.
|
|
345
346
|
* This provides cleaner stack traces that focus on user code.
|
|
@@ -416,8 +417,12 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
416
417
|
* Handles strings, regular Error objects, API response objects, and unknown values.
|
|
417
418
|
* Extracts metadata directly from objects if present.
|
|
418
419
|
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
420
|
+
* This is a "wrapping" operation - the resulting ErrorX represents the same error
|
|
421
|
+
* in ErrorX form. The original source is stored in the `original` property.
|
|
422
|
+
*
|
|
423
|
+
* @param payload - Value to convert to ErrorX
|
|
424
|
+
* @param overrides - Optional overrides to deep-merge with extracted properties
|
|
425
|
+
* @returns ErrorX instance with extracted properties and `original` set
|
|
421
426
|
*
|
|
422
427
|
* @example
|
|
423
428
|
* ```typescript
|
|
@@ -435,12 +440,19 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
435
440
|
* }
|
|
436
441
|
* const error3 = ErrorX.from(apiError)
|
|
437
442
|
* // error3.metadata = { userId: 123, endpoint: '/api/users' }
|
|
443
|
+
* // error3.original = { message: 'User not found', name: undefined, stack: undefined }
|
|
444
|
+
*
|
|
445
|
+
* // With overrides
|
|
446
|
+
* const error4 = ErrorX.from(new Error('Connection failed'), {
|
|
447
|
+
* httpStatus: 500,
|
|
448
|
+
* metadata: { context: 'db-layer' }
|
|
449
|
+
* })
|
|
438
450
|
* ```
|
|
439
451
|
*/
|
|
440
|
-
static from<TMetadata extends ErrorXMetadata = ErrorXMetadata>(error: ErrorX<TMetadata
|
|
441
|
-
static from(
|
|
442
|
-
static from(
|
|
443
|
-
static from(
|
|
452
|
+
static from<TMetadata extends ErrorXMetadata = ErrorXMetadata>(error: ErrorX<TMetadata>, overrides?: Partial<ErrorXOptions<TMetadata>>): ErrorX<TMetadata>;
|
|
453
|
+
static from<TMetadata extends ErrorXMetadata = ErrorXMetadata>(payload: Error, overrides?: Partial<ErrorXOptions<TMetadata>>): ErrorX<TMetadata>;
|
|
454
|
+
static from<TMetadata extends ErrorXMetadata = ErrorXMetadata>(payload: string, overrides?: Partial<ErrorXOptions<TMetadata>>): ErrorX<TMetadata>;
|
|
455
|
+
static from<TMetadata extends ErrorXMetadata = ErrorXMetadata>(payload: unknown, overrides?: Partial<ErrorXOptions<TMetadata>>): ErrorX<TMetadata>;
|
|
444
456
|
/**
|
|
445
457
|
* Converts the ErrorX instance to a detailed string representation.
|
|
446
458
|
* Includes error name, message, code, timestamp, metadata, and stack trace.
|
|
@@ -503,467 +515,1093 @@ declare class ErrorX<TMetadata extends ErrorXMetadata = ErrorXMetadata> extends
|
|
|
503
515
|
* ```
|
|
504
516
|
*/
|
|
505
517
|
static fromJSON<TMetadata extends ErrorXMetadata = ErrorXMetadata>(serialized: ErrorXSerialized): ErrorX<TMetadata>;
|
|
518
|
+
/**
|
|
519
|
+
* Creates a new instance of this error class using optional presets and overrides.
|
|
520
|
+
* This is a factory method that supports preset-based error creation with
|
|
521
|
+
* full TypeScript autocomplete for preset keys.
|
|
522
|
+
*
|
|
523
|
+
* Define static properties on your subclass to customize behavior:
|
|
524
|
+
* - `presets`: Record of preset configurations keyed by identifier
|
|
525
|
+
* - `defaultPreset`: Key of preset to use as fallback
|
|
526
|
+
* - `defaults`: Default values for all errors of this class
|
|
527
|
+
* - `transform`: Function to transform options before instantiation
|
|
528
|
+
*
|
|
529
|
+
* Supported call signatures:
|
|
530
|
+
* - `create()` - uses defaultPreset
|
|
531
|
+
* - `create(presetKey)` - uses specified preset
|
|
532
|
+
* - `create(presetKey, overrides)` - preset with overrides
|
|
533
|
+
* - `create(overrides)` - just overrides, uses defaultPreset
|
|
534
|
+
*
|
|
535
|
+
* @param presetKeyOrOverrides - Preset key (string/number) or overrides object
|
|
536
|
+
* @param overrides - Optional overrides when first arg is preset key
|
|
537
|
+
* @returns New instance of this error class
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* class DBError extends ErrorX<{ query?: string }> {
|
|
542
|
+
* static presets = {
|
|
543
|
+
* 9333: { message: 'Connection timeout', code: 'TIMEOUT' },
|
|
544
|
+
* CONN_REFUSED: { message: 'Connection refused', code: 'CONN_REFUSED' },
|
|
545
|
+
* GENERIC: { message: 'A database error occurred', code: 'ERROR' },
|
|
546
|
+
* }
|
|
547
|
+
* static defaultPreset = 'GENERIC'
|
|
548
|
+
* static defaults = { httpStatus: 500 }
|
|
549
|
+
* static transform = (opts, ctx) => ({
|
|
550
|
+
* ...opts,
|
|
551
|
+
* code: `DB_${opts.code}`,
|
|
552
|
+
* })
|
|
553
|
+
* }
|
|
554
|
+
*
|
|
555
|
+
* DBError.create() // uses defaultPreset
|
|
556
|
+
* DBError.create(9333) // uses preset 9333
|
|
557
|
+
* DBError.create('CONN_REFUSED') // uses preset CONN_REFUSED
|
|
558
|
+
* DBError.create(9333, { message: 'Custom' }) // preset + overrides
|
|
559
|
+
* DBError.create({ message: 'Custom' }) // just overrides
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
static create<T extends ErrorXMetadata = ErrorXMetadata>(this: new (options?: ErrorXOptions<T>) => ErrorX<T>, presetKeyOrOverrides?: string | number | Partial<ErrorXOptions<ErrorXMetadata>>, overrides?: Partial<ErrorXOptions<ErrorXMetadata>>): ErrorX<T>;
|
|
506
563
|
}
|
|
507
564
|
|
|
508
565
|
/**
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
|
|
512
|
-
|
|
566
|
+
* Database error presets for common scenarios.
|
|
567
|
+
* Defined outside the class to enable type inference for preset keys.
|
|
568
|
+
*/
|
|
569
|
+
declare const dbPresets: {
|
|
570
|
+
readonly CONNECTION_FAILED: {
|
|
571
|
+
readonly code: "CONNECTION_FAILED";
|
|
572
|
+
readonly name: "DBConnectionError";
|
|
573
|
+
readonly message: "Failed to connect to database.";
|
|
574
|
+
readonly uiMessage: "Unable to connect to the database. Please try again later.";
|
|
575
|
+
};
|
|
576
|
+
readonly CONNECTION_TIMEOUT: {
|
|
577
|
+
readonly code: "CONNECTION_TIMEOUT";
|
|
578
|
+
readonly name: "DBConnectionTimeoutError";
|
|
579
|
+
readonly message: "Database connection timed out.";
|
|
580
|
+
readonly uiMessage: "The database connection timed out. Please try again.";
|
|
581
|
+
};
|
|
582
|
+
readonly CONNECTION_REFUSED: {
|
|
583
|
+
readonly code: "CONNECTION_REFUSED";
|
|
584
|
+
readonly name: "DBConnectionRefusedError";
|
|
585
|
+
readonly message: "Database connection refused.";
|
|
586
|
+
readonly uiMessage: "Unable to connect to the database. Please try again later.";
|
|
587
|
+
};
|
|
588
|
+
readonly CONNECTION_LOST: {
|
|
589
|
+
readonly code: "CONNECTION_LOST";
|
|
590
|
+
readonly name: "DBConnectionLostError";
|
|
591
|
+
readonly message: "Database connection lost.";
|
|
592
|
+
readonly uiMessage: "The database connection was lost. Please try again.";
|
|
593
|
+
};
|
|
594
|
+
readonly QUERY_FAILED: {
|
|
595
|
+
readonly code: "QUERY_FAILED";
|
|
596
|
+
readonly name: "DBQueryError";
|
|
597
|
+
readonly message: "Database query failed.";
|
|
598
|
+
readonly uiMessage: "The database operation failed. Please try again.";
|
|
599
|
+
};
|
|
600
|
+
readonly QUERY_TIMEOUT: {
|
|
601
|
+
readonly code: "QUERY_TIMEOUT";
|
|
602
|
+
readonly name: "DBQueryTimeoutError";
|
|
603
|
+
readonly message: "Database query timed out.";
|
|
604
|
+
readonly uiMessage: "The database operation took too long. Please try again.";
|
|
605
|
+
};
|
|
606
|
+
readonly SYNTAX_ERROR: {
|
|
607
|
+
readonly code: "SYNTAX_ERROR";
|
|
608
|
+
readonly name: "DBSyntaxError";
|
|
609
|
+
readonly message: "Invalid query syntax.";
|
|
610
|
+
readonly uiMessage: "An internal error occurred. Please contact support.";
|
|
611
|
+
};
|
|
612
|
+
readonly UNIQUE_VIOLATION: {
|
|
613
|
+
readonly code: "UNIQUE_VIOLATION";
|
|
614
|
+
readonly name: "DBUniqueViolationError";
|
|
615
|
+
readonly message: "Unique constraint violation.";
|
|
616
|
+
readonly uiMessage: "This record already exists.";
|
|
617
|
+
readonly httpStatus: 409;
|
|
618
|
+
};
|
|
619
|
+
readonly FOREIGN_KEY_VIOLATION: {
|
|
620
|
+
readonly code: "FOREIGN_KEY_VIOLATION";
|
|
621
|
+
readonly name: "DBForeignKeyError";
|
|
622
|
+
readonly message: "Foreign key constraint violation.";
|
|
623
|
+
readonly uiMessage: "This operation references a record that does not exist.";
|
|
624
|
+
readonly httpStatus: 400;
|
|
625
|
+
};
|
|
626
|
+
readonly NOT_NULL_VIOLATION: {
|
|
627
|
+
readonly code: "NOT_NULL_VIOLATION";
|
|
628
|
+
readonly name: "DBNotNullError";
|
|
629
|
+
readonly message: "Not null constraint violation.";
|
|
630
|
+
readonly uiMessage: "A required field is missing.";
|
|
631
|
+
readonly httpStatus: 400;
|
|
632
|
+
};
|
|
633
|
+
readonly CHECK_VIOLATION: {
|
|
634
|
+
readonly code: "CHECK_VIOLATION";
|
|
635
|
+
readonly name: "DBCheckViolationError";
|
|
636
|
+
readonly message: "Check constraint violation.";
|
|
637
|
+
readonly uiMessage: "The provided data is invalid.";
|
|
638
|
+
readonly httpStatus: 400;
|
|
639
|
+
};
|
|
640
|
+
readonly TRANSACTION_FAILED: {
|
|
641
|
+
readonly code: "TRANSACTION_FAILED";
|
|
642
|
+
readonly name: "DBTransactionError";
|
|
643
|
+
readonly message: "Database transaction failed.";
|
|
644
|
+
readonly uiMessage: "The operation failed. Please try again.";
|
|
645
|
+
};
|
|
646
|
+
readonly DEADLOCK: {
|
|
647
|
+
readonly code: "DEADLOCK";
|
|
648
|
+
readonly name: "DBDeadlockError";
|
|
649
|
+
readonly message: "Database deadlock detected.";
|
|
650
|
+
readonly uiMessage: "The operation encountered a conflict. Please try again.";
|
|
651
|
+
readonly httpStatus: 409;
|
|
652
|
+
};
|
|
653
|
+
readonly NOT_FOUND: {
|
|
654
|
+
readonly code: "NOT_FOUND";
|
|
655
|
+
readonly name: "DBNotFoundError";
|
|
656
|
+
readonly message: "Record not found.";
|
|
657
|
+
readonly uiMessage: "The requested record was not found.";
|
|
658
|
+
readonly httpStatus: 404;
|
|
659
|
+
};
|
|
660
|
+
readonly UNKNOWN: {
|
|
661
|
+
readonly code: "UNKNOWN";
|
|
662
|
+
readonly name: "DBErrorX";
|
|
663
|
+
readonly message: "An unknown database error occurred.";
|
|
664
|
+
readonly uiMessage: "A database error occurred. Please try again later.";
|
|
665
|
+
};
|
|
666
|
+
};
|
|
667
|
+
/**
|
|
668
|
+
* Valid preset keys for DBErrorX.create()
|
|
669
|
+
* Derived from the presets object. Provides autocomplete for known presets
|
|
670
|
+
* while allowing any string for flexibility.
|
|
671
|
+
*/
|
|
672
|
+
type DBErrorXPresetKey = keyof typeof dbPresets | ErrorXBasePresetKey;
|
|
673
|
+
/**
|
|
674
|
+
* Metadata type for database errors.
|
|
675
|
+
* Provides context about the database operation that failed.
|
|
513
676
|
*
|
|
514
|
-
*
|
|
677
|
+
* @public
|
|
678
|
+
*/
|
|
679
|
+
type DBErrorXMetadata = {
|
|
680
|
+
/** The SQL query or operation that failed */
|
|
681
|
+
query?: string;
|
|
682
|
+
/** The table involved in the operation */
|
|
683
|
+
table?: string;
|
|
684
|
+
/** The database name */
|
|
685
|
+
database?: string;
|
|
686
|
+
/** The operation type (SELECT, INSERT, UPDATE, DELETE, etc.) */
|
|
687
|
+
operation?: 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE' | 'TRANSACTION' | 'CONNECTION' | string;
|
|
688
|
+
/** The column involved (for constraint errors) */
|
|
689
|
+
column?: string;
|
|
690
|
+
/** The constraint name (for constraint violations) */
|
|
691
|
+
constraint?: string;
|
|
692
|
+
/** Duration of the operation in milliseconds */
|
|
693
|
+
duration?: number;
|
|
694
|
+
};
|
|
695
|
+
/**
|
|
696
|
+
* Database Error class with presets for common database error scenarios.
|
|
515
697
|
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
* ```
|
|
698
|
+
* Provides a type-safe way to create database errors with:
|
|
699
|
+
* - Common error presets (connection, query, timeout, constraints)
|
|
700
|
+
* - Automatic code prefixing with `DB_`
|
|
701
|
+
* - Full `instanceof` support
|
|
702
|
+
* - Typed metadata for database context
|
|
522
703
|
*
|
|
523
|
-
*
|
|
524
|
-
* Customize the error while keeping other preset values:
|
|
704
|
+
* @example
|
|
525
705
|
* ```typescript
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
* })
|
|
531
|
-
* // Result: 404 error with custom message but keeps httpStatus, code, name, uiMessage, type
|
|
532
|
-
* ```
|
|
706
|
+
* // Basic usage with preset
|
|
707
|
+
* throw DBErrorX.create('CONNECTION_FAILED')
|
|
708
|
+
* throw DBErrorX.create('QUERY_FAILED')
|
|
709
|
+
* throw DBErrorX.create('TIMEOUT')
|
|
533
710
|
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
711
|
+
* // With metadata
|
|
712
|
+
* throw DBErrorX.create('QUERY_FAILED', {
|
|
713
|
+
* message: 'Failed to fetch user',
|
|
714
|
+
* metadata: {
|
|
715
|
+
* query: 'SELECT * FROM users WHERE id = ?',
|
|
716
|
+
* table: 'users',
|
|
717
|
+
* operation: 'SELECT'
|
|
718
|
+
* }
|
|
540
719
|
* })
|
|
541
|
-
* ```
|
|
542
720
|
*
|
|
543
|
-
*
|
|
544
|
-
* Chain errors by adding a cause:
|
|
545
|
-
* ```typescript
|
|
721
|
+
* // With error chaining
|
|
546
722
|
* try {
|
|
547
|
-
*
|
|
548
|
-
* } catch (
|
|
549
|
-
* throw
|
|
550
|
-
* ...http[500],
|
|
551
|
-
* cause: originalError,
|
|
552
|
-
* metadata: { operation: 'database-query' }
|
|
553
|
-
* })
|
|
723
|
+
* await db.query(sql)
|
|
724
|
+
* } catch (err) {
|
|
725
|
+
* throw DBErrorX.create('QUERY_FAILED', { cause: err })
|
|
554
726
|
* }
|
|
555
|
-
* ```
|
|
556
|
-
*
|
|
557
|
-
* ## Common HTTP Presets
|
|
558
|
-
*
|
|
559
|
-
* ### 4xx Client Errors
|
|
560
|
-
* - `400` - Bad Request - Invalid request data
|
|
561
|
-
* - `401` - Unauthorized - Authentication required
|
|
562
|
-
* - `403` - Forbidden - Insufficient permissions
|
|
563
|
-
* - `404` - Not Found - Resource not found
|
|
564
|
-
* - `405` - Method Not Allowed - HTTP method not allowed
|
|
565
|
-
* - `409` - Conflict - Resource conflict
|
|
566
|
-
* - `422` - Unprocessable Entity - Validation failed
|
|
567
|
-
* - `429` - Too Many Requests - Rate limit exceeded
|
|
568
|
-
*
|
|
569
|
-
* ### 5xx Server Errors
|
|
570
|
-
* - `500` - Internal Server Error - Unexpected server error
|
|
571
|
-
* - `501` - Not Implemented - Feature not implemented
|
|
572
|
-
* - `502` - Bad Gateway - Upstream server error
|
|
573
|
-
* - `503` - Service Unavailable - Service temporarily down
|
|
574
|
-
* - `504` - Gateway Timeout - Upstream timeout
|
|
575
|
-
*
|
|
576
|
-
* @example
|
|
577
|
-
* ```typescript
|
|
578
|
-
* // API endpoint example
|
|
579
|
-
* app.get('/users/:id', async (req, res) => {
|
|
580
|
-
* const user = await db.users.findById(req.params.id)
|
|
581
|
-
*
|
|
582
|
-
* if (!user) {
|
|
583
|
-
* throw new ErrorX({
|
|
584
|
-
* ...http[404],
|
|
585
|
-
* message: 'User not found',
|
|
586
|
-
* metadata: { userId: req.params.id }
|
|
587
|
-
* })
|
|
588
|
-
* }
|
|
589
|
-
*
|
|
590
|
-
* res.json(user)
|
|
591
|
-
* })
|
|
592
727
|
*
|
|
593
|
-
* //
|
|
594
|
-
*
|
|
595
|
-
* if (!req.user) {
|
|
596
|
-
* throw new ErrorX(http[401])
|
|
597
|
-
* }
|
|
598
|
-
* next()
|
|
599
|
-
* }
|
|
728
|
+
* // Just overrides (uses default UNKNOWN)
|
|
729
|
+
* throw DBErrorX.create({ message: 'Database error' })
|
|
600
730
|
*
|
|
601
|
-
* //
|
|
602
|
-
* if (
|
|
603
|
-
*
|
|
604
|
-
* ...http[429],
|
|
605
|
-
* metadata: {
|
|
606
|
-
* ip: req.ip,
|
|
607
|
-
* retryAfter: 60
|
|
608
|
-
* }
|
|
609
|
-
* })
|
|
731
|
+
* // instanceof checks
|
|
732
|
+
* if (error instanceof DBErrorX) {
|
|
733
|
+
* console.log(error.metadata?.table)
|
|
610
734
|
* }
|
|
611
735
|
* ```
|
|
612
736
|
*
|
|
613
737
|
* @public
|
|
614
738
|
*/
|
|
615
|
-
declare
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
739
|
+
declare class DBErrorX extends ErrorX<DBErrorXMetadata> {
|
|
740
|
+
/**
|
|
741
|
+
* Database error presets for common scenarios.
|
|
742
|
+
*/
|
|
743
|
+
static presets: {
|
|
744
|
+
readonly CONNECTION_FAILED: {
|
|
745
|
+
readonly code: "CONNECTION_FAILED";
|
|
746
|
+
readonly name: "DBConnectionError";
|
|
747
|
+
readonly message: "Failed to connect to database.";
|
|
748
|
+
readonly uiMessage: "Unable to connect to the database. Please try again later.";
|
|
749
|
+
};
|
|
750
|
+
readonly CONNECTION_TIMEOUT: {
|
|
751
|
+
readonly code: "CONNECTION_TIMEOUT";
|
|
752
|
+
readonly name: "DBConnectionTimeoutError";
|
|
753
|
+
readonly message: "Database connection timed out.";
|
|
754
|
+
readonly uiMessage: "The database connection timed out. Please try again.";
|
|
755
|
+
};
|
|
756
|
+
readonly CONNECTION_REFUSED: {
|
|
757
|
+
readonly code: "CONNECTION_REFUSED";
|
|
758
|
+
readonly name: "DBConnectionRefusedError";
|
|
759
|
+
readonly message: "Database connection refused.";
|
|
760
|
+
readonly uiMessage: "Unable to connect to the database. Please try again later.";
|
|
761
|
+
};
|
|
762
|
+
readonly CONNECTION_LOST: {
|
|
763
|
+
readonly code: "CONNECTION_LOST";
|
|
764
|
+
readonly name: "DBConnectionLostError";
|
|
765
|
+
readonly message: "Database connection lost.";
|
|
766
|
+
readonly uiMessage: "The database connection was lost. Please try again.";
|
|
767
|
+
};
|
|
768
|
+
readonly QUERY_FAILED: {
|
|
769
|
+
readonly code: "QUERY_FAILED";
|
|
770
|
+
readonly name: "DBQueryError";
|
|
771
|
+
readonly message: "Database query failed.";
|
|
772
|
+
readonly uiMessage: "The database operation failed. Please try again.";
|
|
773
|
+
};
|
|
774
|
+
readonly QUERY_TIMEOUT: {
|
|
775
|
+
readonly code: "QUERY_TIMEOUT";
|
|
776
|
+
readonly name: "DBQueryTimeoutError";
|
|
777
|
+
readonly message: "Database query timed out.";
|
|
778
|
+
readonly uiMessage: "The database operation took too long. Please try again.";
|
|
623
779
|
};
|
|
780
|
+
readonly SYNTAX_ERROR: {
|
|
781
|
+
readonly code: "SYNTAX_ERROR";
|
|
782
|
+
readonly name: "DBSyntaxError";
|
|
783
|
+
readonly message: "Invalid query syntax.";
|
|
784
|
+
readonly uiMessage: "An internal error occurred. Please contact support.";
|
|
785
|
+
};
|
|
786
|
+
readonly UNIQUE_VIOLATION: {
|
|
787
|
+
readonly code: "UNIQUE_VIOLATION";
|
|
788
|
+
readonly name: "DBUniqueViolationError";
|
|
789
|
+
readonly message: "Unique constraint violation.";
|
|
790
|
+
readonly uiMessage: "This record already exists.";
|
|
791
|
+
readonly httpStatus: 409;
|
|
792
|
+
};
|
|
793
|
+
readonly FOREIGN_KEY_VIOLATION: {
|
|
794
|
+
readonly code: "FOREIGN_KEY_VIOLATION";
|
|
795
|
+
readonly name: "DBForeignKeyError";
|
|
796
|
+
readonly message: "Foreign key constraint violation.";
|
|
797
|
+
readonly uiMessage: "This operation references a record that does not exist.";
|
|
798
|
+
readonly httpStatus: 400;
|
|
799
|
+
};
|
|
800
|
+
readonly NOT_NULL_VIOLATION: {
|
|
801
|
+
readonly code: "NOT_NULL_VIOLATION";
|
|
802
|
+
readonly name: "DBNotNullError";
|
|
803
|
+
readonly message: "Not null constraint violation.";
|
|
804
|
+
readonly uiMessage: "A required field is missing.";
|
|
805
|
+
readonly httpStatus: 400;
|
|
806
|
+
};
|
|
807
|
+
readonly CHECK_VIOLATION: {
|
|
808
|
+
readonly code: "CHECK_VIOLATION";
|
|
809
|
+
readonly name: "DBCheckViolationError";
|
|
810
|
+
readonly message: "Check constraint violation.";
|
|
811
|
+
readonly uiMessage: "The provided data is invalid.";
|
|
812
|
+
readonly httpStatus: 400;
|
|
813
|
+
};
|
|
814
|
+
readonly TRANSACTION_FAILED: {
|
|
815
|
+
readonly code: "TRANSACTION_FAILED";
|
|
816
|
+
readonly name: "DBTransactionError";
|
|
817
|
+
readonly message: "Database transaction failed.";
|
|
818
|
+
readonly uiMessage: "The operation failed. Please try again.";
|
|
819
|
+
};
|
|
820
|
+
readonly DEADLOCK: {
|
|
821
|
+
readonly code: "DEADLOCK";
|
|
822
|
+
readonly name: "DBDeadlockError";
|
|
823
|
+
readonly message: "Database deadlock detected.";
|
|
824
|
+
readonly uiMessage: "The operation encountered a conflict. Please try again.";
|
|
825
|
+
readonly httpStatus: 409;
|
|
826
|
+
};
|
|
827
|
+
readonly NOT_FOUND: {
|
|
828
|
+
readonly code: "NOT_FOUND";
|
|
829
|
+
readonly name: "DBNotFoundError";
|
|
830
|
+
readonly message: "Record not found.";
|
|
831
|
+
readonly uiMessage: "The requested record was not found.";
|
|
832
|
+
readonly httpStatus: 404;
|
|
833
|
+
};
|
|
834
|
+
readonly UNKNOWN: {
|
|
835
|
+
readonly code: "UNKNOWN";
|
|
836
|
+
readonly name: "DBErrorX";
|
|
837
|
+
readonly message: "An unknown database error occurred.";
|
|
838
|
+
readonly uiMessage: "A database error occurred. Please try again later.";
|
|
839
|
+
};
|
|
840
|
+
};
|
|
841
|
+
/** Default to UNKNOWN when no preset specified */
|
|
842
|
+
static defaultPreset: string;
|
|
843
|
+
/** Default httpStatus for database errors (500 = server error) */
|
|
844
|
+
static defaults: {
|
|
845
|
+
httpStatus: number;
|
|
846
|
+
};
|
|
847
|
+
/**
|
|
848
|
+
* Transform that prefixes all codes with `DB_`.
|
|
849
|
+
*/
|
|
850
|
+
static transform: ErrorXTransform<DBErrorXMetadata>;
|
|
851
|
+
/**
|
|
852
|
+
* Creates a DBErrorX from a preset key.
|
|
853
|
+
*
|
|
854
|
+
* @param presetKey - Preset key (provides autocomplete for known presets)
|
|
855
|
+
* @param overrides - Optional overrides for the preset values
|
|
856
|
+
* @returns DBErrorX instance
|
|
857
|
+
*/
|
|
858
|
+
static create(presetKey?: DBErrorXPresetKey, overrides?: Partial<ErrorXOptions<DBErrorXMetadata>>): DBErrorX;
|
|
859
|
+
static create(overrides?: Partial<ErrorXOptions<DBErrorXMetadata>>): DBErrorX;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* HTTP status code presets for all standard codes.
|
|
864
|
+
* Defined outside the class to enable type inference for preset keys.
|
|
865
|
+
*/
|
|
866
|
+
declare const httpPresets: {
|
|
867
|
+
readonly 400: {
|
|
868
|
+
readonly code: "BAD_REQUEST";
|
|
869
|
+
readonly name: "BadRequestError";
|
|
870
|
+
readonly message: "Bad request.";
|
|
871
|
+
readonly uiMessage: "The request could not be processed. Please check your input and try again.";
|
|
624
872
|
};
|
|
625
873
|
readonly 401: {
|
|
626
|
-
code:
|
|
627
|
-
name:
|
|
628
|
-
message:
|
|
629
|
-
uiMessage:
|
|
630
|
-
metadata: {
|
|
631
|
-
status: number;
|
|
632
|
-
};
|
|
874
|
+
readonly code: "UNAUTHORIZED";
|
|
875
|
+
readonly name: "UnauthorizedError";
|
|
876
|
+
readonly message: "Unauthorized.";
|
|
877
|
+
readonly uiMessage: "Authentication required. Please log in to continue.";
|
|
633
878
|
};
|
|
634
879
|
readonly 402: {
|
|
635
|
-
code:
|
|
636
|
-
name:
|
|
637
|
-
message:
|
|
638
|
-
uiMessage:
|
|
639
|
-
metadata: {
|
|
640
|
-
status: number;
|
|
641
|
-
};
|
|
880
|
+
readonly code: "PAYMENT_REQUIRED";
|
|
881
|
+
readonly name: "PaymentRequiredError";
|
|
882
|
+
readonly message: "Payment required.";
|
|
883
|
+
readonly uiMessage: "Payment is required to access this resource.";
|
|
642
884
|
};
|
|
643
885
|
readonly 403: {
|
|
644
|
-
code:
|
|
645
|
-
name:
|
|
646
|
-
message:
|
|
647
|
-
uiMessage:
|
|
648
|
-
metadata: {
|
|
649
|
-
status: number;
|
|
650
|
-
};
|
|
886
|
+
readonly code: "FORBIDDEN";
|
|
887
|
+
readonly name: "ForbiddenError";
|
|
888
|
+
readonly message: "Forbidden.";
|
|
889
|
+
readonly uiMessage: "You do not have permission to access this resource.";
|
|
651
890
|
};
|
|
652
891
|
readonly 404: {
|
|
653
|
-
code:
|
|
654
|
-
name:
|
|
655
|
-
message:
|
|
656
|
-
uiMessage:
|
|
657
|
-
metadata: {
|
|
658
|
-
status: number;
|
|
659
|
-
};
|
|
892
|
+
readonly code: "NOT_FOUND";
|
|
893
|
+
readonly name: "NotFoundError";
|
|
894
|
+
readonly message: "Not found.";
|
|
895
|
+
readonly uiMessage: "The requested resource could not be found.";
|
|
660
896
|
};
|
|
661
897
|
readonly 405: {
|
|
662
|
-
code:
|
|
663
|
-
name:
|
|
664
|
-
message:
|
|
665
|
-
uiMessage:
|
|
666
|
-
metadata: {
|
|
667
|
-
status: number;
|
|
668
|
-
};
|
|
898
|
+
readonly code: "METHOD_NOT_ALLOWED";
|
|
899
|
+
readonly name: "MethodNotAllowedError";
|
|
900
|
+
readonly message: "Method not allowed.";
|
|
901
|
+
readonly uiMessage: "This action is not allowed for the requested resource.";
|
|
669
902
|
};
|
|
670
903
|
readonly 406: {
|
|
671
|
-
code:
|
|
672
|
-
name:
|
|
673
|
-
message:
|
|
674
|
-
uiMessage:
|
|
675
|
-
metadata: {
|
|
676
|
-
status: number;
|
|
677
|
-
};
|
|
904
|
+
readonly code: "NOT_ACCEPTABLE";
|
|
905
|
+
readonly name: "NotAcceptableError";
|
|
906
|
+
readonly message: "Not acceptable.";
|
|
907
|
+
readonly uiMessage: "The requested format is not supported.";
|
|
678
908
|
};
|
|
679
909
|
readonly 407: {
|
|
680
|
-
code:
|
|
681
|
-
name:
|
|
682
|
-
message:
|
|
683
|
-
uiMessage:
|
|
684
|
-
metadata: {
|
|
685
|
-
status: number;
|
|
686
|
-
};
|
|
910
|
+
readonly code: "PROXY_AUTHENTICATION_REQUIRED";
|
|
911
|
+
readonly name: "ProxyAuthenticationRequiredError";
|
|
912
|
+
readonly message: "Proxy authentication required.";
|
|
913
|
+
readonly uiMessage: "Proxy authentication is required to access this resource.";
|
|
687
914
|
};
|
|
688
915
|
readonly 408: {
|
|
689
|
-
code:
|
|
690
|
-
name:
|
|
691
|
-
message:
|
|
692
|
-
uiMessage:
|
|
693
|
-
metadata: {
|
|
694
|
-
status: number;
|
|
695
|
-
};
|
|
916
|
+
readonly code: "REQUEST_TIMEOUT";
|
|
917
|
+
readonly name: "RequestTimeoutError";
|
|
918
|
+
readonly message: "Request timeout.";
|
|
919
|
+
readonly uiMessage: "The request took too long to complete. Please try again.";
|
|
696
920
|
};
|
|
697
921
|
readonly 409: {
|
|
698
|
-
code:
|
|
699
|
-
name:
|
|
700
|
-
message:
|
|
701
|
-
uiMessage:
|
|
702
|
-
metadata: {
|
|
703
|
-
status: number;
|
|
704
|
-
};
|
|
922
|
+
readonly code: "CONFLICT";
|
|
923
|
+
readonly name: "ConflictError";
|
|
924
|
+
readonly message: "Conflict.";
|
|
925
|
+
readonly uiMessage: "The request conflicts with the current state. Please refresh and try again.";
|
|
705
926
|
};
|
|
706
927
|
readonly 410: {
|
|
707
|
-
code:
|
|
708
|
-
name:
|
|
709
|
-
message:
|
|
710
|
-
uiMessage:
|
|
711
|
-
metadata: {
|
|
712
|
-
status: number;
|
|
713
|
-
};
|
|
928
|
+
readonly code: "GONE";
|
|
929
|
+
readonly name: "GoneError";
|
|
930
|
+
readonly message: "Gone.";
|
|
931
|
+
readonly uiMessage: "This resource is no longer available.";
|
|
714
932
|
};
|
|
715
933
|
readonly 411: {
|
|
716
|
-
code:
|
|
717
|
-
name:
|
|
718
|
-
message:
|
|
719
|
-
uiMessage:
|
|
720
|
-
metadata: {
|
|
721
|
-
status: number;
|
|
722
|
-
};
|
|
934
|
+
readonly code: "LENGTH_REQUIRED";
|
|
935
|
+
readonly name: "LengthRequiredError";
|
|
936
|
+
readonly message: "Length required.";
|
|
937
|
+
readonly uiMessage: "The request is missing required length information.";
|
|
723
938
|
};
|
|
724
939
|
readonly 412: {
|
|
725
|
-
code:
|
|
726
|
-
name:
|
|
727
|
-
message:
|
|
728
|
-
uiMessage:
|
|
729
|
-
metadata: {
|
|
730
|
-
status: number;
|
|
731
|
-
};
|
|
940
|
+
readonly code: "PRECONDITION_FAILED";
|
|
941
|
+
readonly name: "PreconditionFailedError";
|
|
942
|
+
readonly message: "Precondition failed.";
|
|
943
|
+
readonly uiMessage: "A required condition was not met. Please try again.";
|
|
732
944
|
};
|
|
733
945
|
readonly 413: {
|
|
734
|
-
code:
|
|
735
|
-
name:
|
|
736
|
-
message:
|
|
737
|
-
uiMessage:
|
|
738
|
-
metadata: {
|
|
739
|
-
status: number;
|
|
740
|
-
};
|
|
946
|
+
readonly code: "PAYLOAD_TOO_LARGE";
|
|
947
|
+
readonly name: "PayloadTooLargeError";
|
|
948
|
+
readonly message: "Payload too large.";
|
|
949
|
+
readonly uiMessage: "The request is too large. Please reduce the size and try again.";
|
|
741
950
|
};
|
|
742
951
|
readonly 414: {
|
|
743
|
-
code:
|
|
744
|
-
name:
|
|
745
|
-
message:
|
|
746
|
-
uiMessage:
|
|
747
|
-
metadata: {
|
|
748
|
-
status: number;
|
|
749
|
-
};
|
|
952
|
+
readonly code: "URI_TOO_LONG";
|
|
953
|
+
readonly name: "UriTooLongError";
|
|
954
|
+
readonly message: "URI too long.";
|
|
955
|
+
readonly uiMessage: "The request URL is too long.";
|
|
750
956
|
};
|
|
751
957
|
readonly 415: {
|
|
752
|
-
code:
|
|
753
|
-
name:
|
|
754
|
-
message:
|
|
755
|
-
uiMessage:
|
|
756
|
-
metadata: {
|
|
757
|
-
status: number;
|
|
758
|
-
};
|
|
958
|
+
readonly code: "UNSUPPORTED_MEDIA_TYPE";
|
|
959
|
+
readonly name: "UnsupportedMediaTypeError";
|
|
960
|
+
readonly message: "Unsupported media type.";
|
|
961
|
+
readonly uiMessage: "The file type is not supported.";
|
|
759
962
|
};
|
|
760
963
|
readonly 416: {
|
|
761
|
-
code:
|
|
762
|
-
name:
|
|
763
|
-
message:
|
|
764
|
-
uiMessage:
|
|
765
|
-
metadata: {
|
|
766
|
-
status: number;
|
|
767
|
-
};
|
|
964
|
+
readonly code: "RANGE_NOT_SATISFIABLE";
|
|
965
|
+
readonly name: "RangeNotSatisfiableError";
|
|
966
|
+
readonly message: "Range not satisfiable.";
|
|
967
|
+
readonly uiMessage: "The requested range cannot be satisfied.";
|
|
768
968
|
};
|
|
769
969
|
readonly 417: {
|
|
770
|
-
code:
|
|
771
|
-
name:
|
|
772
|
-
message:
|
|
773
|
-
uiMessage:
|
|
774
|
-
metadata: {
|
|
775
|
-
status: number;
|
|
776
|
-
};
|
|
970
|
+
readonly code: "EXPECTATION_FAILED";
|
|
971
|
+
readonly name: "ExpectationFailedError";
|
|
972
|
+
readonly message: "Expectation failed.";
|
|
973
|
+
readonly uiMessage: "The server cannot meet the requirements of the request.";
|
|
777
974
|
};
|
|
778
975
|
readonly 418: {
|
|
779
|
-
code:
|
|
780
|
-
name:
|
|
781
|
-
message:
|
|
782
|
-
uiMessage:
|
|
783
|
-
metadata: {
|
|
784
|
-
status: number;
|
|
785
|
-
};
|
|
976
|
+
readonly code: "IM_A_TEAPOT";
|
|
977
|
+
readonly name: "ImATeapotError";
|
|
978
|
+
readonly message: "I'm a teapot.";
|
|
979
|
+
readonly uiMessage: "I'm a teapot and cannot brew coffee.";
|
|
786
980
|
};
|
|
787
981
|
readonly 422: {
|
|
788
|
-
code:
|
|
789
|
-
name:
|
|
790
|
-
message:
|
|
791
|
-
uiMessage:
|
|
792
|
-
metadata: {
|
|
793
|
-
status: number;
|
|
794
|
-
};
|
|
982
|
+
readonly code: "UNPROCESSABLE_ENTITY";
|
|
983
|
+
readonly name: "UnprocessableEntityError";
|
|
984
|
+
readonly message: "Unprocessable entity.";
|
|
985
|
+
readonly uiMessage: "The request contains invalid data. Please check your input.";
|
|
795
986
|
};
|
|
796
987
|
readonly 423: {
|
|
797
|
-
code:
|
|
798
|
-
name:
|
|
799
|
-
message:
|
|
800
|
-
uiMessage:
|
|
801
|
-
metadata: {
|
|
802
|
-
status: number;
|
|
803
|
-
};
|
|
988
|
+
readonly code: "LOCKED";
|
|
989
|
+
readonly name: "LockedError";
|
|
990
|
+
readonly message: "Locked.";
|
|
991
|
+
readonly uiMessage: "This resource is locked and cannot be modified.";
|
|
804
992
|
};
|
|
805
993
|
readonly 424: {
|
|
806
|
-
code:
|
|
807
|
-
name:
|
|
808
|
-
message:
|
|
809
|
-
uiMessage:
|
|
810
|
-
metadata: {
|
|
811
|
-
status: number;
|
|
812
|
-
};
|
|
994
|
+
readonly code: "FAILED_DEPENDENCY";
|
|
995
|
+
readonly name: "FailedDependencyError";
|
|
996
|
+
readonly message: "Failed dependency.";
|
|
997
|
+
readonly uiMessage: "The request failed due to a dependency error.";
|
|
813
998
|
};
|
|
814
999
|
readonly 425: {
|
|
815
|
-
code:
|
|
816
|
-
name:
|
|
817
|
-
message:
|
|
818
|
-
uiMessage:
|
|
819
|
-
metadata: {
|
|
820
|
-
status: number;
|
|
821
|
-
};
|
|
1000
|
+
readonly code: "TOO_EARLY";
|
|
1001
|
+
readonly name: "TooEarlyError";
|
|
1002
|
+
readonly message: "Too early.";
|
|
1003
|
+
readonly uiMessage: "The request was sent too early. Please try again later.";
|
|
822
1004
|
};
|
|
823
1005
|
readonly 426: {
|
|
824
|
-
code:
|
|
825
|
-
name:
|
|
826
|
-
message:
|
|
827
|
-
uiMessage:
|
|
828
|
-
metadata: {
|
|
829
|
-
status: number;
|
|
830
|
-
};
|
|
1006
|
+
readonly code: "UPGRADE_REQUIRED";
|
|
1007
|
+
readonly name: "UpgradeRequiredError";
|
|
1008
|
+
readonly message: "Upgrade required.";
|
|
1009
|
+
readonly uiMessage: "Please upgrade to continue using this service.";
|
|
831
1010
|
};
|
|
832
1011
|
readonly 428: {
|
|
833
|
-
code:
|
|
834
|
-
name:
|
|
835
|
-
message:
|
|
836
|
-
uiMessage:
|
|
837
|
-
metadata: {
|
|
838
|
-
status: number;
|
|
839
|
-
};
|
|
1012
|
+
readonly code: "PRECONDITION_REQUIRED";
|
|
1013
|
+
readonly name: "PreconditionRequiredError";
|
|
1014
|
+
readonly message: "Precondition required.";
|
|
1015
|
+
readonly uiMessage: "Required conditions are missing from the request.";
|
|
840
1016
|
};
|
|
841
1017
|
readonly 429: {
|
|
842
|
-
code:
|
|
843
|
-
name:
|
|
844
|
-
message:
|
|
845
|
-
uiMessage:
|
|
846
|
-
metadata: {
|
|
847
|
-
status: number;
|
|
848
|
-
};
|
|
1018
|
+
readonly code: "TOO_MANY_REQUESTS";
|
|
1019
|
+
readonly name: "TooManyRequestsError";
|
|
1020
|
+
readonly message: "Too many requests.";
|
|
1021
|
+
readonly uiMessage: "You have made too many requests. Please wait and try again.";
|
|
849
1022
|
};
|
|
850
1023
|
readonly 431: {
|
|
851
|
-
code:
|
|
852
|
-
name:
|
|
853
|
-
message:
|
|
854
|
-
uiMessage:
|
|
855
|
-
metadata: {
|
|
856
|
-
status: number;
|
|
857
|
-
};
|
|
1024
|
+
readonly code: "REQUEST_HEADER_FIELDS_TOO_LARGE";
|
|
1025
|
+
readonly name: "RequestHeaderFieldsTooLargeError";
|
|
1026
|
+
readonly message: "Request header fields too large.";
|
|
1027
|
+
readonly uiMessage: "The request headers are too large.";
|
|
858
1028
|
};
|
|
859
1029
|
readonly 451: {
|
|
860
|
-
code:
|
|
861
|
-
name:
|
|
862
|
-
message:
|
|
863
|
-
uiMessage:
|
|
864
|
-
metadata: {
|
|
865
|
-
status: number;
|
|
866
|
-
};
|
|
1030
|
+
readonly code: "UNAVAILABLE_FOR_LEGAL_REASONS";
|
|
1031
|
+
readonly name: "UnavailableForLegalReasonsError";
|
|
1032
|
+
readonly message: "Unavailable for legal reasons.";
|
|
1033
|
+
readonly uiMessage: "This content is unavailable for legal reasons.";
|
|
867
1034
|
};
|
|
868
1035
|
readonly 500: {
|
|
869
|
-
code:
|
|
870
|
-
name:
|
|
871
|
-
message:
|
|
872
|
-
uiMessage:
|
|
873
|
-
metadata: {
|
|
874
|
-
status: number;
|
|
875
|
-
};
|
|
1036
|
+
readonly code: "INTERNAL_SERVER_ERROR";
|
|
1037
|
+
readonly name: "InternalServerError";
|
|
1038
|
+
readonly message: "Internal server error.";
|
|
1039
|
+
readonly uiMessage: "An unexpected error occurred. Please try again later.";
|
|
876
1040
|
};
|
|
877
1041
|
readonly 501: {
|
|
878
|
-
code:
|
|
879
|
-
name:
|
|
880
|
-
message:
|
|
881
|
-
uiMessage:
|
|
882
|
-
metadata: {
|
|
883
|
-
status: number;
|
|
884
|
-
};
|
|
1042
|
+
readonly code: "NOT_IMPLEMENTED";
|
|
1043
|
+
readonly name: "NotImplementedError";
|
|
1044
|
+
readonly message: "Not implemented.";
|
|
1045
|
+
readonly uiMessage: "This feature is not yet available.";
|
|
885
1046
|
};
|
|
886
1047
|
readonly 502: {
|
|
887
|
-
code:
|
|
888
|
-
name:
|
|
889
|
-
message:
|
|
890
|
-
uiMessage:
|
|
891
|
-
metadata: {
|
|
892
|
-
status: number;
|
|
893
|
-
};
|
|
1048
|
+
readonly code: "BAD_GATEWAY";
|
|
1049
|
+
readonly name: "BadGatewayError";
|
|
1050
|
+
readonly message: "Bad gateway.";
|
|
1051
|
+
readonly uiMessage: "Unable to connect to the server. Please try again later.";
|
|
894
1052
|
};
|
|
895
1053
|
readonly 503: {
|
|
896
|
-
code:
|
|
897
|
-
name:
|
|
898
|
-
message:
|
|
899
|
-
uiMessage:
|
|
900
|
-
metadata: {
|
|
901
|
-
status: number;
|
|
902
|
-
};
|
|
1054
|
+
readonly code: "SERVICE_UNAVAILABLE";
|
|
1055
|
+
readonly name: "ServiceUnavailableError";
|
|
1056
|
+
readonly message: "Service unavailable.";
|
|
1057
|
+
readonly uiMessage: "The service is temporarily unavailable. Please try again later.";
|
|
903
1058
|
};
|
|
904
1059
|
readonly 504: {
|
|
905
|
-
code:
|
|
906
|
-
name:
|
|
907
|
-
message:
|
|
908
|
-
uiMessage:
|
|
909
|
-
metadata: {
|
|
910
|
-
status: number;
|
|
911
|
-
};
|
|
1060
|
+
readonly code: "GATEWAY_TIMEOUT";
|
|
1061
|
+
readonly name: "GatewayTimeoutError";
|
|
1062
|
+
readonly message: "Gateway timeout.";
|
|
1063
|
+
readonly uiMessage: "The server took too long to respond. Please try again.";
|
|
912
1064
|
};
|
|
913
1065
|
readonly 505: {
|
|
914
|
-
code:
|
|
915
|
-
name:
|
|
916
|
-
message:
|
|
917
|
-
uiMessage:
|
|
918
|
-
metadata: {
|
|
919
|
-
status: number;
|
|
920
|
-
};
|
|
1066
|
+
readonly code: "HTTP_VERSION_NOT_SUPPORTED";
|
|
1067
|
+
readonly name: "HttpVersionNotSupportedError";
|
|
1068
|
+
readonly message: "HTTP version not supported.";
|
|
1069
|
+
readonly uiMessage: "Your browser version is not supported.";
|
|
921
1070
|
};
|
|
922
1071
|
readonly 506: {
|
|
923
|
-
code:
|
|
924
|
-
name:
|
|
925
|
-
message:
|
|
926
|
-
uiMessage:
|
|
927
|
-
metadata: {
|
|
928
|
-
status: number;
|
|
929
|
-
};
|
|
1072
|
+
readonly code: "VARIANT_ALSO_NEGOTIATES";
|
|
1073
|
+
readonly name: "VariantAlsoNegotiatesError";
|
|
1074
|
+
readonly message: "Variant also negotiates.";
|
|
1075
|
+
readonly uiMessage: "The server has an internal configuration error.";
|
|
930
1076
|
};
|
|
931
1077
|
readonly 507: {
|
|
932
|
-
code:
|
|
933
|
-
name:
|
|
934
|
-
message:
|
|
935
|
-
uiMessage:
|
|
936
|
-
metadata: {
|
|
937
|
-
status: number;
|
|
938
|
-
};
|
|
1078
|
+
readonly code: "INSUFFICIENT_STORAGE";
|
|
1079
|
+
readonly name: "InsufficientStorageError";
|
|
1080
|
+
readonly message: "Insufficient storage.";
|
|
1081
|
+
readonly uiMessage: "The server has insufficient storage to complete the request.";
|
|
939
1082
|
};
|
|
940
1083
|
readonly 508: {
|
|
941
|
-
code:
|
|
942
|
-
name:
|
|
943
|
-
message:
|
|
944
|
-
uiMessage:
|
|
945
|
-
metadata: {
|
|
946
|
-
status: number;
|
|
947
|
-
};
|
|
1084
|
+
readonly code: "LOOP_DETECTED";
|
|
1085
|
+
readonly name: "LoopDetectedError";
|
|
1086
|
+
readonly message: "Loop detected.";
|
|
1087
|
+
readonly uiMessage: "The server detected an infinite loop.";
|
|
948
1088
|
};
|
|
949
1089
|
readonly 510: {
|
|
950
|
-
code:
|
|
951
|
-
name:
|
|
952
|
-
message:
|
|
953
|
-
uiMessage:
|
|
954
|
-
metadata: {
|
|
955
|
-
status: number;
|
|
956
|
-
};
|
|
1090
|
+
readonly code: "NOT_EXTENDED";
|
|
1091
|
+
readonly name: "NotExtendedError";
|
|
1092
|
+
readonly message: "Not extended.";
|
|
1093
|
+
readonly uiMessage: "Additional extensions are required.";
|
|
957
1094
|
};
|
|
958
1095
|
readonly 511: {
|
|
959
|
-
code:
|
|
1096
|
+
readonly code: "NETWORK_AUTHENTICATION_REQUIRED";
|
|
1097
|
+
readonly name: "NetworkAuthenticationRequiredError";
|
|
1098
|
+
readonly message: "Network authentication required.";
|
|
1099
|
+
readonly uiMessage: "Network authentication is required to access this resource.";
|
|
1100
|
+
};
|
|
1101
|
+
};
|
|
1102
|
+
/**
|
|
1103
|
+
* Valid HTTP status codes for HTTPErrorX.create()
|
|
1104
|
+
* Derived from the presets object. Provides autocomplete for known codes
|
|
1105
|
+
* while allowing any number for flexibility.
|
|
1106
|
+
*/
|
|
1107
|
+
type HTTPErrorXPresetKey = keyof typeof httpPresets | ErrorXBasePresetKey;
|
|
1108
|
+
/**
|
|
1109
|
+
* Metadata type for HTTP errors.
|
|
1110
|
+
* Provides context about the HTTP request that failed.
|
|
1111
|
+
*
|
|
1112
|
+
* @public
|
|
1113
|
+
*/
|
|
1114
|
+
type HTTPErrorXMetadata = {
|
|
1115
|
+
/** The endpoint/URL that was requested */
|
|
1116
|
+
endpoint?: string;
|
|
1117
|
+
/** The HTTP method used (GET, POST, etc.) */
|
|
1118
|
+
method?: string;
|
|
1119
|
+
/** Additional metadata */
|
|
1120
|
+
[key: string]: unknown;
|
|
1121
|
+
};
|
|
1122
|
+
/**
|
|
1123
|
+
* HTTP Error class with presets for all standard HTTP status codes.
|
|
1124
|
+
*
|
|
1125
|
+
* Provides a type-safe, ergonomic way to create HTTP errors with:
|
|
1126
|
+
* - Numeric status code presets (400, 401, 404, 500, etc.)
|
|
1127
|
+
* - Automatic httpStatus from preset key
|
|
1128
|
+
* - Full `instanceof` support
|
|
1129
|
+
* - Typed metadata for HTTP context
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* ```typescript
|
|
1133
|
+
* // Basic usage with status code
|
|
1134
|
+
* throw HTTPErrorX.create(404)
|
|
1135
|
+
* throw HTTPErrorX.create(401)
|
|
1136
|
+
* throw HTTPErrorX.create(500)
|
|
1137
|
+
*
|
|
1138
|
+
* // With custom message
|
|
1139
|
+
* throw HTTPErrorX.create(404, { message: 'User not found' })
|
|
1140
|
+
*
|
|
1141
|
+
* // With metadata
|
|
1142
|
+
* throw HTTPErrorX.create(401, {
|
|
1143
|
+
* message: 'Invalid token',
|
|
1144
|
+
* metadata: { endpoint: '/api/users', method: 'GET' }
|
|
1145
|
+
* })
|
|
1146
|
+
*
|
|
1147
|
+
* // With error chaining
|
|
1148
|
+
* try {
|
|
1149
|
+
* await fetchUser(id)
|
|
1150
|
+
* } catch (err) {
|
|
1151
|
+
* throw HTTPErrorX.create(500, { cause: err })
|
|
1152
|
+
* }
|
|
1153
|
+
*
|
|
1154
|
+
* // Just overrides (uses default 500)
|
|
1155
|
+
* throw HTTPErrorX.create({ message: 'Something went wrong' })
|
|
1156
|
+
*
|
|
1157
|
+
* // instanceof checks
|
|
1158
|
+
* if (error instanceof HTTPErrorX) {
|
|
1159
|
+
* console.log(error.httpStatus) // 404
|
|
1160
|
+
* }
|
|
1161
|
+
* ```
|
|
1162
|
+
*
|
|
1163
|
+
* @public
|
|
1164
|
+
*/
|
|
1165
|
+
declare class HTTPErrorX extends ErrorX<HTTPErrorXMetadata> {
|
|
1166
|
+
/**
|
|
1167
|
+
* HTTP status code presets for all standard codes.
|
|
1168
|
+
* Keys are numeric status codes (400, 401, 404, 500, etc.)
|
|
1169
|
+
*/
|
|
1170
|
+
static presets: {
|
|
1171
|
+
readonly 400: {
|
|
1172
|
+
readonly code: "BAD_REQUEST";
|
|
1173
|
+
readonly name: "BadRequestError";
|
|
1174
|
+
readonly message: "Bad request.";
|
|
1175
|
+
readonly uiMessage: "The request could not be processed. Please check your input and try again.";
|
|
1176
|
+
};
|
|
1177
|
+
readonly 401: {
|
|
1178
|
+
readonly code: "UNAUTHORIZED";
|
|
1179
|
+
readonly name: "UnauthorizedError";
|
|
1180
|
+
readonly message: "Unauthorized.";
|
|
1181
|
+
readonly uiMessage: "Authentication required. Please log in to continue.";
|
|
1182
|
+
};
|
|
1183
|
+
readonly 402: {
|
|
1184
|
+
readonly code: "PAYMENT_REQUIRED";
|
|
1185
|
+
readonly name: "PaymentRequiredError";
|
|
1186
|
+
readonly message: "Payment required.";
|
|
1187
|
+
readonly uiMessage: "Payment is required to access this resource.";
|
|
1188
|
+
};
|
|
1189
|
+
readonly 403: {
|
|
1190
|
+
readonly code: "FORBIDDEN";
|
|
1191
|
+
readonly name: "ForbiddenError";
|
|
1192
|
+
readonly message: "Forbidden.";
|
|
1193
|
+
readonly uiMessage: "You do not have permission to access this resource.";
|
|
1194
|
+
};
|
|
1195
|
+
readonly 404: {
|
|
1196
|
+
readonly code: "NOT_FOUND";
|
|
1197
|
+
readonly name: "NotFoundError";
|
|
1198
|
+
readonly message: "Not found.";
|
|
1199
|
+
readonly uiMessage: "The requested resource could not be found.";
|
|
1200
|
+
};
|
|
1201
|
+
readonly 405: {
|
|
1202
|
+
readonly code: "METHOD_NOT_ALLOWED";
|
|
1203
|
+
readonly name: "MethodNotAllowedError";
|
|
1204
|
+
readonly message: "Method not allowed.";
|
|
1205
|
+
readonly uiMessage: "This action is not allowed for the requested resource.";
|
|
1206
|
+
};
|
|
1207
|
+
readonly 406: {
|
|
1208
|
+
readonly code: "NOT_ACCEPTABLE";
|
|
1209
|
+
readonly name: "NotAcceptableError";
|
|
1210
|
+
readonly message: "Not acceptable.";
|
|
1211
|
+
readonly uiMessage: "The requested format is not supported.";
|
|
1212
|
+
};
|
|
1213
|
+
readonly 407: {
|
|
1214
|
+
readonly code: "PROXY_AUTHENTICATION_REQUIRED";
|
|
1215
|
+
readonly name: "ProxyAuthenticationRequiredError";
|
|
1216
|
+
readonly message: "Proxy authentication required.";
|
|
1217
|
+
readonly uiMessage: "Proxy authentication is required to access this resource.";
|
|
1218
|
+
};
|
|
1219
|
+
readonly 408: {
|
|
1220
|
+
readonly code: "REQUEST_TIMEOUT";
|
|
1221
|
+
readonly name: "RequestTimeoutError";
|
|
1222
|
+
readonly message: "Request timeout.";
|
|
1223
|
+
readonly uiMessage: "The request took too long to complete. Please try again.";
|
|
1224
|
+
};
|
|
1225
|
+
readonly 409: {
|
|
1226
|
+
readonly code: "CONFLICT";
|
|
1227
|
+
readonly name: "ConflictError";
|
|
1228
|
+
readonly message: "Conflict.";
|
|
1229
|
+
readonly uiMessage: "The request conflicts with the current state. Please refresh and try again.";
|
|
1230
|
+
};
|
|
1231
|
+
readonly 410: {
|
|
1232
|
+
readonly code: "GONE";
|
|
1233
|
+
readonly name: "GoneError";
|
|
1234
|
+
readonly message: "Gone.";
|
|
1235
|
+
readonly uiMessage: "This resource is no longer available.";
|
|
1236
|
+
};
|
|
1237
|
+
readonly 411: {
|
|
1238
|
+
readonly code: "LENGTH_REQUIRED";
|
|
1239
|
+
readonly name: "LengthRequiredError";
|
|
1240
|
+
readonly message: "Length required.";
|
|
1241
|
+
readonly uiMessage: "The request is missing required length information.";
|
|
1242
|
+
};
|
|
1243
|
+
readonly 412: {
|
|
1244
|
+
readonly code: "PRECONDITION_FAILED";
|
|
1245
|
+
readonly name: "PreconditionFailedError";
|
|
1246
|
+
readonly message: "Precondition failed.";
|
|
1247
|
+
readonly uiMessage: "A required condition was not met. Please try again.";
|
|
1248
|
+
};
|
|
1249
|
+
readonly 413: {
|
|
1250
|
+
readonly code: "PAYLOAD_TOO_LARGE";
|
|
1251
|
+
readonly name: "PayloadTooLargeError";
|
|
1252
|
+
readonly message: "Payload too large.";
|
|
1253
|
+
readonly uiMessage: "The request is too large. Please reduce the size and try again.";
|
|
1254
|
+
};
|
|
1255
|
+
readonly 414: {
|
|
1256
|
+
readonly code: "URI_TOO_LONG";
|
|
1257
|
+
readonly name: "UriTooLongError";
|
|
1258
|
+
readonly message: "URI too long.";
|
|
1259
|
+
readonly uiMessage: "The request URL is too long.";
|
|
1260
|
+
};
|
|
1261
|
+
readonly 415: {
|
|
1262
|
+
readonly code: "UNSUPPORTED_MEDIA_TYPE";
|
|
1263
|
+
readonly name: "UnsupportedMediaTypeError";
|
|
1264
|
+
readonly message: "Unsupported media type.";
|
|
1265
|
+
readonly uiMessage: "The file type is not supported.";
|
|
1266
|
+
};
|
|
1267
|
+
readonly 416: {
|
|
1268
|
+
readonly code: "RANGE_NOT_SATISFIABLE";
|
|
1269
|
+
readonly name: "RangeNotSatisfiableError";
|
|
1270
|
+
readonly message: "Range not satisfiable.";
|
|
1271
|
+
readonly uiMessage: "The requested range cannot be satisfied.";
|
|
1272
|
+
};
|
|
1273
|
+
readonly 417: {
|
|
1274
|
+
readonly code: "EXPECTATION_FAILED";
|
|
1275
|
+
readonly name: "ExpectationFailedError";
|
|
1276
|
+
readonly message: "Expectation failed.";
|
|
1277
|
+
readonly uiMessage: "The server cannot meet the requirements of the request.";
|
|
1278
|
+
};
|
|
1279
|
+
readonly 418: {
|
|
1280
|
+
readonly code: "IM_A_TEAPOT";
|
|
1281
|
+
readonly name: "ImATeapotError";
|
|
1282
|
+
readonly message: "I'm a teapot.";
|
|
1283
|
+
readonly uiMessage: "I'm a teapot and cannot brew coffee.";
|
|
1284
|
+
};
|
|
1285
|
+
readonly 422: {
|
|
1286
|
+
readonly code: "UNPROCESSABLE_ENTITY";
|
|
1287
|
+
readonly name: "UnprocessableEntityError";
|
|
1288
|
+
readonly message: "Unprocessable entity.";
|
|
1289
|
+
readonly uiMessage: "The request contains invalid data. Please check your input.";
|
|
1290
|
+
};
|
|
1291
|
+
readonly 423: {
|
|
1292
|
+
readonly code: "LOCKED";
|
|
1293
|
+
readonly name: "LockedError";
|
|
1294
|
+
readonly message: "Locked.";
|
|
1295
|
+
readonly uiMessage: "This resource is locked and cannot be modified.";
|
|
1296
|
+
};
|
|
1297
|
+
readonly 424: {
|
|
1298
|
+
readonly code: "FAILED_DEPENDENCY";
|
|
1299
|
+
readonly name: "FailedDependencyError";
|
|
1300
|
+
readonly message: "Failed dependency.";
|
|
1301
|
+
readonly uiMessage: "The request failed due to a dependency error.";
|
|
1302
|
+
};
|
|
1303
|
+
readonly 425: {
|
|
1304
|
+
readonly code: "TOO_EARLY";
|
|
1305
|
+
readonly name: "TooEarlyError";
|
|
1306
|
+
readonly message: "Too early.";
|
|
1307
|
+
readonly uiMessage: "The request was sent too early. Please try again later.";
|
|
1308
|
+
};
|
|
1309
|
+
readonly 426: {
|
|
1310
|
+
readonly code: "UPGRADE_REQUIRED";
|
|
1311
|
+
readonly name: "UpgradeRequiredError";
|
|
1312
|
+
readonly message: "Upgrade required.";
|
|
1313
|
+
readonly uiMessage: "Please upgrade to continue using this service.";
|
|
1314
|
+
};
|
|
1315
|
+
readonly 428: {
|
|
1316
|
+
readonly code: "PRECONDITION_REQUIRED";
|
|
1317
|
+
readonly name: "PreconditionRequiredError";
|
|
1318
|
+
readonly message: "Precondition required.";
|
|
1319
|
+
readonly uiMessage: "Required conditions are missing from the request.";
|
|
1320
|
+
};
|
|
1321
|
+
readonly 429: {
|
|
1322
|
+
readonly code: "TOO_MANY_REQUESTS";
|
|
1323
|
+
readonly name: "TooManyRequestsError";
|
|
1324
|
+
readonly message: "Too many requests.";
|
|
1325
|
+
readonly uiMessage: "You have made too many requests. Please wait and try again.";
|
|
1326
|
+
};
|
|
1327
|
+
readonly 431: {
|
|
1328
|
+
readonly code: "REQUEST_HEADER_FIELDS_TOO_LARGE";
|
|
1329
|
+
readonly name: "RequestHeaderFieldsTooLargeError";
|
|
1330
|
+
readonly message: "Request header fields too large.";
|
|
1331
|
+
readonly uiMessage: "The request headers are too large.";
|
|
1332
|
+
};
|
|
1333
|
+
readonly 451: {
|
|
1334
|
+
readonly code: "UNAVAILABLE_FOR_LEGAL_REASONS";
|
|
1335
|
+
readonly name: "UnavailableForLegalReasonsError";
|
|
1336
|
+
readonly message: "Unavailable for legal reasons.";
|
|
1337
|
+
readonly uiMessage: "This content is unavailable for legal reasons.";
|
|
1338
|
+
};
|
|
1339
|
+
readonly 500: {
|
|
1340
|
+
readonly code: "INTERNAL_SERVER_ERROR";
|
|
1341
|
+
readonly name: "InternalServerError";
|
|
1342
|
+
readonly message: "Internal server error.";
|
|
1343
|
+
readonly uiMessage: "An unexpected error occurred. Please try again later.";
|
|
1344
|
+
};
|
|
1345
|
+
readonly 501: {
|
|
1346
|
+
readonly code: "NOT_IMPLEMENTED";
|
|
1347
|
+
readonly name: "NotImplementedError";
|
|
1348
|
+
readonly message: "Not implemented.";
|
|
1349
|
+
readonly uiMessage: "This feature is not yet available.";
|
|
1350
|
+
};
|
|
1351
|
+
readonly 502: {
|
|
1352
|
+
readonly code: "BAD_GATEWAY";
|
|
1353
|
+
readonly name: "BadGatewayError";
|
|
1354
|
+
readonly message: "Bad gateway.";
|
|
1355
|
+
readonly uiMessage: "Unable to connect to the server. Please try again later.";
|
|
1356
|
+
};
|
|
1357
|
+
readonly 503: {
|
|
1358
|
+
readonly code: "SERVICE_UNAVAILABLE";
|
|
1359
|
+
readonly name: "ServiceUnavailableError";
|
|
1360
|
+
readonly message: "Service unavailable.";
|
|
1361
|
+
readonly uiMessage: "The service is temporarily unavailable. Please try again later.";
|
|
1362
|
+
};
|
|
1363
|
+
readonly 504: {
|
|
1364
|
+
readonly code: "GATEWAY_TIMEOUT";
|
|
1365
|
+
readonly name: "GatewayTimeoutError";
|
|
1366
|
+
readonly message: "Gateway timeout.";
|
|
1367
|
+
readonly uiMessage: "The server took too long to respond. Please try again.";
|
|
1368
|
+
};
|
|
1369
|
+
readonly 505: {
|
|
1370
|
+
readonly code: "HTTP_VERSION_NOT_SUPPORTED";
|
|
1371
|
+
readonly name: "HttpVersionNotSupportedError";
|
|
1372
|
+
readonly message: "HTTP version not supported.";
|
|
1373
|
+
readonly uiMessage: "Your browser version is not supported.";
|
|
1374
|
+
};
|
|
1375
|
+
readonly 506: {
|
|
1376
|
+
readonly code: "VARIANT_ALSO_NEGOTIATES";
|
|
1377
|
+
readonly name: "VariantAlsoNegotiatesError";
|
|
1378
|
+
readonly message: "Variant also negotiates.";
|
|
1379
|
+
readonly uiMessage: "The server has an internal configuration error.";
|
|
1380
|
+
};
|
|
1381
|
+
readonly 507: {
|
|
1382
|
+
readonly code: "INSUFFICIENT_STORAGE";
|
|
1383
|
+
readonly name: "InsufficientStorageError";
|
|
1384
|
+
readonly message: "Insufficient storage.";
|
|
1385
|
+
readonly uiMessage: "The server has insufficient storage to complete the request.";
|
|
1386
|
+
};
|
|
1387
|
+
readonly 508: {
|
|
1388
|
+
readonly code: "LOOP_DETECTED";
|
|
1389
|
+
readonly name: "LoopDetectedError";
|
|
1390
|
+
readonly message: "Loop detected.";
|
|
1391
|
+
readonly uiMessage: "The server detected an infinite loop.";
|
|
1392
|
+
};
|
|
1393
|
+
readonly 510: {
|
|
1394
|
+
readonly code: "NOT_EXTENDED";
|
|
1395
|
+
readonly name: "NotExtendedError";
|
|
1396
|
+
readonly message: "Not extended.";
|
|
1397
|
+
readonly uiMessage: "Additional extensions are required.";
|
|
1398
|
+
};
|
|
1399
|
+
readonly 511: {
|
|
1400
|
+
readonly code: "NETWORK_AUTHENTICATION_REQUIRED";
|
|
1401
|
+
readonly name: "NetworkAuthenticationRequiredError";
|
|
1402
|
+
readonly message: "Network authentication required.";
|
|
1403
|
+
readonly uiMessage: "Network authentication is required to access this resource.";
|
|
1404
|
+
};
|
|
1405
|
+
};
|
|
1406
|
+
/** Default to 500 Internal Server Error when no preset specified */
|
|
1407
|
+
static defaultPreset: number;
|
|
1408
|
+
/** Default httpStatus for all HTTPErrorXs */
|
|
1409
|
+
static defaults: {
|
|
1410
|
+
httpStatus: number;
|
|
1411
|
+
};
|
|
1412
|
+
/**
|
|
1413
|
+
* Transform that automatically sets httpStatus from the preset key.
|
|
1414
|
+
* Only sets httpStatus from presetKey if it matches a known preset.
|
|
1415
|
+
*/
|
|
1416
|
+
static transform: ErrorXTransform<HTTPErrorXMetadata>;
|
|
1417
|
+
/**
|
|
1418
|
+
* Creates an HTTPErrorX from a status code preset.
|
|
1419
|
+
*
|
|
1420
|
+
* @param statusCode - HTTP status code (provides autocomplete for standard codes)
|
|
1421
|
+
* @param overrides - Optional overrides for the preset values
|
|
1422
|
+
* @returns HTTPErrorX instance
|
|
1423
|
+
*/
|
|
1424
|
+
static create(statusCode?: HTTPErrorXPresetKey, overrides?: Partial<ErrorXOptions<HTTPErrorXMetadata>>): HTTPErrorX;
|
|
1425
|
+
static create(overrides?: Partial<ErrorXOptions<HTTPErrorXMetadata>>): HTTPErrorX;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* Zod issue structure for type compatibility.
|
|
1430
|
+
* Matches the shape of Zod's ZodIssue type.
|
|
1431
|
+
*
|
|
1432
|
+
* @public
|
|
1433
|
+
*/
|
|
1434
|
+
type ZodIssue = {
|
|
1435
|
+
code: string;
|
|
1436
|
+
path: (string | number)[];
|
|
1437
|
+
message: string;
|
|
1438
|
+
expected?: string;
|
|
1439
|
+
received?: string;
|
|
1440
|
+
minimum?: number;
|
|
1441
|
+
maximum?: number;
|
|
1442
|
+
inclusive?: boolean;
|
|
1443
|
+
exact?: boolean;
|
|
1444
|
+
type?: string;
|
|
1445
|
+
};
|
|
1446
|
+
/**
|
|
1447
|
+
* Metadata type for validation errors.
|
|
1448
|
+
* Designed to capture all relevant Zod validation context.
|
|
1449
|
+
*
|
|
1450
|
+
* @public
|
|
1451
|
+
*/
|
|
1452
|
+
type ValidationErrorXMetadata = {
|
|
1453
|
+
/** The field that failed validation (dot-notation path) */
|
|
1454
|
+
field?: string;
|
|
1455
|
+
/** The full path to the field (for nested objects) */
|
|
1456
|
+
path?: (string | number)[];
|
|
1457
|
+
/** The Zod issue code (e.g., 'invalid_type', 'too_small') */
|
|
1458
|
+
zodCode?: string;
|
|
1459
|
+
/** The expected type or value */
|
|
1460
|
+
expected?: string;
|
|
1461
|
+
/** The received value (sanitized for safety) */
|
|
1462
|
+
received?: string;
|
|
1463
|
+
/** Total number of validation issues */
|
|
1464
|
+
issueCount?: number;
|
|
1465
|
+
/** All Zod issues (for multi-error handling) */
|
|
1466
|
+
issues?: ZodIssue[];
|
|
1467
|
+
};
|
|
1468
|
+
/**
|
|
1469
|
+
* Validation Error class designed for Zod integration.
|
|
1470
|
+
*
|
|
1471
|
+
* This class demonstrates how to map external library data (Zod) to ErrorX.
|
|
1472
|
+
* Instead of using presets, it dynamically creates errors from Zod's validation output.
|
|
1473
|
+
*
|
|
1474
|
+
* Key features:
|
|
1475
|
+
* - `fromZodError()` - Primary factory method for Zod integration
|
|
1476
|
+
* - Dynamic code mapping from Zod issue codes
|
|
1477
|
+
* - Full `instanceof` support for catch blocks
|
|
1478
|
+
* - Typed metadata capturing all Zod context
|
|
1479
|
+
*
|
|
1480
|
+
* @example
|
|
1481
|
+
* ```typescript
|
|
1482
|
+
* import { z } from 'zod'
|
|
1483
|
+
* import { ValidationErrorX } from 'error-x'
|
|
1484
|
+
*
|
|
1485
|
+
* const userSchema = z.object({
|
|
1486
|
+
* email: z.string().email(),
|
|
1487
|
+
* age: z.number().min(18),
|
|
1488
|
+
* })
|
|
1489
|
+
*
|
|
1490
|
+
* // Primary usage: fromZodError()
|
|
1491
|
+
* try {
|
|
1492
|
+
* userSchema.parse({ email: 'invalid', age: 15 })
|
|
1493
|
+
* } catch (err) {
|
|
1494
|
+
* if (err instanceof z.ZodError) {
|
|
1495
|
+
* throw ValidationErrorX.fromZodError(err)
|
|
1496
|
+
* // → code: 'VALIDATION_INVALID_STRING'
|
|
1497
|
+
* // → message: 'Invalid email'
|
|
1498
|
+
* // → metadata.field: 'email'
|
|
1499
|
+
* // → metadata.zodCode: 'invalid_string'
|
|
1500
|
+
* }
|
|
1501
|
+
* }
|
|
1502
|
+
*
|
|
1503
|
+
* // With overrides
|
|
1504
|
+
* ValidationErrorX.fromZodError(zodError, {
|
|
1505
|
+
* uiMessage: 'Please check your input',
|
|
1506
|
+
* httpStatus: 422,
|
|
1507
|
+
* })
|
|
1508
|
+
*
|
|
1509
|
+
* // Direct creation (without Zod)
|
|
1510
|
+
* ValidationErrorX.create({
|
|
1511
|
+
* message: 'Invalid email format',
|
|
1512
|
+
* code: 'INVALID_EMAIL',
|
|
1513
|
+
* metadata: { field: 'email' }
|
|
1514
|
+
* })
|
|
1515
|
+
*
|
|
1516
|
+
* // instanceof in catch blocks
|
|
1517
|
+
* try {
|
|
1518
|
+
* await processRequest(data)
|
|
1519
|
+
* } catch (err) {
|
|
1520
|
+
* if (err instanceof ValidationErrorX) {
|
|
1521
|
+
* return res.status(err.httpStatus).json({
|
|
1522
|
+
* error: err.code,
|
|
1523
|
+
* message: err.uiMessage,
|
|
1524
|
+
* field: err.metadata?.field,
|
|
1525
|
+
* })
|
|
1526
|
+
* }
|
|
1527
|
+
* }
|
|
1528
|
+
* ```
|
|
1529
|
+
*
|
|
1530
|
+
* @public
|
|
1531
|
+
*/
|
|
1532
|
+
declare class ValidationErrorX extends ErrorX<ValidationErrorXMetadata> {
|
|
1533
|
+
/** Default httpStatus for validation errors (400 = bad request) */
|
|
1534
|
+
static defaults: {
|
|
1535
|
+
httpStatus: number;
|
|
960
1536
|
name: string;
|
|
1537
|
+
code: string;
|
|
961
1538
|
message: string;
|
|
962
1539
|
uiMessage: string;
|
|
963
|
-
metadata: {
|
|
964
|
-
status: number;
|
|
965
|
-
};
|
|
966
1540
|
};
|
|
967
|
-
|
|
1541
|
+
/**
|
|
1542
|
+
* Transform that maps Zod issue codes to VALIDATION_ prefixed codes.
|
|
1543
|
+
* Converts Zod's snake_case codes to SCREAMING_SNAKE_CASE.
|
|
1544
|
+
*/
|
|
1545
|
+
static transform: ErrorXTransform<ValidationErrorXMetadata>;
|
|
1546
|
+
/**
|
|
1547
|
+
* Creates a ValidationErrorX from a Zod error.
|
|
1548
|
+
*
|
|
1549
|
+
* Maps Zod's error structure to ErrorX:
|
|
1550
|
+
* - Uses first issue's message as the error message
|
|
1551
|
+
* - Converts Zod issue code to ErrorX code (e.g., 'invalid_type' → 'VALIDATION_INVALID_TYPE')
|
|
1552
|
+
* - Captures all issues in metadata for multi-error handling
|
|
1553
|
+
*
|
|
1554
|
+
* @param zodError - The Zod error object (or any object with `issues` array)
|
|
1555
|
+
* @param overrides - Optional overrides for any ErrorX options
|
|
1556
|
+
* @returns ValidationErrorX instance
|
|
1557
|
+
*
|
|
1558
|
+
* @example
|
|
1559
|
+
* ```typescript
|
|
1560
|
+
* // Basic usage
|
|
1561
|
+
* try {
|
|
1562
|
+
* schema.parse(data)
|
|
1563
|
+
* } catch (err) {
|
|
1564
|
+
* if (err instanceof ZodError) {
|
|
1565
|
+
* throw ValidationErrorX.fromZodError(err)
|
|
1566
|
+
* }
|
|
1567
|
+
* }
|
|
1568
|
+
*
|
|
1569
|
+
* // With custom uiMessage
|
|
1570
|
+
* ValidationErrorX.fromZodError(zodError, {
|
|
1571
|
+
* uiMessage: 'Please fix the form errors',
|
|
1572
|
+
* })
|
|
1573
|
+
*
|
|
1574
|
+
* // Access all issues
|
|
1575
|
+
* const error = ValidationErrorX.fromZodError(zodError)
|
|
1576
|
+
* error.metadata?.issues?.forEach(issue => {
|
|
1577
|
+
* console.log(`${issue.path.join('.')}: ${issue.message}`)
|
|
1578
|
+
* })
|
|
1579
|
+
* ```
|
|
1580
|
+
*/
|
|
1581
|
+
static fromZodError(zodError: {
|
|
1582
|
+
issues: ZodIssue[];
|
|
1583
|
+
}, overrides?: Partial<ErrorXOptions<ValidationErrorXMetadata>>): ValidationErrorX;
|
|
1584
|
+
/**
|
|
1585
|
+
* Creates a ValidationErrorX for a specific field.
|
|
1586
|
+
* Convenience method for manual validation errors.
|
|
1587
|
+
*
|
|
1588
|
+
* @param field - The field name that failed validation
|
|
1589
|
+
* @param message - The error message
|
|
1590
|
+
* @param options - Additional options
|
|
1591
|
+
* @returns ValidationErrorX instance
|
|
1592
|
+
*
|
|
1593
|
+
* @example
|
|
1594
|
+
* ```typescript
|
|
1595
|
+
* // Simple field error
|
|
1596
|
+
* throw ValidationErrorX.forField('email', 'Invalid email format')
|
|
1597
|
+
*
|
|
1598
|
+
* // With code
|
|
1599
|
+
* throw ValidationErrorX.forField('age', 'Must be 18 or older', {
|
|
1600
|
+
* code: 'TOO_YOUNG',
|
|
1601
|
+
* })
|
|
1602
|
+
* ```
|
|
1603
|
+
*/
|
|
1604
|
+
static forField(field: string, message: string, options?: Partial<ErrorXOptions<ValidationErrorXMetadata>>): ValidationErrorX;
|
|
1605
|
+
}
|
|
968
1606
|
|
|
969
|
-
export { ErrorX, type ErrorXCause, type ErrorXConfig, type ErrorXMetadata, type ErrorXOptions, type ErrorXSerialized,
|
|
1607
|
+
export { type DBErrorXPresetKey as DBErrorPreset, DBErrorX, type DBErrorXMetadata, ErrorX, type ErrorXCause, type ErrorXConfig, type ErrorXMetadata, type ErrorXOptions, type ErrorXSerialized, type ErrorXTransform, type ErrorXTransformContext, HTTPErrorX, type HTTPErrorXMetadata, type HTTPErrorXPresetKey as HTTPStatusCode, ValidationErrorX, type ValidationErrorXMetadata, type ZodIssue };
|