@devp0nt/error0 1.0.0-next.40 → 1.0.0-next.41
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 +61 -98
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +55 -59
- package/dist/esm/index.d.ts +55 -59
- package/dist/esm/index.js +61 -98
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +66 -86
- package/src/index.ts +140 -213
package/src/index.test.ts
CHANGED
|
@@ -22,35 +22,21 @@ const fixStack = (stack: string | undefined) => {
|
|
|
22
22
|
describe('Error0', () => {
|
|
23
23
|
const statusExtension = Error0.extension()
|
|
24
24
|
.prop('status', {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!Number.isNaN(status)) {
|
|
30
|
-
return status
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return undefined
|
|
34
|
-
},
|
|
35
|
-
serialize: (value) => value,
|
|
25
|
+
init: (input: number) => input,
|
|
26
|
+
resolve: ({ flow }) => flow.find(Boolean),
|
|
27
|
+
serialize: ({ value }) => value,
|
|
28
|
+
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
36
29
|
})
|
|
37
30
|
.method('isStatus', (error, status: number) => error.status === status)
|
|
38
31
|
|
|
39
32
|
const codes = ['NOT_FOUND', 'BAD_REQUEST', 'UNAUTHORIZED'] as const
|
|
40
33
|
type Code = (typeof codes)[number]
|
|
41
34
|
const codeExtension = Error0.extension().extend('prop', 'code', {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return value as Code
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return undefined
|
|
52
|
-
},
|
|
53
|
-
serialize: (value, error, isPublic) => (isPublic ? undefined : value),
|
|
35
|
+
init: (input: Code) => input,
|
|
36
|
+
resolve: ({ flow }) => flow.find(Boolean),
|
|
37
|
+
serialize: ({ value, isPublic }) => (isPublic ? undefined : value),
|
|
38
|
+
deserialize: ({ value }) =>
|
|
39
|
+
typeof value === 'string' && codes.includes(value as Code) ? (value as Code) : undefined,
|
|
54
40
|
})
|
|
55
41
|
|
|
56
42
|
it('simple', () => {
|
|
@@ -68,17 +54,10 @@ describe('Error0', () => {
|
|
|
68
54
|
|
|
69
55
|
it('with direct prop extension', () => {
|
|
70
56
|
const AppError = Error0.extend('prop', 'status', {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!Number.isNaN(status)) {
|
|
76
|
-
return status
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return undefined
|
|
80
|
-
},
|
|
81
|
-
serialize: (value: number | undefined) => value,
|
|
57
|
+
init: (input: number) => input,
|
|
58
|
+
resolve: ({ flow }) => flow.find(Boolean),
|
|
59
|
+
serialize: ({ value }) => value,
|
|
60
|
+
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
82
61
|
})
|
|
83
62
|
const error = new AppError('test', { status: 400 })
|
|
84
63
|
expect(error).toBeInstanceOf(AppError)
|
|
@@ -94,6 +73,30 @@ describe('Error0', () => {
|
|
|
94
73
|
expectTypeOf<typeof AppError>().toExtend<ClassError0>()
|
|
95
74
|
})
|
|
96
75
|
|
|
76
|
+
it('class helpers prop/method/refine mirror extend API', () => {
|
|
77
|
+
const AppError = Error0.prop('status', {
|
|
78
|
+
init: (value: number) => value,
|
|
79
|
+
resolve: ({ value, flow }) => {
|
|
80
|
+
return typeof value === 'number' ? value : undefined
|
|
81
|
+
},
|
|
82
|
+
serialize: ({ value }) => value,
|
|
83
|
+
deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
|
|
84
|
+
})
|
|
85
|
+
.method('isStatus', (error, expectedStatus: number) => error.status === expectedStatus)
|
|
86
|
+
.refine((error) => {
|
|
87
|
+
if (error.cause instanceof Error && error.status === undefined) {
|
|
88
|
+
return { status: 500 }
|
|
89
|
+
}
|
|
90
|
+
return undefined
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const error = AppError.from(new Error('inner'))
|
|
94
|
+
expect(error.status).toBe(500)
|
|
95
|
+
expect(error.isStatus(500)).toBe(true)
|
|
96
|
+
expect(AppError.isStatus(error, 500)).toBe(true)
|
|
97
|
+
expectTypeOf<typeof AppError>().toExtend<ClassError0>()
|
|
98
|
+
})
|
|
99
|
+
|
|
97
100
|
it('with defined extension', () => {
|
|
98
101
|
const AppError = Error0.extend(statusExtension)
|
|
99
102
|
const error = new AppError('test', { status: 400 })
|
|
@@ -162,20 +165,14 @@ describe('Error0', () => {
|
|
|
162
165
|
})
|
|
163
166
|
|
|
164
167
|
it('serialize uses identity by default and skips undefined extension values', () => {
|
|
165
|
-
const AppError = Error0.extend(statusExtension).
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for (const value of error.flow('code')) {
|
|
169
|
-
if (typeof value === 'string') {
|
|
170
|
-
return value
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return undefined
|
|
174
|
-
},
|
|
168
|
+
const AppError = Error0.extend(statusExtension).prop('code', {
|
|
169
|
+
init: (input: string) => input,
|
|
170
|
+
resolve: ({ flow }) => flow.find(Boolean),
|
|
175
171
|
serialize: () => undefined,
|
|
172
|
+
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
176
173
|
})
|
|
177
174
|
const error = new AppError('test', { status: 401, code: 'secret' })
|
|
178
|
-
const json = AppError.serialize(error)
|
|
175
|
+
const json = AppError.serialize(error)
|
|
179
176
|
expect(json.status).toBe(401)
|
|
180
177
|
expect('code' in json).toBe(false)
|
|
181
178
|
})
|
|
@@ -183,24 +180,19 @@ describe('Error0', () => {
|
|
|
183
180
|
it('serialize keeps stack by default without stack extension', () => {
|
|
184
181
|
const AppError = Error0.extend(statusExtension)
|
|
185
182
|
const error = new AppError('test', { status: 500 })
|
|
186
|
-
const json = AppError.serialize(error)
|
|
183
|
+
const json = AppError.serialize(error)
|
|
187
184
|
expect(json.stack).toBe(error.stack)
|
|
188
185
|
})
|
|
189
186
|
|
|
190
187
|
it('stack extension can customize serialization of stack prop', () => {
|
|
191
|
-
const AppError = Error0.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return stack
|
|
197
|
-
}
|
|
198
|
-
return undefined
|
|
199
|
-
},
|
|
200
|
-
serialize: () => undefined,
|
|
188
|
+
const AppError = Error0.prop('stack', {
|
|
189
|
+
init: (input: string) => input,
|
|
190
|
+
resolve: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
191
|
+
serialize: ({ value }) => undefined,
|
|
192
|
+
deserialize: ({ value }) => (typeof value === 'string' ? value : undefined),
|
|
201
193
|
})
|
|
202
194
|
const error = new AppError('test')
|
|
203
|
-
const json = AppError.serialize(error)
|
|
195
|
+
const json = AppError.serialize(error)
|
|
204
196
|
expect('stack' in json).toBe(false)
|
|
205
197
|
})
|
|
206
198
|
|
|
@@ -215,27 +207,21 @@ describe('Error0', () => {
|
|
|
215
207
|
expect(AppError.serialize(recreated, false)).toEqual(json)
|
|
216
208
|
})
|
|
217
209
|
|
|
218
|
-
it('
|
|
219
|
-
const AppError = Error0.extend(statusExtension)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const error = new AppError('test', { status: 400, code: 'NOT_FOUND' })
|
|
227
|
-
expect(error.summary).toBe('test:NOT_FOUND')
|
|
228
|
-
expect(error.hasCode('NOT_FOUND')).toBe(true)
|
|
229
|
-
expect(AppError.hasCode(error, 'NOT_FOUND')).toBe(true)
|
|
230
|
-
expect(AppError.hasCode('just string', 'NOT_FOUND')).toBe(false)
|
|
231
|
-
expect('summary' in AppError.serialize(error)).toBe(false)
|
|
210
|
+
it('.serialize() floated props and not serialize causes', () => {
|
|
211
|
+
const AppError = Error0.extend(statusExtension).extend(codeExtension)
|
|
212
|
+
const error1 = new AppError('test', { status: 409 })
|
|
213
|
+
const error2 = new AppError('test', { code: 'NOT_FOUND', cause: error1 })
|
|
214
|
+
const json = AppError.serialize(error2, false)
|
|
215
|
+
expect(json.status).toBe(409)
|
|
216
|
+
expect(json.code).toBe('NOT_FOUND')
|
|
217
|
+
expect('cause' in json).toBe(false)
|
|
232
218
|
})
|
|
233
219
|
|
|
234
220
|
it('serialize can hide props for public output', () => {
|
|
235
221
|
const AppError = Error0.extend(statusExtension).extend(codeExtension)
|
|
236
222
|
const error = new AppError('test', { status: 401, code: 'NOT_FOUND' })
|
|
237
|
-
const privateJson = AppError.serialize(error, false)
|
|
238
|
-
const publicJson = AppError.serialize(error, true)
|
|
223
|
+
const privateJson = AppError.serialize(error, false)
|
|
224
|
+
const publicJson = AppError.serialize(error, true)
|
|
239
225
|
expect(privateJson.code).toBe('NOT_FOUND')
|
|
240
226
|
expect('code' in publicJson).toBe(false)
|
|
241
227
|
})
|
|
@@ -308,20 +294,14 @@ describe('Error0', () => {
|
|
|
308
294
|
|
|
309
295
|
it('expected prop can be realized to send or not to send error to your error tracker', () => {
|
|
310
296
|
const AppError = Error0.extend(statusExtension)
|
|
311
|
-
.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return value
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return undefined
|
|
320
|
-
},
|
|
321
|
-
serialize: (value) => value,
|
|
297
|
+
.prop('expected', {
|
|
298
|
+
init: (input: boolean) => input,
|
|
299
|
+
resolve: ({ flow }) => flow.find((value) => typeof value === 'boolean'),
|
|
300
|
+
serialize: ({ value }) => value,
|
|
301
|
+
deserialize: ({ value }) => (typeof value === 'boolean' ? value : undefined),
|
|
322
302
|
})
|
|
323
|
-
.
|
|
324
|
-
return error.expected
|
|
303
|
+
.method('isExpected', (error) => {
|
|
304
|
+
return error.expected ?? false
|
|
325
305
|
})
|
|
326
306
|
const errorExpected = new AppError('test', { status: 400, expected: true })
|
|
327
307
|
const errorUnexpected = new AppError('test', { status: 400, expected: false })
|