@devp0nt/error0 1.0.0-next.5 → 1.0.0-next.50

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 (75) hide show
  1. package/dist/cjs/index.cjs +614 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/cjs/index.d.cts +336 -414
  4. package/dist/cjs/plugins/cause-serialize.cjs +41 -0
  5. package/dist/cjs/plugins/cause-serialize.cjs.map +1 -0
  6. package/dist/cjs/plugins/cause-serialize.d.cts +7 -0
  7. package/dist/cjs/plugins/code.cjs +38 -0
  8. package/dist/cjs/plugins/code.cjs.map +1 -0
  9. package/dist/cjs/plugins/code.d.cts +7 -0
  10. package/dist/cjs/plugins/expected.cjs +54 -0
  11. package/dist/cjs/plugins/expected.cjs.map +1 -0
  12. package/dist/cjs/plugins/expected.d.cts +36 -0
  13. package/dist/cjs/plugins/message-merge.cjs +39 -0
  14. package/dist/cjs/plugins/message-merge.cjs.map +1 -0
  15. package/dist/cjs/plugins/message-merge.d.cts +8 -0
  16. package/dist/cjs/plugins/meta.cjs +78 -0
  17. package/dist/cjs/plugins/meta.cjs.map +1 -0
  18. package/dist/cjs/plugins/meta.d.cts +7 -0
  19. package/dist/cjs/plugins/stack-merge.cjs +42 -0
  20. package/dist/cjs/plugins/stack-merge.cjs.map +1 -0
  21. package/dist/cjs/plugins/stack-merge.d.cts +8 -0
  22. package/dist/cjs/plugins/status.cjs +54 -0
  23. package/dist/cjs/plugins/status.cjs.map +1 -0
  24. package/dist/cjs/plugins/status.d.cts +8 -0
  25. package/dist/cjs/plugins/tags.cjs +73 -0
  26. package/dist/cjs/plugins/tags.cjs.map +1 -0
  27. package/dist/cjs/plugins/tags.d.cts +12 -0
  28. package/dist/esm/index.d.ts +336 -414
  29. package/dist/esm/index.js +530 -341
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/plugins/cause-serialize.d.ts +7 -0
  32. package/dist/esm/plugins/cause-serialize.js +17 -0
  33. package/dist/esm/plugins/cause-serialize.js.map +1 -0
  34. package/dist/esm/plugins/code.d.ts +7 -0
  35. package/dist/esm/plugins/code.js +14 -0
  36. package/dist/esm/plugins/code.js.map +1 -0
  37. package/dist/esm/plugins/expected.d.ts +36 -0
  38. package/dist/esm/plugins/expected.js +30 -0
  39. package/dist/esm/plugins/expected.js.map +1 -0
  40. package/dist/esm/plugins/message-merge.d.ts +8 -0
  41. package/dist/esm/plugins/message-merge.js +15 -0
  42. package/dist/esm/plugins/message-merge.js.map +1 -0
  43. package/dist/esm/plugins/meta.d.ts +7 -0
  44. package/dist/esm/plugins/meta.js +54 -0
  45. package/dist/esm/plugins/meta.js.map +1 -0
  46. package/dist/esm/plugins/stack-merge.d.ts +8 -0
  47. package/dist/esm/plugins/stack-merge.js +18 -0
  48. package/dist/esm/plugins/stack-merge.js.map +1 -0
  49. package/dist/esm/plugins/status.d.ts +8 -0
  50. package/dist/esm/plugins/status.js +30 -0
  51. package/dist/esm/plugins/status.js.map +1 -0
  52. package/dist/esm/plugins/tags.d.ts +12 -0
  53. package/dist/esm/plugins/tags.js +49 -0
  54. package/dist/esm/plugins/tags.js.map +1 -0
  55. package/package.json +53 -23
  56. package/src/index.test.ts +689 -452
  57. package/src/index.ts +1163 -502
  58. package/src/plugins/cause-serialize.test.ts +53 -0
  59. package/src/plugins/cause-serialize.ts +15 -0
  60. package/src/plugins/code.test.ts +27 -0
  61. package/src/plugins/code.ts +12 -0
  62. package/src/plugins/expected.test.ts +47 -0
  63. package/src/plugins/expected.ts +31 -0
  64. package/src/plugins/message-merge.test.ts +32 -0
  65. package/src/plugins/message-merge.ts +19 -0
  66. package/src/plugins/meta.test.ts +32 -0
  67. package/src/plugins/meta.ts +59 -0
  68. package/src/plugins/stack-merge.test.ts +64 -0
  69. package/src/plugins/stack-merge.ts +20 -0
  70. package/src/plugins/status.test.ts +54 -0
  71. package/src/plugins/status.ts +29 -0
  72. package/src/plugins/tags.test.ts +74 -0
  73. package/src/plugins/tags.ts +51 -0
  74. package/dist/cjs/index.js +0 -435
  75. package/dist/cjs/index.js.map +0 -1
