@devp0nt/error0 1.0.0-next.46 → 1.0.0-next.48
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 +87 -49
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +35 -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 +35 -12
- package/dist/esm/index.js +87 -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 +74 -82
- package/src/index.ts +130 -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 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/message-merge.ts"],"sourcesContent":["import { Error0 } from '../index.js'\n\nexport const messageMergePlugin = Error0.plugin().use('message', {\n serialize: ({ error }) => {\n return (\n error\n .causes()\n .map((cause) => {\n return cause instanceof Error ? cause.message : undefined\n })\n .filter((value): value is string => typeof value === 'string')\n .join(': ') || 'Unknown error'\n )\n },\n})\n"],"mappings":"AAAA,SAAS,cAAc;AAEhB,MAAM,qBAAqB,OAAO,OAAO,EAAE,IAAI,WAAW;AAAA,EAC/D,WAAW,CAAC,EAAE,MAAM,MAAM;AACxB,WACE,MACG,OAAO,EACP,IAAI,CAAC,UAAU;AACd,aAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD,CAAC,EACA,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,EAC5D,KAAK,IAAI,KAAK;AAAA,EAErB;AACF,CAAC;","names":[]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { PluginError0, ErrorPluginPropOptions, Error0 } from '../index.js';
|
|
2
|
+
|
|
3
|
+
declare const metaPlugin: PluginError0<Record<never, never> & Record<"meta", ErrorPluginPropOptions<Record<string, unknown>, Record<string, unknown>, Error0, Record<string, unknown> | undefined>>, Record<never, never>>;
|
|
4
|
+
|
|
5
|
+
export { metaPlugin };
|
|
@@ -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.48",
|
|
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' })
|
|
@@ -239,6 +267,24 @@ describe('Error0', () => {
|
|
|
239
267
|
expect(AppError.serialize(recreated, false)).toEqual(json)
|
|
240
268
|
})
|
|
241
269
|
|
|
270
|
+
it('.round() static and instance do serialize/from roundtrip', () => {
|
|
271
|
+
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
272
|
+
const error = new AppError('test', { status: 409, code: 'NOT_FOUND' })
|
|
273
|
+
const roundedStatic = AppError.round(error, false)
|
|
274
|
+
const roundedInstance = error.round(false)
|
|
275
|
+
|
|
276
|
+
expect(roundedStatic).toBeInstanceOf(AppError)
|
|
277
|
+
expect(roundedInstance).toBeInstanceOf(AppError)
|
|
278
|
+
expect(roundedStatic.status).toBe(409)
|
|
279
|
+
expect(roundedStatic.code).toBe('NOT_FOUND')
|
|
280
|
+
expect(roundedInstance.status).toBe(409)
|
|
281
|
+
expect(roundedInstance.code).toBe('NOT_FOUND')
|
|
282
|
+
expectTypeOf(roundedStatic.status).toEqualTypeOf<number | undefined>()
|
|
283
|
+
expectTypeOf(roundedStatic.code).toEqualTypeOf<'NOT_FOUND' | 'BAD_REQUEST' | 'UNAUTHORIZED' | undefined>()
|
|
284
|
+
expectTypeOf(roundedInstance.status).toEqualTypeOf<number | undefined>()
|
|
285
|
+
expectTypeOf(roundedInstance.code).toEqualTypeOf<'NOT_FOUND' | 'BAD_REQUEST' | 'UNAUTHORIZED' | undefined>()
|
|
286
|
+
})
|
|
287
|
+
|
|
242
288
|
it('.serialize() floated props and not serialize causes', () => {
|
|
243
289
|
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
244
290
|
const error1 = new AppError('test', { status: 409 })
|
|
@@ -249,22 +295,11 @@ describe('Error0', () => {
|
|
|
249
295
|
expect('cause' in json).toBe(false)
|
|
250
296
|
})
|
|
251
297
|
|
|
252
|
-
it('
|
|
253
|
-
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
254
|
-
const
|
|
255
|
-
const error = new AppError('root', { status: 500, cause: causeError })
|
|
256
|
-
|
|
298
|
+
it('by default causes not serialized', () => {
|
|
299
|
+
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
300
|
+
const error = new AppError('test', { status: 400, code: 'NOT_FOUND' })
|
|
257
301
|
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')
|
|
302
|
+
expect('cause' in json).toBe(false)
|
|
268
303
|
})
|
|
269
304
|
|
|
270
305
|
it('serialize can hide props for public output', () => {
|
|
@@ -536,41 +571,18 @@ describe('Error0', () => {
|
|
|
536
571
|
expect(error1.code).toBe(undefined)
|
|
537
572
|
})
|
|
538
573
|
|
|
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
|
-
|
|
567
574
|
it('messages can be combined on serialization', () => {
|
|
568
575
|
const AppError = Error0.use(statusPlugin)
|
|
569
576
|
.use(codePlugin)
|
|
570
|
-
.use('
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
577
|
+
.use('message', {
|
|
578
|
+
serialize: ({ error }) =>
|
|
579
|
+
error
|
|
580
|
+
.causes()
|
|
581
|
+
.map((cause) => {
|
|
582
|
+
return cause instanceof Error ? cause.message : undefined
|
|
583
|
+
})
|
|
584
|
+
.filter((value): value is string => typeof value === 'string')
|
|
585
|
+
.join(': '),
|
|
574
586
|
})
|
|
575
587
|
const error1 = new AppError('test1', { status: 400, code: 'NOT_FOUND' })
|
|
576
588
|
const error2 = new AppError({ message: 'test2', status: 401, cause: error1 })
|
|
@@ -584,36 +596,16 @@ describe('Error0', () => {
|
|
|
584
596
|
it('stack plugin can merge stack across causes in one serialized value', () => {
|
|
585
597
|
const AppError = Error0.use(statusPlugin)
|
|
586
598
|
.use(codePlugin)
|
|
587
|
-
.use('stack',
|
|
588
|
-
error
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const error2 = new AppError('test2', { status: 401, cause: error1 })
|
|
598
|
-
const mergedStack1 = error1.serialize().stack as string
|
|
599
|
-
const mergedStack2 = error2.serialize().stack as string
|
|
600
|
-
expect(mergedStack1).toContain('Error0: test1')
|
|
601
|
-
expect(mergedStack2).toContain('Error0: test2')
|
|
602
|
-
expect(mergedStack2).toContain('Error0: test1')
|
|
603
|
-
expect(fixStack(mergedStack1)).toMatchInlineSnapshot(`
|
|
604
|
-
"Error0: test1
|
|
605
|
-
at <anonymous> (...)"
|
|
606
|
-
`)
|
|
607
|
-
expect(fixStack(mergedStack2)).toMatchInlineSnapshot(`
|
|
608
|
-
"Error0: test2
|
|
609
|
-
at <anonymous> (...)
|
|
610
|
-
Error0: test1
|
|
611
|
-
at <anonymous> (...)"
|
|
612
|
-
`)
|
|
613
|
-
})
|
|
614
|
-
|
|
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')
|
|
599
|
+
.use('stack', {
|
|
600
|
+
serialize: ({ error }) =>
|
|
601
|
+
error
|
|
602
|
+
.causes()
|
|
603
|
+
.map((cause) => {
|
|
604
|
+
return cause instanceof Error ? cause.stack : undefined
|
|
605
|
+
})
|
|
606
|
+
.filter((value): value is string => typeof value === 'string')
|
|
607
|
+
.join('\n'),
|
|
608
|
+
})
|
|
617
609
|
const error1 = new AppError('test1', { status: 400, code: 'NOT_FOUND' })
|
|
618
610
|
const error2 = new AppError('test2', { status: 401, cause: error1 })
|
|
619
611
|
const mergedStack1 = error1.serialize().stack as string
|