@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 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 | 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
- | `uiMessage` | `string \| undefined` | User-friendly message for UI display |
96
- | `httpStatus` | `number \| undefined` | HTTP status code associated with this error |
97
- | `metadata` | `TMetadata \| undefined` | Additional context (type-safe with generics) |
98
- | `timestamp` | `number` | Unix epoch timestamp (ms) when error was created |
99
- | `stack` | `string \| undefined` | Stack trace (inherited from Error) |
100
- | `chain` | `readonly ErrorX[]` | Full error sequence: `[this, parent, grandparent, ..., root]` |
101
- | `root` | `ErrorX \| undefined` | Error that started the whole error chain |
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, uiMessage, stack, metadata, timestamp, httpStatus, original, chain }
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