package/src/index.ts CHANGED
@@ -1,585 +1,1246 @@
1
- import { Meta0 } from '@devp0nt/meta0'
2
- import { type AxiosError, HttpStatusCode, isAxiosError } from 'axios'
3
- import get from 'lodash/get.js'
4
- import { ZodError } from 'zod'
5
-
6
- // TODO: В эррор0 добавить ориджинал
7
- // TODO: store tags as array from all causes
8
- // TODO: not use self stack if toError0
9
- // TODO: fix default message in extended error0, should be used in constuctor of Error0
10
- // TODO: remove defaults prop from getPropsFromUnknown
11
- // TODO: code has enum type, fn to check if code exists
12
-
13
- export interface Error0Input {
14
- message?: string
15
- tag?: string
16
- code?: string
17
- httpStatus?: HttpStatusCode | HttpStatusCodeString
18
- expected?: boolean | ExpectedFn
19
- clientMessage?: string
20
- cause?: Error0Cause
21
- stack?: string
22
- meta?: Meta0.Meta0OrValueTypeNullish
23
- zodError?: ZodError
24
- axiosError?: AxiosError
1
+ type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false
2
+ type NormalizeUnknownToUndefined<T> = IsUnknown<T> extends true ? undefined : T
3
+ type IsOnlyUndefined<T> = [Exclude<T, undefined>] extends [never] ? true : false
4
+ type InferFirstArg<TFn> = TFn extends (...args: infer TArgs) => unknown
5
+ ? TArgs extends [infer TFirst, ...unknown[]]
6
+ ? TFirst
7
+ : undefined
8
+ : undefined
9
+ type InferPluginPropInput<TProp extends ErrorPluginPropOptions<any, any, any, any>> = TProp extends {
10
+ init: infer TInit
25
11
  }
12
+ ? NormalizeUnknownToUndefined<InferFirstArg<TInit>>
13
+ : undefined
14
+ type ErrorPluginPropInit<TInputValue, TOutputValue> = ((input: TInputValue) => TOutputValue) | (() => TOutputValue)
15
+ type ErrorPluginPropSerializeOptions<
16
+ TOutputValue,
17
+ TError extends Error0,
18
+ TResolveValue extends TOutputValue | undefined,
19
+ > = {
20
+ own: TOutputValue | undefined
21
+ flow: Array<TOutputValue | undefined>
22
+ resolved: TResolveValue
23
+ error: TError
24
+ isPublic: boolean
25
+ }
26
+ type ErrorPluginPropSerialize<TOutputValue, TError extends Error0, TResolveValue extends TOutputValue | undefined> =
27
+ | ((options: ErrorPluginPropSerializeOptions<TOutputValue, TError, TResolveValue>) => unknown)
28
+ | false
29
+ type ErrorPluginPropDeserialize<TOutputValue> =
30
+ | ((options: { value: unknown; record: Record<string, unknown> }) => TOutputValue | undefined)
31
+ | false
32
+ type ErrorPluginPropOptionsResolveOptions<TOutputValue, TError extends Error0> = {
33
+ own: TOutputValue | undefined
34
+ flow: Array<TOutputValue | undefined>
35
+ error: TError
36
+ }
37
+ type ErrorPluginPropOptionsBase<TOutputValue, TError extends Error0, TResolveValue extends TOutputValue | undefined> = {
38
+ resolve: (options: ErrorPluginPropOptionsResolveOptions<TOutputValue, TError>) => TResolveValue
39
+ serialize: ErrorPluginPropSerialize<TOutputValue, TError, TResolveValue>
40
+ deserialize: ErrorPluginPropDeserialize<TOutputValue>
41
+ }
42
+ type ErrorPluginPropOptionsWithInit<
43
+ TInputValue,
44
+ TOutputValue,
45
+ TError extends Error0,
46
+ TResolveValue extends TOutputValue | undefined,
47
+ > = ErrorPluginPropOptionsBase<TOutputValue, TError, TResolveValue> & {
48
+ init: ErrorPluginPropInit<TInputValue, TOutputValue>
49
+ }
50
+ type ErrorPluginPropOptionsWithoutInit<
51
+ TOutputValue,
52
+ TError extends Error0,
53
+ TResolveValue extends TOutputValue | undefined,
54
+ > = ErrorPluginPropOptionsBase<TOutputValue, TError, TResolveValue> & {
55
+ init?: undefined
56
+ }
57
+ export type ErrorPluginPropOptions<
58
+ TInputValue = undefined,
59
+ TOutputValue = unknown,
60
+ TError extends Error0 = Error0,
61
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
62
+ > =
63
+ | ErrorPluginPropOptionsWithInit<TInputValue, TOutputValue, TError, TResolveValue>
64
+ | ErrorPluginPropOptionsWithoutInit<TOutputValue, TError, TResolveValue>
65
+ export type ErrorPluginMethodFn<TOutputValue, TArgs extends unknown[] = unknown[], TError extends Error0 = Error0> = (
66
+ error: TError,
67
+ ...args: TArgs
68
+ ) => TOutputValue
69
+ type ErrorPluginAnyMethodFn = (error: any, ...args: any[]) => any
70
+ export type ErrorPluginAdaptResult<TOutputProps extends Record<string, unknown>> = Partial<TOutputProps> | undefined
71
+ export type ErrorPluginAdaptFn<
72
+ TError extends Error0 = Error0,
73
+ TOutputProps extends Record<string, unknown> = Record<never, never>,
74
+ > = ((error: TError) => void) | ((error: TError) => ErrorPluginAdaptResult<TOutputProps>)
75
+ export type ErrorPluginStackSerialize<TError extends Error0> = (options: {
76
+ value: string | undefined
77
+ error: TError
78
+ isPublic: boolean
79
+ }) => unknown
80
+ export type ErrorPluginStack<TError extends Error0 = Error0> = { serialize: ErrorPluginStackSerialize<TError> }
81
+ export type ErrorPluginCauseSerialize<TError extends Error0> = (options: {
82
+ value: unknown
83
+ error: TError
84
+ isPublic: boolean
85
+ }) => unknown
86
+ export type ErrorPluginCause<TError extends Error0 = Error0> = { serialize: ErrorPluginCauseSerialize<TError> }
87
+ export type ErrorPluginMessageSerialize<TError extends Error0> = (options: {
88
+ value: string
89
+ error: TError
90
+ isPublic: boolean
91
+ }) => unknown
92
+ export type ErrorPluginMessage<TError extends Error0 = Error0> = { serialize: ErrorPluginMessageSerialize<TError> }
93
+ type ErrorMethodRecord = { fn: ErrorPluginAnyMethodFn }
94
+
95
+ export type ErrorPluginProps = { [key: string]: ErrorPluginPropOptions<any, any> }
96
+ export type ErrorPluginMethods = { [key: string]: ErrorPluginAnyMethodFn }
97
+
98
+ export type ErrorPlugin<
99
+ TProps extends ErrorPluginProps = Record<never, never>,
100
+ TMethods extends ErrorPluginMethods = Record<never, never>,
101
+ > = {
102
+ props?: TProps
103
+ methods?: TMethods
104
+ adapt?: Array<ErrorPluginAdaptFn<Error0, PluginOutputProps<TProps>>>
105
+ stack?: ErrorPluginStack
106
+ cause?: ErrorPluginCause
107
+ message?: ErrorPluginMessage
108
+ }
109
+ type AddPropToPluginProps<
110
+ TProps extends ErrorPluginProps,
111
+ TKey extends string,
112
+ TInputValue,
113
+ TOutputValue,
114
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
115
+ > = TProps & Record<TKey, ErrorPluginPropOptions<TInputValue, TOutputValue, Error0, TResolveValue>>
116
+ type AddMethodToPluginMethods<
117
+ TMethods extends ErrorPluginMethods,
118
+ TKey extends string,
119
+ TMethod extends ErrorPluginAnyMethodFn,
120
+ > = TMethods & Record<TKey, TMethod>
121
+ type PluginOutputProps<TProps extends ErrorPluginProps> = {
122
+ [TKey in keyof TProps]: TProps[TKey] extends ErrorPluginPropOptions<any, any, any, infer TResolveValue>
123
+ ? TResolveValue
124
+ : never
125
+ }
126
+ export type ErrorPluginsMap = {
127
+ props: Record<string, { init: unknown; output: unknown; resolve: unknown }>
128
+ methods: Record<string, ErrorMethodRecord>
129
+ }
130
+ export type IsEmptyObject<T> = keyof T extends never ? true : false
131
+ export type ErrorInputBase = {
132
+ cause?: unknown
133
+ }
134
+ type ErrorInputPluginProps<TPluginsMap extends ErrorPluginsMap> = {
135
+ [TKey in keyof TPluginsMap['props'] as IsOnlyUndefined<TPluginsMap['props'][TKey]['init']> extends true
136
+ ? never
137
+ : TKey]?: TPluginsMap['props'][TKey]['init']
138
+ }
139
+ export type ErrorInput<TPluginsMap extends ErrorPluginsMap> =
140
+ IsEmptyObject<TPluginsMap['props']> extends true
141
+ ? ErrorInputBase
142
+ : ErrorInputBase & ErrorInputPluginProps<TPluginsMap>
26
143
 
27
- interface Error0GeneralProps {
28
- message: Error0Input['message']
29
- tag: Error0Input['tag']
30
- code: Error0Input['code']
31
- httpStatus: number | undefined
32
- expected: boolean | undefined
33
- clientMessage: Error0Input['clientMessage']
34
- anyMessage: string | undefined
35
- cause: Error0Input['cause']
36
- stack: Error['stack']
37
- meta: Meta0.ValueType
38
- zodError?: ZodError
39
- axiosError?: AxiosError
144
+ type ErrorResolvedProps<TPluginsMap extends ErrorPluginsMap> = {
145
+ [TKey in keyof TPluginsMap['props']]: TPluginsMap['props'][TKey]['resolve']
146
+ }
147
+ type ErrorOwnProps<TPluginsMap extends ErrorPluginsMap> = {
148
+ [TKey in keyof TPluginsMap['props']]: TPluginsMap['props'][TKey]['output'] | undefined
149
+ }
150
+ type ErrorOwnMethods<TPluginsMap extends ErrorPluginsMap> = {
151
+ own: {
152
+ (): ErrorOwnProps<TPluginsMap>
153
+ <TKey extends keyof TPluginsMap['props'] & string>(key: TKey): ErrorOwnProps<TPluginsMap>[TKey]
154
+ }
155
+ flow: <TKey extends keyof TPluginsMap['props'] & string>(key: TKey) => Array<ErrorOwnProps<TPluginsMap>[TKey]>
156
+ }
157
+ type ErrorResolveMethods<TPluginsMap extends ErrorPluginsMap> = {
158
+ resolve: () => ErrorResolvedProps<TPluginsMap>
159
+ }
160
+ type BindInstanceMethod<TMethod> = TMethod extends {
161
+ (error: any, ...args: infer TArgs1): infer TOutput1
162
+ (error: any, ...args: infer TArgs2): infer TOutput2
163
+ (error: any, ...args: infer TArgs3): infer TOutput3
164
+ (error: any, ...args: infer TArgs4): infer TOutput4
165
+ }
166
+ ? {
167
+ (...args: TArgs1): TOutput1
168
+ (...args: TArgs2): TOutput2
169
+ (...args: TArgs3): TOutput3
170
+ (...args: TArgs4): TOutput4
171
+ }
172
+ : TMethod extends {
173
+ (error: any, ...args: infer TArgs1): infer TOutput1
174
+ (error: any, ...args: infer TArgs2): infer TOutput2
175
+ (error: any, ...args: infer TArgs3): infer TOutput3
176
+ }
177
+ ? {
178
+ (...args: TArgs1): TOutput1
179
+ (...args: TArgs2): TOutput2
180
+ (...args: TArgs3): TOutput3
181
+ }
182
+ : TMethod extends {
183
+ (error: any, ...args: infer TArgs1): infer TOutput1
184
+ (error: any, ...args: infer TArgs2): infer TOutput2
185
+ }
186
+ ? {
187
+ (...args: TArgs1): TOutput1
188
+ (...args: TArgs2): TOutput2
189
+ }
190
+ : TMethod extends (error: any, ...args: infer TArgs) => infer TOutput
191
+ ? (...args: TArgs) => TOutput
192
+ : never
193
+ type BindStaticMethod<TMethod> = TMethod extends {
194
+ (error: any, ...args: infer TArgs1): infer TOutput1
195
+ (error: any, ...args: infer TArgs2): infer TOutput2
196
+ (error: any, ...args: infer TArgs3): infer TOutput3
197
+ (error: any, ...args: infer TArgs4): infer TOutput4
198
+ }
199
+ ? {
200
+ (error: unknown, ...args: TArgs1): TOutput1
201
+ (error: unknown, ...args: TArgs2): TOutput2
202
+ (error: unknown, ...args: TArgs3): TOutput3
203
+ (error: unknown, ...args: TArgs4): TOutput4
204
+ }
205
+ : TMethod extends {
206
+ (error: any, ...args: infer TArgs1): infer TOutput1
207
+ (error: any, ...args: infer TArgs2): infer TOutput2
208
+ (error: any, ...args: infer TArgs3): infer TOutput3
209
+ }
210
+ ? {
211
+ (error: unknown, ...args: TArgs1): TOutput1
212
+ (error: unknown, ...args: TArgs2): TOutput2
213
+ (error: unknown, ...args: TArgs3): TOutput3
214
+ }
215
+ : TMethod extends {
216
+ (error: any, ...args: infer TArgs1): infer TOutput1
217
+ (error: any, ...args: infer TArgs2): infer TOutput2
218
+ }
219
+ ? {
220
+ (error: unknown, ...args: TArgs1): TOutput1
221
+ (error: unknown, ...args: TArgs2): TOutput2
222
+ }
223
+ : TMethod extends (error: any, ...args: infer TArgs) => infer TOutput
224
+ ? (error: unknown, ...args: TArgs) => TOutput
225
+ : never
226
+ type ErrorMethods<TPluginsMap extends ErrorPluginsMap> = {
227
+ [TKey in keyof TPluginsMap['methods']]: BindInstanceMethod<TPluginsMap['methods'][TKey]['fn']>
40
228
  }
229
+ export type ErrorResolved<TPluginsMap extends ErrorPluginsMap> = ErrorResolvedProps<TPluginsMap> &
230
+ ErrorMethods<TPluginsMap>
41
231
 
42
- type HttpStatusCodeString = keyof typeof HttpStatusCode
43
- type Error0Cause = Error | Error0 | unknown
44
- type ExpectedFn = (error: Error0GeneralProps) => boolean | undefined
232
+ type ErrorStaticMethods<TPluginsMap extends ErrorPluginsMap> = {
233
+ [TKey in keyof TPluginsMap['methods']]: BindStaticMethod<TPluginsMap['methods'][TKey]['fn']>
234
+ }
45
235
 
46
- const isFilled = <T>(value: T): value is NonNullable<T> => value !== null && value !== undefined && value !== ''
236
+ type EmptyPluginsMap = {
237
+ props: Record<never, { init: never; output: never; resolve: never }>
238
+ methods: Record<never, ErrorMethodRecord>
239
+ }
47
240
 
48
- export class Error0 extends Error {
49
- public readonly __I_AM_ERROR_0: true = true
50
-
51
- public readonly tag?: Error0GeneralProps['tag']
52
- public readonly code?: Error0GeneralProps['code']
53
- public readonly httpStatus?: Error0GeneralProps['httpStatus']
54
- public readonly expected?: Error0GeneralProps['expected']
55
- public readonly clientMessage?: Error0GeneralProps['clientMessage']
56
- public readonly anyMessage?: Error0GeneralProps['anyMessage']
57
- public override readonly cause?: Error0GeneralProps['cause']
58
- public readonly meta?: Meta0.Meta0OrValueTypeNullish
59
- public readonly zodError?: Error0GeneralProps['zodError']
60
- public readonly axiosError?: Error0GeneralProps['axiosError']
61
-
62
- static defaultMessage = 'Unknown error'
63
- static defaultCode?: Error0GeneralProps['code']
64
- static defaultHttpStatus?: Error0GeneralProps['httpStatus']
65
- static defaultExpected?: Error0GeneralProps['expected']
66
- static defaultClientMessage?: Error0GeneralProps['clientMessage']
67
- static defaultMeta?: Meta0.Meta0OrValueTypeNullish
68
-
69
- public readonly propsOriginal: Error0GeneralProps
70
-
71
- constructor(message: string)
72
- constructor(input: Error0Input)
73
- constructor(message: string, input: Error0Input)
74
- constructor(error: Error)
75
- constructor(error: Error, input: Error0Input)
76
- constructor(value: unknown)
77
- constructor(value: unknown, input: Error0Input)
78
- constructor(...args: unknown[]) {
79
- const input: Partial<Error0Input> = {}
80
- if (args[0] instanceof Error) {
81
- input.cause = args[0]
82
- } else if (typeof args[0] === 'object' && args[0] !== null) {
83
- Object.assign(input, args[0])
84
- } else if (typeof args[0] === 'string') {
85
- input.message = args[0]
86
- }
87
- if (typeof args[1] === 'object' && args[1] !== null) {
88
- Object.assign(input, args[1])
89
- }
90
- const safeInput = Error0._safeParseInput(input)
91
-
92
- const message = safeInput.message || Error0.defaultMessage
93
- super(message)
94
- Object.setPrototypeOf(this, (this.constructor as typeof Error0).prototype)
95
- this.name = 'Error0'
241
+ type ErrorPluginResolved = {
242
+ props: Record<string, ErrorPluginPropOptions<unknown>>
243
+ methods: Record<string, ErrorPluginMethodFn<unknown>>
244
+ adapt: Array<ErrorPluginAdaptFn<Error0, Record<string, unknown>>>
245
+ stack?: ErrorPluginStack
246
+ cause?: ErrorPluginCause
247
+ message?: ErrorPluginMessage
248
+ propKeys: string[]
249
+ propEntries: Array<[string, ErrorPluginPropOptions<unknown>]>
250
+ methodEntries: Array<[string, ErrorPluginMethodFn<unknown>]>
251
+ }
252
+ const RESERVED_STACK_PROP_ERROR = 'Error0: "stack" is a reserved prop key. Use .stack(...) plugin API instead'
253
+ const RESERVED_MESSAGE_PROP_ERROR = 'Error0: "message" is a reserved prop key. Use .message(...) plugin API instead'
96
254
 
97
- this.propsOriginal = (this.constructor as typeof Error0)._getSelfGeneralProps({
98
- error0Input: safeInput,
99
- message,
100
- stack: safeInput.stack || this.stack,
101
- })
102
- const causesProps = (this.constructor as typeof Error0)._getCausesPropsFromError0Props(
103
- this.propsOriginal,
104
- (this.constructor as typeof Error0).defaultMaxLevel,
105
- )
106
- const propsFloated = (this.constructor as typeof Error0)._getSelfPropsFloated(causesProps)
107
- this.tag = propsFloated.tag
108
- this.code = propsFloated.code
109
- this.httpStatus = propsFloated.httpStatus
110
- this.expected = propsFloated.expected
111
- this.clientMessage = propsFloated.clientMessage
112
- this.cause = propsFloated.cause
113
- this.stack = propsFloated.stack
114
- this.meta = propsFloated.meta
115
- this.zodError = propsFloated.zodError
116
- this.axiosError = propsFloated.axiosError
117
- }
118
-
119
- // settings
120
-
121
- static defaultMaxLevel = 10
122
-
123
- // props
124
-
125
- public static _safeParseInput(error0Input: Record<string, unknown>): Error0Input {
126
- const result: Error0Input = {}
127
- result.message = typeof error0Input.message === 'string' ? error0Input.message : undefined
128
- result.tag = typeof error0Input.tag === 'string' ? error0Input.tag : undefined
129
- result.code = typeof error0Input.code === 'string' ? error0Input.code : undefined
130
- result.httpStatus =
131
- typeof error0Input.httpStatus === 'number' || typeof error0Input.httpStatus === 'string'
132
- ? (error0Input.httpStatus as never)
133
- : undefined
134
- result.expected =
135
- typeof error0Input.expected === 'function' || typeof error0Input.expected === 'boolean'
136
- ? (error0Input.expected as never)
137
- : undefined
138
- result.clientMessage = typeof error0Input.clientMessage === 'string' ? error0Input.clientMessage : undefined
139
- result.cause = error0Input.cause
140
- result.stack = typeof error0Input.stack === 'string' ? error0Input.stack : undefined
141
- // result.meta0 =
142
- // error0Input.meta0 instanceof Meta0 ? error0Input.meta0 : undefined
143
- // result.meta =
144
- // typeof error0Input.meta === "object" && error0Input.meta !== null
145
- // ? error0Input.meta
146
- // : undefined
147
- result.meta =
148
- error0Input.meta instanceof Meta0
149
- ? error0Input.meta
150
- : typeof error0Input.meta === 'object' && error0Input.meta !== null
151
- ? (error0Input.meta as Meta0.ValueType)
152
- : undefined
153
- result.zodError = error0Input.zodError instanceof ZodError ? error0Input.zodError : undefined
154
- result.axiosError = isAxiosError(error0Input.axiosError) ? error0Input.axiosError : undefined
155
- return result
156
- }
157
-
158
- public static _getSelfGeneralProps({
159
- error0Input,
160
- message,
161
- stack,
162
- }: {
163
- error0Input: Error0Input
164
- message: string
165
- stack: Error0GeneralProps['stack']
166
- }): Error0GeneralProps {
167
- // const meta = Meta0.merge(error0Input.meta0, error0Input.meta).value
168
- const meta0 = Meta0.extend(error0Input.meta, this.defaultMeta)
169
- const meta = meta0.getValue()
170
- const finalTag = meta0.getFinalTag(error0Input.tag)
171
- const clientMessage = error0Input.clientMessage || this.defaultClientMessage
172
- const result: Error0GeneralProps = {
173
- message: error0Input.message || this.defaultMessage,
174
- tag: finalTag,
175
- code: error0Input.code || meta.code || this.defaultCode,
176
- httpStatus:
177
- typeof error0Input.httpStatus === 'number'
178
- ? error0Input.httpStatus
179
- : error0Input.httpStatus &&
180
- typeof error0Input.httpStatus === 'string' &&
181
- error0Input.httpStatus in HttpStatusCode
182
- ? HttpStatusCode[error0Input.httpStatus]
183
- : meta.httpStatus || this.defaultHttpStatus,
184
- expected: undefined,
185
- clientMessage,
186
- anyMessage: clientMessage || message,
187
- cause: error0Input.cause,
188
- stack: undefined,
189
- meta,
190
- zodError: error0Input.zodError,
191
- axiosError: error0Input.axiosError,
192
- }
193
- result.expected = this._normalizeSelfExpected(
194
- result,
195
- typeof error0Input.expected === 'boolean' || typeof error0Input.expected === 'function'
196
- ? error0Input.expected
197
- : meta.expected || this.defaultExpected,
198
- )
199
- result.stack = this._removeConstructorStackPart(stack)
200
- return result
201
- }
202
-
203
- public static _getSelfPropsFloated(causesProps: Error0GeneralProps[]): Error0GeneralProps {
204
- const cause = this._getClosestPropValue(causesProps, 'cause')
205
- const stack = this._mergeStack(causesProps[1]?.stack, causesProps[0]?.stack)
206
- const closestTag = this._getClosestPropValue(causesProps, 'tag')
207
- const meta = this._getMergedMetaValue(causesProps)
208
- const tag = Meta0.getFinalTag(meta, closestTag)
209
- const propsFloated: Error0GeneralProps = {
210
- message: this._getClosestPropValue(causesProps, 'message'),
211
- tag,
212
- code: this._getClosestPropValue(causesProps, 'code'),
213
- httpStatus: this._getClosestPropValue(causesProps, 'httpStatus'),
214
- expected: this._isExpected(causesProps),
215
- clientMessage: this._getClosestPropValue(causesProps, 'clientMessage'),
216
- cause,
217
- stack,
218
- anyMessage: causesProps[0].anyMessage,
219
- meta,
220
- zodError: this._getClosestPropValue(causesProps, 'zodError'),
221
- axiosError: this._getClosestPropValue(causesProps, 'axiosError'),
222
- }
223
- return propsFloated
224
- }
225
-
226
- // sepcial
227
-
228
- public static _getExtraError0PropsByZodError(zodError: ZodError): Partial<Error0GeneralProps> {
229
- return {
230
- message: `Zod Validation Error: ${zodError.message}`,
231
- }
255
+ type PluginPropsMapOf<TPlugin extends ErrorPlugin> = {
256
+ [TKey in keyof NonNullable<TPlugin['props']>]: NonNullable<TPlugin['props']>[TKey] extends ErrorPluginPropOptions<
257
+ any,
258
+ infer TOutputValue,
259
+ any,
260
+ infer TResolveValue
261
+ >
262
+ ? {
263
+ init: InferPluginPropInput<NonNullable<TPlugin['props']>[TKey]>
264
+ output: TOutputValue
265
+ resolve: TResolveValue
266
+ }
267
+ : never
268
+ }
269
+ type PluginMethodsMapOf<TPlugin extends ErrorPlugin> = {
270
+ [TKey in keyof NonNullable<TPlugin['methods']>]: {
271
+ fn: NonNullable<TPlugin['methods']>[TKey] extends ErrorPluginAnyMethodFn
272
+ ? NonNullable<TPlugin['methods']>[TKey]
273
+ : never
232
274
  }
275
+ }
276
+ type ErrorPluginsMapOfPlugin<TPlugin extends ErrorPlugin> = {
277
+ props: PluginPropsMapOf<TPlugin>
278
+ methods: PluginMethodsMapOf<TPlugin>
279
+ }
280
+ type ExtendErrorPluginsMap<TMap extends ErrorPluginsMap, TPlugin extends ErrorPlugin> = {
281
+ props: TMap['props'] & ErrorPluginsMapOfPlugin<TPlugin>['props']
282
+ methods: TMap['methods'] & ErrorPluginsMapOfPlugin<TPlugin>['methods']
283
+ }
284
+ type ExtendErrorPluginsMapWithProp<
285
+ TMap extends ErrorPluginsMap,
286
+ TKey extends string,
287
+ TInputValue,
288
+ TOutputValue,
289
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
290
+ > = ExtendErrorPluginsMap<
291
+ TMap,
292
+ ErrorPlugin<Record<TKey, ErrorPluginPropOptions<TInputValue, TOutputValue, Error0, TResolveValue>>>
293
+ >
294
+ type ExtendErrorPluginsMapWithMethod<
295
+ TMap extends ErrorPluginsMap,
296
+ TKey extends string,
297
+ TMethod extends ErrorPluginAnyMethodFn,
298
+ > = ExtendErrorPluginsMap<TMap, ErrorPlugin<Record<never, never>, Record<TKey, TMethod>>>
233
299
 
234
- public static _getExtraError0PropsByAxiosError(axiosError: AxiosError): Partial<Error0GeneralProps> {
235
- return {
236
- message: 'Axios Error',
237
- meta: {
238
- axiosData: (() => {
239
- try {
240
- return JSON.stringify(axiosError.response?.data)
241
- } catch {
242
- return undefined
243
- }
244
- })(),
245
- axiosStatus: axiosError.response?.status,
246
- },
247
- }
248
- }
249
-
250
- public static _assignError0Props(
251
- error0Props: Error0GeneralProps,
252
- extraError0Props: Partial<Error0GeneralProps>,
253
- ): void {
254
- const metaValue = Meta0.mergeValues(error0Props.meta, extraError0Props.meta)
255
- Object.assign(error0Props, extraError0Props, { meta: metaValue })
300
+ type PluginsMapOf<TClass> = TClass extends { __pluginsMap?: infer TPluginsMap }
301
+ ? TPluginsMap extends ErrorPluginsMap
302
+ ? TPluginsMap
303
+ : EmptyPluginsMap
304
+ : EmptyPluginsMap
305
+ type PluginsMapOfInstance<TInstance> = TInstance extends { __pluginsMap?: infer TPluginsMap }
306
+ ? TPluginsMap extends ErrorPluginsMap
307
+ ? TPluginsMap
308
+ : EmptyPluginsMap
309
+ : EmptyPluginsMap
310
+
311
+ type PluginsMapFromParts<
312
+ TProps extends ErrorPluginProps,
313
+ TMethods extends ErrorPluginMethods,
314
+ > = ErrorPluginsMapOfPlugin<ErrorPlugin<TProps, TMethods>>
315
+ type ErrorInstanceOfMap<TMap extends ErrorPluginsMap> = Error0 &
316
+ ErrorResolved<TMap> &
317
+ ErrorOwnMethods<TMap> &
318
+ ErrorResolveMethods<TMap> & { readonly __pluginsMap?: TMap }
319
+ type BuilderError0<TProps extends ErrorPluginProps, TMethods extends ErrorPluginMethods> = Error0 &
320
+ ErrorResolved<PluginsMapFromParts<TProps, TMethods>> &
321
+ ErrorOwnMethods<PluginsMapFromParts<TProps, TMethods>> &
322
+ ErrorResolveMethods<PluginsMapFromParts<TProps, TMethods>> & {
323
+ readonly __pluginsMap?: PluginsMapFromParts<TProps, TMethods>
256
324
  }
257
325
 
258
- // expected
326
+ type PluginOfBuilder<TBuilder> =
327
+ TBuilder extends PluginError0<infer TProps, infer TMethods> ? ErrorPlugin<TProps, TMethods> : never
328
+
329
+ export class PluginError0<
330
+ TProps extends ErrorPluginProps = Record<never, never>,
331
+ TMethods extends ErrorPluginMethods = Record<never, never>,
332
+ > {
333
+ private readonly _plugin: ErrorPlugin<ErrorPluginProps, ErrorPluginMethods>
334
+
335
+ readonly Infer = undefined as unknown as {
336
+ props: TProps
337
+ methods: TMethods
338
+ }
259
339
 
260
- public static _normalizeSelfExpected(
261
- error0Props: Error0GeneralProps,
262
- expectedProvided: Error0Input['expected'],
263
- ): boolean | undefined {
264
- if (typeof expectedProvided === 'function') {
265
- return expectedProvided(error0Props)
340
+ constructor(plugin?: ErrorPlugin<ErrorPluginProps, ErrorPluginMethods>) {
341
+ this._plugin = {
342
+ props: { ...(plugin?.props ?? {}) },
343
+ methods: { ...(plugin?.methods ?? {}) },
344
+ adapt: [...(plugin?.adapt ?? [])],
345
+ stack: plugin?.stack,
346
+ cause: plugin?.cause,
347
+ message: plugin?.message,
266
348
  }
267
- return expectedProvided
268
349
  }
269
350
 
270
- public static _isExpected(causesProps: Error0GeneralProps[]): boolean {
271
- let hasExpectedTrue = false
272
- for (const causeProps of causesProps) {
273
- if (causeProps.expected === false) {
274
- return false
351
+ prop<
352
+ TKey extends string,
353
+ TInputValue = undefined,
354
+ TOutputValue = unknown,
355
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
356
+ >(
357
+ key: TKey,
358
+ value: ErrorPluginPropOptions<TInputValue, TOutputValue, BuilderError0<TProps, TMethods>, TResolveValue>,
359
+ ): PluginError0<AddPropToPluginProps<TProps, TKey, TInputValue, TOutputValue, TResolveValue>, TMethods> {
360
+ return this.use('prop', key, value)
361
+ }
362
+
363
+ method<TKey extends string, TMethod extends (error: BuilderError0<TProps, TMethods>, ...args: any[]) => any>(
364
+ key: TKey,
365
+ value: TMethod,
366
+ ): PluginError0<TProps, AddMethodToPluginMethods<TMethods, TKey, TMethod>> {
367
+ return this.use('method', key, value)
368
+ }
369
+
370
+ adapt(
371
+ value: ErrorPluginAdaptFn<BuilderError0<TProps, TMethods>, PluginOutputProps<TProps>>,
372
+ ): PluginError0<TProps, TMethods> {
373
+ return this.use('adapt', value)
374
+ }
375
+
376
+ stack(value: ErrorPluginStack<BuilderError0<TProps, TMethods>>): PluginError0<TProps, TMethods> {
377
+ return this.use('stack', value)
378
+ }
379
+
380
+ cause(value: ErrorPluginCause<BuilderError0<TProps, TMethods>>): PluginError0<TProps, TMethods> {
381
+ return this.use('cause', value)
382
+ }
383
+
384
+ message(value: ErrorPluginMessage<BuilderError0<TProps, TMethods>>): PluginError0<TProps, TMethods> {
385
+ return this.use('message', value)
386
+ }
387
+
388
+ use<
389
+ TKey extends string,
390
+ TInputValue = undefined,
391
+ TOutputValue = unknown,
392
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
393
+ >(
394
+ kind: 'prop',
395
+ key: TKey,
396
+ value: ErrorPluginPropOptions<TInputValue, TOutputValue, BuilderError0<TProps, TMethods>, TResolveValue>,
397
+ ): PluginError0<AddPropToPluginProps<TProps, TKey, TInputValue, TOutputValue, TResolveValue>, TMethods>
398
+ use<TKey extends string, TMethod extends (error: BuilderError0<TProps, TMethods>, ...args: any[]) => any>(
399
+ kind: 'method',
400
+ key: TKey,
401
+ value: TMethod,
402
+ ): PluginError0<TProps, AddMethodToPluginMethods<TMethods, TKey, TMethod>>
403
+ use(
404
+ kind: 'adapt',
405
+ value: ErrorPluginAdaptFn<BuilderError0<TProps, TMethods>, PluginOutputProps<TProps>>,
406
+ ): PluginError0<TProps, TMethods>
407
+ use(kind: 'stack', value: ErrorPluginStack<BuilderError0<TProps, TMethods>>): PluginError0<TProps, TMethods>
408
+ use(kind: 'cause', value: ErrorPluginCause<BuilderError0<TProps, TMethods>>): PluginError0<TProps, TMethods>
409
+ use(kind: 'message', value: ErrorPluginMessage<BuilderError0<TProps, TMethods>>): PluginError0<TProps, TMethods>
410
+ use(
411
+ kind: 'prop' | 'method' | 'adapt' | 'stack' | 'cause' | 'message',
412
+ keyOrValue: unknown,
413
+ value?: ErrorPluginPropOptions<unknown, unknown, any> | ErrorPluginMethodFn<unknown, unknown[], any>,
414
+ ): PluginError0<any, any> {
415
+ const nextProps: ErrorPluginProps = { ...(this._plugin.props ?? {}) }
416
+ const nextMethods: ErrorPluginMethods = { ...(this._plugin.methods ?? {}) }
417
+ const nextAdapt: Array<ErrorPluginAdaptFn<Error0, Record<string, unknown>>> = [...(this._plugin.adapt ?? [])]
418
+ let nextStack: ErrorPluginStack | undefined = this._plugin.stack
419
+ let nextCause: ErrorPluginCause | undefined = this._plugin.cause
420
+ let nextMessage: ErrorPluginMessage | undefined = this._plugin.message
421
+ if (kind === 'prop') {
422
+ const key = keyOrValue as string
423
+ if (key === 'stack') {
424
+ throw new Error(RESERVED_STACK_PROP_ERROR)
425
+ }
426
+ if (key === 'message') {
427
+ throw new Error(RESERVED_MESSAGE_PROP_ERROR)
428
+ }
429
+ if (value === undefined) {
430
+ throw new Error('PluginError0.use("prop", key, value) requires value')
275
431
  }
276
- if (causeProps.expected === true) {
277
- hasExpectedTrue = true
432
+ nextProps[key] = value as ErrorPluginPropOptions<any, any>
433
+ } else if (kind === 'method') {
434
+ const key = keyOrValue as string
435
+ if (value === undefined) {
436
+ throw new Error('PluginError0.use("method", key, value) requires value')
278
437
  }
438
+ nextMethods[key] = value as ErrorPluginMethodFn<any, any[]>
439
+ } else if (kind === 'adapt') {
440
+ nextAdapt.push(keyOrValue as ErrorPluginAdaptFn<Error0, Record<string, unknown>>)
441
+ } else if (kind === 'stack') {
442
+ nextStack = keyOrValue as ErrorPluginStack
443
+ } else if (kind === 'cause') {
444
+ nextCause = keyOrValue as ErrorPluginCause
445
+ } else {
446
+ nextMessage = keyOrValue as ErrorPluginMessage
279
447
  }
280
- return hasExpectedTrue
448
+ return new PluginError0({
449
+ props: nextProps,
450
+ methods: nextMethods,
451
+ adapt: nextAdapt,
452
+ stack: nextStack,
453
+ cause: nextCause,
454
+ message: nextMessage,
455
+ })
281
456
  }
457
+ }
282
458
 
283
- // getters
459
+ const OWN_SYMBOL: unique symbol = Symbol('Error0.own')
460
+ type ErrorOwnStore = Record<string, unknown>
284
461
 
285
- public static _getPropsFromUnknown(error: unknown, defaults?: Error0Input): Error0GeneralProps {
286
- if (typeof error !== 'object' || error === null) {
287
- return {
288
- message: undefined,
289
- tag: undefined,
290
- code: undefined,
291
- httpStatus: undefined,
292
- expected: undefined,
293
- clientMessage: undefined,
294
- anyMessage: this.defaultMessage,
295
- cause: undefined,
296
- stack: undefined,
297
- zodError: undefined,
298
- axiosError: undefined,
299
- meta: {},
300
- }
301
- }
302
- const message = 'message' in error && typeof error.message === 'string' ? error.message : undefined
303
- const clientMessage =
304
- 'clientMessage' in error && typeof error.clientMessage === 'string'
305
- ? error.clientMessage
306
- : defaults?.clientMessage || undefined
307
- const result: Error0GeneralProps = {
308
- message,
309
- code: 'code' in error && typeof error.code === 'string' ? error.code : defaults?.code || undefined,
310
- clientMessage,
311
- anyMessage: clientMessage || message || this.defaultMessage,
312
- expected: undefined,
313
- stack: 'stack' in error && typeof error.stack === 'string' ? error.stack : undefined,
314
- tag: 'tag' in error && typeof error.tag === 'string' ? error.tag : defaults?.tag || undefined,
315
- cause: 'cause' in error ? error.cause : defaults?.cause || undefined,
316
- meta:
317
- 'meta' in error && typeof error.meta === 'object' && error.meta !== null
318
- ? Meta0.getValue(error.meta as Meta0.ValueType)
319
- : Meta0.getValue(defaults?.meta) || {},
320
- httpStatus:
321
- 'httpStatus' in error && typeof error.httpStatus === 'number' && error.httpStatus in HttpStatusCode
322
- ? error.httpStatus
323
- : typeof defaults?.httpStatus === 'string'
324
- ? HttpStatusCode[defaults.httpStatus]
325
- : defaults?.httpStatus,
326
- zodError:
327
- 'zodError' in error && error.zodError instanceof ZodError
328
- ? error.zodError
329
- : error instanceof ZodError
330
- ? error
331
- : defaults?.zodError,
332
- axiosError:
333
- 'axiosError' in error && isAxiosError(error.axiosError)
334
- ? error.axiosError
335
- : isAxiosError(error)
336
- ? error
337
- : defaults?.axiosError,
338
- }
339
- result.expected = this._normalizeSelfExpected(
340
- result,
341
- 'expected' in error && (typeof error.expected === 'boolean' || typeof error.expected === 'function')
342
- ? (error.expected as ExpectedFn)
343
- : defaults?.expected || undefined,
344
- )
345
- if (result.zodError) {
346
- this._assignError0Props(result, this._getExtraError0PropsByZodError(result.zodError))
347
- }
348
- if (result.axiosError) {
349
- this._assignError0Props(result, this._getExtraError0PropsByAxiosError(result.axiosError))
462
+ export type ClassError0<TPluginsMap extends ErrorPluginsMap = EmptyPluginsMap> = {
463
+ MAX_CAUSES_DEPTH: number
464
+ new (
465
+ message: string,
466
+ input?: ErrorInput<TPluginsMap>,
467
+ ): Error0 &
468
+ ErrorResolved<TPluginsMap> &
469
+ ErrorOwnMethods<TPluginsMap> &
470
+ ErrorResolveMethods<TPluginsMap> & { readonly __pluginsMap?: TPluginsMap }
471
+ new (
472
+ input: { message: string } & ErrorInput<TPluginsMap>,
473
+ ): Error0 &
474
+ ErrorResolved<TPluginsMap> &
475
+ ErrorOwnMethods<TPluginsMap> &
476
+ ErrorResolveMethods<TPluginsMap> & { readonly __pluginsMap?: TPluginsMap }
477
+ readonly __pluginsMap?: TPluginsMap
478
+ from: (
479
+ error: unknown,
480
+ ) => Error0 & ErrorResolved<TPluginsMap> & ErrorOwnMethods<TPluginsMap> & ErrorResolveMethods<TPluginsMap>
481
+ round: (
482
+ error: unknown,
483
+ isPublic?: boolean,
484
+ ) => Error0 & ErrorResolved<TPluginsMap> & ErrorOwnMethods<TPluginsMap> & ErrorResolveMethods<TPluginsMap>
485
+ causes: {
486
+ (error: unknown, instancesOnly?: false): unknown[]
487
+ (
488
+ error: unknown,
489
+ instancesOnly: true,
490
+ ): Array<Error0 & ErrorResolved<TPluginsMap> & ErrorOwnMethods<TPluginsMap> & ErrorResolveMethods<TPluginsMap>>
491
+ }
492
+ resolve: (error: unknown) => ErrorResolvedProps<TPluginsMap>
493
+ serialize: (error: unknown, isPublic?: boolean) => Record<string, unknown>
494
+ own: {
495
+ (error: object): ErrorOwnProps<TPluginsMap>
496
+ <TKey extends keyof TPluginsMap['props'] & string>(error: object, key: TKey): ErrorOwnProps<TPluginsMap>[TKey]
497
+ }
498
+ flow: <TKey extends keyof TPluginsMap['props'] & string>(
499
+ error: object,
500
+ key: TKey,
501
+ ) => Array<ErrorOwnProps<TPluginsMap>[TKey]>
502
+ // prop: <
503
+ // TKey extends string,
504
+ // TInputValue = undefined,
505
+ // TOutputValue = unknown,
506
+ // TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
507
+ // >(
508
+ // key: TKey,
509
+ // value: ErrorPluginPropOptions<TInputValue, TOutputValue, ErrorInstanceOfMap<TPluginsMap>, TResolveValue>,
510
+ // ) => ClassError0<ExtendErrorPluginsMapWithProp<TPluginsMap, TKey, TInputValue, TOutputValue, TResolveValue>>
511
+ // method: <TKey extends string, TMethod extends (error: ErrorInstanceOfMap<TPluginsMap>, ...args: any[]) => any>(
512
+ // key: TKey,
513
+ // value: TMethod,
514
+ // ) => ClassError0<ExtendErrorPluginsMapWithMethod<TPluginsMap, TKey, TMethod>>
515
+ // adapt: (
516
+ // value: ErrorPluginAdaptFn<ErrorInstanceOfMap<TPluginsMap>, ErrorResolvedProps<TPluginsMap>>,
517
+ // ) => ClassError0<TPluginsMap>
518
+ // stack: (value: ErrorPluginStack<ErrorInstanceOfMap<TPluginsMap>>) => ClassError0<TPluginsMap>
519
+ // cause: (value: ErrorPluginCause<ErrorInstanceOfMap<TPluginsMap>>) => ClassError0<TPluginsMap>
520
+ use: {
521
+ <TBuilder extends PluginError0>(
522
+ plugin: TBuilder,
523
+ ): ClassError0<ExtendErrorPluginsMap<TPluginsMap, PluginOfBuilder<TBuilder>>>
524
+ <
525
+ TKey extends string,
526
+ TInputValue = undefined,
527
+ TOutputValue = unknown,
528
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
529
+ >(
530
+ kind: 'prop',
531
+ key: TKey,
532
+ value: ErrorPluginPropOptions<TInputValue, TOutputValue, ErrorInstanceOfMap<TPluginsMap>, TResolveValue>,
533
+ ): ClassError0<ExtendErrorPluginsMapWithProp<TPluginsMap, TKey, TInputValue, TOutputValue, TResolveValue>>
534
+ <TKey extends string, TMethod extends (error: ErrorInstanceOfMap<TPluginsMap>, ...args: any[]) => any>(
535
+ kind: 'method',
536
+ key: TKey,
537
+ value: TMethod,
538
+ ): ClassError0<ExtendErrorPluginsMapWithMethod<TPluginsMap, TKey, TMethod>>
539
+ (
540
+ kind: 'adapt',
541
+ value: ErrorPluginAdaptFn<ErrorInstanceOfMap<TPluginsMap>, ErrorResolvedProps<TPluginsMap>>,
542
+ ): ClassError0<TPluginsMap>
543
+ (kind: 'stack', value: ErrorPluginStack<ErrorInstanceOfMap<TPluginsMap>>): ClassError0<TPluginsMap>
544
+ (kind: 'cause', value: ErrorPluginCause<ErrorInstanceOfMap<TPluginsMap>>): ClassError0<TPluginsMap>
545
+ (kind: 'message', value: ErrorPluginMessage<ErrorInstanceOfMap<TPluginsMap>>): ClassError0<TPluginsMap>
546
+ }
547
+ plugin: () => PluginError0
548
+ } & ErrorStaticMethods<TPluginsMap>
549
+
550
+ export class Error0 extends Error {
551
+ static readonly __pluginsMap?: EmptyPluginsMap
552
+ readonly __pluginsMap?: EmptyPluginsMap
553
+ static MAX_CAUSES_DEPTH = 99
554
+ protected static _plugins: ErrorPlugin[] = []
555
+ protected static _resolvedPlugin?: ErrorPluginResolved
556
+
557
+ private static readonly _emptyPlugin: ErrorPluginResolved = {
558
+ props: {},
559
+ methods: {},
560
+ adapt: [],
561
+ stack: undefined,
562
+ cause: undefined,
563
+ message: undefined,
564
+ propKeys: [],
565
+ propEntries: [],
566
+ methodEntries: [],
567
+ }
568
+
569
+ private static _indexResolvedPlugin(
570
+ resolved: Omit<ErrorPluginResolved, 'propKeys' | 'propEntries' | 'methodEntries'>,
571
+ ): ErrorPluginResolved {
572
+ return {
573
+ ...resolved,
574
+ propKeys: Object.keys(resolved.props),
575
+ propEntries: Object.entries(resolved.props),
576
+ methodEntries: Object.entries(resolved.methods),
350
577
  }
351
- return result
352
578
  }
353
579
 
354
- public static _getCausesPropsFromUnknown(error: unknown, maxLevel: number): Error0GeneralProps[] {
355
- if (!error) {
356
- return []
580
+ private static _applyPlugin(
581
+ resolved: Omit<ErrorPluginResolved, 'propKeys' | 'propEntries' | 'methodEntries'>,
582
+ plugin: ErrorPlugin,
583
+ ): void {
584
+ if (plugin.props && 'stack' in plugin.props) {
585
+ throw new Error(RESERVED_STACK_PROP_ERROR)
586
+ }
587
+ if (plugin.props && 'message' in plugin.props) {
588
+ throw new Error(RESERVED_MESSAGE_PROP_ERROR)
589
+ }
590
+ Object.assign(resolved.props, plugin.props ?? this._emptyPlugin.props)
591
+ Object.assign(resolved.methods, plugin.methods ?? this._emptyPlugin.methods)
592
+ resolved.adapt.push(...(plugin.adapt ?? this._emptyPlugin.adapt))
593
+ if (typeof plugin.stack !== 'undefined') {
594
+ resolved.stack = plugin.stack
357
595
  }
358
- const causeProps = this._getPropsFromUnknown(error)
359
- const causesProps: Error0GeneralProps[] = [causeProps]
360
- if (!causeProps.cause) {
361
- return causesProps
596
+ if (typeof plugin.cause !== 'undefined') {
597
+ resolved.cause = plugin.cause
362
598
  }
363
- if (maxLevel > 0) {
364
- causesProps.push(...this._getCausesPropsFromUnknown(this._getPropsFromUnknown(causeProps.cause), maxLevel - 1))
599
+ if (typeof plugin.message !== 'undefined') {
600
+ resolved.message = plugin.message
365
601
  }
366
- return causesProps
367
602
  }
368
603
 
369
- public static _getCausesPropsFromError0Props(
370
- error0Props: Error0GeneralProps,
371
- maxLevel: number,
372
- ): Error0GeneralProps[] {
373
- return [error0Props, ...this._getCausesPropsFromUnknown(error0Props.cause, maxLevel - 1)]
604
+ private static _mergeResolvedPlugin(
605
+ this: typeof Error0,
606
+ base: ErrorPluginResolved,
607
+ plugin: ErrorPlugin,
608
+ ): ErrorPluginResolved {
609
+ const merged: Omit<ErrorPluginResolved, 'propKeys' | 'propEntries' | 'methodEntries'> = {
610
+ props: { ...base.props },
611
+ methods: { ...base.methods },
612
+ adapt: [...base.adapt],
613
+ stack: base.stack,
614
+ cause: base.cause,
615
+ message: base.message,
616
+ }
617
+ this._applyPlugin(merged, plugin)
618
+ return this._indexResolvedPlugin(merged)
374
619
  }
375
620
 
376
- public static _getClosestPropValue<TPropKey extends keyof Error0GeneralProps>(
377
- causesProps: Error0GeneralProps[],
378
- propKey: TPropKey,
379
- ): NonNullable<Error0GeneralProps[TPropKey]> | undefined {
380
- for (const causeProps of causesProps) {
381
- const propValue = causeProps[propKey]
382
- if (isFilled(propValue)) {
383
- return propValue as NonNullable<Error0GeneralProps[TPropKey]>
384
- }
621
+ private static _getResolvedPlugin(this: typeof Error0): ErrorPluginResolved {
622
+ if (Object.prototype.hasOwnProperty.call(this, '_resolvedPlugin') && this._resolvedPlugin) {
623
+ return this._resolvedPlugin
385
624
  }
386
- return undefined
625
+ const resolved: ErrorPluginResolved = {
626
+ props: {},
627
+ methods: {},
628
+ adapt: [],
629
+ propKeys: [],
630
+ propEntries: [],
631
+ methodEntries: [],
632
+ }
633
+ for (const plugin of this._plugins) {
634
+ this._applyPlugin(resolved, plugin)
635
+ }
636
+ const indexed = this._indexResolvedPlugin(resolved)
637
+ Object.defineProperty(this, '_resolvedPlugin', {
638
+ value: indexed,
639
+ writable: true,
640
+ enumerable: false,
641
+ configurable: true,
642
+ })
643
+ return indexed
387
644
  }
388
645
 
389
- // private static getClosestByGetter<TResult>(
390
- // causesProps: Error0GeneralProps[],
391
- // getter: (props: Error0GeneralProps) => TResult,
392
- // ): NonNullable<TResult> | undefined {
393
- // for (const causeProps of causesProps) {
394
- // const result = getter(causeProps)
395
- // if (isFilled(result)) {
396
- // return result
397
- // }
398
- // }
399
- // return undefined
400
- // }
646
+ constructor(message: string, input?: ErrorInput<EmptyPluginsMap>)
647
+ constructor(input: { message: string } & ErrorInput<EmptyPluginsMap>)
648
+ constructor(
649
+ ...args:
650
+ | [message: string, input?: ErrorInput<EmptyPluginsMap>]
651
+ | [{ message: string } & ErrorInput<EmptyPluginsMap>]
652
+ ) {
653
+ const [first, second] = args
654
+ const input = typeof first === 'string' ? { message: first, ...(second ?? {}) } : first
655
+
656
+ super(input.message, { cause: input.cause })
657
+ this.name = 'Error0'
401
658
 
402
- public static _getFilledPropValues<TPropKey extends keyof Error0Input>(
403
- causesProps: Error0GeneralProps[],
404
- propKey: TPropKey,
405
- ): NonNullable<Error0GeneralProps[TPropKey]>[] {
406
- const values: NonNullable<Error0GeneralProps[TPropKey]>[] = []
407
- for (const causeProps of causesProps) {
408
- const propValue = causeProps[propKey]
409
- if (isFilled(propValue)) {
410
- values.push(propValue as NonNullable<Error0GeneralProps[TPropKey]>)
659
+ const ctor = this.constructor as typeof Error0
660
+ const plugin = ctor._getResolvedPlugin()
661
+ const ownStore = Object.create(null) as ErrorOwnStore
662
+ Object.defineProperty(this, OWN_SYMBOL, { value: ownStore, writable: true, enumerable: false, configurable: true })
663
+
664
+ for (const [key, prop] of plugin.propEntries) {
665
+ if (key === 'stack') {
666
+ continue
667
+ }
668
+ Object.defineProperty(this, key, {
669
+ get: () =>
670
+ prop.resolve({
671
+ own: ownStore[key],
672
+ flow: this.flow(key as never),
673
+ error: this,
674
+ }),
675
+ set: (value) => {
676
+ ownStore[key] = value
677
+ },
678
+ enumerable: true,
679
+ configurable: true,
680
+ })
681
+ if (key in input) {
682
+ const ownValue = (input as Record<string, unknown>)[key]
683
+ ownStore[key] = typeof prop.init === 'function' ? prop.init(ownValue) : ownValue
411
684
  }
412
685
  }
413
- return values
414
686
  }
415
687
 
416
- public static _getMergedMetaValue(causesProps: Error0GeneralProps[]): Meta0.ValueType {
417
- const metas = this._getFilledPropValues(causesProps, 'meta')
418
- if (metas.length === 0) {
419
- return {}
420
- } else if (metas.length === 1) {
421
- return metas[0]
422
- } else {
423
- return Meta0.mergeValues(metas[0], ...metas.slice(1))
688
+ private static _getOwnStore(object: object): ErrorOwnStore | undefined {
689
+ const record = object as Record<string | symbol, unknown>
690
+ const existing = record[OWN_SYMBOL]
691
+ if (existing && typeof existing === 'object') {
692
+ return existing as ErrorOwnStore
424
693
  }
694
+ return undefined
425
695
  }
426
696
 
427
- // stack
428
-
429
- public static _removeConstructorStackPart(stack: Error0GeneralProps['stack']): Error0GeneralProps['stack'] {
430
- if (!stack) {
431
- return stack
697
+ private static readonly isOwnProperty = (object: object, key: string): boolean => {
698
+ const ownStore = this._getOwnStore(object)
699
+ if (ownStore) {
700
+ return Object.prototype.hasOwnProperty.call(ownStore, key)
701
+ }
702
+ return !!Object.getOwnPropertyDescriptor(object, key)
703
+ }
704
+ private static _ownByKey(error: object, key: string): unknown {
705
+ const ownStore = this._getOwnStore(error)
706
+ if (ownStore) {
707
+ return ownStore[key]
432
708
  }
433
- let lines = stack.split('\n')
434
- const removeAllLinesContains = (search: string) => {
435
- lines = lines.filter((line) => !line.includes(search))
709
+ return (error as Record<string, unknown>)[key]
710
+ }
711
+ private static _flowByKey(error: object, key: string): unknown[] {
712
+ const causes = this.causes(error, true)
713
+ const values = new Array<unknown>(causes.length)
714
+ for (let i = 0; i < causes.length; i += 1) {
715
+ values[i] = this._ownByKey(causes[i], key)
436
716
  }
437
- removeAllLinesContains('at new Error0')
438
- removeAllLinesContains('at _toError0')
439
- removeAllLinesContains('at Error0.from')
440
- removeAllLinesContains('at Error0._toError0')
441
- return lines.join('\n')
717
+ return values
442
718
  }
443
719
 
444
- public static _mergeStack(
445
- prevStack: Error0GeneralProps['stack'],
446
- nextStack: Error0GeneralProps['stack'],
447
- ): Error0GeneralProps['stack'] {
448
- return [nextStack, prevStack].filter(Boolean).join('\n\n') || undefined
720
+ static own<TThis extends typeof Error0>(this: TThis, error: unknown): ErrorOwnProps<PluginsMapOf<TThis>>
721
+ static own<TThis extends typeof Error0, TKey extends keyof PluginsMapOf<TThis>['props'] & string>(
722
+ this: TThis,
723
+ error: unknown,
724
+ key: TKey,
725
+ ): ErrorOwnProps<PluginsMapOf<TThis>>[TKey]
726
+ static own(error: unknown, key?: string): unknown {
727
+ const error0 = this.from(error)
728
+ if (key === undefined) {
729
+ const ownValues: Record<string, unknown> = {}
730
+ const plugin = this._getResolvedPlugin()
731
+ for (const ownKey of plugin.propKeys) {
732
+ ownValues[ownKey] = this._ownByKey(error0, ownKey)
733
+ }
734
+ return ownValues
735
+ }
736
+ return this._ownByKey(error0, key)
737
+ }
738
+ own<TThis extends Error0>(this: TThis): ErrorOwnProps<PluginsMapOfInstance<TThis>>
739
+ own<TThis extends Error0, TKey extends keyof PluginsMapOfInstance<TThis>['props'] & string>(
740
+ this: TThis,
741
+ key: TKey,
742
+ ): ErrorOwnProps<PluginsMapOfInstance<TThis>>[TKey]
743
+ own(key?: string): unknown {
744
+ const ctor = this.constructor as typeof Error0
745
+ if (key === undefined) {
746
+ return ctor.own(this)
747
+ }
748
+ return ctor._ownByKey(this, key)
449
749
  }
450
750
 
451
- // transformations
751
+ static flow<TThis extends typeof Error0, TKey extends keyof PluginsMapOf<TThis>['props'] & string>(
752
+ this: TThis,
753
+ error: unknown,
754
+ key: TKey,
755
+ ): Array<ErrorOwnProps<PluginsMapOf<TThis>>[TKey]>
756
+ static flow(error: unknown, key: string): unknown[] {
757
+ const error0 = this.from(error)
758
+ return this._flowByKey(error0, key)
759
+ }
760
+ flow<TThis extends Error0, TKey extends keyof PluginsMapOfInstance<TThis>['props'] & string>(
761
+ this: TThis,
762
+ key: TKey,
763
+ ): Array<ErrorOwnProps<PluginsMapOfInstance<TThis>>[TKey]>
764
+ flow(key: string): unknown[] {
765
+ const ctor = this.constructor as typeof Error0
766
+ return ctor._flowByKey(this, key)
767
+ }
452
768
 
453
- static isError0(error: unknown): error is Error0 {
454
- return error instanceof Error0
769
+ static _resolveByKey(error: Error0, key: string, plugin: ErrorPluginResolved): unknown {
770
+ try {
771
+ const options = {
772
+ get own() {
773
+ return error.own(key as never)
774
+ },
775
+ get flow() {
776
+ return error.flow(key as never)
777
+ },
778
+ error,
779
+ }
780
+ const prop = plugin.props[key]
781
+ const resolver = prop.resolve
782
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
783
+ if (!resolver) {
784
+ return (error as any)[key]
785
+ }
786
+ return resolver(options as ErrorPluginPropOptionsResolveOptions<any, any>)
787
+ } catch {
788
+ // eslint-disable-next-line no-console
789
+ console.error(`Error0: failed to resolve property ${key}`, error)
790
+ return undefined
791
+ }
455
792
  }
456
793
 
457
- static isLikelyError0(error: unknown): error is Error0 {
458
- if (error instanceof Error0) {
459
- return true
794
+ static resolve<TThis extends typeof Error0>(this: TThis, error: unknown): ErrorResolvedProps<PluginsMapOf<TThis>>
795
+ static resolve(error: unknown): Record<string, unknown>
796
+ static resolve(error: unknown): Record<string, unknown> {
797
+ const error0 = this.from(error)
798
+ const resolved: Record<string, unknown> = {}
799
+ const plugin = this._getResolvedPlugin()
800
+ for (const key of plugin.propKeys) {
801
+ resolved[key] = this._resolveByKey(error0, key, plugin)
460
802
  }
803
+ return resolved
804
+ }
805
+ resolve<TThis extends Error0>(this: TThis): ErrorResolvedProps<PluginsMapOfInstance<TThis>>
806
+ resolve(): Record<string, unknown> {
807
+ const ctor = this.constructor as typeof Error0
808
+ return ctor.resolve(this)
809
+ }
461
810
 
462
- if (typeof error === 'object' && error !== null) {
463
- if ('__I_AM_ERROR_0' in error && error.__I_AM_ERROR_0 === true) {
464
- return true
811
+ static causes(error: unknown, instancesOnly?: false): unknown[]
812
+ static causes<T extends typeof Error0>(this: T, error: unknown, instancesOnly: true): Array<InstanceType<T>>
813
+ static causes(error: unknown, instancesOnly?: boolean): unknown[] {
814
+ const causes: unknown[] = []
815
+ let current: unknown = error
816
+ const seen = new Set<unknown>()
817
+ let depth = 0
818
+ while (depth < this.MAX_CAUSES_DEPTH) {
819
+ if (seen.has(current)) {
820
+ break
821
+ }
822
+ seen.add(current)
823
+ if (!instancesOnly || this.is(current)) {
824
+ causes.push(current)
825
+ }
826
+ if (!current || typeof current !== 'object') {
827
+ break
465
828
  }
829
+ current = (current as { cause?: unknown }).cause
830
+ depth += 1
466
831
  }
832
+ return causes
833
+ }
834
+ causes<TThis extends Error0>(this: TThis, instancesOnly?: false): [TThis, ...unknown[]]
835
+ causes<TThis extends Error0>(this: TThis, instancesOnly: true): [TThis, ...TThis[]]
836
+ causes(instancesOnly?: boolean): unknown[] {
837
+ const ctor = this.constructor as typeof Error0
838
+ if (instancesOnly) {
839
+ return ctor.causes(this, true)
840
+ }
841
+ return ctor.causes(this)
842
+ }
467
843
 
468
- return false
844
+ static is<T extends typeof Error0>(this: T, error: unknown): error is InstanceType<T> {
845
+ return error instanceof this
469
846
  }
470
847
 
471
- public static _toError0(error: unknown, inputOverride: Error0Input = {}): Error0 {
472
- if (error instanceof Error0) {
848
+ static isSerialized(error: unknown): error is Record<string, unknown> {
849
+ return !this.is(error) && typeof error === 'object' && error !== null && 'name' in error && error.name === 'Error0'
850
+ }
851
+
852
+ static from(error: unknown): Error0 {
853
+ if (this.is(error)) {
473
854
  return error
474
855
  }
856
+ if (this.isSerialized(error)) {
857
+ return this._fromSerialized(error)
858
+ }
859
+ return this._fromNonError0(error)
860
+ }
861
+
862
+ static round(error: unknown, isPublic = false): Error0 {
863
+ return this.from(this.serialize(error, isPublic))
864
+ }
475
865
 
476
- if (typeof error === 'string') {
477
- return new Error0(error, inputOverride)
866
+ private static _applyAdapt(error: Error0): Error0 {
867
+ const plugin = this._getResolvedPlugin()
868
+ for (const adapt of plugin.adapt) {
869
+ const adapted = adapt(error as any)
870
+ if (adapted && typeof adapted === 'object') {
871
+ Object.assign(error as unknown as Record<string, unknown>, adapted)
872
+ }
478
873
  }
874
+ return error
875
+ }
479
876
 
877
+ private static _fromSerialized(error: unknown): Error0 {
878
+ const message = this._extractMessage(error)
480
879
  if (typeof error !== 'object' || error === null) {
481
- return new Error0({
482
- message: this.defaultMessage,
483
- ...inputOverride,
880
+ return this._applyAdapt(new this(message, { cause: error }))
881
+ }
882
+ const errorRecord = error as Record<string, unknown>
883
+ const recreated = new this(message)
884
+ const plugin = this._getResolvedPlugin()
885
+ for (const [key, prop] of plugin.propEntries) {
886
+ if (prop.deserialize === false) {
887
+ continue
888
+ }
889
+ if (!(key in errorRecord)) {
890
+ continue
891
+ }
892
+ try {
893
+ const value = prop.deserialize({ value: errorRecord[key], record: errorRecord })
894
+ ;(recreated as unknown as Record<string, unknown>)[key] = value
895
+ } catch {
896
+ // eslint-disable-next-line no-console
897
+ console.error(`Error0: failed to deserialize property ${key}`, errorRecord)
898
+ }
899
+ }
900
+ // we do not serialize causes
901
+ // ;(recreated as unknown as { cause?: unknown }).cause = errorRecord.cause
902
+ if ('stack' in errorRecord) {
903
+ try {
904
+ if (typeof errorRecord.stack === 'string') {
905
+ recreated.stack = errorRecord.stack
906
+ }
907
+ } catch {
908
+ // eslint-disable-next-line no-console
909
+ console.error('Error0: failed to deserialize stack', errorRecord)
910
+ }
911
+ }
912
+ const causePlugin = plugin.cause
913
+ if (causePlugin?.serialize && 'cause' in errorRecord) {
914
+ try {
915
+ if (this.isSerialized(errorRecord.cause)) {
916
+ ;(recreated as { cause?: unknown }).cause = this._fromSerialized(errorRecord.cause)
917
+ } else {
918
+ ;(recreated as { cause?: unknown }).cause = errorRecord.cause
919
+ }
920
+ } catch {
921
+ // eslint-disable-next-line no-console
922
+ console.error('Error0: failed to deserialize cause', errorRecord)
923
+ }
924
+ }
925
+ return recreated
926
+ }
927
+
928
+ private static _fromNonError0(error: unknown): Error0 {
929
+ const message = this._extractMessage(error)
930
+ return this._applyAdapt(new this(message, { cause: error }))
931
+ }
932
+
933
+ private static _extractMessage(error: unknown): string {
934
+ return (
935
+ (typeof error === 'string'
936
+ ? error
937
+ : typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string'
938
+ ? error.message
939
+ : undefined) || 'Unknown error'
940
+ )
941
+ }
942
+
943
+ private static _useWithPlugin(
944
+ this: typeof Error0,
945
+ plugin: ErrorPlugin<ErrorPluginProps, ErrorPluginMethods>,
946
+ ): ClassError0 {
947
+ const Base = this as unknown as typeof Error0
948
+ const Error0Extended = class Error0 extends Base {}
949
+ ;(Error0Extended as typeof Error0)._plugins = [...Base._plugins, plugin]
950
+ const resolved = this._mergeResolvedPlugin(Base._getResolvedPlugin(), plugin)
951
+ ;(Error0Extended as typeof Error0)._resolvedPlugin = resolved
952
+ for (const [key, method] of resolved.methodEntries) {
953
+ Object.defineProperty((Error0Extended as typeof Error0).prototype, key, {
954
+ value: function (...args: unknown[]) {
955
+ return method(this as Error0, ...args)
956
+ },
957
+ writable: true,
958
+ enumerable: true,
959
+ configurable: true,
960
+ })
961
+ Object.defineProperty(Error0Extended, key, {
962
+ value: function (error: unknown, ...args: unknown[]) {
963
+ return method(this.from(error), ...args)
964
+ },
965
+ writable: true,
966
+ enumerable: true,
967
+ configurable: true,
484
968
  })
485
969
  }
486
970
 
487
- const inputFromData = get(error, 'data')
488
- if (inputFromData) {
489
- if (Error0.isLikelyError0(inputFromData)) {
490
- return this._toError0(inputFromData, inputOverride)
491
- }
971
+ return Error0Extended as unknown as ClassError0
972
+ }
973
+
974
+ private static _pluginFromBuilder(plugin: PluginError0): ErrorPlugin<ErrorPluginProps, ErrorPluginMethods> {
975
+ const pluginRecord = plugin as unknown as {
976
+ _plugin: ErrorPlugin<ErrorPluginProps, ErrorPluginMethods>
977
+ }
978
+ return {
979
+ props: { ...(pluginRecord._plugin.props ?? {}) },
980
+ methods: { ...(pluginRecord._plugin.methods ?? {}) },
981
+ adapt: [...(pluginRecord._plugin.adapt ?? [])],
982
+ stack: pluginRecord._plugin.stack,
983
+ cause: pluginRecord._plugin.cause,
984
+ message: pluginRecord._plugin.message,
492
985
  }
986
+ }
987
+
988
+ // static prop<
989
+ // TThis extends typeof Error0,
990
+ // TKey extends string,
991
+ // TInputValue = undefined,
992
+ // TOutputValue = unknown,
993
+ // TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
994
+ // >(
995
+ // this: TThis,
996
+ // key: TKey,
997
+ // value: ErrorPluginPropOptions<TInputValue, TOutputValue, ErrorInstanceOfMap<PluginsMapOf<TThis>>, TResolveValue>,
998
+ // ): ClassError0<ExtendErrorPluginsMapWithProp<PluginsMapOf<TThis>, TKey, TInputValue, TOutputValue, TResolveValue>> {
999
+ // return this.use('prop', key, value)
1000
+ // }
1001
+
1002
+ // static method<TThis extends typeof Error0, TKey extends string, TMethod extends (error: ErrorInstanceOfMap<PluginsMapOf<TThis>>, ...args: any[]) => any>(
1003
+ // this: TThis,
1004
+ // key: TKey,
1005
+ // value: TMethod,
1006
+ // ): ClassError0<ExtendErrorPluginsMapWithMethod<PluginsMapOf<TThis>, TKey, TMethod>> {
1007
+ // return this.use('method', key, value)
1008
+ // }
1009
+
1010
+ // static adapt<TThis extends typeof Error0>(
1011
+ // this: TThis,
1012
+ // value: ErrorPluginAdaptFn<ErrorInstanceOfMap<PluginsMapOf<TThis>>, ErrorResolvedProps<PluginsMapOf<TThis>>>,
1013
+ // ): ClassError0<PluginsMapOf<TThis>> {
1014
+ // return this.use('adapt', value)
1015
+ // }
1016
+
1017
+ // static stack<TThis extends typeof Error0>(
1018
+ // this: TThis,
1019
+ // value: ErrorPluginStack<ErrorInstanceOfMap<PluginsMapOf<TThis>>>,
1020
+ // ): ClassError0<PluginsMapOf<TThis>> {
1021
+ // return this.use('stack', value)
1022
+ // }
493
1023
 
494
- const inputFromDataError0 = get(error, 'data.error0')
495
- if (inputFromDataError0) {
496
- if (Error0.isLikelyError0(inputFromDataError0)) {
497
- return this._toError0(inputFromDataError0, inputOverride)
1024
+ // static cause<TThis extends typeof Error0>(
1025
+ // this: TThis,
1026
+ // value: ErrorPluginCause<ErrorInstanceOfMap<PluginsMapOf<TThis>>>,
1027
+ // ): ClassError0<PluginsMapOf<TThis>> {
1028
+ // return this.use('cause', value)
1029
+ // }
1030
+
1031
+ static use<TThis extends typeof Error0, TBuilder extends PluginError0>(
1032
+ this: TThis,
1033
+ plugin: TBuilder,
1034
+ ): ClassError0<ExtendErrorPluginsMap<PluginsMapOf<TThis>, PluginOfBuilder<TBuilder>>>
1035
+ static use<
1036
+ TThis extends typeof Error0,
1037
+ TKey extends string,
1038
+ TInputValue = undefined,
1039
+ TOutputValue = unknown,
1040
+ TResolveValue extends TOutputValue | undefined = TOutputValue | undefined,
1041
+ >(
1042
+ this: TThis,
1043
+ kind: 'prop',
1044
+ key: TKey,
1045
+ value: ErrorPluginPropOptions<TInputValue, TOutputValue, ErrorInstanceOfMap<PluginsMapOf<TThis>>, TResolveValue>,
1046
+ ): ClassError0<ExtendErrorPluginsMapWithProp<PluginsMapOf<TThis>, TKey, TInputValue, TOutputValue, TResolveValue>>
1047
+ static use<
1048
+ TThis extends typeof Error0,
1049
+ TKey extends string,
1050
+ TMethod extends (error: ErrorInstanceOfMap<PluginsMapOf<TThis>>, ...args: any[]) => any,
1051
+ >(
1052
+ this: TThis,
1053
+ kind: 'method',
1054
+ key: TKey,
1055
+ value: TMethod,
1056
+ ): ClassError0<ExtendErrorPluginsMapWithMethod<PluginsMapOf<TThis>, TKey, TMethod>>
1057
+ static use<TThis extends typeof Error0>(
1058
+ this: TThis,
1059
+ kind: 'adapt',
1060
+ value: ErrorPluginAdaptFn<ErrorInstanceOfMap<PluginsMapOf<TThis>>, ErrorResolvedProps<PluginsMapOf<TThis>>>,
1061
+ ): ClassError0<PluginsMapOf<TThis>>
1062
+ static use<TThis extends typeof Error0>(
1063
+ this: TThis,
1064
+ kind: 'stack',
1065
+ value: ErrorPluginStack<ErrorInstanceOfMap<PluginsMapOf<TThis>>>,
1066
+ ): ClassError0<PluginsMapOf<TThis>>
1067
+ static use<TThis extends typeof Error0>(
1068
+ this: TThis,
1069
+ kind: 'cause',
1070
+ value: ErrorPluginCause<ErrorInstanceOfMap<PluginsMapOf<TThis>>>,
1071
+ ): ClassError0<PluginsMapOf<TThis>>
1072
+ static use<TThis extends typeof Error0>(
1073
+ this: TThis,
1074
+ kind: 'message',
1075
+ value: ErrorPluginMessage<ErrorInstanceOfMap<PluginsMapOf<TThis>>>,
1076
+ ): ClassError0<PluginsMapOf<TThis>>
1077
+ static use(
1078
+ this: typeof Error0,
1079
+ first: PluginError0 | 'prop' | 'method' | 'adapt' | 'stack' | 'cause' | 'message',
1080
+ key?: unknown,
1081
+ value?: ErrorPluginPropOptions<unknown> | ErrorPluginMethodFn<unknown>,
1082
+ ): ClassError0 {
1083
+ if (first instanceof PluginError0) {
1084
+ return this._useWithPlugin(this._pluginFromBuilder(first))
1085
+ }
1086
+ if (first === 'stack') {
1087
+ if (typeof key === 'undefined') {
1088
+ throw new Error('Error0.use("stack", value) requires stack plugin value')
1089
+ }
1090
+ if (typeof key !== 'object' || key === null || typeof (key as { serialize?: unknown }).serialize !== 'function') {
1091
+ throw new Error('Error0.use("stack", value) expects { serialize: function }')
1092
+ }
1093
+ return this._useWithPlugin({
1094
+ stack: key as ErrorPluginStack,
1095
+ })
1096
+ }
1097
+ if (first === 'cause') {
1098
+ if (typeof key === 'undefined') {
1099
+ throw new Error('Error0.use("cause", value) requires cause plugin value')
1100
+ }
1101
+ if (typeof key !== 'object' || key === null || typeof (key as { serialize?: unknown }).serialize !== 'function') {
1102
+ throw new Error('Error0.use("cause", value) expects { serialize: function }')
1103
+ }
1104
+ return this._useWithPlugin({
1105
+ cause: key as ErrorPluginCause,
1106
+ })
1107
+ }
1108
+ if (first === 'message') {
1109
+ if (typeof key === 'undefined') {
1110
+ throw new Error('Error0.use("message", value) requires message plugin value')
1111
+ }
1112
+ if (typeof key !== 'object' || key === null || typeof (key as { serialize?: unknown }).serialize !== 'function') {
1113
+ throw new Error('Error0.use("message", value) expects { serialize: function }')
1114
+ }
1115
+ return this._useWithPlugin({
1116
+ message: key as ErrorPluginMessage,
1117
+ })
1118
+ }
1119
+ if (first === 'adapt') {
1120
+ if (typeof key !== 'function') {
1121
+ throw new Error('Error0.use("adapt", value) requires adapt function')
498
1122
  }
1123
+ return this._useWithPlugin({
1124
+ adapt: [key as ErrorPluginAdaptFn<Error0, Record<string, unknown>>],
1125
+ })
1126
+ }
1127
+ if (typeof key !== 'string' || value === undefined) {
1128
+ throw new Error('Error0.use(kind, key, value) requires key and value')
499
1129
  }
500
1130
 
501
- return new Error0(this._getPropsFromUnknown(error, inputOverride))
1131
+ if (first === 'prop') {
1132
+ if (key === 'stack') {
1133
+ throw new Error(RESERVED_STACK_PROP_ERROR)
1134
+ }
1135
+ if (key === 'message') {
1136
+ throw new Error(RESERVED_MESSAGE_PROP_ERROR)
1137
+ }
1138
+ return this._useWithPlugin({
1139
+ props: { [key]: value as ErrorPluginPropOptions<unknown> },
1140
+ })
1141
+ }
1142
+ return this._useWithPlugin({
1143
+ methods: { [key]: value as ErrorPluginMethodFn<unknown> },
1144
+ })
502
1145
  }
503
1146
 
504
- static from(error: unknown, inputOverride?: Error0Input): Error0 {
505
- return this._toError0(error, inputOverride)
1147
+ static plugin(): PluginError0 {
1148
+ return new PluginError0()
506
1149
  }
507
1150
 
508
- static extend(props: {
509
- defaultMessage?: Error0GeneralProps['message']
510
- defaultCode?: Error0GeneralProps['code']
511
- defaultHttpStatus?: Error0GeneralProps['httpStatus']
512
- defaultExpected?: Error0GeneralProps['expected']
513
- defaultClientMessage?: Error0GeneralProps['clientMessage']
514
- defaultMeta?: Meta0.Meta0OrValueTypeNullish
515
- }) {
516
- const parent = this
517
- return class Error0 extends parent {
518
- static override defaultMessage = props.defaultMessage ?? parent.defaultMessage
519
- static override defaultCode = props.defaultCode ?? parent.defaultCode
520
- static override defaultHttpStatus = props.defaultHttpStatus ?? parent.defaultHttpStatus
521
- static override defaultExpected = props.defaultExpected ?? parent.defaultExpected
522
- static override defaultClientMessage = props.defaultClientMessage ?? parent.defaultClientMessage
523
- static override defaultMeta = Meta0.extend(props.defaultMeta, parent.defaultMeta)
1151
+ static serialize(error: unknown, isPublic = true): Record<string, unknown> {
1152
+ const error0 = this.from(error)
1153
+ const plugin = this._getResolvedPlugin()
1154
+ const resolveByKey = (targetError: Error0, key: string, targetPlugin: ErrorPluginResolved): unknown =>
1155
+ this._resolveByKey(targetError, key, targetPlugin)
1156
+ const messagePlugin = plugin.message
1157
+ let serializedMessage: unknown = error0.message
1158
+ try {
1159
+ if (messagePlugin) {
1160
+ serializedMessage = messagePlugin.serialize({ value: error0.message, error: error0, isPublic })
1161
+ }
1162
+ } catch {
1163
+ // eslint-disable-next-line no-console
1164
+ console.error('Error0: failed to serialize message', error0)
1165
+ serializedMessage = error0.message
1166
+ }
1167
+ const json: Record<string, unknown> = {
1168
+ name: error0.name,
1169
+ // we do not serialize causes, it is enough that we have floated props and adapt helper
1170
+ // cause: error0.cause,
1171
+ }
1172
+ if (serializedMessage !== undefined) {
1173
+ json.message = serializedMessage
524
1174
  }
525
- }
526
1175
 
527
- static extendCollection<T extends Record<string, typeof Error0>>(
528
- classes: T,
529
- props: {
530
- defaultMessage?: Error0GeneralProps['message']
531
- defaultCode?: Error0GeneralProps['code']
532
- defaultHttpStatus?: Error0GeneralProps['httpStatus']
533
- defaultExpected?: Error0GeneralProps['expected']
534
- defaultClientMessage?: Error0GeneralProps['clientMessage']
535
- defaultMeta?: Meta0.Meta0OrValueTypeNullish
536
- },
537
- ): T {
538
- return Object.fromEntries(Object.entries(classes).map(([name, Class]) => [name, Class.extend(props)])) as T
1176
+ for (const [key, prop] of plugin.propEntries) {
1177
+ if (prop.serialize === false) {
1178
+ continue
1179
+ }
1180
+ try {
1181
+ const options = {
1182
+ get own() {
1183
+ return error0.own(key as never)
1184
+ },
1185
+ get flow() {
1186
+ return error0.flow(key as never)
1187
+ },
1188
+ get resolved() {
1189
+ return resolveByKey(error0, key, plugin)
1190
+ },
1191
+ error: error0,
1192
+ isPublic,
1193
+ }
1194
+ const jsonValue = prop.serialize(options as ErrorPluginPropSerializeOptions<any, any, any>)
1195
+ if (jsonValue !== undefined) {
1196
+ json[key] = jsonValue
1197
+ }
1198
+ } catch {
1199
+ // eslint-disable-next-line no-console
1200
+ console.error(`Error0: failed to serialize property ${key}`, error0)
1201
+ }
1202
+ }
1203
+ const stackPlugin = plugin.stack
1204
+ try {
1205
+ let serializedStack: unknown
1206
+ if (stackPlugin) {
1207
+ serializedStack = stackPlugin.serialize({ value: error0.stack, error: error0, isPublic })
1208
+ } else {
1209
+ serializedStack = error0.stack
1210
+ }
1211
+ if (serializedStack !== undefined) {
1212
+ json.stack = serializedStack
1213
+ }
1214
+ } catch {
1215
+ // eslint-disable-next-line no-console
1216
+ console.error('Error0: failed to serialize stack', error0)
1217
+ }
1218
+ const causePlugin = plugin.cause
1219
+ if (causePlugin?.serialize) {
1220
+ try {
1221
+ const serializedCause = causePlugin.serialize({
1222
+ value: (error0 as { cause?: unknown }).cause,
1223
+ error: error0,
1224
+ isPublic,
1225
+ })
1226
+ if (serializedCause !== undefined) {
1227
+ json.cause = serializedCause
1228
+ }
1229
+ } catch {
1230
+ // eslint-disable-next-line no-console
1231
+ console.error('Error0: failed to serialize cause', error0)
1232
+ }
1233
+ }
1234
+ return json
539
1235
  }
540
1236
 
541
- toJSON() {
542
- return {
543
- message: this.message,
544
- tag: this.tag,
545
- code: this.code,
546
- httpStatus: this.httpStatus,
547
- expected: this.expected,
548
- clientMessage: this.clientMessage,
549
- anyMessage: this.anyMessage,
550
- cause: this.cause,
551
- meta: Meta0.getValue(this.meta),
552
- stack: this.stack,
553
- __I_AM_ERROR_0: this.__I_AM_ERROR_0,
554
- }
555
- }
556
- static toJSON(error: unknown, inputOverride?: Error0Input) {
557
- const error0 = this.from(error, inputOverride)
558
- return error0.toJSON()
559
- }
560
-
561
- toResponse(data?: Record<string, unknown>) {
562
- return Response.json(
563
- {
564
- ...this.toJSON(),
565
- ...data,
566
- },
567
- {
568
- status: this.httpStatus,
569
- statusText: this.message,
570
- },
571
- )
1237
+ serialize(isPublic = true): Record<string, unknown> {
1238
+ const ctor = this.constructor as typeof Error0
1239
+ return ctor.serialize(this, isPublic)
572
1240
  }
573
- }
574
1241
 
575
- export namespace Error0 {
576
- export type JSON = ReturnType<Error0['toJSON']>
577
- export type Collection = Record<string, typeof Error0>
1242
+ round<TThis extends Error0>(this: TThis, isPublic = true): TThis {
1243
+ const ctor = this.constructor as typeof Error0
1244
+ return ctor.round(this, isPublic) as TThis
1245
+ }
578
1246
  }
579
-
580
- export const e0s = {
581
- Default: Error0,
582
- Expected: Error0.extend({
583
- defaultExpected: true,
584
- }) as typeof Error0,
585
- } satisfies Error0.Collection