@bombillazo/error-x 0.5.1 → 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 +173 -21
- package/dist/index.cjs +216 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +222 -156
- package/dist/index.d.ts +222 -156
- package/dist/index.js +213 -125
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,19 +87,18 @@ 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
|
-
| `parent` | `ErrorX \| undefined` | Error that immediately precedes this error in the chain |
|
|
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 |
|
|
103
102
|
| `original` | `ErrorXSnapshot \| undefined` | Stores the original non-ErrorX error used to create this error |
|
|
104
103
|
|
|
105
104
|
#### Static Methods
|
|
@@ -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
|
});
|
|
@@ -163,7 +161,6 @@ new ErrorX<UserMeta>({
|
|
|
163
161
|
| message | `string` | `'An error occurred'` | Technical error message |
|
|
164
162
|
| name | `string` | `'Error'` | Error type/name |
|
|
165
163
|
| code | `string \| number` | Auto-generated | Error identifier (UPPER_SNAKE_CASE) |
|
|
166
|
-
| uiMessage | `string` | `undefined` | User-friendly message |
|
|
167
164
|
| httpStatus | `number` | `undefined` | HTTP status code |
|
|
168
165
|
| metadata | `TMetadata` | `undefined` | Additional context |
|
|
169
166
|
| cause | `unknown` | `undefined` | Error that caused this (builds the chain) |
|
|
@@ -311,7 +308,7 @@ Serialize ErrorX instances for network transfer or storage.
|
|
|
311
308
|
```typescript
|
|
312
309
|
// Serialize
|
|
313
310
|
const json = error.toJSON();
|
|
314
|
-
// { name, message, code,
|
|
311
|
+
// { name, message, code, stack, metadata, timestamp, httpStatus, original, chain }
|
|
315
312
|
|
|
316
313
|
// Deserialize
|
|
317
314
|
const restored = ErrorX.fromJSON(json);
|
|
@@ -474,7 +471,6 @@ try {
|
|
|
474
471
|
|
|
475
472
|
// With overrides
|
|
476
473
|
ValidationErrorX.fromZodError(zodError, {
|
|
477
|
-
uiMessage: "Please check your input",
|
|
478
474
|
httpStatus: 422,
|
|
479
475
|
});
|
|
480
476
|
|
|
@@ -519,25 +515,29 @@ const paymentPresets = {
|
|
|
519
515
|
code: "INSUFFICIENT_FUNDS",
|
|
520
516
|
name: "PaymentError",
|
|
521
517
|
message: "Insufficient funds.",
|
|
522
|
-
uiMessage: "Your payment method has insufficient funds.",
|
|
523
518
|
httpStatus: 402,
|
|
524
519
|
},
|
|
525
520
|
CARD_DECLINED: {
|
|
526
521
|
code: "CARD_DECLINED",
|
|
527
522
|
name: "PaymentError",
|
|
528
523
|
message: "Card declined.",
|
|
529
|
-
uiMessage: "Your card was declined. Please try another payment method.",
|
|
530
524
|
httpStatus: 402,
|
|
531
525
|
},
|
|
532
526
|
EXPIRED_CARD: {
|
|
533
527
|
code: "EXPIRED_CARD",
|
|
534
528
|
name: "PaymentError",
|
|
535
529
|
message: "Card expired.",
|
|
536
|
-
uiMessage: "Your card has expired. Please update your payment method.",
|
|
537
530
|
httpStatus: 402,
|
|
538
531
|
},
|
|
539
532
|
} as const satisfies Record<string, ErrorXOptions>;
|
|
540
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
|
+
|
|
541
541
|
// 3. Derive preset key type
|
|
542
542
|
type PaymentPresetKey = keyof typeof paymentPresets | (string & {});
|
|
543
543
|
|
|
@@ -578,6 +578,158 @@ if (error instanceof PaymentErrorX) {
|
|
|
578
578
|
}
|
|
579
579
|
```
|
|
580
580
|
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## ErrorXResolver
|
|
584
|
+
|
|
585
|
+
The `ErrorXResolver` class resolves `ErrorX` instances to enhanced presentation objects with i18n support, documentation URLs, and custom properties.
|
|
586
|
+
|
|
587
|
+
### Basic Usage
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import { ErrorXResolver, HTTPErrorX } from "@bombillazo/error-x";
|
|
591
|
+
|
|
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
|
+
},
|
|
603
|
+
});
|
|
604
|
+
|
|
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
|
+
```
|
|
609
|
+
|
|
610
|
+
### With i18n Integration
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
import i18next from "i18next";
|
|
614
|
+
|
|
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', ... }
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Custom Config Properties
|
|
635
|
+
|
|
636
|
+
Extend the resolver with custom properties for your domain:
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
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
|
+
```
|
|
667
|
+
|
|
668
|
+
### Custom Result Type
|
|
669
|
+
|
|
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
|
+
});
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
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
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
import {
|
|
696
|
+
httpErrorUiMessages,
|
|
697
|
+
dbErrorUiMessages,
|
|
698
|
+
validationErrorUiMessage,
|
|
699
|
+
} from "@bombillazo/error-x";
|
|
700
|
+
|
|
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."
|
|
704
|
+
|
|
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."
|
|
708
|
+
|
|
709
|
+
// Validation error default message
|
|
710
|
+
validationErrorUiMessage; // "The provided input is invalid. Please check your data."
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Usage with ErrorXResolver
|
|
714
|
+
|
|
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
|
+
```
|
|
732
|
+
|
|
581
733
|
## License
|
|
582
734
|
|
|
583
735
|
MIT
|