@bombillazo/error-x 0.5.0 → 0.6.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 +218 -72
- package/dist/index.cjs +264 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +232 -166
- package/dist/index.d.ts +232 -166
- package/dist/index.js +261 -152
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ A smart, isomorphic, and type-safe error library for TypeScript applications. Pr
|
|
|
18
18
|
- **Custom metadata** with type-safe generics for additional context
|
|
19
19
|
- **Global configuration** for stack cleaning and defaults
|
|
20
20
|
- **Serialization/deserialization** for network transfer and storage
|
|
21
|
+
- **ErrorXResolver** for i18n, documentation URLs, and custom presentation logic
|
|
21
22
|
- **Custom ErrorX class** examples:
|
|
22
23
|
- `HTTPErrorX` - HTTP status code presets (400-511)
|
|
23
24
|
- `DBErrorX` - Database error presets (connection, query, constraints)
|
|
@@ -60,7 +61,6 @@ throw new ErrorX({
|
|
|
60
61
|
message: "User authentication failed",
|
|
61
62
|
name: "AuthError",
|
|
62
63
|
code: "AUTH_FAILED",
|
|
63
|
-
uiMessage: "Please check your credentials",
|
|
64
64
|
httpStatus: 401,
|
|
65
65
|
metadata: { userId: 123 },
|
|
66
66
|
});
|
|
@@ -87,20 +87,19 @@ The base error class that extends the native `Error` with enhanced capabilities.
|
|
|
87
87
|
|
|
88
88
|
#### Properties
|
|
89
89
|
|
|
90
|
-
| Property | Type
|
|
91
|
-
| ------------ |
|
|
92
|
-
| `message` | `string`
|
|
93
|
-
| `name` | `string`
|
|
94
|
-
| `code` | `string`
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `original` | `ErrorXCause \| undefined` | Stores the original non-ErrorX error used to create this error |
|
|
90
|
+
| Property | Type | Description |
|
|
91
|
+
| ------------ | ----------------------------- | -------------------------------------------------------------- |
|
|
92
|
+
| `message` | `string` | Technical error message |
|
|
93
|
+
| `name` | `string` | Error type/name |
|
|
94
|
+
| `code` | `string` | Error identifier code (auto-generated from name if not set) |
|
|
95
|
+
| `httpStatus` | `number \| undefined` | HTTP status code associated with this error |
|
|
96
|
+
| `metadata` | `TMetadata \| undefined` | Additional context (type-safe with generics) |
|
|
97
|
+
| `timestamp` | `number` | Unix epoch timestamp (ms) when error was created |
|
|
98
|
+
| `stack` | `string \| undefined` | Stack trace (inherited from Error) |
|
|
99
|
+
| `chain` | `readonly ErrorX[]` | Full error sequence: `[this, parent, grandparent, ..., root]` |
|
|
100
|
+
| `root` | `ErrorX \| undefined` | Error that started the whole error chain |
|
|
101
|
+
| `parent` | `ErrorX \| undefined` | Error that immediately precedes this error in the chain |
|
|
102
|
+
| `original` | `ErrorXSnapshot \| undefined` | Stores the original non-ErrorX error used to create this error |
|
|
104
103
|
|
|
105
104
|
#### Static Methods
|
|
106
105
|
|
|
@@ -143,7 +142,6 @@ new ErrorX({
|
|
|
143
142
|
message: "User not found",
|
|
144
143
|
name: "NotFoundError",
|
|
145
144
|
code: "USER_NOT_FOUND",
|
|
146
|
-
uiMessage: "The requested user does not exist",
|
|
147
145
|
httpStatus: 404,
|
|
148
146
|
metadata: { userId: 123 },
|
|
149
147
|
});
|
|
@@ -158,15 +156,78 @@ new ErrorX<UserMeta>({
|
|
|
158
156
|
|
|
159
157
|
### ErrorXOptions
|
|
160
158
|
|
|
161
|
-
| Property | Type
|
|
162
|
-
| ---------- |
|
|
163
|
-
| message | `string`
|
|
164
|
-
| name | `string`
|
|
165
|
-
| code | `string \| number`
|
|
166
|
-
|
|
|
167
|
-
|
|
|
168
|
-
|
|
|
169
|
-
|
|
159
|
+
| Property | Type | Default | Description |
|
|
160
|
+
| ---------- | ------------------ | --------------------- | ----------------------------------------- |
|
|
161
|
+
| message | `string` | `'An error occurred'` | Technical error message |
|
|
162
|
+
| name | `string` | `'Error'` | Error type/name |
|
|
163
|
+
| code | `string \| number` | Auto-generated | Error identifier (UPPER_SNAKE_CASE) |
|
|
164
|
+
| httpStatus | `number` | `undefined` | HTTP status code |
|
|
165
|
+
| metadata | `TMetadata` | `undefined` | Additional context |
|
|
166
|
+
| cause | `unknown` | `undefined` | Error that caused this (builds the chain) |
|
|
167
|
+
|
|
168
|
+
## Global Configuration
|
|
169
|
+
|
|
170
|
+
Configure stack trace cleaning and other global settings.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { ErrorX } from "@bombillazo/error-x";
|
|
174
|
+
|
|
175
|
+
// Enable stack cleaning with custom delimiter
|
|
176
|
+
ErrorX.configure({
|
|
177
|
+
cleanStack: true,
|
|
178
|
+
cleanStackDelimiter: "my-app-entry",
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Custom patterns to remove from stack traces
|
|
182
|
+
ErrorX.configure({
|
|
183
|
+
cleanStack: ["node_modules", "internal/"],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Disable stack cleaning
|
|
187
|
+
ErrorX.configure({ cleanStack: false });
|
|
188
|
+
|
|
189
|
+
// Get current config
|
|
190
|
+
const config = ErrorX.getConfig();
|
|
191
|
+
|
|
192
|
+
// Reset to defaults
|
|
193
|
+
ErrorX.resetConfig();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Auto Code Generation
|
|
197
|
+
|
|
198
|
+
Error codes are automatically generated from names in UPPER_SNAKE_CASE when not provided:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
new ErrorX({ name: "DatabaseError" });
|
|
202
|
+
// → code: 'DATABASE_ERROR'
|
|
203
|
+
|
|
204
|
+
new ErrorX({ name: "userAuthError" });
|
|
205
|
+
// → code: 'USER_AUTH_ERROR'
|
|
206
|
+
|
|
207
|
+
new ErrorX({ name: "API Timeout" });
|
|
208
|
+
// → code: 'API_TIMEOUT'
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Message Formatting
|
|
212
|
+
|
|
213
|
+
ErrorX does NOT auto-format messages. Messages are passed through as-is:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
new ErrorX({ message: "test error" });
|
|
217
|
+
// → message: 'test error'
|
|
218
|
+
|
|
219
|
+
new ErrorX({ message: "Test error." });
|
|
220
|
+
// → message: 'Test error.'
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Empty or whitespace-only messages default to `'An error occurred'`:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
new ErrorX({ message: "" });
|
|
227
|
+
// → message: 'An error occurred'
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Preset messages in specialized classes (HTTPErrorX, DBErrorX) are properly formatted with sentence casing and periods.
|
|
170
231
|
|
|
171
232
|
---
|
|
172
233
|
|
|
@@ -247,7 +308,7 @@ Serialize ErrorX instances for network transfer or storage.
|
|
|
247
308
|
```typescript
|
|
248
309
|
// Serialize
|
|
249
310
|
const json = error.toJSON();
|
|
250
|
-
// { name, message, code,
|
|
311
|
+
// { name, message, code, stack, metadata, timestamp, httpStatus, original, chain }
|
|
251
312
|
|
|
252
313
|
// Deserialize
|
|
253
314
|
const restored = ErrorX.fromJSON(json);
|
|
@@ -288,7 +349,7 @@ try {
|
|
|
288
349
|
if (ErrorX.isErrorX(error)) {
|
|
289
350
|
console.log(
|
|
290
351
|
"Error chain:",
|
|
291
|
-
error.chain.map((e) => e.name)
|
|
352
|
+
error.chain.map((e) => e.name),
|
|
292
353
|
);
|
|
293
354
|
console.log("Root cause:", error.root?.original);
|
|
294
355
|
}
|
|
@@ -410,7 +471,6 @@ try {
|
|
|
410
471
|
|
|
411
472
|
// With overrides
|
|
412
473
|
ValidationErrorX.fromZodError(zodError, {
|
|
413
|
-
uiMessage: "Please check your input",
|
|
414
474
|
httpStatus: 422,
|
|
415
475
|
});
|
|
416
476
|
|
|
@@ -455,25 +515,29 @@ const paymentPresets = {
|
|
|
455
515
|
code: "INSUFFICIENT_FUNDS",
|
|
456
516
|
name: "PaymentError",
|
|
457
517
|
message: "Insufficient funds.",
|
|
458
|
-
uiMessage: "Your payment method has insufficient funds.",
|
|
459
518
|
httpStatus: 402,
|
|
460
519
|
},
|
|
461
520
|
CARD_DECLINED: {
|
|
462
521
|
code: "CARD_DECLINED",
|
|
463
522
|
name: "PaymentError",
|
|
464
523
|
message: "Card declined.",
|
|
465
|
-
uiMessage: "Your card was declined. Please try another payment method.",
|
|
466
524
|
httpStatus: 402,
|
|
467
525
|
},
|
|
468
526
|
EXPIRED_CARD: {
|
|
469
527
|
code: "EXPIRED_CARD",
|
|
470
528
|
name: "PaymentError",
|
|
471
529
|
message: "Card expired.",
|
|
472
|
-
uiMessage: "Your card has expired. Please update your payment method.",
|
|
473
530
|
httpStatus: 402,
|
|
474
531
|
},
|
|
475
532
|
} as const satisfies Record<string, ErrorXOptions>;
|
|
476
533
|
|
|
534
|
+
// Optional: Define user-friendly messages separately
|
|
535
|
+
export const paymentErrorUiMessages: Record<keyof typeof paymentPresets, string> = {
|
|
536
|
+
INSUFFICIENT_FUNDS: "Your payment method has insufficient funds.",
|
|
537
|
+
CARD_DECLINED: "Your card was declined. Please try another payment method.",
|
|
538
|
+
EXPIRED_CARD: "Your card has expired. Please update your payment method.",
|
|
539
|
+
};
|
|
540
|
+
|
|
477
541
|
// 3. Derive preset key type
|
|
478
542
|
type PaymentPresetKey = keyof typeof paymentPresets | (string & {});
|
|
479
543
|
|
|
@@ -492,12 +556,12 @@ export class PaymentErrorX extends ErrorX<PaymentMetadata> {
|
|
|
492
556
|
// Override create for proper typing
|
|
493
557
|
static override create(
|
|
494
558
|
presetKey?: PaymentPresetKey,
|
|
495
|
-
overrides?: Partial<ErrorXOptions<PaymentMetadata
|
|
559
|
+
overrides?: Partial<ErrorXOptions<PaymentMetadata>>,
|
|
496
560
|
): PaymentErrorX {
|
|
497
561
|
return ErrorX.create.call(
|
|
498
562
|
PaymentErrorX,
|
|
499
563
|
presetKey,
|
|
500
|
-
overrides
|
|
564
|
+
overrides,
|
|
501
565
|
) as PaymentErrorX;
|
|
502
566
|
}
|
|
503
567
|
}
|
|
@@ -516,73 +580,155 @@ if (error instanceof PaymentErrorX) {
|
|
|
516
580
|
|
|
517
581
|
---
|
|
518
582
|
|
|
519
|
-
##
|
|
583
|
+
## ErrorXResolver
|
|
520
584
|
|
|
521
|
-
|
|
585
|
+
The `ErrorXResolver` class resolves `ErrorX` instances to enhanced presentation objects with i18n support, documentation URLs, and custom properties.
|
|
522
586
|
|
|
523
|
-
|
|
587
|
+
### Basic Usage
|
|
524
588
|
|
|
525
589
|
```typescript
|
|
526
|
-
import {
|
|
590
|
+
import { ErrorXResolver, HTTPErrorX } from "@bombillazo/error-x";
|
|
527
591
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
592
|
+
const resolver = new ErrorXResolver({
|
|
593
|
+
// Required: determine error type from error instance
|
|
594
|
+
onResolveType: (error) => {
|
|
595
|
+
if (error instanceof HTTPErrorX) return "http";
|
|
596
|
+
return "general";
|
|
597
|
+
},
|
|
598
|
+
// Per-type configuration
|
|
599
|
+
configs: {
|
|
600
|
+
http: { namespace: "errors.http" },
|
|
601
|
+
general: { namespace: "errors" },
|
|
602
|
+
},
|
|
532
603
|
});
|
|
533
604
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
605
|
+
const error = HTTPErrorX.create(404);
|
|
606
|
+
const result = resolver.resolve(error);
|
|
607
|
+
// → { uiMessage: undefined, docsUrl: '', i18nKey: 'errors.http.NOT_FOUND', errorType: 'http', config: {...} }
|
|
608
|
+
```
|
|
538
609
|
|
|
539
|
-
|
|
540
|
-
ErrorX.configure({ cleanStack: false });
|
|
610
|
+
### With i18n Integration
|
|
541
611
|
|
|
542
|
-
|
|
543
|
-
|
|
612
|
+
```typescript
|
|
613
|
+
import i18next from "i18next";
|
|
544
614
|
|
|
545
|
-
|
|
546
|
-
|
|
615
|
+
const resolver = new ErrorXResolver({
|
|
616
|
+
i18n: {
|
|
617
|
+
resolver: (key, params) => i18next.t(key, params),
|
|
618
|
+
keyTemplate: "{namespace}.{code}", // default
|
|
619
|
+
},
|
|
620
|
+
docs: {
|
|
621
|
+
baseUrl: "https://docs.example.com/errors",
|
|
622
|
+
},
|
|
623
|
+
onResolveType: (error) => (error.code.startsWith("HTTP_") ? "http" : "general"),
|
|
624
|
+
configs: {
|
|
625
|
+
http: { namespace: "errors.http", docsPath: "/http" },
|
|
626
|
+
general: { namespace: "errors.general" },
|
|
627
|
+
},
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
const result = resolver.resolve(error);
|
|
631
|
+
// → { uiMessage: 'Not found', docsUrl: 'https://docs.example.com/errors/http#NOT_FOUND', ... }
|
|
547
632
|
```
|
|
548
633
|
|
|
549
|
-
###
|
|
634
|
+
### Custom Config Properties
|
|
550
635
|
|
|
551
|
-
|
|
636
|
+
Extend the resolver with custom properties for your domain:
|
|
552
637
|
|
|
553
638
|
```typescript
|
|
554
|
-
|
|
555
|
-
|
|
639
|
+
import { ErrorXResolver, type ErrorXResolverConfig } from "@bombillazo/error-x";
|
|
640
|
+
|
|
641
|
+
// Define custom config with additional properties
|
|
642
|
+
type MyConfig = ErrorXResolverConfig<{
|
|
643
|
+
severity: "error" | "warning" | "info";
|
|
644
|
+
retryable: boolean;
|
|
645
|
+
}>;
|
|
646
|
+
|
|
647
|
+
const resolver = new ErrorXResolver<MyConfig>({
|
|
648
|
+
onResolveType: (error) => "api",
|
|
649
|
+
defaults: {
|
|
650
|
+
namespace: "errors",
|
|
651
|
+
severity: "error",
|
|
652
|
+
retryable: false,
|
|
653
|
+
},
|
|
654
|
+
configs: {
|
|
655
|
+
api: {
|
|
656
|
+
namespace: "errors.api",
|
|
657
|
+
severity: "warning",
|
|
658
|
+
retryable: true,
|
|
659
|
+
// Per-code overrides
|
|
660
|
+
presets: {
|
|
661
|
+
NOT_FOUND: { severity: "info", retryable: false },
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
});
|
|
666
|
+
```
|
|
556
667
|
|
|
557
|
-
|
|
558
|
-
// → code: 'USER_AUTH_ERROR'
|
|
668
|
+
### Custom Result Type
|
|
559
669
|
|
|
560
|
-
|
|
561
|
-
|
|
670
|
+
Transform the resolve output to match your API:
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
type MyResult = { message: string; docs: string; canRetry: boolean };
|
|
674
|
+
|
|
675
|
+
const resolver = new ErrorXResolver<MyConfig, MyResult>({
|
|
676
|
+
onResolveType: (error) => "api",
|
|
677
|
+
onResolve: (error, context) => ({
|
|
678
|
+
message: context.uiMessage ?? error.message,
|
|
679
|
+
docs: context.docsUrl,
|
|
680
|
+
canRetry: context.config.retryable,
|
|
681
|
+
}),
|
|
682
|
+
configs: { api: { namespace: "errors.api", retryable: true } },
|
|
683
|
+
});
|
|
562
684
|
```
|
|
563
685
|
|
|
564
|
-
|
|
686
|
+
---
|
|
565
687
|
|
|
566
|
-
|
|
688
|
+
## UI Messages
|
|
689
|
+
|
|
690
|
+
User-friendly messages are provided separately from error presets. This allows errors to remain technical while UI messages can be managed independently (e.g., for i18n).
|
|
691
|
+
|
|
692
|
+
### Available Exports
|
|
567
693
|
|
|
568
694
|
```typescript
|
|
569
|
-
|
|
570
|
-
|
|
695
|
+
import {
|
|
696
|
+
httpErrorUiMessages,
|
|
697
|
+
dbErrorUiMessages,
|
|
698
|
+
validationErrorUiMessage,
|
|
699
|
+
} from "@bombillazo/error-x";
|
|
571
700
|
|
|
572
|
-
|
|
573
|
-
//
|
|
574
|
-
|
|
701
|
+
// HTTP error messages keyed by status code
|
|
702
|
+
httpErrorUiMessages[404]; // "The requested resource could not be found."
|
|
703
|
+
httpErrorUiMessages[500]; // "An unexpected error occurred. Please try again later."
|
|
575
704
|
|
|
576
|
-
|
|
705
|
+
// Database error messages keyed by preset name
|
|
706
|
+
dbErrorUiMessages.CONNECTION_FAILED; // "Unable to connect to the database. Please try again later."
|
|
707
|
+
dbErrorUiMessages.UNIQUE_VIOLATION; // "This record already exists."
|
|
577
708
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
// → message: 'An error occurred'
|
|
709
|
+
// Validation error default message
|
|
710
|
+
validationErrorUiMessage; // "The provided input is invalid. Please check your data."
|
|
581
711
|
```
|
|
582
712
|
|
|
583
|
-
|
|
713
|
+
### Usage with ErrorXResolver
|
|
584
714
|
|
|
585
|
-
|
|
715
|
+
```typescript
|
|
716
|
+
import { ErrorXResolver, HTTPErrorX, httpErrorUiMessages } from "@bombillazo/error-x";
|
|
717
|
+
|
|
718
|
+
const resolver = new ErrorXResolver({
|
|
719
|
+
onResolveType: () => "http",
|
|
720
|
+
configs: {
|
|
721
|
+
http: {
|
|
722
|
+
namespace: "errors.http",
|
|
723
|
+
presets: {
|
|
724
|
+
// Use provided UI messages as static fallbacks
|
|
725
|
+
NOT_FOUND: { uiMessage: httpErrorUiMessages[404] },
|
|
726
|
+
UNAUTHORIZED: { uiMessage: httpErrorUiMessages[401] },
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
```
|
|
586
732
|
|
|
587
733
|
## License
|
|
588
734
|
|