@devp0nt/error0 1.0.0-next.48 → 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 +158 -85
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +44 -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 +44 -16
- package/dist/esm/index.js +158 -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 +79 -21
- package/src/index.ts +216 -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')
|
|
@@ -315,7 +341,7 @@ describe('Error0', () => {
|
|
|
315
341
|
const AppError = Error0.use('prop', 'computed', {
|
|
316
342
|
init: () => undefined as number | undefined,
|
|
317
343
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
318
|
-
serialize: ({
|
|
344
|
+
serialize: ({ resolved }) => resolved,
|
|
319
345
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
320
346
|
})
|
|
321
347
|
|
|
@@ -331,7 +357,7 @@ describe('Error0', () => {
|
|
|
331
357
|
it('prop without init omits constructor input and infers resolve output', () => {
|
|
332
358
|
const AppError = Error0.use('prop', 'statusCode', {
|
|
333
359
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
334
|
-
serialize: ({
|
|
360
|
+
serialize: ({ resolved }) => resolved,
|
|
335
361
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
336
362
|
})
|
|
337
363
|
|
|
@@ -348,7 +374,7 @@ describe('Error0', () => {
|
|
|
348
374
|
const AppError = Error0.use('prop', 'x', {
|
|
349
375
|
init: (input: number) => input,
|
|
350
376
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') || 500,
|
|
351
|
-
serialize: ({
|
|
377
|
+
serialize: ({ resolved }) => resolved,
|
|
352
378
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
353
379
|
})
|
|
354
380
|
|
|
@@ -362,7 +388,7 @@ describe('Error0', () => {
|
|
|
362
388
|
init: (input: number) => input,
|
|
363
389
|
// @ts-expect-error - resolve type extends init type
|
|
364
390
|
resolve: ({ flow }) => 'string',
|
|
365
|
-
serialize: ({
|
|
391
|
+
serialize: ({ resolved }) => resolved,
|
|
366
392
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
367
393
|
})
|
|
368
394
|
})
|
|
@@ -371,7 +397,7 @@ describe('Error0', () => {
|
|
|
371
397
|
const AppError = Error0.use('prop', 'code', {
|
|
372
398
|
init: (input: number | 'fallback') => input,
|
|
373
399
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') ?? 500,
|
|
374
|
-
serialize: ({
|
|
400
|
+
serialize: ({ resolved }) => resolved,
|
|
375
401
|
deserialize: ({ value }) => (typeof value === 'number' || value === 'fallback' ? value : undefined),
|
|
376
402
|
})
|
|
377
403
|
const error = new AppError('test')
|
|
@@ -392,12 +418,12 @@ describe('Error0', () => {
|
|
|
392
418
|
const AppError = Error0.use('prop', 'status', {
|
|
393
419
|
init: (input: number) => input,
|
|
394
420
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
395
|
-
serialize: ({
|
|
421
|
+
serialize: ({ resolved }) => resolved,
|
|
396
422
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
397
423
|
}).use('prop', 'code', {
|
|
398
424
|
init: (input: Code) => input,
|
|
399
425
|
resolve: ({ flow }) => flow.find(isCode),
|
|
400
|
-
serialize: ({
|
|
426
|
+
serialize: ({ resolved }) => resolved,
|
|
401
427
|
deserialize: ({ value }) => (value === 'A' || value === 'B' ? value : undefined),
|
|
402
428
|
})
|
|
403
429
|
|
|
@@ -419,12 +445,12 @@ describe('Error0', () => {
|
|
|
419
445
|
const AppError = Error0.use('prop', 'status', {
|
|
420
446
|
init: (input: number) => input,
|
|
421
447
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number'),
|
|
422
|
-
serialize: ({
|
|
448
|
+
serialize: ({ resolved }) => resolved,
|
|
423
449
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
424
450
|
}).use('prop', 'code', {
|
|
425
451
|
init: (input: Code) => input,
|
|
426
452
|
resolve: ({ flow }) => flow.find(isCode),
|
|
427
|
-
serialize: ({
|
|
453
|
+
serialize: ({ resolved }) => resolved,
|
|
428
454
|
deserialize: ({ value }) => (value === 'A' || value === 'B' ? value : undefined),
|
|
429
455
|
})
|
|
430
456
|
|
|
@@ -446,13 +472,13 @@ describe('Error0', () => {
|
|
|
446
472
|
const AppError = Error0.use('prop', 'status', {
|
|
447
473
|
init: (input: number) => input,
|
|
448
474
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') ?? 500,
|
|
449
|
-
serialize: ({
|
|
475
|
+
serialize: ({ resolved }) => resolved,
|
|
450
476
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
451
477
|
})
|
|
452
478
|
.use('prop', 'code', {
|
|
453
479
|
init: (input: Code) => input,
|
|
454
480
|
resolve: ({ flow }) => flow.find(isCode),
|
|
455
|
-
serialize: ({
|
|
481
|
+
serialize: ({ resolved }) => resolved,
|
|
456
482
|
deserialize: ({ value }) => (value === 'A' || value === 'B' ? value : undefined),
|
|
457
483
|
})
|
|
458
484
|
.use('method', 'isStatus', (error, status: number) => error.status === status)
|
|
@@ -473,7 +499,7 @@ describe('Error0', () => {
|
|
|
473
499
|
it('prop resolved type can be not undefined with init not provided', () => {
|
|
474
500
|
const AppError = Error0.use('prop', 'x', {
|
|
475
501
|
resolve: ({ flow }) => flow.find((item) => typeof item === 'number') || 500,
|
|
476
|
-
serialize: ({
|
|
502
|
+
serialize: ({ resolved }) => resolved,
|
|
477
503
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
478
504
|
})
|
|
479
505
|
|
|
@@ -485,7 +511,7 @@ describe('Error0', () => {
|
|
|
485
511
|
init: (input: number) => input,
|
|
486
512
|
// @ts-expect-error - resolve type extends init type
|
|
487
513
|
resolve: ({ flow }) => 'string',
|
|
488
|
-
serialize: ({
|
|
514
|
+
serialize: ({ resolved }) => resolved,
|
|
489
515
|
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
490
516
|
})
|
|
491
517
|
})
|
|
@@ -625,6 +651,38 @@ describe('Error0', () => {
|
|
|
625
651
|
`)
|
|
626
652
|
})
|
|
627
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
|
+
})
|
|
685
|
+
|
|
628
686
|
it('Error0 assignable to LikeError0', () => {
|
|
629
687
|
type LikeError0 = {
|
|
630
688
|
from: (error: unknown) => Error
|