@wireio/shared 1.0.70 → 1.0.71
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/lib/cjs/errors/NestedError.d.ts +110 -6
- package/lib/cjs/errors/NestedError.d.ts.map +1 -1
- package/lib/cjs/errors/NestedError.js +139 -9
- package/lib/esm/errors/NestedError.d.ts +110 -6
- package/lib/esm/errors/NestedError.d.ts.map +1 -1
- package/lib/esm/errors/NestedError.js +139 -9
- package/lib/test-cjs/errors/NestedError.test.js +81 -34
- package/lib/test-cjs/errors/NestedError.test.js.map +1 -1
- package/lib/tsconfig.cjs.jest.tsbuildinfo +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,20 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An Error that PRESERVES what it wraps instead of restringing it. Given a new
|
|
3
|
+
* message string, it ALSO retains the full cause chain: the originating error(s)
|
|
4
|
+
* survive as {@link NestedError.causes} (and the native `Error.cause`) — the real
|
|
5
|
+
* Error objects, so **each cause keeps its own stack trace and message** — and a
|
|
6
|
+
* nested-exception `Caused by:` section per cause is appended to this error's
|
|
7
|
+
* {@link NestedError.stack}, so the whole chain (each cause's stack + message) is
|
|
8
|
+
* visible wherever the error is printed, not only via the programmatic accessors.
|
|
9
|
+
* The diagnostic {@link NestedError.context} (the `text`, the schema / type name, …)
|
|
10
|
+
* is BOTH a structured property AND folded into the message.
|
|
11
|
+
*
|
|
12
|
+
* Never rewrite a caught error into a bare ``new Error(`… ${err.message}`)`` — that
|
|
13
|
+
* SWALLOWS the cause and the context. Wrap it:
|
|
14
|
+
* `new NestedError("…", { cause: err, context: { … } })`.
|
|
15
|
+
*/
|
|
1
16
|
export class NestedError extends Error {
|
|
17
|
+
/** The originating error chain (flattened + coerced to Error); `causes[0]` is the native `.cause`. */
|
|
2
18
|
causes;
|
|
3
|
-
|
|
4
|
-
|
|
19
|
+
/** The diagnostic context captured at the throw site. */
|
|
20
|
+
context;
|
|
21
|
+
/**
|
|
22
|
+
* @param message - The high-level failure description.
|
|
23
|
+
* @param options - The {@link NestedErrorOptions.cause} to preserve + the {@link NestedErrorOptions.context} to capture.
|
|
24
|
+
*/
|
|
25
|
+
constructor(message, options = {}) {
|
|
26
|
+
const causes = NestedError.toErrors(options.cause);
|
|
27
|
+
super(NestedError.composeMessage(message, options.context), causes.length > 0 ? { cause: causes[0] } : undefined);
|
|
5
28
|
this.name = "NestedError";
|
|
6
|
-
this.causes = causes
|
|
29
|
+
this.causes = causes;
|
|
30
|
+
this.context = options.context ?? {};
|
|
31
|
+
// Retain each cause's stack + message VISIBLY: append a `Caused by:` section
|
|
32
|
+
// per cause so the full chain shows in `.stack` (and any logger that prints it),
|
|
33
|
+
// not only via the programmatic `.causes` / native `.cause`.
|
|
34
|
+
this.stack = NestedError.composeStack(this.stack, causes);
|
|
7
35
|
}
|
|
8
36
|
}
|
|
9
37
|
(function (NestedError) {
|
|
10
|
-
|
|
11
|
-
|
|
38
|
+
/** Max characters of any single context value folded into the message — long `text` is truncated. */
|
|
39
|
+
NestedError.ContextPreviewMaxChars = 200;
|
|
40
|
+
/** The marker appended when a folded context value is truncated. */
|
|
41
|
+
NestedError.TruncationSuffix = "…";
|
|
42
|
+
/** Prefix marking each retained cause in the composed stack (nested-exception style). */
|
|
43
|
+
NestedError.CausedByPrefix = "Caused by: ";
|
|
44
|
+
/**
|
|
45
|
+
* Append a `Caused by:` section per cause to `stack`, so every cause's stack trace
|
|
46
|
+
* AND message stay visible in `.stack` (and any logger that prints it) — not only
|
|
47
|
+
* via the programmatic {@link NestedError.causes} / native `Error.cause`. Each
|
|
48
|
+
* cause's own `.stack` already begins with its `name: message`, so both are kept.
|
|
49
|
+
*
|
|
50
|
+
* @param stack - The wrapping error's own stack (may be undefined in exotic runtimes).
|
|
51
|
+
* @param causes - The retained cause chain.
|
|
52
|
+
* @returns The wrapping stack with one appended `Caused by:` section per cause.
|
|
53
|
+
*/
|
|
54
|
+
function composeStack(stack, causes) {
|
|
55
|
+
return causes.reduce((composed, cause) => `${composed}\n${NestedError.CausedByPrefix}${cause.stack ?? `${cause.name}: ${cause.message}`}`, stack ?? "");
|
|
56
|
+
}
|
|
57
|
+
NestedError.composeStack = composeStack;
|
|
58
|
+
/**
|
|
59
|
+
* Create a {@link NestedError}.
|
|
60
|
+
*
|
|
61
|
+
* @param message - The high-level failure description.
|
|
62
|
+
* @param options - The cause to preserve + the context to capture.
|
|
63
|
+
* @returns The constructed error.
|
|
64
|
+
*/
|
|
65
|
+
function create(message, options = {}) {
|
|
66
|
+
return new NestedError(message, options);
|
|
12
67
|
}
|
|
13
68
|
NestedError.create = create;
|
|
14
|
-
|
|
15
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Create + throw a {@link NestedError}.
|
|
71
|
+
*
|
|
72
|
+
* @param message - The high-level failure description.
|
|
73
|
+
* @param options - The cause to preserve + the context to capture.
|
|
74
|
+
*/
|
|
75
|
+
function throwError(message, options = {}) {
|
|
76
|
+
throw create(message, options);
|
|
16
77
|
}
|
|
17
78
|
NestedError.throwError = throwError;
|
|
79
|
+
/**
|
|
80
|
+
* Coerce a thrown value (an Error, an array of them, or any value) into a flat
|
|
81
|
+
* `Error[]`, so a caught `unknown` can be preserved as a cause without loss.
|
|
82
|
+
*
|
|
83
|
+
* @param cause - The caught / rejected value.
|
|
84
|
+
* @returns The coerced error chain (empty when `cause` is nullish).
|
|
85
|
+
*/
|
|
86
|
+
function toErrors(cause) {
|
|
87
|
+
return (Array.isArray(cause) ? cause : cause == null ? [] : [cause]).map(toError);
|
|
88
|
+
}
|
|
89
|
+
NestedError.toErrors = toErrors;
|
|
90
|
+
/**
|
|
91
|
+
* Coerce a single thrown value into an Error (Errors pass through unchanged).
|
|
92
|
+
*
|
|
93
|
+
* @param value - The caught value.
|
|
94
|
+
* @returns `value` when it is an Error, else a new Error wrapping its string form.
|
|
95
|
+
*/
|
|
96
|
+
function toError(value) {
|
|
97
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
98
|
+
}
|
|
99
|
+
NestedError.toError = toError;
|
|
100
|
+
/**
|
|
101
|
+
* Fold a compact, truncated {@link NestedErrorContext} summary into the message so
|
|
102
|
+
* it is visible wherever the message is (logs, stack traces), not only on the
|
|
103
|
+
* `.context` property.
|
|
104
|
+
*
|
|
105
|
+
* @param message - The base message.
|
|
106
|
+
* @param context - The context to summarize, if any.
|
|
107
|
+
* @returns `message` with a ` (key=value, …)` suffix when context is non-empty.
|
|
108
|
+
*/
|
|
109
|
+
function composeMessage(message, context) {
|
|
110
|
+
const entries = context == null ? [] : Object.entries(context);
|
|
111
|
+
return entries.length === 0
|
|
112
|
+
? message
|
|
113
|
+
: `${message} (${entries
|
|
114
|
+
.map(([key, value]) => `${key}=${previewValue(value)}`)
|
|
115
|
+
.join(", ")})`;
|
|
116
|
+
}
|
|
117
|
+
NestedError.composeMessage = composeMessage;
|
|
118
|
+
/**
|
|
119
|
+
* Render one context value for the message suffix — strings verbatim, everything
|
|
120
|
+
* else via a bigint-/cycle-safe form, truncated to {@link ContextPreviewMaxChars}.
|
|
121
|
+
*
|
|
122
|
+
* @param value - The context value.
|
|
123
|
+
* @returns The truncated preview string.
|
|
124
|
+
*/
|
|
125
|
+
function previewValue(value) {
|
|
126
|
+
const rendered = typeof value === "string" ? value : safeStringify(value);
|
|
127
|
+
return rendered.length > NestedError.ContextPreviewMaxChars
|
|
128
|
+
? `${rendered.slice(0, NestedError.ContextPreviewMaxChars)}${NestedError.TruncationSuffix}`
|
|
129
|
+
: rendered;
|
|
130
|
+
}
|
|
131
|
+
NestedError.previewValue = previewValue;
|
|
18
132
|
})(NestedError || (NestedError = {}));
|
|
19
|
-
|
|
20
|
-
|
|
133
|
+
/**
|
|
134
|
+
* A best-effort rendering of `value` for a message preview: JSON when serializable,
|
|
135
|
+
* else `String(value)`. Pure display fallback — a BigInt or a circular object makes
|
|
136
|
+
* `JSON.stringify` throw — NOT error handling, so the catch is a deliberate render
|
|
137
|
+
* fallback rather than a swallow.
|
|
138
|
+
*
|
|
139
|
+
* @param value - The value to render.
|
|
140
|
+
* @returns A single-line string form of `value`.
|
|
141
|
+
*/
|
|
142
|
+
function safeStringify(value) {
|
|
143
|
+
try {
|
|
144
|
+
return JSON.stringify(value) ?? String(value);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return String(value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmVzdGVkRXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXJyb3JzL05lc3RlZEVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXdCQTs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sT0FBTyxXQUVYLFNBQVEsS0FBSztJQUNiLHNHQUFzRztJQUM3RixNQUFNLENBQVM7SUFFeEIseURBQXlEO0lBQ2hELE9BQU8sQ0FBUztJQUV6Qjs7O09BR0c7SUFDSCxZQUFZLE9BQWUsRUFBRSxVQUF1QyxFQUFFO1FBQ3BFLE1BQU0sTUFBTSxHQUFHLFdBQVcsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBQ2xELEtBQUssQ0FDSCxXQUFXLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQ3BELE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUNyRCxDQUFBO1FBQ0QsSUFBSSxDQUFDLElBQUksR0FBRyxhQUFhLENBQUE7UUFDekIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7UUFDcEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxJQUFLLEVBQWMsQ0FBQTtRQUNqRCw2RUFBNkU7UUFDN0UsaUZBQWlGO1FBQ2pGLDZEQUE2RDtRQUM3RCxJQUFJLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUMzRCxDQUFDO0NBQ0Y7QUFFRCxXQUFpQixXQUFXO0lBQzFCLHFHQUFxRztJQUN4RixrQ0FBc0IsR0FBRyxHQUFHLENBQUE7SUFFekMsb0VBQW9FO0lBQ3ZELDRCQUFnQixHQUFHLEdBQUcsQ0FBQTtJQUVuQyx5RkFB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QUMsRUEvR2dCLFdBQVcsS0FBWCxXQUFXLFFBK0czQjtBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsU0FBUyxhQUFhLENBQUMsS0FBYztJQUNuQyxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQy9DLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN0QixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU3RydWN0dXJlZCwgbWFjaGluZS1yZWFkYWJsZSBkaWFnbm9zdGljIGNvbnRleHQgY2FwdHVyZWQgYXQgYSB7QGxpbmsgTmVzdGVkRXJyb3J9XG4gKiB0aHJvdyBzaXRlIOKAlCB0aGUgdmFsdWVzIHRoYXQgbWFrZSBhIGZhaWx1cmUgZGlhZ25vc2FibGUgKHRoZSBgdGV4dGAgdGhhdCBmYWlsZWQgdG9cbiAqIHBhcnNlLCBhIHNjaGVtYSAvIHR5cGUgbmFtZSwgYW4gaWQsIOKApikuIEhldGVyb2dlbmVvdXMgYnkgbmF0dXJlLCBzbyB0aGUgdmFsdWVzIGFyZVxuICogYHVua25vd25gIChhIGRvY3VtZW50ZWQgdHlwZS1lcmFzZWQgYmFnLCBub3QgYSBzaG9ydGN1dCkuXG4gKi9cbmV4cG9ydCB0eXBlIE5lc3RlZEVycm9yQ29udGV4dCA9IFJlY29yZDxzdHJpbmcsIHVua25vd24+XG5cbi8qKlxuICogT3B0aW9ucyBmb3Ige0BsaW5rIE5lc3RlZEVycm9yfSDigJQgd2hhdCBpdCBwcmVzZXJ2ZXMgYWJvdXQgdGhlIGZhaWx1cmUgaXQgd3JhcHMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTmVzdGVkRXJyb3JPcHRpb25zPFxuICBDb250ZXh0IGV4dGVuZHMgTmVzdGVkRXJyb3JDb250ZXh0ID0gTmVzdGVkRXJyb3JDb250ZXh0XG4+IHtcbiAgLyoqXG4gICAqIFRoZSBvcmlnaW5hdGluZyBlcnJvcihzKSDigJQgdGhlIHJvb3QtY2F1c2UgY2hhaW4gdGhhdCBtdXN0IE5PVCBiZSBzd2FsbG93ZWQuIEFcbiAgICogbm9uLUVycm9yIHRocm93biB2YWx1ZSBpcyBjb2VyY2VkIHRvIGFuIEVycm9yOyB0aGUgZmlyc3QgYmVjb21lcyB0aGUgbmF0aXZlXG4gICAqIEVTMjAyMiBgRXJyb3IuY2F1c2VgLlxuICAgKi9cbiAgY2F1c2U/OiB1bmtub3duXG4gIC8qKiBEaWFnbm9zdGljIGNvbnRleHQgY2FwdHVyZWQgYXQgdGhlIHRocm93IHNpdGUuICovXG4gIGNvbnRleHQ/OiBDb250ZXh0XG59XG5cbi8qKlxuICogQW4gRXJyb3IgdGhhdCBQUkVTRVJWRVMgd2hhdCBpdCB3cmFwcyBpbnN0ZWFkIG9mIHJlc3RyaW5naW5nIGl0LiBHaXZlbiBhIG5ld1xuICogbWVzc2FnZSBzdHJpbmcsIGl0IEFMU08gcmV0YWlucyB0aGUgZnVsbCBjYXVzZSBjaGFpbjogdGhlIG9yaWdpbmF0aW5nIGVycm9yKHMpXG4gKiBzdXJ2aXZlIGFzIHtAbGluayBOZXN0ZWRFcnJvci5jYXVzZXN9IChhbmQgdGhlIG5hdGl2ZSBgRXJyb3IuY2F1c2VgKSDigJQgdGhlIHJlYWxcbiAqIEVycm9yIG9iamVjdHMsIHNvICoqZWFjaCBjYXVzZSBrZWVwcyBpdHMgb3duIHN0YWNrIHRyYWNlIGFuZCBtZXNzYWdlKiog4oCUIGFuZCBhXG4gKiBuZXN0ZWQtZXhjZXB0aW9uIGBDYXVzZWQgYnk6YCBzZWN0aW9uIHBlciBjYXVzZSBpcyBhcHBlbmRlZCB0byB0aGlzIGVycm9yJ3NcbiAqIHtAbGluayBOZXN0ZWRFcnJvci5zdGFja30sIHNvIHRoZSB3aG9sZSBjaGFpbiAoZWFjaCBjYXVzZSdzIHN0YWNrICsgbWVzc2FnZSkgaXNcbiAqIHZpc2libGUgd2hlcmV2ZXIgdGhlIGVycm9yIGlzIHByaW50ZWQsIG5vdCBvbmx5IHZpYSB0aGUgcHJvZ3JhbW1hdGljIGFjY2Vzc29ycy5cbiAqIFRoZSBkaWFnbm9zdGljIHtAbGluayBOZXN0ZWRFcnJvci5jb250ZXh0fSAodGhlIGB0ZXh0YCwgdGhlIHNjaGVtYSAvIHR5cGUgbmFtZSwg4oCmKVxuICogaXMgQk9USCBhIHN0cnVjdHVyZWQgcHJvcGVydHkgQU5EIGZvbGRlZCBpbnRvIHRoZSBtZXNzYWdlLlxuICpcbiAqIE5ldmVyIHJld3JpdGUgYSBjYXVnaHQgZXJyb3IgaW50byBhIGJhcmUgYGBuZXcgRXJyb3IoYOKApiAke2Vyci5tZXNzYWdlfWApYGAg4oCUIHRoYXRcbiAqIFNXQUxMT1dTIHRoZSBjYXVzZSBhbmQgdGhlIGNvbnRleHQuIFdyYXAgaXQ6XG4gKiBgbmV3IE5lc3RlZEVycm9yKFwi4oCmXCIsIHsgY2F1c2U6IGVyciwgY29udGV4dDogeyDigKYgfSB9KWAuXG4gKi9cbmV4cG9ydCBjbGFzcyBOZXN0ZWRFcnJvcjxcbiAgQ29udGV4dCBleHRlbmRzIE5lc3RlZEVycm9yQ29udGV4dCA9IE5lc3RlZEVycm9yQ29udGV4dFxuPiBleHRlbmRzIEVycm9yIHtcbiAgLyoqIFRoZSBvcmlnaW5hdGluZyBlcnJvciBjaGFpbiAoZmxhdHRlbmVkICsgY29lcmNlZCB0byBFcnJvcik7IGBjYXVzZXNbMF1gIGlzIHRoZSBuYXRpdmUgYC5jYXVzZWAuICovXG4gIHJlYWRvbmx5IGNhdXNlczogRXJyb3JbXVxuXG4gIC8qKiBUaGUgZGlhZ25vc3RpYyBjb250ZXh0IGNhcHR1cmVkIGF0IHRoZSB0aHJvdyBzaXRlLiAqL1xuICByZWFkb25seSBjb250ZXh0OiBDb250ZXh0XG5cbiAgLyoqXG4gICAqIEBwYXJhbSBtZXNzYWdlIC0gVGhlIGhpZ2gtbGV2ZWwgZmFpbHVyZSBkZXNjcmlwdGlvbi5cbiAgICogQHBhcmFtIG9wdGlvbnMgLSBUaGUge0BsaW5rIE5lc3RlZEVycm9yT3B0aW9ucy5jYXVzZX0gdG8gcHJlc2VydmUgKyB0aGUge0BsaW5rIE5lc3RlZEVycm9yT3B0aW9ucy5jb250ZXh0fSB0byBjYXB0dXJlLlxuICAgKi9cbiAgY29uc3RydWN0b3IobWVzc2FnZTogc3RyaW5nLCBvcHRpb25zOiBOZXN0ZWRFcnJvck9wdGlvbnM8Q29udGV4dD4gPSB7fSkge1xuICAgIGNvbnN0IGNhdXNlcyA9IE5lc3RlZEVycm9yLnRvRXJyb3JzKG9wdGlvbnMuY2F1c2UpXG4gICAgc3VwZXIoXG4gICAgICBOZXN0ZWRFcnJvci5jb21wb3NlTWVzc2FnZShtZXNzYWdlLCBvcHRpb25zLmNvbnRleHQpLFxuICAgICAgY2F1c2VzLmxlbmd0aCA+IDAgPyB7IGNhdXNlOiBjYXVzZXNbMF0gfSA6IHVuZGVmaW5lZFxuICAgIClcbiAgICB0aGlzLm5hbWUgPSBcIk5lc3RlZEVycm9yXCJcbiAgICB0aGlzLmNhdXNlcyA9IGNhdXNlc1xuICAgIHRoaXMuY29udGV4dCA9IG9wdGlvbnMuY29udGV4dCA/PyAoe30gYXMgQ29udGV4dClcbiAgICAvLyBSZXRhaW4gZWFjaCBjYXVzZSdzIHN0YWNrICsgbWVzc2FnZSBWSVNJQkxZOiBhcHBlbmQgYSBgQ2F1c2VkIGJ5OmAgc2VjdGlvblxuICAgIC8vIHBlciBjYXVzZSBzbyB0aGUgZnVsbCBjaGFpbiBzaG93cyBpbiBgLnN0YWNrYCAoYW5kIGFueSBsb2dnZXIgdGhhdCBwcmludHMgaXQpLFxuICAgIC8vIG5vdCBvbmx5IHZpYSB0aGUgcHJvZ3JhbW1hdGljIGAuY2F1c2VzYCAvIG5hdGl2ZSBgLmNhdXNlYC5cbiAgICB0aGlzLnN0YWNrID0gTmVzdGVkRXJyb3IuY29tcG9zZVN0YWNrKHRoaXMuc3RhY2ssIGNhdXNlcylcbiAgfVxufVxuXG5leHBvcnQgbmFtZXNwYWNlIE5lc3RlZEVycm9yIHtcbiAgLyoqIE1heCBjaGFyYWN0ZXJzIG9mIGFueSBzaW5nbGUgY29udGV4dCB2YWx1ZSBmb2xkZWQgaW50byB0aGUgbWVzc2FnZSDigJQgbG9uZyBgdGV4dGAgaXMgdHJ1bmNhdGVkLiAqL1xuICBleHBvcnQgY29uc3QgQ29udGV4dFByZXZpZXdNYXhDaGFycyA9IDIwMFxuXG4gIC8qKiBUaGUgbWFya2VyIGFwcGVuZGVkIHdoZW4gYSBmb2xkZWQgY29udGV4dCB2YWx1ZSBpcyB0cnVuY2F0ZWQuICovXG4gIGV4cG9ydCBjb25zdCBUcnVuY2F0aW9uU3VmZml4ID0gXCLigKZcIlxuXG4gIC8qKiBQcmVmaXggbWFya2luZyBlYWNoIHJldGFpbmVkIGNhdXNlIGluIHRoZSBjb21wb3NlZCBzdGFjayAobmVzdGVkLWV4Y2VwdGlvbiBzdHlsZSkuICovXG4gIGV4cG9ydCBjb25zdCBDYXVzZWRCeVByZWZpeCA9IFwiQ2F1c2VkIGJ5OiBcIlxuXG4gIC8qKlxuICAgKiBBcHBlbmQgYSBgQ2F1c2VkIGJ5OmAgc2VjdGlvbiBwZXIgY2F1c2UgdG8gYHN0YWNrYCwgc28gZXZlcnkgY2F1c2UncyBzdGFjayB0cmFjZVxuICAgKiBBTkQgbWVzc2FnZSBzdGF5IHZpc2libGUgaW4gYC5zdGFja2AgKGFuZCBhbnkgbG9nZ2VyIHRoYXQgcHJpbnRzIGl0KSDigJQgbm90IG9ubHlcbiAgICogdmlhIHRoZSBwcm9ncmFtbWF0aWMge0BsaW5rIE5lc3RlZEVycm9yLmNhdXNlc30gLyBuYXRpdmUgYEVycm9yLmNhdXNlYC4gRWFjaFxuICAgKiBjYXVzZSdzIG93biBgLnN0YWNrYCBhbHJlYWR5IGJlZ2lucyB3aXRoIGl0cyBgbmFtZTogbWVzc2FnZWAsIHNvIGJvdGggYXJlIGtlcHQuXG4gICAqXG4gICAqIEBwYXJhbSBzdGFjayAtIFRoZSB3cmFwcGluZyBlcnJvcidzIG93biBzdGFjayAobWF5IGJlIHVuZGVmaW5lZCBpbiBleG90aWMgcnVudGltZXMpLlxuICAgKiBAcGFyYW0gY2F1c2VzIC0gVGhlIHJldGFpbmVkIGNhdXNlIGNoYWluLlxuICAgKiBAcmV0dXJucyBUaGUgd3JhcHBpbmcgc3RhY2sgd2l0aCBvbmUgYXBwZW5kZWQgYENhdXNlZCBieTpgIHNlY3Rpb24gcGVyIGNhdXNlLlxuICAgKi9cbiAgZXhwb3J0IGZ1bmN0aW9uIGNvbXBvc2VTdGFjayhzdGFjazogc3RyaW5nIHwgdW5kZWZpbmVkLCBjYXVzZXM6IEVycm9yW10pOiBzdHJpbmcge1xuICAgIHJldHVybiBjYXVzZXMucmVkdWNlKFxuICAgICAgKGNvbXBvc2VkLCBjYXVzZSkgPT5cbiAgICAgICAgYCR7Y29tcG9zZWR9XFxuJHtDYXVzZWRCeVByZWZpeH0ke2NhdXNlLnN0YWNrID8/IGAke2NhdXNlLm5hbWV9OiAke2NhdXNlLm1lc3NhZ2V9YH1gLFxuICAgICAgc3RhY2sgPz8gXCJcIlxuICAgIClcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGUgYSB7QGxpbmsgTmVzdGVkRXJyb3J9LlxuICAgKlxuICAgKiBAcGFyYW0gbWVzc2FnZSAtIFRoZSBoaWdoLWxldmVsIGZhaWx1cmUgZGVzY3JpcHRpb24uXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gVGhlIGNhdXNlIHRvIHByZXNlcnZlICsgdGhlIGNvbnRleHQgdG8gY2FwdHVyZS5cbiAgICogQHJldHVybnMgVGhlIGNvbnN0cnVjdGVkIGVycm9yLlxuICAgKi9cbiAgZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZTxDb250ZXh0IGV4dGVuZHMgTmVzdGVkRXJyb3JDb250ZXh0ID0gTmVzdGVkRXJyb3JDb250ZXh0PihcbiAgICBtZXNzYWdlOiBzdHJpbmcsXG4gICAgb3B0aW9uczogTmVzdGVkRXJyb3JPcHRpb25zPENvbnRleHQ+ID0ge31cbiAgKTogTmVzdGVkRXJyb3I8Q29udGV4dD4ge1xuICAgIHJldHVybiBuZXcgTmVzdGVkRXJyb3IobWVzc2FnZSwgb3B0aW9ucylcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGUgKyB0aHJvdyBhIHtAbGluayBOZXN0ZWRFcnJvcn0uXG4gICAqXG4gICAqIEBwYXJhbSBtZXNzYWdlIC0gVGhlIGhpZ2gtbGV2ZWwgZmFpbHVyZSBkZXNjcmlwdGlvbi5cbiAgICogQHBhcmFtIG9wdGlvbnMgLSBUaGUgY2F1c2UgdG8gcHJlc2VydmUgKyB0aGUgY29udGV4dCB0byBjYXB0dXJlLlxuICAgKi9cbiAgZXhwb3J0IGZ1bmN0aW9uIHRocm93RXJyb3I8XG4gICAgQ29udGV4dCBleHRlbmRzIE5lc3RlZEVycm9yQ29udGV4dCA9IE5lc3RlZEVycm9yQ29udGV4dFxuICA+KG1lc3NhZ2U6IHN0cmluZywgb3B0aW9uczogTmVzdGVkRXJyb3JPcHRpb25zPENvbnRleHQ+ID0ge30pOiBuZXZlciB7XG4gICAgdGhyb3cgY3JlYXRlKG1lc3NhZ2UsIG9wdGlvbnMpXG4gIH1cblxuICAvKipcbiAgICogQ29lcmNlIGEgdGhyb3duIHZhbHVlIChhbiBFcnJvciwgYW4gYXJyYXkgb2YgdGhlbSwgb3IgYW55IHZhbHVlKSBpbnRvIGEgZmxhdFxuICAgKiBgRXJyb3JbXWAsIHNvIGEgY2F1Z2h0IGB1bmtub3duYCBjYW4gYmUgcHJlc2VydmVkIGFzIGEgY2F1c2Ugd2l0aG91dCBsb3NzLlxuICAgKlxuICAgKiBAcGFyYW0gY2F1c2UgLSBUaGUgY2F1Z2h0IC8gcmVqZWN0ZWQgdmFsdWUuXG4gICAqIEByZXR1cm5zIFRoZSBjb2VyY2VkIGVycm9yIGNoYWluIChlbXB0eSB3aGVuIGBjYXVzZWAgaXMgbnVsbGlzaCkuXG4gICAqL1xuICBleHBvcnQgZnVuY3Rpb24gdG9FcnJvcnMoY2F1c2U6IHVua25vd24pOiBFcnJvcltdIHtcbiAgICByZXR1cm4gKEFycmF5LmlzQXJyYXkoY2F1c2UpID8gY2F1c2UgOiBjYXVzZSA9PSBudWxsID8gW10gOiBbY2F1c2VdKS5tYXAoXG4gICAgICB0b0Vycm9yXG4gICAgKVxuICB9XG5cbiAgLyoqXG4gICAqIENvZXJjZSBhIHNpbmdsZSB0aHJvd24gdmFsdWUgaW50byBhbiBFcnJvciAoRXJyb3JzIHBhc3MgdGhyb3VnaCB1bmNoYW5nZWQpLlxuICAgKlxuICAgKiBAcGFyYW0gdmFsdWUgLSBUaGUgY2F1Z2h0IHZhbHVlLlxuICAgKiBAcmV0dXJucyBgdmFsdWVgIHdoZW4gaXQgaXMgYW4gRXJyb3IsIGVsc2UgYSBuZXcgRXJyb3Igd3JhcHBpbmcgaXRzIHN0cmluZyBmb3JtLlxuICAgKi9cbiAgZXhwb3J0IGZ1bmN0aW9uIHRvRXJyb3IodmFsdWU6IHVua25vd24pOiBFcnJvciB7XG4gICAgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgRXJyb3IgPyB2YWx1ZSA6IG5ldyBFcnJvcihTdHJpbmcodmFsdWUpKVxuICB9XG5cbiAgLyoqXG4gICAqIEZvbGQgYSBjb21wYWN0LCB0cnVuY2F0ZWQge0BsaW5rIE5lc3RlZEVycm9yQ29udGV4dH0gc3VtbWFyeSBpbnRvIHRoZSBtZXNzYWdlIHNvXG4gICAqIGl0IGlzIHZpc2libGUgd2hlcmV2ZXIgdGhlIG1lc3NhZ2UgaXMgKGxvZ3MsIHN0YWNrIHRyYWNlcyksIG5vdCBvbmx5IG9uIHRoZVxuICAgKiBgLmNvbnRleHRgIHByb3BlcnR5LlxuICAgKlxuICAgKiBAcGFyYW0gbWVzc2FnZSAtIFRoZSBiYXNlIG1lc3NhZ2UuXG4gICAqIEBwYXJhbSBjb250ZXh0IC0gVGhlIGNvbnRleHQgdG8gc3VtbWFyaXplLCBpZiBhbnkuXG4gICAqIEByZXR1cm5zIGBtZXNzYWdlYCB3aXRoIGEgYCAoa2V5PXZhbHVlLCDigKYpYCBzdWZmaXggd2hlbiBjb250ZXh0IGlzIG5vbi1lbXB0eS5cbiAgICovXG4gIGV4cG9ydCBmdW5jdGlvbiBjb21wb3NlTWVzc2FnZShcbiAgICBtZXNzYWdlOiBzdHJpbmcsXG4gICAgY29udGV4dD86IE5lc3RlZEVycm9yQ29udGV4dFxuICApOiBzdHJpbmcge1xuICAgIGNvbnN0IGVudHJpZXMgPSBjb250ZXh0ID09IG51bGwgPyBbXSA6IE9iamVjdC5lbnRyaWVzKGNvbnRleHQpXG4gICAgcmV0dXJuIGVudHJpZXMubGVuZ3RoID09PSAwXG4gICAgICA/IG1lc3NhZ2VcbiAgICAgIDogYCR7bWVzc2FnZX0gKCR7ZW50cmllc1xuICAgICAgICAgIC5tYXAoKFtrZXksIHZhbHVlXSkgPT4gYCR7a2V5fT0ke3ByZXZpZXdWYWx1ZSh2YWx1ZSl9YClcbiAgICAgICAgICAuam9pbihcIiwgXCIpfSlgXG4gIH1cblxuICAvKipcbiAgICogUmVuZGVyIG9uZSBjb250ZXh0IHZhbHVlIGZvciB0aGUgbWVzc2FnZSBzdWZmaXgg4oCUIHN0cmluZ3MgdmVyYmF0aW0sIGV2ZXJ5dGhpbmdcbiAgICogZWxzZSB2aWEgYSBiaWdpbnQtL2N5Y2xlLXNhZmUgZm9ybSwgdHJ1bmNhdGVkIHRvIHtAbGluayBDb250ZXh0UHJldmlld01heENoYXJzfS5cbiAgICpcbiAgICogQHBhcmFtIHZhbHVlIC0gVGhlIGNvbnRleHQgdmFsdWUuXG4gICAqIEByZXR1cm5zIFRoZSB0cnVuY2F0ZWQgcHJldmlldyBzdHJpbmcuXG4gICAqL1xuICBleHBvcnQgZnVuY3Rpb24gcHJldmlld1ZhbHVlKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcbiAgICBjb25zdCByZW5kZXJlZCA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlIDogc2FmZVN0cmluZ2lmeSh2YWx1ZSlcbiAgICByZXR1cm4gcmVuZGVyZWQubGVuZ3RoID4gQ29udGV4dFByZXZpZXdNYXhDaGFyc1xuICAgICAgPyBgJHtyZW5kZXJlZC5zbGljZSgwLCBDb250ZXh0UHJldmlld01heENoYXJzKX0ke1RydW5jYXRpb25TdWZmaXh9YFxuICAgICAgOiByZW5kZXJlZFxuICB9XG59XG5cbi8qKlxuICogQSBiZXN0LWVmZm9ydCByZW5kZXJpbmcgb2YgYHZhbHVlYCBmb3IgYSBtZXNzYWdlIHByZXZpZXc6IEpTT04gd2hlbiBzZXJpYWxpemFibGUsXG4gKiBlbHNlIGBTdHJpbmcodmFsdWUpYC4gUHVyZSBkaXNwbGF5IGZhbGxiYWNrIOKAlCBhIEJpZ0ludCBvciBhIGNpcmN1bGFyIG9iamVjdCBtYWtlc1xuICogYEpTT04uc3RyaW5naWZ5YCB0aHJvdyDigJQgTk9UIGVycm9yIGhhbmRsaW5nLCBzbyB0aGUgY2F0Y2ggaXMgYSBkZWxpYmVyYXRlIHJlbmRlclxuICogZmFsbGJhY2sgcmF0aGVyIHRoYW4gYSBzd2FsbG93LlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byByZW5kZXIuXG4gKiBAcmV0dXJucyBBIHNpbmdsZS1saW5lIHN0cmluZyBmb3JtIG9mIGB2YWx1ZWAuXG4gKi9cbmZ1bmN0aW9uIHNhZmVTdHJpbmdpZnkodmFsdWU6IHVua25vd24pOiBzdHJpbmcge1xuICB0cnkge1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh2YWx1ZSkgPz8gU3RyaW5nKHZhbHVlKVxuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gU3RyaW5nKHZhbHVlKVxuICB9XG59XG4iXX0=
|
|
@@ -2,51 +2,98 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const NestedError_1 = require("@wireio/shared/errors/NestedError");
|
|
4
4
|
describe("NestedError", () => {
|
|
5
|
-
it("
|
|
6
|
-
const cause = new Error("root
|
|
7
|
-
const error = new NestedError_1.NestedError("wrapper", cause);
|
|
8
|
-
expect(error
|
|
5
|
+
it("keeps the new message and preserves the caught error as cause", () => {
|
|
6
|
+
const cause = new Error("root boom");
|
|
7
|
+
const error = new NestedError_1.NestedError("wrapper failed", { cause });
|
|
8
|
+
expect(error).toBeInstanceOf(Error);
|
|
9
9
|
expect(error.name).toBe("NestedError");
|
|
10
|
+
expect(error.message).toBe("wrapper failed");
|
|
11
|
+
// The real Error object is retained — native `.cause` AND `.causes` — so its
|
|
12
|
+
// stack trace and message survive intact.
|
|
13
|
+
expect(error.cause).toBe(cause);
|
|
10
14
|
expect(error.causes).toEqual([cause]);
|
|
15
|
+
expect(error.causes[0].message).toBe("root boom");
|
|
16
|
+
expect(error.causes[0].stack).toBe(cause.stack);
|
|
11
17
|
});
|
|
12
|
-
it("
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
expect(error).
|
|
18
|
+
it("makes each cause's stack + message visible in .stack (nested-exception style)", () => {
|
|
19
|
+
const inner = new Error("inner detail");
|
|
20
|
+
const error = new NestedError_1.NestedError("outer failed", { cause: inner });
|
|
21
|
+
expect(error.stack).toContain("outer failed");
|
|
22
|
+
expect(error.stack).toContain(NestedError_1.NestedError.CausedByPrefix);
|
|
23
|
+
// The cause's stack begins with `Error: inner detail`, so its message is kept too.
|
|
24
|
+
expect(error.stack).toContain("inner detail");
|
|
25
|
+
expect(error.stack).toContain(inner.stack);
|
|
16
26
|
});
|
|
17
|
-
it("
|
|
27
|
+
it("retains every cause when given an array of causes", () => {
|
|
18
28
|
const first = new Error("first");
|
|
19
29
|
const second = new Error("second");
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
const error = new NestedError_1.NestedError("multi", { cause: [first, second] });
|
|
31
|
+
expect(error.causes).toEqual([first, second]);
|
|
32
|
+
// The first is the native `.cause`; both show in the composed stack.
|
|
33
|
+
expect(error.cause).toBe(first);
|
|
34
|
+
expect(error.stack).toContain("first");
|
|
35
|
+
expect(error.stack).toContain("second");
|
|
36
|
+
});
|
|
37
|
+
it("coerces a non-Error thrown value into a retained Error cause", () => {
|
|
38
|
+
const error = new NestedError_1.NestedError("boom", { cause: "a plain string throw" });
|
|
39
|
+
expect(error.causes).toHaveLength(1);
|
|
40
|
+
expect(error.causes[0]).toBeInstanceOf(Error);
|
|
41
|
+
expect(error.causes[0].message).toBe("a plain string throw");
|
|
42
|
+
expect(error.cause).toBe(error.causes[0]);
|
|
43
|
+
});
|
|
44
|
+
it("captures structured context AND folds a summary into the message", () => {
|
|
45
|
+
const error = new NestedError_1.NestedError("invalid JSON", {
|
|
46
|
+
context: { text: "{bad", schemaName: "ClusterConfig" }
|
|
47
|
+
});
|
|
48
|
+
expect(error.context).toEqual({ text: "{bad", schemaName: "ClusterConfig" });
|
|
49
|
+
expect(error.message).toContain("text={bad");
|
|
50
|
+
expect(error.message).toContain("schemaName=ClusterConfig");
|
|
23
51
|
});
|
|
24
|
-
it("
|
|
25
|
-
const
|
|
52
|
+
it("truncates a long folded context value", () => {
|
|
53
|
+
const long = "x".repeat(NestedError_1.NestedError.ContextPreviewMaxChars + 50);
|
|
54
|
+
const error = new NestedError_1.NestedError("big", { context: { text: long } });
|
|
55
|
+
expect(error.message).toContain(NestedError_1.NestedError.TruncationSuffix);
|
|
56
|
+
// The folded preview is bounded regardless of the source length.
|
|
57
|
+
expect(error.message.length).toBeLessThan(long.length + NestedError_1.NestedError.ContextPreviewMaxChars);
|
|
58
|
+
// The full untruncated value is still on the structured property.
|
|
59
|
+
expect(error.context.text).toBe(long);
|
|
60
|
+
});
|
|
61
|
+
it("renders a bigint context value without throwing (JSON.stringify would)", () => {
|
|
62
|
+
const error = new NestedError_1.NestedError("amount", { context: { amount: 5n } });
|
|
63
|
+
expect(error.message).toContain("amount=5");
|
|
64
|
+
});
|
|
65
|
+
it("has no cause chain and an empty context when none are supplied", () => {
|
|
66
|
+
const error = new NestedError_1.NestedError("standalone");
|
|
26
67
|
expect(error.causes).toEqual([]);
|
|
68
|
+
expect(error.cause).toBeUndefined();
|
|
69
|
+
expect(error.context).toEqual({});
|
|
70
|
+
expect(error.message).toBe("standalone");
|
|
27
71
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
72
|
+
it("create returns an instance; throwError throws one", () => {
|
|
73
|
+
const cause = new Error("x");
|
|
74
|
+
expect(NestedError_1.NestedError.create("made", { cause })).toBeInstanceOf(NestedError_1.NestedError);
|
|
75
|
+
expect(() => NestedError_1.NestedError.throwError("thrown", { cause })).toThrow(NestedError_1.NestedError);
|
|
76
|
+
try {
|
|
77
|
+
NestedError_1.NestedError.throwError("thrown", { cause });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
34
80
|
expect(error.causes).toEqual([cause]);
|
|
35
|
-
}
|
|
81
|
+
}
|
|
36
82
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
83
|
+
it("toError passes Errors through and wraps non-Errors", () => {
|
|
84
|
+
const real = new Error("real");
|
|
85
|
+
expect(NestedError_1.NestedError.toError(real)).toBe(real);
|
|
86
|
+
expect(NestedError_1.NestedError.toError("str")).toBeInstanceOf(Error);
|
|
87
|
+
expect(NestedError_1.NestedError.toError("str").message).toBe("str");
|
|
88
|
+
});
|
|
89
|
+
it("toErrors flattens and coerces; nullish yields an empty chain", () => {
|
|
90
|
+
const a = new Error("a");
|
|
91
|
+
expect(NestedError_1.NestedError.toErrors([a, "b"]).map(error => error.message)).toEqual([
|
|
92
|
+
"a",
|
|
93
|
+
"b"
|
|
94
|
+
]);
|
|
95
|
+
expect(NestedError_1.NestedError.toErrors(null)).toEqual([]);
|
|
96
|
+
expect(NestedError_1.NestedError.toErrors(a)).toEqual([a]);
|
|
50
97
|
});
|
|
51
98
|
});
|
|
52
99
|
//# sourceMappingURL=NestedError.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NestedError.test.js","sourceRoot":"","sources":["../../../tests/errors/NestedError.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { NestedError } from \"@wireio/shared/errors/NestedError\"\n\ndescribe(\"NestedError\", () => {\n it(\"sets message, name, and causes\", () => {\n const cause = new Error(\"root cause\")\n const error = new NestedError(\"wrapper\", cause)\n\n expect(error.message).toBe(\"wrapper\")\n expect(error.name).toBe(\"NestedError\")\n expect(error.causes).toEqual([cause])\n })\n\n it(\"extends Error\", () => {\n const error = new NestedError(\"wrapper\")\n expect(error).toBeInstanceOf(Error)\n expect(error).toBeInstanceOf(NestedError)\n })\n\n it(\"flattens nested cause arrays\", () => {\n const first = new Error(\"first\")\n const second = new Error(\"second\")\n const third = new Error(\"third\")\n const error = new NestedError(\"wrapper\", [first, second], third)\n\n expect(error.causes).toEqual([first, second, third])\n })\n\n it(\"accepts no causes\", () => {\n const error = new NestedError(\"wrapper\")\n\n expect(error.causes).toEqual([])\n })\n\n describe(\"create\", () => {\n it(\"returns a NestedError with message and causes\", () => {\n const cause = new Error(\"root cause\")\n const error = NestedError.create(\"wrapper\", cause)\n\n expect(error).toBeInstanceOf(NestedError)\n expect(error.message).toBe(\"wrapper\")\n expect(error.causes).toEqual([cause])\n })\n })\n\n describe(\"throwError\", () => {\n it(\"throws a NestedError with message and causes\", () => {\n const cause = new Error(\"root cause\")\n\n expect(() => NestedError.throwError(\"wrapper\", cause)).toThrow(\n NestedError\n )\n expect(() => NestedError.throwError(\"wrapper\", cause)).toThrow(\"wrapper\")\n\n try {\n NestedError.throwError(\"wrapper\", cause)\n } catch (error) {\n expect(error).toBeInstanceOf(NestedError)\n expect((error as NestedError).causes).toEqual([cause])\n }\n })\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"NestedError.test.js","sourceRoot":"","sources":["../../../tests/errors/NestedError.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { NestedError } from \"@wireio/shared/errors/NestedError\"\n\ndescribe(\"NestedError\", () => {\n it(\"keeps the new message and preserves the caught error as cause\", () => {\n const cause = new Error(\"root boom\")\n const error = new NestedError(\"wrapper failed\", { cause })\n\n expect(error).toBeInstanceOf(Error)\n expect(error.name).toBe(\"NestedError\")\n expect(error.message).toBe(\"wrapper failed\")\n // The real Error object is retained — native `.cause` AND `.causes` — so its\n // stack trace and message survive intact.\n expect(error.cause).toBe(cause)\n expect(error.causes).toEqual([cause])\n expect(error.causes[0].message).toBe(\"root boom\")\n expect(error.causes[0].stack).toBe(cause.stack)\n })\n\n it(\"makes each cause's stack + message visible in .stack (nested-exception style)\", () => {\n const inner = new Error(\"inner detail\")\n const error = new NestedError(\"outer failed\", { cause: inner })\n\n expect(error.stack).toContain(\"outer failed\")\n expect(error.stack).toContain(NestedError.CausedByPrefix)\n // The cause's stack begins with `Error: inner detail`, so its message is kept too.\n expect(error.stack).toContain(\"inner detail\")\n expect(error.stack).toContain(inner.stack)\n })\n\n it(\"retains every cause when given an array of causes\", () => {\n const first = new Error(\"first\")\n const second = new Error(\"second\")\n const error = new NestedError(\"multi\", { cause: [first, second] })\n\n expect(error.causes).toEqual([first, second])\n // The first is the native `.cause`; both show in the composed stack.\n expect(error.cause).toBe(first)\n expect(error.stack).toContain(\"first\")\n expect(error.stack).toContain(\"second\")\n })\n\n it(\"coerces a non-Error thrown value into a retained Error cause\", () => {\n const error = new NestedError(\"boom\", { cause: \"a plain string throw\" })\n\n expect(error.causes).toHaveLength(1)\n expect(error.causes[0]).toBeInstanceOf(Error)\n expect(error.causes[0].message).toBe(\"a plain string throw\")\n expect(error.cause).toBe(error.causes[0])\n })\n\n it(\"captures structured context AND folds a summary into the message\", () => {\n const error = new NestedError(\"invalid JSON\", {\n context: { text: \"{bad\", schemaName: \"ClusterConfig\" }\n })\n\n expect(error.context).toEqual({ text: \"{bad\", schemaName: \"ClusterConfig\" })\n expect(error.message).toContain(\"text={bad\")\n expect(error.message).toContain(\"schemaName=ClusterConfig\")\n })\n\n it(\"truncates a long folded context value\", () => {\n const long = \"x\".repeat(NestedError.ContextPreviewMaxChars + 50)\n const error = new NestedError(\"big\", { context: { text: long } })\n\n expect(error.message).toContain(NestedError.TruncationSuffix)\n // The folded preview is bounded regardless of the source length.\n expect(error.message.length).toBeLessThan(\n long.length + NestedError.ContextPreviewMaxChars\n )\n // The full untruncated value is still on the structured property.\n expect(error.context.text).toBe(long)\n })\n\n it(\"renders a bigint context value without throwing (JSON.stringify would)\", () => {\n const error = new NestedError(\"amount\", { context: { amount: 5n } })\n expect(error.message).toContain(\"amount=5\")\n })\n\n it(\"has no cause chain and an empty context when none are supplied\", () => {\n const error = new NestedError(\"standalone\")\n expect(error.causes).toEqual([])\n expect(error.cause).toBeUndefined()\n expect(error.context).toEqual({})\n expect(error.message).toBe(\"standalone\")\n })\n\n it(\"create returns an instance; throwError throws one\", () => {\n const cause = new Error(\"x\")\n expect(NestedError.create(\"made\", { cause })).toBeInstanceOf(NestedError)\n expect(() => NestedError.throwError(\"thrown\", { cause })).toThrow(NestedError)\n try {\n NestedError.throwError(\"thrown\", { cause })\n } catch (error) {\n expect((error as NestedError).causes).toEqual([cause])\n }\n })\n\n it(\"toError passes Errors through and wraps non-Errors\", () => {\n const real = new Error(\"real\")\n expect(NestedError.toError(real)).toBe(real)\n expect(NestedError.toError(\"str\")).toBeInstanceOf(Error)\n expect(NestedError.toError(\"str\").message).toBe(\"str\")\n })\n\n it(\"toErrors flattens and coerces; nullish yields an empty chain\", () => {\n const a = new Error(\"a\")\n expect(NestedError.toErrors([a, \"b\"]).map(error => error.message)).toEqual([\n \"a\",\n \"b\"\n ])\n expect(NestedError.toErrors(null)).toEqual([])\n expect(NestedError.toErrors(a)).toEqual([a])\n })\n})\n"]}
|