yerror 9.1.1 → 11.0.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/src/index.ts CHANGED
@@ -1,36 +1,50 @@
1
1
  import { EOL } from 'node:os';
2
2
 
3
- export type YErrorDebugValue = unknown[];
3
+ export interface YErrorRegistry {
4
+ E_UNEXPECTED: unknown[];
5
+ }
6
+
7
+ export type YErrorDebug<C extends string> = C extends keyof YErrorRegistry
8
+ ? YErrorRegistry[C] extends unknown[]
9
+ ? YErrorRegistry[C]
10
+ : never
11
+ : unknown[];
4
12
 
5
13
  /**
6
- * A YError class able to contain some debugValues and
14
+ * A YError class able to contain some debug and
7
15
  * print better stack traces
8
16
  * @extends Error
9
17
  */
10
- class YError<T extends unknown[] = YErrorDebugValue> extends Error {
18
+ class YError<
19
+ C extends string = string,
20
+ CC extends string = string,
21
+ > extends Error {
11
22
  code: string;
12
- debugValues: T = [] as unknown as T;
13
- wrappedErrors: (Error | YError)[] = [];
23
+ debug: YErrorDebug<C>;
24
+ cause?: Error | YError<CC> | undefined;
14
25
  /**
15
26
  * Creates a new YError with an error code
16
- * and some debugValues as debug values.
27
+ * and some debug as debug values.
17
28
  * @param {string} [errorCode = 'E_UNEXPECTED']
18
29
  * The error code corresponding to the actual error
19
- * @param {any} [debugValues]
30
+ * @param {any} [debug]
20
31
  * Some additional debugging values
32
+ * The error options
33
+ * @param {Object} options
34
+ * The error options
21
35
  */
22
36
  constructor(
23
- errorCode?: string,
24
- debugValues: T = [] as unknown as T,
25
- wrappedErrors: (Error | YError)[] = [],
37
+ errorCode: C = 'E_UNEXPECTED' as C,
38
+ debug?: YErrorDebug<C>,
39
+ options: { cause?: Error | YError<CC> } = {},
26
40
  ) {
27
41
  // Call the parent constructor
28
42
  super(errorCode);
29
43
 
30
44
  // Filling error
31
- this.code = errorCode || 'E_UNEXPECTED';
32
- this.debugValues = debugValues;
33
- this.wrappedErrors = wrappedErrors;
45
+ this.code = errorCode;
46
+ this.debug = (debug || []) as YErrorDebug<C>;
47
+ this.cause = options.cause;
34
48
  this.name = this.toString();
35
49
 
36
50
  if (Error.captureStackTrace) {
@@ -40,58 +54,55 @@ class YError<T extends unknown[] = YErrorDebugValue> extends Error {
40
54
 
41
55
  /**
42
56
  * Wraps any error and output a YError with an error
43
- * code and some debugValues as debug values.
57
+ * code and some debug as debug values.
44
58
  * @param {Error} err
45
59
  * The error to wrap
46
60
  * @param {string} [errorCode = 'E_UNEXPECTED']
47
61
  * The error code corresponding to the actual error
48
- * @param {any} [debugValues]
62
+ * @param {any} [debug]
49
63
  * Some additional debugging values
50
64
  * @return {YError}
51
65
  * The wrapped error
52
66
  */
53
- static wrap<T extends unknown[] = YErrorDebugValue>(
54
- err: Error | YError,
55
- errorCode?: string,
56
- debugValues: T = [] as unknown as T,
57
- ): YError {
67
+ static wrap<C extends string, CC extends string>(
68
+ err: Error | YError<CC>,
69
+ errorCode?: C,
70
+ debug?: YErrorDebug<C>,
71
+ ): YError<C, CC> {
58
72
  const wrappedErrorIsACode = looksLikeAYErrorCode(err.message);
59
- const wrappedErrors = (
60
- 'wrappedErrors' in err ? err.wrappedErrors : []
61
- ).concat([err]);
62
73
 
63
74
  if (!errorCode) {
64
75
  if (wrappedErrorIsACode) {
65
- errorCode = err.message;
76
+ errorCode = err.message as C;
66
77
  } else {
67
- errorCode = 'E_UNEXPECTED';
78
+ errorCode = 'E_UNEXPECTED' as C;
68
79
  }
69
80
  }
70
81
 
71
- return new YError<T>(errorCode, debugValues, wrappedErrors);
82
+ return new YError(errorCode, debug, { cause: err });
72
83
  }
73
84
 
74
85
  /**
75
86
  * Return a YError as is or wraps any other error and output
76
- * a YError with a code and some debugValues as debug values.
87
+ * a YError with a code and some debug as debug values.
77
88
  * @param {Error} err
78
89
  * The error to cast
79
90
  * @param {string} [errorCode = 'E_UNEXPECTED']
80
91
  * The error code corresponding to the actual error
81
- * @param {any} [debugValues]
92
+ * @param {any} [debug]
82
93
  * Some additional debugging values
83
94
  * @return {YError}
84
95
  * The wrapped error
85
96
  */
86
- static cast<T extends unknown[] = YErrorDebugValue>(
87
- err: Error | YError,
88
- errorCode?: string,
89
- debugValues: T = [] as unknown as T,
90
- ): YError {
97
+ static cast<C extends string, CC extends string>(
98
+ err: Error | YError<C> | YError<CC>,
99
+ errorCode?: C,
100
+ debug?: YErrorDebug<C>,
101
+ ): YError<C> | YError<C, CC> {
91
102
  if (looksLikeAYError(err)) {
92
- return err;
103
+ return err as YError<C>;
93
104
  }
94
- return YError.wrap(err, errorCode, debugValues);
105
+ return YError.wrap<C, CC>(err, errorCode, debug);
95
106
  }
96
107
 
97
108
  /**
@@ -102,36 +113,32 @@ class YError<T extends unknown[] = YErrorDebugValue> extends Error {
102
113
  * The error to bump
103
114
  * @param {string} [errorCode = 'E_UNEXPECTED']
104
115
  * The error code corresponding to the actual error
105
- * @param {any} [debugValues]
116
+ * @param {any} [debug]
106
117
  * Some additional debugging values
107
118
  * @return {YError}
108
119
  * The wrapped error
109
120
  */
110
- static bump<T extends unknown[] = YErrorDebugValue>(
111
- err: Error | YError,
112
- errorCode?: string,
113
- debugValues: T = [] as unknown as T,
114
- ): YError {
121
+ static bump<C extends string, CC extends string>(
122
+ err: Error | YError<CC>,
123
+ errorCode?: C,
124
+ debug?: YErrorDebug<C>,
125
+ ): YError<C, CC> | YError<CC, CC> {
115
126
  if (looksLikeAYError(err)) {
116
- return YError.wrap(err, err.code, err.debugValues);
127
+ return YError.wrap(err, err.code as CC, err.debug as YErrorDebug<CC>);
117
128
  }
118
- return YError.wrap(err, errorCode, debugValues);
129
+ return YError.wrap(err, errorCode, debug);
119
130
  }
120
131
 
121
132
  toString(): string {
122
- let debugValuesAsString: string;
133
+ let debugAsString: string;
123
134
 
124
135
  try {
125
- debugValuesAsString = JSON.stringify(this.debugValues);
136
+ debugAsString = JSON.stringify(this.debug);
126
137
  } catch {
127
- debugValuesAsString = '<circular>';
138
+ debugAsString = '<circular>';
128
139
  }
129
140
 
130
- return `${
131
- this.wrappedErrors.length
132
- ? this.wrappedErrors[this.wrappedErrors.length - 1].stack + EOL
133
- : ''
134
- }${this.constructor.name}: ${this.code} (${debugValuesAsString})`;
141
+ return `${this.constructor.name}: ${this.code} (${debugAsString})`;
135
142
  }
136
143
  }
137
144
 
@@ -143,14 +150,28 @@ class YError<T extends unknown[] = YErrorDebugValue> extends Error {
143
150
  * @return {string}
144
151
  * The stack trace if any
145
152
  */
146
- export function printStackTrace(err: Error | YError): string {
147
- return typeof err === 'object' && typeof err.stack === 'string'
148
- ? err.stack
149
- : `[no_stack_trace]: error is ${
153
+ export function printStackTrace(err: Error | YError | unknown): string {
154
+ let errorAsString: string;
155
+
156
+ if (
157
+ typeof err === 'object' &&
158
+ err instanceof Error &&
159
+ typeof err.stack === 'string'
160
+ ) {
161
+ errorAsString = err.stack;
162
+ } else {
163
+ try {
164
+ errorAsString = `[no_stack_trace]: error is serializable (${JSON.stringify(err)})`;
165
+ } catch {
166
+ errorAsString = `[no_stack_trace]: error is circular ("${
150
167
  err != null && typeof err.toString === 'function'
151
168
  ? err.toString()
152
169
  : typeof err
153
- }`;
170
+ }")`;
171
+ }
172
+ }
173
+
174
+ return `${errorAsString}${err instanceof Error && err.cause ? EOL + 'Caused by: ' + printStackTrace(err.cause) : ''}`;
154
175
  }
155
176
 
156
177
  export function looksLikeAYErrorCode(str: string): boolean {
@@ -159,19 +180,21 @@ export function looksLikeAYErrorCode(str: string): boolean {
159
180
 
160
181
  // In order to keep compatibility through major versions
161
182
  // we have to make kind of a cross major version instanceof
162
- export function looksLikeAYError(err: Error | YError): err is YError {
183
+ export function looksLikeAYError(err: Error | YError | unknown): err is YError {
163
184
  return (
164
185
  !!(err instanceof YError) ||
165
186
  !!(
187
+ err &&
188
+ typeof err === 'object' &&
166
189
  err.constructor &&
167
190
  err.constructor.name &&
168
191
  err.constructor.name.endsWith('Error') &&
169
192
  'code' in err &&
170
193
  'string' === typeof err.code &&
171
194
  looksLikeAYErrorCode(err.code) &&
172
- 'debugValues' in err &&
173
- err.debugValues &&
174
- err.debugValues instanceof Array
195
+ 'debug' in err &&
196
+ err.debug &&
197
+ err.debug instanceof Array
175
198
  )
176
199
  );
177
200
  }
@@ -185,10 +208,10 @@ export function looksLikeAYError(err: Error | YError): err is YError {
185
208
  * @return {boolean}
186
209
  * The result
187
210
  */
188
- export function hasYErrorCode<T extends unknown[] = YErrorDebugValue>(
189
- err: Error | YError,
190
- code: string,
191
- ): err is YError<T> {
211
+ export function hasYErrorCode<C extends string>(
212
+ err: Error | YError | unknown,
213
+ code: C,
214
+ ): err is YError<C> {
192
215
  return looksLikeAYError(err) && err.code === code;
193
216
  }
194
217
 
@@ -201,17 +224,17 @@ export function hasYErrorCode<T extends unknown[] = YErrorDebugValue>(
201
224
  * @return {boolean}
202
225
  * The result
203
226
  */
204
- export function pickYErrorWithCode<T extends unknown[] = YErrorDebugValue>(
205
- err: Error | YError,
206
- code: string,
207
- ): YError<T> | null {
208
- for (const currentError of [err].concat(
209
- 'wrappedErrors' in err ? err.wrappedErrors : [],
210
- )) {
211
- if (hasYErrorCode(currentError, code)) {
212
- return currentError as YError<T>;
227
+ export function pickYErrorWithCode<C extends string>(
228
+ err: Error | YError | unknown,
229
+ code: C,
230
+ ): YError<C> | null {
231
+ do {
232
+ if (hasYErrorCode(err, code)) {
233
+ return err;
213
234
  }
214
- }
235
+ err =
236
+ err && typeof err === 'object' && 'cause' in err && (err.cause as Error);
237
+ } while (err);
215
238
 
216
239
  return null;
217
240
  }