@bombillazo/error-x 0.4.0 → 0.4.2

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
@@ -36,7 +36,6 @@ yarn add @bombillazo/error-x
36
36
 
37
37
  This library uses modern JavaScript features and ES2022 APIs. For browser compatibility, ensure your build tool (e.g., Vite, webpack, esbuild) is configured to target ES2022 or transpile accordingly.
38
38
 
39
-
40
39
  > [!WARNING]
41
40
  >
42
41
  > This library is currently in pre-v1.0 development. While we strive to minimize breaking changes, the API may evolve based on feedback and real-world usage. We recommend pinning to specific versions and reviewing release notes when updating.
@@ -87,7 +86,7 @@ try {
87
86
  ### Constructor
88
87
 
89
88
  ```typescript
90
- new ErrorX(message?: string | ErrorXOptions)
89
+ new ErrorX(input?: string | ErrorXOptions)
91
90
  ```
92
91
 
93
92
  All parameters are optional. ErrorX uses sensible defaults:
@@ -149,6 +148,7 @@ All presets use **camelCase naming** and include:
149
148
  - `type`: Set to `'http'` for all HTTP presets
150
149
 
151
150
  **4xx Client Errors:**
151
+
152
152
  `badRequest`, `unauthorized`, `paymentRequired`, `forbidden`, `notFound`, `methodNotAllowed`, `notAcceptable`, `proxyAuthenticationRequired`, `requestTimeout`, `conflict`, `gone`, `lengthRequired`, `preconditionFailed`, `payloadTooLarge`, `uriTooLong`, `unsupportedMediaType`, `rangeNotSatisfiable`, `expectationFailed`, `imATeapot`, `unprocessableEntity`, `locked`, `failedDependency`, `tooEarly`, `upgradeRequired`, `preconditionRequired`, `tooManyRequests`, `requestHeaderFieldsTooLarge`, `unavailableForLegalReasons`
153
153
 
154
154
  **5xx Server Errors:**
@@ -281,14 +281,19 @@ const enriched = error.withMetadata({ b: 2 })
281
281
  // enriched.metadata = { a: 1, b: 2 }
282
282
  ```
283
283
 
284
- #### `cleanStackTrace(delimiter?: string): ErrorX`
284
+ #### `ErrorX.cleanStack(stack?: string, delimiter?: string): string`
285
285
 
286
- Removes stack trace lines before a delimiter:
286
+ Cleans a stack trace by removing ErrorX internal calls and optionally trimming after a delimiter:
287
287
 
288
288
  ```typescript
289
289
  const error = new ErrorX('test')
290
- const cleaned = error.cleanStackTrace('my-app-boundary')
291
- // Stack trace only includes lines after 'my-app-boundary'
290
+
291
+ // Clean with pattern-based removal only
292
+ const cleaned = ErrorX.cleanStack(error.stack)
293
+
294
+ // Clean and trim after delimiter
295
+ const trimmed = ErrorX.cleanStack(error.stack, 'my-app-boundary')
296
+ // Returns stack trace starting after the line containing 'my-app-boundary'
292
297
  ```
293
298
 
294
299
  #### `toJSON(): ErrorXSerialized`
package/dist/index.cjs CHANGED
@@ -46,6 +46,8 @@ var ErrorX = class _ErrorX extends Error {
46
46
  docsUrl;
47
47
  /** Where the error originated (service name, module, component) */
48
48
  source;
49
+ /** Original error that caused this error (preserves error chain) */
50
+ cause;
49
51
  /**
50
52
  * Creates a new ErrorX instance with enhanced error handling capabilities.
51
53
  *
@@ -89,15 +91,9 @@ var ErrorX = class _ErrorX extends Error {
89
91
  }
90
92
  const envConfig = _ErrorX.getConfig();
91
93
  const message = options.message?.trim() ? options.message : "An error occurred";
92
- super(message, { cause: options.cause });
93
- if (options.cause !== void 0 && !Object.hasOwn(this, "cause")) {
94
- Object.defineProperty(this, "cause", {
95
- value: options.cause,
96
- writable: true,
97
- enumerable: false,
98
- configurable: true
99
- });
100
- }
94
+ const convertedCause = _ErrorX.toErrorXCause(options.cause);
95
+ super(message);
96
+ this.cause = convertedCause;
101
97
  this.name = options.name ?? _ErrorX.getDefaultName();
102
98
  this.code = options.code != null ? String(options.code) : _ErrorX.generateDefaultCode(options.name);
103
99
  this.uiMessage = options.uiMessage;
@@ -117,8 +113,8 @@ var ErrorX = class _ErrorX extends Error {
117
113
  }
118
114
  }
119
115
  this.docsUrl = options.docsUrl ?? generatedDocsUrl;
120
- if (options.cause instanceof Error) {
121
- this.stack = _ErrorX.preserveOriginalStack(options.cause, this);
116
+ if (convertedCause?.stack) {
117
+ this.stack = _ErrorX.preserveOriginalStackFromCause(convertedCause, this);
122
118
  } else {
123
119
  if (typeof Error.captureStackTrace === "function") {
124
120
  Error.captureStackTrace(this, this.constructor);
@@ -133,6 +129,44 @@ var ErrorX = class _ErrorX extends Error {
133
129
  static getDefaultName() {
134
130
  return "Error";
135
131
  }
132
+ /**
133
+ * Converts any value to ErrorXCause format.
134
+ * @param value - Value to convert to ErrorXCause
135
+ * @returns ErrorXCause object or undefined if value is null/undefined
136
+ */
137
+ static toErrorXCause(value) {
138
+ if (value === void 0 || value === null) {
139
+ return void 0;
140
+ }
141
+ if (value instanceof Error) {
142
+ const cause = {
143
+ message: value.message
144
+ };
145
+ if (value.name) {
146
+ cause.name = value.name;
147
+ }
148
+ if (value.stack) {
149
+ cause.stack = value.stack;
150
+ }
151
+ return cause;
152
+ }
153
+ if (typeof value === "object") {
154
+ const obj = value;
155
+ const cause = {
156
+ message: String(obj.message || obj)
157
+ };
158
+ if (obj.name) {
159
+ cause.name = String(obj.name);
160
+ }
161
+ if (obj.stack) {
162
+ cause.stack = String(obj.stack);
163
+ }
164
+ return cause;
165
+ }
166
+ return {
167
+ message: String(value)
168
+ };
169
+ }
136
170
  /**
137
171
  * Configure global ErrorX settings.
138
172
  * This method allows you to set defaults for all ErrorX instances.
@@ -147,7 +181,8 @@ var ErrorX = class _ErrorX extends Error {
147
181
  * docsMap: {
148
182
  * 'AUTH_FAILED': 'authentication-errors',
149
183
  * 'DB_ERROR': 'database-errors'
150
- * }
184
+ * },
185
+ * cleanStackDelimiter: 'app-entry-point' // Trim stack traces after this line
151
186
  * })
152
187
  * ```
153
188
  */
@@ -161,6 +196,19 @@ var ErrorX = class _ErrorX extends Error {
161
196
  static getConfig() {
162
197
  return _ErrorX._config;
163
198
  }
199
+ /**
200
+ * Reset global configuration to null.
201
+ * Useful for testing or when you want to clear all configuration.
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * ErrorX.resetConfig()
206
+ * const config = ErrorX.getConfig() // null
207
+ * ```
208
+ */
209
+ static resetConfig() {
210
+ _ErrorX._config = null;
211
+ }
164
212
  /**
165
213
  * Validates HTTP status code to ensure it's within valid range (100-599)
166
214
  *
@@ -234,27 +282,38 @@ var ErrorX = class _ErrorX extends Error {
234
282
  }
235
283
  /**
236
284
  * Preserves the original error's stack trace while updating the error message.
237
- * Combines the new error's message with the original error's stack trace.
285
+ * Combines the new error's message with the original error's stack trace from ErrorXCause.
238
286
  *
239
- * @param originalError - The original error whose stack to preserve
287
+ * @param cause - The ErrorXCause containing the original stack to preserve
240
288
  * @param newError - The new error whose message to use
241
289
  * @returns Combined stack trace with new error message and original stack
242
290
  */
243
- static preserveOriginalStack(originalError, newError) {
244
- if (!originalError.stack) return newError.stack || "";
291
+ static preserveOriginalStackFromCause(cause, newError) {
292
+ if (!cause.stack) return newError.stack || "";
245
293
  const newErrorFirstLine = `${newError.name}: ${newError.message}`;
246
- const originalStackLines = originalError.stack.split("\n");
294
+ const originalStackLines = cause.stack.split("\n");
247
295
  const originalStackTrace = originalStackLines.slice(1);
248
296
  return [newErrorFirstLine, ...originalStackTrace].join("\n");
249
297
  }
250
298
  /**
251
- * Cleans the stack trace by removing ErrorX internal method calls.
299
+ * Cleans a stack trace by removing ErrorX internal method calls and optionally trimming after a delimiter.
252
300
  * This provides cleaner stack traces that focus on user code.
253
301
  *
254
- * @param stack - Raw stack trace to clean
255
- * @returns Cleaned stack trace without ErrorX internal calls
302
+ * @param stack - Raw stack trace string to clean
303
+ * @param delimiter - Optional delimiter to trim stack trace after (overrides config delimiter)
304
+ * @returns Cleaned stack trace without internal calls and optionally trimmed after delimiter
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * // Clean with pattern-based removal only
309
+ * const cleaned = ErrorX.cleanStack(error.stack)
310
+ *
311
+ * // Clean and trim after delimiter
312
+ * const trimmed = ErrorX.cleanStack(error.stack, 'my-app-entry')
313
+ * // Returns stack trace starting after the line containing 'my-app-entry'
314
+ * ```
256
315
  */
257
- static cleanStack(stack) {
316
+ static cleanStack(stack, delimiter) {
258
317
  if (!stack) return "";
259
318
  const config = _ErrorX.getConfig();
260
319
  const cleanStackConfig = config?.cleanStack ?? true;
@@ -278,30 +337,15 @@ var ErrorX = class _ErrorX extends Error {
278
337
  }
279
338
  cleanedLines.push(line);
280
339
  }
281
- return cleanedLines.join("\n");
282
- }
283
- /**
284
- * Processes an error's stack trace to trim it after a specified delimiter.
285
- * Useful for removing irrelevant stack frames before a specific function.
286
- *
287
- * @param error - Error whose stack to process
288
- * @param delimiter - String to search for in stack lines
289
- * @returns Processed stack trace starting after the delimiter
290
- *
291
- * @example
292
- * ```typescript
293
- * const processed = ErrorX.processErrorStack(error, 'my-app-entry')
294
- * // Returns stack trace starting after the line containing 'my-app-entry'
295
- * ```
296
- */
297
- static processErrorStack(error, delimiter) {
298
- let stack = error.stack ?? "";
299
- const stackLines = stack.split("\n");
300
- const delimiterIndex = stackLines.findIndex((line) => line.includes(delimiter));
301
- if (delimiterIndex !== -1) {
302
- stack = stackLines.slice(delimiterIndex + 1).join("\n");
340
+ let cleanedStack = cleanedLines.join("\n");
341
+ const activeDelimiter = delimiter ?? config?.cleanStackDelimiter;
342
+ if (activeDelimiter) {
343
+ const delimiterIndex = cleanedLines.findIndex((line) => line.includes(activeDelimiter));
344
+ if (delimiterIndex !== -1) {
345
+ cleanedStack = cleanedLines.slice(delimiterIndex + 1).join("\n");
346
+ }
303
347
  }
304
- return stack;
348
+ return cleanedStack;
305
349
  }
306
350
  /**
307
351
  * Creates a new ErrorX instance with additional metadata merged with existing metadata.
@@ -466,43 +510,6 @@ var ErrorX = class _ErrorX extends Error {
466
510
  const options = _ErrorX.convertUnknownToOptions(error);
467
511
  return new _ErrorX(options);
468
512
  }
469
- /**
470
- * Creates a new ErrorX instance with cleaned stack trace using the specified delimiter.
471
- * Returns the same instance if no delimiter is provided or no stack is available.
472
- *
473
- * @param delimiter - Optional string to search for in stack lines
474
- * @returns New ErrorX instance with cleaned stack trace, or the same instance if no cleaning needed
475
- *
476
- * @example
477
- * ```typescript
478
- * const error = new ErrorX({ message: 'Database error' })
479
- * const cleanedError = error.cleanStackTrace('database-layer')
480
- * // Returns new ErrorX with stack trace starting after 'database-layer'
481
- * ```
482
- */
483
- cleanStackTrace(delimiter) {
484
- if (delimiter && this.stack) {
485
- const options = {
486
- message: this.message,
487
- name: this.name,
488
- code: this.code,
489
- uiMessage: this.uiMessage,
490
- cause: this.cause,
491
- httpStatus: this.httpStatus,
492
- type: this.type,
493
- sourceUrl: this.sourceUrl,
494
- docsUrl: this.docsUrl,
495
- source: this.source
496
- };
497
- if (this.metadata !== void 0) {
498
- options.metadata = this.metadata;
499
- }
500
- const newError = new _ErrorX(options);
501
- newError.stack = _ErrorX.processErrorStack(this, delimiter);
502
- return newError;
503
- }
504
- return this;
505
- }
506
513
  /**
507
514
  * Converts the ErrorX instance to a detailed string representation.
508
515
  * Includes error name, message, code, timestamp, metadata, and stack trace.
@@ -590,34 +597,7 @@ ${this.stack}`;
590
597
  serialized.stack = this.stack;
591
598
  }
592
599
  if (this.cause) {
593
- if (this.cause instanceof Error) {
594
- const cause = {
595
- message: this.cause.message
596
- };
597
- if (this.cause.name) {
598
- cause.name = this.cause.name;
599
- }
600
- if (this.cause.stack) {
601
- cause.stack = this.cause.stack;
602
- }
603
- serialized.cause = cause;
604
- } else if (typeof this.cause === "object" && this.cause !== null) {
605
- const causeObj = this.cause;
606
- const cause = {
607
- message: String(causeObj.message || causeObj)
608
- };
609
- if (causeObj.name) {
610
- cause.name = String(causeObj.name);
611
- }
612
- if (causeObj.stack) {
613
- cause.stack = String(causeObj.stack);
614
- }
615
- serialized.cause = cause;
616
- } else {
617
- serialized.cause = {
618
- message: String(this.cause)
619
- };
620
- }
600
+ serialized.cause = this.cause;
621
601
  }
622
602
  return serialized;
623
603
  }
@@ -653,7 +633,8 @@ ${this.stack}`;
653
633
  type: serialized.type,
654
634
  sourceUrl: serialized.sourceUrl,
655
635
  docsUrl: serialized.docsUrl,
656
- source: serialized.source
636
+ source: serialized.source,
637
+ cause: serialized.cause
657
638
  };
658
639
  if (serialized.metadata !== void 0) {
659
640
  options.metadata = serialized.metadata;
@@ -663,19 +644,6 @@ ${this.stack}`;
663
644
  error.stack = serialized.stack;
664
645
  }
665
646
  error.timestamp = new Date(serialized.timestamp);
666
- if (serialized.cause) {
667
- const causeError = new Error(serialized.cause.message);
668
- if (serialized.cause.name) {
669
- causeError.name = serialized.cause.name;
670
- }
671
- if (serialized.cause.stack) {
672
- causeError.stack = serialized.cause.stack;
673
- }
674
- Object.defineProperty(error, "cause", {
675
- value: causeError,
676
- writable: true
677
- });
678
- }
679
647
  return error;
680
648
  }
681
649
  };