@devp0nt/error0 1.0.0-next.46 → 1.0.0-next.47
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/dist/cjs/index.cjs +80 -49
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +32 -12
- package/dist/cjs/plugins/cause-serialize.cjs +38 -0
- package/dist/cjs/plugins/cause-serialize.cjs.map +1 -0
- package/dist/cjs/plugins/cause-serialize.d.cts +5 -0
- package/dist/cjs/plugins/expected.cjs +49 -0
- package/dist/cjs/plugins/expected.cjs.map +1 -0
- package/dist/cjs/plugins/expected.d.cts +5 -0
- package/dist/cjs/plugins/message-merge.cjs +36 -0
- package/dist/cjs/plugins/message-merge.cjs.map +1 -0
- package/dist/cjs/plugins/message-merge.d.cts +5 -0
- package/dist/cjs/plugins/meta.cjs +73 -0
- package/dist/cjs/plugins/meta.cjs.map +1 -0
- package/dist/cjs/plugins/meta.d.cts +5 -0
- package/dist/cjs/plugins/stack-merge.cjs +39 -0
- package/dist/cjs/plugins/stack-merge.cjs.map +1 -0
- package/dist/cjs/plugins/stack-merge.d.cts +5 -0
- package/dist/cjs/plugins/tags.cjs +48 -0
- package/dist/cjs/plugins/tags.cjs.map +1 -0
- package/dist/cjs/plugins/tags.d.cts +5 -0
- package/dist/esm/index.d.ts +32 -12
- package/dist/esm/index.js +80 -49
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/plugins/cause-serialize.d.ts +5 -0
- package/dist/esm/plugins/cause-serialize.js +14 -0
- package/dist/esm/plugins/cause-serialize.js.map +1 -0
- package/dist/esm/plugins/expected.d.ts +5 -0
- package/dist/esm/plugins/expected.js +25 -0
- package/dist/esm/plugins/expected.js.map +1 -0
- package/dist/esm/plugins/message-merge.d.ts +5 -0
- package/dist/esm/plugins/message-merge.js +12 -0
- package/dist/esm/plugins/message-merge.js.map +1 -0
- package/dist/esm/plugins/meta.d.ts +5 -0
- package/dist/esm/plugins/meta.js +49 -0
- package/dist/esm/plugins/meta.js.map +1 -0
- package/dist/esm/plugins/stack-merge.d.ts +5 -0
- package/dist/esm/plugins/stack-merge.js +15 -0
- package/dist/esm/plugins/stack-merge.js.map +1 -0
- package/dist/esm/plugins/tags.d.ts +5 -0
- package/dist/esm/plugins/tags.js +24 -0
- package/dist/esm/plugins/tags.js.map +1 -0
- package/package.json +9 -1
- package/src/index.test.ts +56 -79
- package/src/index.ts +117 -64
- package/src/plugins/cause-serialize.test.ts +51 -0
- package/src/plugins/cause-serialize.ts +11 -0
- package/src/plugins/expected.test.ts +47 -0
- package/src/plugins/expected.ts +25 -0
- package/src/plugins/message-merge.test.ts +32 -0
- package/src/plugins/message-merge.ts +15 -0
- package/src/plugins/meta.test.ts +32 -0
- package/src/plugins/meta.ts +53 -0
- package/src/plugins/stack-merge.test.ts +64 -0
- package/src/plugins/stack-merge.ts +16 -0
- package/src/plugins/tags.test.ts +22 -0
- package/src/plugins/tags.ts +21 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Error0 } from "../index.js";
|
|
2
|
+
const toJsonSafe = (input) => {
|
|
3
|
+
if (input === null) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
if (typeof input === "string" || typeof input === "number" || typeof input === "boolean") {
|
|
7
|
+
return input;
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(input)) {
|
|
10
|
+
return input.map((value) => toJsonSafe(value));
|
|
11
|
+
}
|
|
12
|
+
if (typeof input === "object") {
|
|
13
|
+
const output = {};
|
|
14
|
+
for (const [key, value] of Object.entries(input)) {
|
|
15
|
+
const jsonValue = toJsonSafe(value);
|
|
16
|
+
if (jsonValue !== void 0) {
|
|
17
|
+
output[key] = jsonValue;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return output;
|
|
21
|
+
}
|
|
22
|
+
return void 0;
|
|
23
|
+
};
|
|
24
|
+
const isMetaRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
+
const metaPlugin = Error0.plugin().use("prop", "meta", {
|
|
26
|
+
init: (input) => input,
|
|
27
|
+
resolve: ({ flow }) => {
|
|
28
|
+
const values = flow.filter(isMetaRecord);
|
|
29
|
+
if (values.length === 0) {
|
|
30
|
+
return void 0;
|
|
31
|
+
}
|
|
32
|
+
const merged = {};
|
|
33
|
+
for (const value of [...values].reverse()) {
|
|
34
|
+
Object.assign(merged, value);
|
|
35
|
+
}
|
|
36
|
+
return merged;
|
|
37
|
+
},
|
|
38
|
+
serialize: ({ value, isPublic }) => isPublic ? void 0 : toJsonSafe(value),
|
|
39
|
+
deserialize: ({ value }) => {
|
|
40
|
+
if (!isMetaRecord(value)) {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
export {
|
|
47
|
+
metaPlugin
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/meta.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json }\n\nconst toJsonSafe = (input: unknown): Json | undefined => {\n if (input === null) {\n return null\n }\n if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') {\n return input\n }\n if (Array.isArray(input)) {\n return input.map((value) => toJsonSafe(value)) as Json[]\n }\n if (typeof input === 'object') {\n const output: Record<string, Json> = {}\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n const jsonValue = toJsonSafe(value)\n if (jsonValue !== undefined) {\n output[key] = jsonValue\n }\n }\n return output\n }\n return undefined\n}\n\nconst isMetaRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nexport const metaPlugin = Error0.plugin().use('prop', 'meta', {\n init: (input: Record<string, unknown>) => input,\n resolve: ({ flow }) => {\n const values = flow.filter(isMetaRecord)\n if (values.length === 0) {\n return undefined\n }\n\n // Merge cause meta into the current error; nearer errors win on conflicts.\n const merged: Record<string, unknown> = {}\n for (const value of [...values].reverse()) {\n Object.assign(merged, value)\n }\n return merged\n },\n serialize: ({ value, isPublic }) => (isPublic ? undefined : toJsonSafe(value)),\n deserialize: ({ value }) => {\n if (!isMetaRecord(value)) {\n return undefined\n }\n return value\n },\n})\n"],"mappings":"AAAA,SAAS,cAAc;AAIvB,MAAM,aAAa,CAAC,UAAqC;AACvD,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AACxF,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,UAAU,WAAW,KAAK,CAAC;AAAA,EAC/C;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAA+B,CAAC;AACtC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAgC,GAAG;AAC3E,YAAM,YAAY,WAAW,KAAK;AAClC,UAAI,cAAc,QAAW;AAC3B,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,MAAM,eAAe,CAAC,UACpB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAE9D,MAAM,aAAa,OAAO,OAAO,EAAE,IAAI,QAAQ,QAAQ;AAAA,EAC5D,MAAM,CAAC,UAAmC;AAAA,EAC1C,SAAS,CAAC,EAAE,KAAK,MAAM;AACrB,UAAM,SAAS,KAAK,OAAO,YAAY;AACvC,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AAGA,UAAM,SAAkC,CAAC;AACzC,eAAW,SAAS,CAAC,GAAG,MAAM,EAAE,QAAQ,GAAG;AACzC,aAAO,OAAO,QAAQ,KAAK;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AAAA,EACA,WAAW,CAAC,EAAE,OAAO,SAAS,MAAO,WAAW,SAAY,WAAW,KAAK;AAAA,EAC5E,aAAa,CAAC,EAAE,MAAM,MAAM;AAC1B,QAAI,CAAC,aAAa,KAAK,GAAG;AACxB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Error0 } from "../index.js";
|
|
2
|
+
const stackMergePlugin = Error0.plugin().use("stack", {
|
|
3
|
+
serialize: ({ error, isPublic }) => {
|
|
4
|
+
if (isPublic) {
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
return error.causes().map((cause) => {
|
|
8
|
+
return cause instanceof Error ? cause.stack : void 0;
|
|
9
|
+
}).filter((value) => typeof value === "string").join("\n");
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
export {
|
|
13
|
+
stackMergePlugin
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=stack-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/stack-merge.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const stackMergePlugin = Error0.plugin().use('stack', {\n serialize: ({ error, isPublic }) => {\n if (isPublic) {\n return undefined\n }\n return error\n .causes()\n .map((cause) => {\n return cause instanceof Error ? cause.stack : undefined\n })\n .filter((value): value is string => typeof value === 'string')\n .join('\\n')\n },\n})\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,mBAAmB,OAAO,OAAO,EAAE,IAAI,SAAS;AAAA,EAC3D,WAAW,CAAC,EAAE,OAAO,SAAS,MAAM;AAClC,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AACA,WAAO,MACJ,OAAO,EACP,IAAI,CAAC,UAAU;AACd,aAAO,iBAAiB,QAAQ,MAAM,QAAQ;AAAA,IAChD,CAAC,EACA,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,EAC5D,KAAK,IAAI;AAAA,EACd;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { PluginError0, ErrorPluginPropOptions, Error0 } from '../index.js';
|
|
2
|
+
|
|
3
|
+
declare const tagsPlugin: PluginError0<Record<never, never> & Record<"tags", ErrorPluginPropOptions<string[], string[], Error0, string[] | undefined>>, Record<never, never>>;
|
|
4
|
+
|
|
5
|
+
export { tagsPlugin };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Error0 } from "../index.js";
|
|
2
|
+
const tagsPlugin = Error0.plugin().use("prop", "tags", {
|
|
3
|
+
init: (input) => input,
|
|
4
|
+
resolve: ({ flow }) => {
|
|
5
|
+
const merged = [];
|
|
6
|
+
for (const value of flow) {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
merged.push(...value.filter((item) => typeof item === "string"));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return merged.length > 0 ? Array.from(new Set(merged)) : void 0;
|
|
12
|
+
},
|
|
13
|
+
serialize: ({ value, isPublic }) => isPublic ? void 0 : value,
|
|
14
|
+
deserialize: ({ value }) => {
|
|
15
|
+
if (!Array.isArray(value)) {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
return value.filter((item) => typeof item === "string");
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
export {
|
|
22
|
+
tagsPlugin
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/tags.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const tagsPlugin = Error0.plugin().use('prop', 'tags', {\n init: (input: string[]) => input,\n resolve: ({ flow }) => {\n const merged: string[] = []\n for (const value of flow) {\n if (Array.isArray(value)) {\n merged.push(...value.filter((item): item is string => typeof item === 'string'))\n }\n }\n return merged.length > 0 ? Array.from(new Set(merged)) : undefined\n },\n serialize: ({ value, isPublic }) => (isPublic ? undefined : value),\n deserialize: ({ value }) => {\n if (!Array.isArray(value)) {\n return undefined\n }\n return value.filter((item): item is string => typeof item === 'string')\n },\n})\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,aAAa,OAAO,OAAO,EAAE,IAAI,QAAQ,QAAQ;AAAA,EAC5D,MAAM,CAAC,UAAoB;AAAA,EAC3B,SAAS,CAAC,EAAE,KAAK,MAAM;AACrB,UAAM,SAAmB,CAAC;AAC1B,eAAW,SAAS,MAAM;AACxB,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,eAAO,KAAK,GAAG,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,CAAC;AAAA,MACjF;AAAA,IACF;AACA,WAAO,OAAO,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI;AAAA,EAC3D;AAAA,EACA,WAAW,CAAC,EAAE,OAAO,SAAS,MAAO,WAAW,SAAY;AAAA,EAC5D,aAAa,CAAC,EAAE,MAAM,MAAM;AAC1B,QAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,aAAO;AAAA,IACT;AACA,WAAO,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ;AAAA,EACxE;AACF,CAAC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devp0nt/error0",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.47",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Sergei Dmitriev",
|
|
@@ -23,12 +23,20 @@
|
|
|
23
23
|
"types": "./dist/esm/index.d.ts",
|
|
24
24
|
"require": "./dist/cjs/index.cjs",
|
|
25
25
|
"import": "./dist/esm/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./plugins/*": {
|
|
28
|
+
"types": "./dist/esm/plugins/*.d.ts",
|
|
29
|
+
"require": "./dist/cjs/plugins/*.cjs",
|
|
30
|
+
"import": "./dist/esm/plugins/*.js"
|
|
26
31
|
}
|
|
27
32
|
},
|
|
28
33
|
"typesVersions": {
|
|
29
34
|
"*": {
|
|
30
35
|
"": [
|
|
31
36
|
"./dist/esm/index.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"plugins/*": [
|
|
39
|
+
"./dist/esm/plugins/*"
|
|
32
40
|
]
|
|
33
41
|
}
|
|
34
42
|
},
|
package/src/index.test.ts
CHANGED
|
@@ -185,27 +185,35 @@ describe('Error0', () => {
|
|
|
185
185
|
})
|
|
186
186
|
|
|
187
187
|
it('stack plugin can customize stack serialization without defining prop plugin', () => {
|
|
188
|
-
const AppError = Error0.use('stack', ({ value }) => (value ? `custom:${value}` : undefined))
|
|
188
|
+
const AppError = Error0.use('stack', { serialize: ({ value }) => (value ? `custom:${value}` : undefined) })
|
|
189
189
|
const error = new AppError('test')
|
|
190
190
|
const json = AppError.serialize(error)
|
|
191
191
|
expect(typeof json.stack).toBe('string')
|
|
192
192
|
expect((json.stack as string).startsWith('custom:')).toBe(true)
|
|
193
193
|
})
|
|
194
194
|
|
|
195
|
-
it('stack plugin
|
|
196
|
-
const AppError = Error0.use('stack',
|
|
195
|
+
it('stack plugin can keep default stack via identity function', () => {
|
|
196
|
+
const AppError = Error0.use('stack', { serialize: ({ value }) => value })
|
|
197
197
|
const error = new AppError('test')
|
|
198
198
|
const json = AppError.serialize(error)
|
|
199
199
|
expect(json.stack).toBe(error.stack)
|
|
200
200
|
})
|
|
201
201
|
|
|
202
|
-
it('stack plugin
|
|
203
|
-
const AppError = Error0.use('stack',
|
|
202
|
+
it('stack plugin can disable stack serialization via function', () => {
|
|
203
|
+
const AppError = Error0.use('stack', { serialize: () => undefined })
|
|
204
204
|
const error = new AppError('test')
|
|
205
205
|
const json = AppError.serialize(error)
|
|
206
206
|
expect('stack' in json).toBe(false)
|
|
207
207
|
})
|
|
208
208
|
|
|
209
|
+
it('stack plugin rejects boolean config', () => {
|
|
210
|
+
expect(() => Error0.use('stack', true as any)).toThrow('expects { serialize: function }')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('message plugin rejects boolean config', () => {
|
|
214
|
+
expect(() => Error0.use('message', true as any)).toThrow('expects { serialize: function }')
|
|
215
|
+
})
|
|
216
|
+
|
|
209
217
|
it('prop("stack") throws and suggests using stack plugin', () => {
|
|
210
218
|
expect(() =>
|
|
211
219
|
Error0.use('prop', 'stack', {
|
|
@@ -228,6 +236,26 @@ describe('Error0', () => {
|
|
|
228
236
|
).toThrow('reserved prop key')
|
|
229
237
|
})
|
|
230
238
|
|
|
239
|
+
it('prop("message") throws and suggests using message plugin', () => {
|
|
240
|
+
expect(() =>
|
|
241
|
+
Error0.use('prop', 'message', {
|
|
242
|
+
resolve: ({ own }) => own as string,
|
|
243
|
+
serialize: ({ value }) => value,
|
|
244
|
+
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
245
|
+
}),
|
|
246
|
+
).toThrow('reserved prop key')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('plugin builder also rejects prop("message") as reserved key', () => {
|
|
250
|
+
expect(() =>
|
|
251
|
+
Error0.plugin().prop('message', {
|
|
252
|
+
resolve: ({ own }) => own as string,
|
|
253
|
+
serialize: ({ value }) => value,
|
|
254
|
+
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
255
|
+
}),
|
|
256
|
+
).toThrow('reserved prop key')
|
|
257
|
+
})
|
|
258
|
+
|
|
231
259
|
it('.serialize() -> .from() roundtrip keeps plugin values', () => {
|
|
232
260
|
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
233
261
|
const error = new AppError('test', { status: 409, code: 'NOT_FOUND' })
|
|
@@ -249,22 +277,12 @@ describe('Error0', () => {
|
|
|
249
277
|
expect('cause' in json).toBe(false)
|
|
250
278
|
})
|
|
251
279
|
|
|
252
|
-
it('cause plugin true serializes and deserializes nested Error0 causes', () => {
|
|
253
|
-
const AppError = Error0.use(statusPlugin).use(codePlugin).use('cause', true)
|
|
254
|
-
const causeError = new AppError('cause', { status: 409, code: 'NOT_FOUND' })
|
|
255
|
-
const error = new AppError('root', { status: 500, cause: causeError })
|
|
256
280
|
|
|
281
|
+
it('by default causes not serialized', () => {
|
|
282
|
+
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
283
|
+
const error = new AppError('test', { status: 400, code: 'NOT_FOUND' })
|
|
257
284
|
const json = AppError.serialize(error, false)
|
|
258
|
-
expect(
|
|
259
|
-
expect((json.cause as Record<string, unknown>).message).toBe('cause')
|
|
260
|
-
expect((json.cause as Record<string, unknown>).status).toBe(409)
|
|
261
|
-
expect((json.cause as Record<string, unknown>).code).toBe('NOT_FOUND')
|
|
262
|
-
|
|
263
|
-
const recreated = AppError.from(json)
|
|
264
|
-
expect(recreated).toBeInstanceOf(AppError)
|
|
265
|
-
expect(recreated.cause).toBeInstanceOf(AppError)
|
|
266
|
-
expect((recreated.cause as InstanceType<typeof AppError>).status).toBe(409)
|
|
267
|
-
expect((recreated.cause as InstanceType<typeof AppError>).code).toBe('NOT_FOUND')
|
|
285
|
+
expect('cause' in json).toBe(false)
|
|
268
286
|
})
|
|
269
287
|
|
|
270
288
|
it('serialize can hide props for public output', () => {
|
|
@@ -536,41 +554,19 @@ describe('Error0', () => {
|
|
|
536
554
|
expect(error1.code).toBe(undefined)
|
|
537
555
|
})
|
|
538
556
|
|
|
539
|
-
it('expected prop can be realized to send or not to send error to your error tracker', () => {
|
|
540
|
-
const AppError = Error0.use(statusPlugin)
|
|
541
|
-
.use('prop', 'expected', {
|
|
542
|
-
init: (input: boolean) => input,
|
|
543
|
-
resolve: ({ flow }) => flow.find((value) => typeof value === 'boolean'),
|
|
544
|
-
serialize: ({ value }) => value,
|
|
545
|
-
deserialize: ({ value }) => (typeof value === 'boolean' ? value : undefined),
|
|
546
|
-
})
|
|
547
|
-
.use('method', 'isExpected', (error) => {
|
|
548
|
-
return error.expected ?? false
|
|
549
|
-
})
|
|
550
|
-
const errorExpected = new AppError('test', { status: 400, expected: true })
|
|
551
|
-
const errorUnexpected = new AppError('test', { status: 400, expected: false })
|
|
552
|
-
const usualError = new Error('test')
|
|
553
|
-
const errorFromUsualError = AppError.from(usualError)
|
|
554
|
-
const errorWithExpectedErrorAsCause = new AppError('test', { status: 400, cause: errorExpected })
|
|
555
|
-
const errorWithUnexpectedErrorAsCause = new AppError('test', { status: 400, cause: errorUnexpected })
|
|
556
|
-
expect(errorExpected.expected).toBe(true)
|
|
557
|
-
expect(errorUnexpected.expected).toBe(false)
|
|
558
|
-
expect(AppError.isExpected(usualError)).toBe(false)
|
|
559
|
-
expect(errorFromUsualError.expected).toBe(undefined)
|
|
560
|
-
expect(errorFromUsualError.isExpected()).toBe(false)
|
|
561
|
-
expect(errorWithExpectedErrorAsCause.expected).toBe(true)
|
|
562
|
-
expect(errorWithExpectedErrorAsCause.isExpected()).toBe(true)
|
|
563
|
-
expect(errorWithUnexpectedErrorAsCause.expected).toBe(false)
|
|
564
|
-
expect(errorWithUnexpectedErrorAsCause.isExpected()).toBe(false)
|
|
565
|
-
})
|
|
566
557
|
|
|
567
558
|
it('messages can be combined on serialization', () => {
|
|
568
559
|
const AppError = Error0.use(statusPlugin)
|
|
569
560
|
.use(codePlugin)
|
|
570
|
-
.use('
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
561
|
+
.use('message', {
|
|
562
|
+
serialize: ({ error }) =>
|
|
563
|
+
error
|
|
564
|
+
.causes()
|
|
565
|
+
.map((cause) => {
|
|
566
|
+
return cause instanceof Error ? cause.message : undefined
|
|
567
|
+
})
|
|
568
|
+
.filter((value): value is string => typeof value === 'string')
|
|
569
|
+
.join(': '),
|
|
574
570
|
})
|
|
575
571
|
const error1 = new AppError('test1', { status: 400, code: 'NOT_FOUND' })
|
|
576
572
|
const error2 = new AppError({ message: 'test2', status: 401, cause: error1 })
|
|
@@ -584,15 +580,16 @@ describe('Error0', () => {
|
|
|
584
580
|
it('stack plugin can merge stack across causes in one serialized value', () => {
|
|
585
581
|
const AppError = Error0.use(statusPlugin)
|
|
586
582
|
.use(codePlugin)
|
|
587
|
-
.use('stack',
|
|
588
|
-
error
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
583
|
+
.use('stack', {
|
|
584
|
+
serialize: ({ error }) =>
|
|
585
|
+
error
|
|
586
|
+
.causes()
|
|
587
|
+
.map((cause) => {
|
|
588
|
+
return cause instanceof Error ? cause.stack : undefined
|
|
589
|
+
})
|
|
590
|
+
.filter((value): value is string => typeof value === 'string')
|
|
591
|
+
.join('\n'),
|
|
592
|
+
})
|
|
596
593
|
const error1 = new AppError('test1', { status: 400, code: 'NOT_FOUND' })
|
|
597
594
|
const error2 = new AppError('test2', { status: 401, cause: error1 })
|
|
598
595
|
const mergedStack1 = error1.serialize().stack as string
|
|
@@ -612,26 +609,6 @@ describe('Error0', () => {
|
|
|
612
609
|
`)
|
|
613
610
|
})
|
|
614
611
|
|
|
615
|
-
it('stack plugin can merge stack across causes in one serialized value by helper "merge"', () => {
|
|
616
|
-
const AppError = Error0.use(statusPlugin).use(codePlugin).use('stack', 'merge')
|
|
617
|
-
const error1 = new AppError('test1', { status: 400, code: 'NOT_FOUND' })
|
|
618
|
-
const error2 = new AppError('test2', { status: 401, cause: error1 })
|
|
619
|
-
const mergedStack1 = error1.serialize().stack as string
|
|
620
|
-
const mergedStack2 = error2.serialize().stack as string
|
|
621
|
-
expect(mergedStack1).toContain('Error0: test1')
|
|
622
|
-
expect(mergedStack2).toContain('Error0: test2')
|
|
623
|
-
expect(mergedStack2).toContain('Error0: test1')
|
|
624
|
-
expect(fixStack(mergedStack1)).toMatchInlineSnapshot(`
|
|
625
|
-
"Error0: test1
|
|
626
|
-
at <anonymous> (...)"
|
|
627
|
-
`)
|
|
628
|
-
expect(fixStack(mergedStack2)).toMatchInlineSnapshot(`
|
|
629
|
-
"Error0: test2
|
|
630
|
-
at <anonymous> (...)
|
|
631
|
-
Error0: test1
|
|
632
|
-
at <anonymous> (...)"
|
|
633
|
-
`)
|
|
634
|
-
})
|
|
635
612
|
|
|
636
613
|
it('Error0 assignable to LikeError0', () => {
|
|
637
614
|
type LikeError0 = {
|