@devp0nt/error0 1.0.0-next.47 → 1.0.0-next.49
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 +165 -85
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +47 -16
- package/dist/cjs/plugins/cause-serialize.cjs +4 -1
- package/dist/cjs/plugins/cause-serialize.cjs.map +1 -1
- package/dist/cjs/plugins/cause-serialize.d.cts +3 -1
- package/dist/cjs/plugins/expected.cjs +7 -2
- package/dist/cjs/plugins/expected.cjs.map +1 -1
- package/dist/cjs/plugins/expected.d.cts +3 -1
- package/dist/cjs/plugins/message-merge.cjs +5 -2
- package/dist/cjs/plugins/message-merge.cjs.map +1 -1
- package/dist/cjs/plugins/message-merge.d.cts +4 -1
- package/dist/cjs/plugins/meta.cjs +7 -2
- package/dist/cjs/plugins/meta.cjs.map +1 -1
- package/dist/cjs/plugins/meta.d.cts +3 -1
- package/dist/cjs/plugins/stack-merge.cjs +6 -3
- package/dist/cjs/plugins/stack-merge.cjs.map +1 -1
- package/dist/cjs/plugins/stack-merge.d.cts +4 -1
- package/dist/cjs/plugins/tags.cjs +7 -2
- package/dist/cjs/plugins/tags.cjs.map +1 -1
- package/dist/cjs/plugins/tags.d.cts +3 -1
- package/dist/esm/index.d.ts +47 -16
- package/dist/esm/index.js +165 -85
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/plugins/cause-serialize.d.ts +3 -1
- package/dist/esm/plugins/cause-serialize.js +4 -1
- package/dist/esm/plugins/cause-serialize.js.map +1 -1
- package/dist/esm/plugins/expected.d.ts +3 -1
- package/dist/esm/plugins/expected.js +7 -2
- package/dist/esm/plugins/expected.js.map +1 -1
- package/dist/esm/plugins/message-merge.d.ts +4 -1
- package/dist/esm/plugins/message-merge.js +5 -2
- package/dist/esm/plugins/message-merge.js.map +1 -1
- package/dist/esm/plugins/meta.d.ts +3 -1
- package/dist/esm/plugins/meta.js +7 -2
- package/dist/esm/plugins/meta.js.map +1 -1
- package/dist/esm/plugins/stack-merge.d.ts +4 -1
- package/dist/esm/plugins/stack-merge.js +6 -3
- package/dist/esm/plugins/stack-merge.js.map +1 -1
- package/dist/esm/plugins/tags.d.ts +3 -1
- package/dist/esm/plugins/tags.js +7 -2
- package/dist/esm/plugins/tags.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +96 -23
- package/src/index.ts +229 -101
- package/src/plugins/cause-serialize.test.ts +6 -4
- package/src/plugins/cause-serialize.ts +13 -9
- package/src/plugins/expected.test.ts +4 -4
- package/src/plugins/expected.ts +16 -10
- package/src/plugins/message-merge.test.ts +3 -3
- package/src/plugins/message-merge.ts +17 -13
- package/src/plugins/meta.test.ts +2 -2
- package/src/plugins/meta.ts +28 -22
- package/src/plugins/stack-merge.test.ts +4 -4
- package/src/plugins/stack-merge.ts +18 -14
- package/src/plugins/tags.test.ts +2 -2
- package/src/plugins/tags.ts +24 -18
package/src/index.test.ts
CHANGED
|
@@ -24,7 +24,7 @@ describe('Error0', () => {
|
|
|
24
24
|
.prop('status', {
|
|
25
25
|
init: (input: number) => input,
|
|
26
26
|
resolve: ({ flow }) => flow.find(Boolean),
|
|
27
|
-
serialize: ({
|
|
27
|
+
serialize: ({ resolved }) => resolved,
|
|
28
28
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
29
29
|
})
|
|
30
30
|
.method('isStatus', (error, status: number) => error.status === status)
|
|
@@ -34,7 +34,7 @@ describe('Error0', () => {
|
|
|
34
34
|
const codePlugin = Error0.plugin().use('prop', 'code', {
|
|
35
35
|
init: (input: Code) => input,
|
|
36
36
|
resolve: ({ flow }) => flow.find(Boolean),
|
|
37
|
-
serialize: ({
|
|
37
|
+
serialize: ({ resolved, isPublic }) => (isPublic ? undefined : resolved),
|
|
38
38
|
deserialize: ({ value }) =>
|
|
39
39
|
typeof value === 'string' && codes.includes(value as Code) ? (value as Code) : undefined,
|
|
40
40
|
})
|
|
@@ -56,7 +56,7 @@ describe('Error0', () => {
|
|
|
56
56
|
const AppError = Error0.use('prop', 'status', {
|
|
57
57
|
init: (input: number) => input,
|
|
58
58
|
resolve: ({ flow }) => flow.find(Boolean),
|
|
59
|
-
serialize: ({
|
|
59
|
+
serialize: ({ resolved }) => resolved,
|
|
60
60
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
61
61
|
})
|
|
62
62
|
const error = new AppError('test', { status: 400 })
|
|
@@ -79,7 +79,7 @@ describe('Error0', () => {
|
|
|
79
79
|
resolve: ({ own, flow }) => {
|
|
80
80
|
return typeof own === 'number' ? own : undefined
|
|
81
81
|
},
|
|
82
|
-
serialize: ({
|
|
82
|
+
serialize: ({ resolved }) => resolved,
|
|
83
83
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
84
84
|
})
|
|
85
85
|
.use('method', 'isStatus', (error, expectedStatus: number) => error.status === expectedStatus)
|
|
@@ -152,6 +152,19 @@ describe('Error0', () => {
|
|
|
152
152
|
expect(Error0.causes(error2)).toEqual([error2, error1, anotherError])
|
|
153
153
|
})
|
|
154
154
|
|
|
155
|
+
it('can limit causes depth via MAX_CAUSES_DEPTH on class', () => {
|
|
156
|
+
const AppError = Error0.use(statusPlugin)
|
|
157
|
+
const base = new AppError('base', { status: 400 })
|
|
158
|
+
const level1 = new AppError('level1', { status: 401, cause: base })
|
|
159
|
+
const level2 = new AppError('level2', { status: 402, cause: level1 })
|
|
160
|
+
|
|
161
|
+
AppError.MAX_CAUSES_DEPTH = 2
|
|
162
|
+
expect(AppError.causes(level2)).toEqual([level2, level1])
|
|
163
|
+
|
|
164
|
+
AppError.MAX_CAUSES_DEPTH = 999
|
|
165
|
+
expect(AppError.causes(level2)).toEqual([level2, level1, base])
|
|
166
|
+
})
|
|
167
|
+
|
|
155
168
|
it('properties floating', () => {
|
|
156
169
|
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
157
170
|
const anotherError = new Error('another error')
|
|
@@ -164,6 +177,19 @@ describe('Error0', () => {
|
|
|
164
177
|
expect(Error0.causes(error2)).toEqual([error2, error1, anotherError])
|
|
165
178
|
})
|
|
166
179
|
|
|
180
|
+
it('property getter return resolved value, not own value', () => {
|
|
181
|
+
const AppError = Error0.use('prop', 'status', {
|
|
182
|
+
init: (input: number) => input,
|
|
183
|
+
resolve: () => 500,
|
|
184
|
+
serialize: ({ resolved }) => resolved,
|
|
185
|
+
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
186
|
+
})
|
|
187
|
+
const error = new AppError('another error', { status: 400 })
|
|
188
|
+
expect(error.status).toBe(500)
|
|
189
|
+
expect(error.own('status')).toBe(400)
|
|
190
|
+
expect(error.flow('status')).toEqual([400])
|
|
191
|
+
})
|
|
192
|
+
|
|
167
193
|
it('serialize uses identity by default and skips undefined plugin values', () => {
|
|
168
194
|
const AppError = Error0.use(statusPlugin).use('prop', 'code', {
|
|
169
195
|
init: (input: string) => input,
|
|
@@ -219,7 +245,7 @@ describe('Error0', () => {
|
|
|
219
245
|
Error0.use('prop', 'stack', {
|
|
220
246
|
init: (input: string) => input,
|
|
221
247
|
resolve: ({ own }) => (typeof own === 'string' ? own : undefined),
|
|
222
|
-
serialize: ({
|
|
248
|
+
serialize: ({ resolved }) => resolved,
|
|
223
249
|
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
224
250
|
}),
|
|
225
251
|
).toThrow('reserved prop key')
|
|
@@ -230,7 +256,7 @@ describe('Error0', () => {
|
|
|
230
256
|
Error0.plugin().prop('stack', {
|
|
231
257
|
init: (input: string) => input,
|
|
232
258
|
resolve: ({ own }) => (typeof own === 'string' ? own : undefined),
|
|
233
|
-
serialize: ({
|
|
259
|
+
serialize: ({ resolved }) => resolved,
|
|
234
260
|
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
235
261
|
}),
|
|
236
262
|
).toThrow('reserved prop key')
|
|
@@ -240,7 +266,7 @@ describe('Error0', () => {
|
|
|
240
266
|
expect(() =>
|
|
241
267
|
Error0.use('prop', 'message', {
|
|
242
268
|
resolve: ({ own }) => own as string,
|
|
243
|
-
serialize: ({
|
|
269
|
+
serialize: ({ resolved }) => resolved,
|
|
244
270
|
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
245
271
|
}),
|
|
246
272
|
).toThrow('reserved prop key')
|
|
@@ -250,7 +276,7 @@ describe('Error0', () => {
|
|
|
250
276
|
expect(() =>
|
|
251
277
|
Error0.plugin().prop('message', {
|
|
252
278
|
resolve: ({ own }) => own as string,
|
|
253
|
-
serialize: ({
|
|
279
|
+
serialize: ({ resolved }) => resolved,
|
|
254
280
|
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
255
281
|
}),
|
|
256
282
|
).toThrow('reserved prop key')
|
|
@@ -267,6 +293,24 @@ describe('Error0', () => {
|
|
|
267
293
|
expect(AppError.serialize(recreated, false)).toEqual(json)
|
|
268
294
|
})
|
|
269
295
|
|
|
296
|
+
it('.round() static and instance do serialize/from roundtrip', () => {
|
|
297
|
+
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
298
|
+
const error = new AppError('test', { status: 409, code: 'NOT_FOUND' })
|
|
299
|
+
const roundedStatic = AppError.round(error, false)
|
|
300
|
+
const roundedInstance = error.round(false)
|
|
301
|
+
|
|
302
|
+
expect(roundedStatic).toBeInstanceOf(AppError)
|
|
303
|
+
expect(roundedInstance).toBeInstanceOf(AppError)
|
|
304
|
+
expect(roundedStatic.status).toBe(409)
|
|
305
|
+
expect(roundedStatic.code).toBe('NOT_FOUND')
|
|
306
|
+
expect(roundedInstance.status).toBe(409)
|
|
307
|
+
expect(roundedInstance.code).toBe('NOT_FOUND')
|
|
308
|
+
expectTypeOf(roundedStatic.status).toEqualTypeOf<number | undefined>()
|
|
309
|
+
expectTypeOf(roundedStatic.code).toEqualTypeOf<'NOT_FOUND' | 'BAD_REQUEST' | 'UNAUTHORIZED' | undefined>()
|
|
310
|
+
expectTypeOf(roundedInstance.status).toEqualTypeOf<number | undefined>()
|
|
311
|
+
expectTypeOf(roundedInstance.code).toEqualTypeOf<'NOT_FOUND' | 'BAD_REQUEST' | 'UNAUTHORIZED' | undefined>()
|
|
312
|
+
})
|
|
313
|
+
|
|
270
314
|
it('.serialize() floated props and not serialize causes', () => {
|
|
271
315
|
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
272
316
|
const error1 = new AppError('test', { status: 409 })
|
|
@@ -277,7 +321,6 @@ describe('Error0', () => {
|
|
|
277
321
|
expect('cause' in json).toBe(false)
|
|
278
322
|
})
|
|
279
323
|
|
|
280
|
-
|
|
281
324
|
it('by default causes not serialized', () => {
|
|
282
325
|
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
283
326
|
const error = new AppError('test', { status: 400, code: 'NOT_FOUND' })
|
|
@@ -298,7 +341,7 @@ describe('Error0', () => {
|
|
|
298
341
|
const AppError = Error0.use('prop', 'computed', {
|
|
299
342
|
init: () => undefined as number | undefined,
|
|
300
343
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
301
|
-
serialize: ({
|
|
344
|
+
serialize: ({ resolved }) => resolved,
|
|
302
345
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
303
346
|
})
|
|
304
347
|
|
|
@@ -314,7 +357,7 @@ describe('Error0', () => {
|
|
|
314
357
|
it('prop without init omits constructor input and infers resolve output', () => {
|
|
315
358
|
const AppError = Error0.use('prop', 'statusCode', {
|
|
316
359
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
317
|
-
serialize: ({
|
|
360
|
+
serialize: ({ resolved }) => resolved,
|
|
318
361
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
319
362
|
})
|
|
320
363
|
|
|
@@ -331,7 +374,7 @@ describe('Error0', () => {
|
|
|
331
374
|
const AppError = Error0.use('prop', 'x', {
|
|
332
375
|
init: (input: number) => input,
|
|
333
376
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') || 500,
|
|
334
|
-
serialize: ({
|
|
377
|
+
serialize: ({ resolved }) => resolved,
|
|
335
378
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
336
379
|
})
|
|
337
380
|
|
|
@@ -345,7 +388,7 @@ describe('Error0', () => {
|
|
|
345
388
|
init: (input: number) => input,
|
|
346
389
|
// @ts-expect-error - resolve type extends init type
|
|
347
390
|
resolve: ({ flow }) => 'string',
|
|
348
|
-
serialize: ({
|
|
391
|
+
serialize: ({ resolved }) => resolved,
|
|
349
392
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
350
393
|
})
|
|
351
394
|
})
|
|
@@ -354,7 +397,7 @@ describe('Error0', () => {
|
|
|
354
397
|
const AppError = Error0.use('prop', 'code', {
|
|
355
398
|
init: (input: number | 'fallback') => input,
|
|
356
399
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') ?? 500,
|
|
357
|
-
serialize: ({
|
|
400
|
+
serialize: ({ resolved }) => resolved,
|
|
358
401
|
deserialize: ({ value }) => (typeof value === 'number' || value === 'fallback' ? value : undefined),
|
|
359
402
|
})
|
|
360
403
|
const error = new AppError('test')
|
|
@@ -375,12 +418,12 @@ describe('Error0', () => {
|
|
|
375
418
|
const AppError = Error0.use('prop', 'status', {
|
|
376
419
|
init: (input: number) => input,
|
|
377
420
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
378
|
-
serialize: ({
|
|
421
|
+
serialize: ({ resolved }) => resolved,
|
|
379
422
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
380
423
|
}).use('prop', 'code', {
|
|
381
424
|
init: (input: Code) => input,
|
|
382
425
|
resolve: ({ flow }) => flow.find(isCode),
|
|
383
|
-
serialize: ({
|
|
426
|
+
serialize: ({ resolved }) => resolved,
|
|
384
427
|
deserialize: ({ value }) => (value === 'A' || value === 'B' ? value : undefined),
|
|
385
428
|
})
|
|
386
429
|
|
|
@@ -402,12 +445,12 @@ describe('Error0', () => {
|
|
|
402
445
|
const AppError = Error0.use('prop', 'status', {
|
|
403
446
|
init: (input: number) => input,
|
|
404
447
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
405
|
-
serialize: ({
|
|
448
|
+
serialize: ({ resolved }) => resolved,
|
|
406
449
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
407
450
|
}).use('prop', 'code', {
|
|
408
451
|
init: (input: Code) => input,
|
|
409
452
|
resolve: ({ flow }) => flow.find(isCode),
|
|
410
|
-
serialize: ({
|
|
453
|
+
serialize: ({ resolved }) => resolved,
|
|
411
454
|
deserialize: ({ value }) => (value === 'A' || value === 'B' ? value : undefined),
|
|
412
455
|
})
|
|
413
456
|
|
|
@@ -429,13 +472,13 @@ describe('Error0', () => {
|
|
|
429
472
|
const AppError = Error0.use('prop', 'status', {
|
|
430
473
|
init: (input: number) => input,
|
|
431
474
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') ?? 500,
|
|
432
|
-
serialize: ({
|
|
475
|
+
serialize: ({ resolved }) => resolved,
|
|
433
476
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
434
477
|
})
|
|
435
478
|
.use('prop', 'code', {
|
|
436
479
|
init: (input: Code) => input,
|
|
437
480
|
resolve: ({ flow }) => flow.find(isCode),
|
|
438
|
-
serialize: ({
|
|
481
|
+
serialize: ({ resolved }) => resolved,
|
|
439
482
|
deserialize: ({ value }) => (value === 'A' || value === 'B' ? value : undefined),
|
|
440
483
|
})
|
|
441
484
|
.use('method', 'isStatus', (error, status: number) => error.status === status)
|
|
@@ -456,7 +499,7 @@ describe('Error0', () => {
|
|
|
456
499
|
it('prop resolved type can be not undefined with init not provided', () => {
|
|
457
500
|
const AppError = Error0.use('prop', 'x', {
|
|
458
501
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') || 500,
|
|
459
|
-
serialize: ({
|
|
502
|
+
serialize: ({ resolved }) => resolved,
|
|
460
503
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
461
504
|
})
|
|
462
505
|
|
|
@@ -468,7 +511,7 @@ describe('Error0', () => {
|
|
|
468
511
|
init: (input: number) => input,
|
|
469
512
|
// @ts-expect-error - resolve type extends init type
|
|
470
513
|
resolve: ({ flow }) => 'string',
|
|
471
|
-
serialize: ({
|
|
514
|
+
serialize: ({ resolved }) => resolved,
|
|
472
515
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
473
516
|
})
|
|
474
517
|
})
|
|
@@ -554,7 +597,6 @@ describe('Error0', () => {
|
|
|
554
597
|
expect(error1.code).toBe(undefined)
|
|
555
598
|
})
|
|
556
599
|
|
|
557
|
-
|
|
558
600
|
it('messages can be combined on serialization', () => {
|
|
559
601
|
const AppError = Error0.use(statusPlugin)
|
|
560
602
|
.use(codePlugin)
|
|
@@ -609,6 +651,37 @@ describe('Error0', () => {
|
|
|
609
651
|
`)
|
|
610
652
|
})
|
|
611
653
|
|
|
654
|
+
it('stress: resolve/serialize/flow stays within perf budget', () => {
|
|
655
|
+
const AppError = Error0.use(statusPlugin).use(codePlugin)
|
|
656
|
+
|
|
657
|
+
let current: InstanceType<typeof AppError> = new AppError('root', {
|
|
658
|
+
status: 500,
|
|
659
|
+
code: 'BAD_REQUEST',
|
|
660
|
+
})
|
|
661
|
+
for (let i = 0; i < 300; i += 1) {
|
|
662
|
+
current = new AppError(`level-${i}`, {
|
|
663
|
+
status: 500,
|
|
664
|
+
code: i % 2 === 0 ? 'NOT_FOUND' : 'BAD_REQUEST',
|
|
665
|
+
cause: current,
|
|
666
|
+
})
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
let checksum = 0
|
|
670
|
+
const startedAt = performance.now()
|
|
671
|
+
for (let i = 0; i < 3000; i += 1) {
|
|
672
|
+
const resolved = AppError.resolve(current)
|
|
673
|
+
const serialized = AppError.serialize(current, false)
|
|
674
|
+
const flow = current.flow('status')
|
|
675
|
+
checksum += resolved.status ?? 0
|
|
676
|
+
checksum += (serialized.status as number | undefined) ?? 0
|
|
677
|
+
checksum += flow.length
|
|
678
|
+
}
|
|
679
|
+
const elapsedMs = performance.now() - startedAt
|
|
680
|
+
const budgetMs = process.env.CI ? 8000 : 4000
|
|
681
|
+
|
|
682
|
+
expect(checksum).toBeGreaterThan(0)
|
|
683
|
+
expect(elapsedMs).toBeLessThan(budgetMs)
|
|
684
|
+
})
|
|
612
685
|
|
|
613
686
|
it('Error0 assignable to LikeError0', () => {
|
|
614
687
|
type LikeError0 = {
|