@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.
Files changed (57) hide show
  1. package/dist/cjs/index.cjs +87 -49
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.d.cts +35 -12
  4. package/dist/cjs/plugins/cause-serialize.cjs +38 -0
  5. package/dist/cjs/plugins/cause-serialize.cjs.map +1 -0
  6. package/dist/cjs/plugins/cause-serialize.d.cts +5 -0
  7. package/dist/cjs/plugins/expected.cjs +49 -0
  8. package/dist/cjs/plugins/expected.cjs.map +1 -0
  9. package/dist/cjs/plugins/expected.d.cts +5 -0
  10. package/dist/cjs/plugins/message-merge.cjs +36 -0
  11. package/dist/cjs/plugins/message-merge.cjs.map +1 -0
  12. package/dist/cjs/plugins/message-merge.d.cts +5 -0
  13. package/dist/cjs/plugins/meta.cjs +73 -0
  14. package/dist/cjs/plugins/meta.cjs.map +1 -0
  15. package/dist/cjs/plugins/meta.d.cts +5 -0
  16. package/dist/cjs/plugins/stack-merge.cjs +39 -0
  17. package/dist/cjs/plugins/stack-merge.cjs.map +1 -0
  18. package/dist/cjs/plugins/stack-merge.d.cts +5 -0
  19. package/dist/cjs/plugins/tags.cjs +48 -0
  20. package/dist/cjs/plugins/tags.cjs.map +1 -0
  21. package/dist/cjs/plugins/tags.d.cts +5 -0
  22. package/dist/esm/index.d.ts +35 -12
  23. package/dist/esm/index.js +87 -49
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/plugins/cause-serialize.d.ts +5 -0
  26. package/dist/esm/plugins/cause-serialize.js +14 -0
  27. package/dist/esm/plugins/cause-serialize.js.map +1 -0
  28. package/dist/esm/plugins/expected.d.ts +5 -0
  29. package/dist/esm/plugins/expected.js +25 -0
  30. package/dist/esm/plugins/expected.js.map +1 -0
  31. package/dist/esm/plugins/message-merge.d.ts +5 -0
  32. package/dist/esm/plugins/message-merge.js +12 -0
  33. package/dist/esm/plugins/message-merge.js.map +1 -0
  34. package/dist/esm/plugins/meta.d.ts +5 -0
  35. package/dist/esm/plugins/meta.js +49 -0
  36. package/dist/esm/plugins/meta.js.map +1 -0
  37. package/dist/esm/plugins/stack-merge.d.ts +5 -0
  38. package/dist/esm/plugins/stack-merge.js +15 -0
  39. package/dist/esm/plugins/stack-merge.js.map +1 -0
  40. package/dist/esm/plugins/tags.d.ts +5 -0
  41. package/dist/esm/plugins/tags.js +24 -0
  42. package/dist/esm/plugins/tags.js.map +1 -0
  43. package/package.json +9 -1
  44. package/src/index.test.ts +74 -82
  45. package/src/index.ts +130 -64
  46. package/src/plugins/cause-serialize.test.ts +51 -0
  47. package/src/plugins/cause-serialize.ts +11 -0
  48. package/src/plugins/expected.test.ts +47 -0
  49. package/src/plugins/expected.ts +25 -0
  50. package/src/plugins/message-merge.test.ts +32 -0
  51. package/src/plugins/message-merge.ts +15 -0
  52. package/src/plugins/meta.test.ts +32 -0
  53. package/src/plugins/meta.ts +53 -0
  54. package/src/plugins/stack-merge.test.ts +64 -0
  55. package/src/plugins/stack-merge.ts +16 -0
  56. package/src/plugins/tags.test.ts +22 -0
  57. 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,5 @@
1
+ import { PluginError0 } from '../index.js';
2
+
3
+ declare const stackMergePlugin: PluginError0<Record<never, never>, Record<never, never>>;
4
+
5
+ export { stackMergePlugin };
@@ -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.46",
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 serialize true keeps default stack', () => {
196
- const AppError = Error0.use('stack', true)
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 serialize false disables stack serialization', () => {
203
- const AppError = Error0.use('stack', false)
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('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
-
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(typeof json.cause).toBe('object')
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('prop', 'message', {
571
- resolve: ({ own }) => own as string,
572
- serialize: ({ value, error }) => error.flow('message').join(': '),
573
- deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
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', ({ error }) =>
588
- error
589
- .causes()
590
- .map((cause) => {
591
- return cause instanceof Error ? cause.stack : undefined
592
- })
593
- .filter((value): value is string => typeof value === 'string')
594
- .join('\n'),
595
- )
596
- const error1 = new AppError('test1', { status: 400, code: 'NOT_FOUND' })
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