@devp0nt/error0 1.0.0-next.4 → 1.0.0-next.40

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/src/index.ts CHANGED
@@ -1,585 +1,716 @@
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
+ export type ErrorExtensionPropOptions<TInputValue, TOutputValue, TError extends Error0 = Error0> = {
2
+ input: (value: TInputValue) => TOutputValue
3
+ output: (error: TError) => TOutputValue
4
+ serialize: (value: TOutputValue, error: TError, isPublic: boolean) => unknown
25
5
  }
26
-
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
6
+ export type ErrorExtensionCopmputedFn<TOutputValue, TError extends Error0 = Error0> = (error: TError) => TOutputValue
7
+ export type ErrorExtensionMethodFn<
8
+ TOutputValue,
9
+ TArgs extends unknown[] = unknown[],
10
+ TError extends Error0 = Error0,
11
+ > = (error: TError, ...args: TArgs) => TOutputValue
12
+ export type ErrorExtensionRefineResult<TOutputProps extends Record<string, unknown>> = Partial<TOutputProps> | undefined
13
+ export type ErrorExtensionRefineFn<
14
+ TError extends Error0 = Error0,
15
+ TOutputProps extends Record<string, unknown> = Record<never, never>,
16
+ > = ((error: TError) => void) | ((error: TError) => ErrorExtensionRefineResult<TOutputProps>)
17
+ type ErrorMethodRecord = {
18
+ args: unknown[]
19
+ output: unknown
40
20
  }
41
21
 
42
- type HttpStatusCodeString = keyof typeof HttpStatusCode
43
- type Error0Cause = Error | Error0 | unknown
44
- type ExpectedFn = (error: Error0GeneralProps) => boolean | undefined
45
-
46
- const isFilled = <T>(value: T): value is NonNullable<T> => value !== null && value !== undefined && value !== ''
22
+ export type ErrorExtensionProps = { [key: string]: ErrorExtensionPropOptions<any, any> }
23
+ export type ErrorExtensionComputed = { [key: string]: ErrorExtensionCopmputedFn<any> }
24
+ export type ErrorExtensionMethods = { [key: string]: ErrorExtensionMethodFn<any, any[]> }
25
+
26
+ export type ErrorExtension<
27
+ TProps extends ErrorExtensionProps = Record<never, never>,
28
+ TComputed extends ErrorExtensionComputed = Record<never, never>,
29
+ TMethods extends ErrorExtensionMethods = Record<never, never>,
30
+ > = {
31
+ props?: TProps
32
+ computed?: TComputed
33
+ methods?: TMethods
34
+ refine?: Array<ErrorExtensionRefineFn<Error0, ExtensionOutputProps<TProps>>>
35
+ }
36
+ type AddPropToExtensionProps<
37
+ TProps extends ErrorExtensionProps,
38
+ TKey extends string,
39
+ TInputValue,
40
+ TOutputValue,
41
+ > = TProps & Record<TKey, ErrorExtensionPropOptions<TInputValue, TOutputValue>>
42
+ type AddComputedToExtensionComputed<
43
+ TComputed extends ErrorExtensionComputed,
44
+ TKey extends string,
45
+ TOutputValue,
46
+ > = TComputed & Record<TKey, ErrorExtensionCopmputedFn<TOutputValue>>
47
+ type AddMethodToExtensionMethods<
48
+ TMethods extends ErrorExtensionMethods,
49
+ TKey extends string,
50
+ TArgs extends unknown[],
51
+ TOutputValue,
52
+ > = TMethods & Record<TKey, ErrorExtensionMethodFn<TOutputValue, TArgs>>
53
+ type ExtensionOutputProps<TProps extends ErrorExtensionProps> = {
54
+ [TKey in keyof TProps]: TProps[TKey] extends ErrorExtensionPropOptions<any, infer TOutputValue> ? TOutputValue : never
55
+ }
56
+ export type ErrorExtensionsMap = {
57
+ props: Record<string, { input: unknown; output: unknown }>
58
+ computed: Record<string, unknown>
59
+ methods: Record<string, ErrorMethodRecord>
60
+ }
61
+ export type IsEmptyObject<T> = keyof T extends never ? true : false
62
+ export type ErrorInputBase = {
63
+ cause?: unknown
64
+ }
65
+ export type ErrorInput<TExtensionsMap extends ErrorExtensionsMap> =
66
+ IsEmptyObject<TExtensionsMap['props']> extends true
67
+ ? ErrorInputBase
68
+ : ErrorInputBase &
69
+ Partial<{
70
+ [TKey in keyof TExtensionsMap['props']]: TExtensionsMap['props'][TKey]['input']
71
+ }>
72
+
73
+ type ErrorOutputProps<TExtensionsMap extends ErrorExtensionsMap> = {
74
+ [TKey in keyof TExtensionsMap['props']]: TExtensionsMap['props'][TKey]['output']
75
+ }
76
+ type ErrorOutputComputed<TExtensionsMap extends ErrorExtensionsMap> = {
77
+ [TKey in keyof TExtensionsMap['computed']]: TExtensionsMap['computed'][TKey]
78
+ }
79
+ type ErrorOutputMethods<TExtensionsMap extends ErrorExtensionsMap> = {
80
+ [TKey in keyof TExtensionsMap['methods']]: TExtensionsMap['methods'][TKey] extends {
81
+ args: infer TArgs extends unknown[]
82
+ output: infer TOutput
83
+ }
84
+ ? (...args: TArgs) => TOutput
85
+ : never
86
+ }
87
+ export type ErrorOutput<TExtensionsMap extends ErrorExtensionsMap> = ErrorOutputProps<TExtensionsMap> &
88
+ ErrorOutputComputed<TExtensionsMap> &
89
+ ErrorOutputMethods<TExtensionsMap>
90
+
91
+ type ErrorStaticMethods<TExtensionsMap extends ErrorExtensionsMap> = {
92
+ [TKey in keyof TExtensionsMap['methods']]: TExtensionsMap['methods'][TKey] extends {
93
+ args: infer TArgs extends unknown[]
94
+ output: infer TOutput
95
+ }
96
+ ? (error: unknown, ...args: TArgs) => TOutput
97
+ : never
98
+ }
47
99
 
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)
100
+ type EmptyExtensionsMap = {
101
+ props: Record<never, { input: never; output: never }>
102
+ computed: Record<never, never>
103
+ methods: Record<never, ErrorMethodRecord>
104
+ }
91
105
 
92
- const message = safeInput.message || Error0.defaultMessage
93
- super(message)
94
- Object.setPrototypeOf(this, (this.constructor as typeof Error0).prototype)
95
- this.name = 'Error0'
106
+ type ErrorExtensionResolved = {
107
+ props: Record<string, ErrorExtensionPropOptions<unknown, unknown>>
108
+ computed: Record<string, ErrorExtensionCopmputedFn<unknown>>
109
+ methods: Record<string, ErrorExtensionMethodFn<unknown>>
110
+ refine: Array<ErrorExtensionRefineFn<Error0, Record<string, unknown>>>
111
+ }
96
112
 
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
113
+ type ExtensionPropsMapOf<TExtension extends ErrorExtension> = {
114
+ [TKey in keyof NonNullable<TExtension['props']>]: NonNullable<
115
+ TExtension['props']
116
+ >[TKey] extends ErrorExtensionPropOptions<infer TInputValue, infer TOutputValue>
117
+ ? { input: TInputValue; output: TOutputValue }
118
+ : never
119
+ }
120
+ type ExtensionComputedMapOf<TExtension extends ErrorExtension> = {
121
+ [TKey in keyof NonNullable<TExtension['computed']>]: NonNullable<
122
+ TExtension['computed']
123
+ >[TKey] extends ErrorExtensionCopmputedFn<infer TOutputValue>
124
+ ? TOutputValue
125
+ : never
126
+ }
127
+ type ExtensionMethodsMapOf<TExtension extends ErrorExtension> = {
128
+ [TKey in keyof NonNullable<TExtension['methods']>]: NonNullable<TExtension['methods']>[TKey] extends (
129
+ error: Error0,
130
+ ...args: infer TArgs extends unknown[]
131
+ ) => infer TOutput
132
+ ? { args: TArgs; output: TOutput }
133
+ : never
134
+ }
135
+ type ErrorExtensionsMapOfExtension<TExtension extends ErrorExtension> = {
136
+ props: ExtensionPropsMapOf<TExtension>
137
+ computed: ExtensionComputedMapOf<TExtension>
138
+ methods: ExtensionMethodsMapOf<TExtension>
139
+ }
140
+ type ExtendErrorExtensionsMap<TMap extends ErrorExtensionsMap, TExtension extends ErrorExtension> = {
141
+ props: TMap['props'] & ErrorExtensionsMapOfExtension<TExtension>['props']
142
+ computed: TMap['computed'] & ErrorExtensionsMapOfExtension<TExtension>['computed']
143
+ methods: TMap['methods'] & ErrorExtensionsMapOfExtension<TExtension>['methods']
144
+ }
145
+ type ExtendErrorExtensionsMapWithProp<
146
+ TMap extends ErrorExtensionsMap,
147
+ TKey extends string,
148
+ TInputValue,
149
+ TOutputValue,
150
+ > = ExtendErrorExtensionsMap<TMap, ErrorExtension<Record<TKey, ErrorExtensionPropOptions<TInputValue, TOutputValue>>>>
151
+ type ExtendErrorExtensionsMapWithComputed<
152
+ TMap extends ErrorExtensionsMap,
153
+ TKey extends string,
154
+ TOutputValue,
155
+ > = ExtendErrorExtensionsMap<
156
+ TMap,
157
+ ErrorExtension<Record<never, never>, Record<TKey, ErrorExtensionCopmputedFn<TOutputValue>>>
158
+ >
159
+ type ExtendErrorExtensionsMapWithMethod<
160
+ TMap extends ErrorExtensionsMap,
161
+ TKey extends string,
162
+ TArgs extends unknown[],
163
+ TOutputValue,
164
+ > = ExtendErrorExtensionsMap<
165
+ TMap,
166
+ ErrorExtension<Record<never, never>, Record<never, never>, Record<TKey, ErrorExtensionMethodFn<TOutputValue, TArgs>>>
167
+ >
168
+
169
+ type ExtensionsMapOf<TClass> = TClass extends { __extensionsMap?: infer TExtensionsMap }
170
+ ? TExtensionsMap extends ErrorExtensionsMap
171
+ ? TExtensionsMap
172
+ : EmptyExtensionsMap
173
+ : EmptyExtensionsMap
174
+
175
+ type ExtensionsMapFromParts<
176
+ TProps extends ErrorExtensionProps,
177
+ TComputed extends ErrorExtensionComputed,
178
+ TMethods extends ErrorExtensionMethods,
179
+ > = ErrorExtensionsMapOfExtension<ErrorExtension<TProps, TComputed, TMethods>>
180
+ type ErrorInstanceOfMap<TMap extends ErrorExtensionsMap> = Error0 & ErrorOutput<TMap>
181
+ type BuilderError0<
182
+ TProps extends ErrorExtensionProps,
183
+ TComputed extends ErrorExtensionComputed,
184
+ TMethods extends ErrorExtensionMethods,
185
+ > = Error0 & ErrorOutput<ExtensionsMapFromParts<TProps, TComputed, TMethods>>
186
+
187
+ type ExtensionOfBuilder<TBuilder> =
188
+ TBuilder extends ExtensionError0<infer TProps, infer TComputed, infer TMethods>
189
+ ? ErrorExtension<TProps, TComputed, TMethods>
190
+ : never
191
+
192
+ export type ErrorFlowPolicy = 'instance' | 'error0' | 'likeError0' | 'all'
193
+
194
+ export class ExtensionError0<
195
+ TProps extends ErrorExtensionProps = Record<never, never>,
196
+ TComputed extends ErrorExtensionComputed = Record<never, never>,
197
+ TMethods extends ErrorExtensionMethods = Record<never, never>,
198
+ > {
199
+ private readonly _extension: ErrorExtension<ErrorExtensionProps, ErrorExtensionComputed, ErrorExtensionMethods>
200
+
201
+ readonly Infer = undefined as unknown as {
202
+ props: TProps
203
+ computed: TComputed
204
+ methods: TMethods
224
205
  }
225
206
 
226
- // sepcial
227
-
228
- public static _getExtraError0PropsByZodError(zodError: ZodError): Partial<Error0GeneralProps> {
229
- return {
230
- message: `Zod Validation Error: ${zodError.message}`,
207
+ constructor(extension?: ErrorExtension<ErrorExtensionProps, ErrorExtensionComputed, ErrorExtensionMethods>) {
208
+ this._extension = {
209
+ props: { ...(extension?.props ?? {}) },
210
+ computed: { ...(extension?.computed ?? {}) },
211
+ methods: { ...(extension?.methods ?? {}) },
212
+ refine: [...(extension?.refine ?? [])],
231
213
  }
232
214
  }
233
215
 
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
- }
216
+ prop<TKey extends string, TInputValue, TOutputValue>(
217
+ key: TKey,
218
+ value: ErrorExtensionPropOptions<TInputValue, TOutputValue, BuilderError0<TProps, TComputed, TMethods>>,
219
+ ): ExtensionError0<AddPropToExtensionProps<TProps, TKey, TInputValue, TOutputValue>, TComputed, TMethods> {
220
+ return this.extend('prop', key, value)
248
221
  }
249
222
 
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 })
223
+ computed<TKey extends string, TOutputValue>(
224
+ key: TKey,
225
+ value: ErrorExtensionCopmputedFn<TOutputValue, BuilderError0<TProps, TComputed, TMethods>>,
226
+ ): ExtensionError0<TProps, AddComputedToExtensionComputed<TComputed, TKey, TOutputValue>, TMethods> {
227
+ return this.extend('computed', key, value)
256
228
  }
257
229
 
258
- // expected
230
+ method<TKey extends string, TArgs extends unknown[], TOutputValue>(
231
+ key: TKey,
232
+ value: ErrorExtensionMethodFn<TOutputValue, TArgs, BuilderError0<TProps, TComputed, TMethods>>,
233
+ ): ExtensionError0<TProps, TComputed, AddMethodToExtensionMethods<TMethods, TKey, TArgs, TOutputValue>> {
234
+ return this.extend('method', key, value)
235
+ }
259
236
 
260
- public static _normalizeSelfExpected(
261
- error0Props: Error0GeneralProps,
262
- expectedProvided: Error0Input['expected'],
263
- ): boolean | undefined {
264
- if (typeof expectedProvided === 'function') {
265
- return expectedProvided(error0Props)
266
- }
267
- return expectedProvided
237
+ refine(
238
+ value: ErrorExtensionRefineFn<BuilderError0<TProps, TComputed, TMethods>, ExtensionOutputProps<TProps>>,
239
+ ): ExtensionError0<TProps, TComputed, TMethods> {
240
+ return this.extend('refine', value)
268
241
  }
269
242
 
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
243
+ extend<TKey extends string, TInputValue, TOutputValue>(
244
+ kind: 'prop',
245
+ key: TKey,
246
+ value: ErrorExtensionPropOptions<TInputValue, TOutputValue, BuilderError0<TProps, TComputed, TMethods>>,
247
+ ): ExtensionError0<AddPropToExtensionProps<TProps, TKey, TInputValue, TOutputValue>, TComputed, TMethods>
248
+ extend<TKey extends string, TOutputValue>(
249
+ kind: 'computed',
250
+ key: TKey,
251
+ value: ErrorExtensionCopmputedFn<TOutputValue, BuilderError0<TProps, TComputed, TMethods>>,
252
+ ): ExtensionError0<TProps, AddComputedToExtensionComputed<TComputed, TKey, TOutputValue>, TMethods>
253
+ extend<TKey extends string, TArgs extends unknown[], TOutputValue>(
254
+ kind: 'method',
255
+ key: TKey,
256
+ value: ErrorExtensionMethodFn<TOutputValue, TArgs, BuilderError0<TProps, TComputed, TMethods>>,
257
+ ): ExtensionError0<TProps, TComputed, AddMethodToExtensionMethods<TMethods, TKey, TArgs, TOutputValue>>
258
+ extend(
259
+ kind: 'refine',
260
+ value: ErrorExtensionRefineFn<BuilderError0<TProps, TComputed, TMethods>, ExtensionOutputProps<TProps>>,
261
+ ): ExtensionError0<TProps, TComputed, TMethods>
262
+ extend(
263
+ kind: 'prop' | 'computed' | 'method' | 'refine',
264
+ keyOrValue: string | ErrorExtensionRefineFn<any, any>,
265
+ value?:
266
+ | ErrorExtensionPropOptions<unknown, unknown, any>
267
+ | ErrorExtensionCopmputedFn<unknown, any>
268
+ | ErrorExtensionMethodFn<unknown, unknown[], any>,
269
+ ): ExtensionError0<any, any, any> {
270
+ const nextProps: ErrorExtensionProps = { ...(this._extension.props ?? {}) }
271
+ const nextComputed: ErrorExtensionComputed = { ...(this._extension.computed ?? {}) }
272
+ const nextMethods: ErrorExtensionMethods = { ...(this._extension.methods ?? {}) }
273
+ const nextRefine: Array<ErrorExtensionRefineFn<Error0, Record<string, unknown>>> = [
274
+ ...(this._extension.refine ?? []),
275
+ ]
276
+ if (kind === 'prop') {
277
+ const key = keyOrValue as string
278
+ if (value === undefined) {
279
+ throw new Error('ExtensionError0.extend("prop", key, value) requires value')
280
+ }
281
+ nextProps[key] = value as ErrorExtensionPropOptions<any, any>
282
+ } else if (kind === 'computed') {
283
+ const key = keyOrValue as string
284
+ if (value === undefined) {
285
+ throw new Error('ExtensionError0.extend("computed", key, value) requires value')
275
286
  }
276
- if (causeProps.expected === true) {
277
- hasExpectedTrue = true
287
+ nextComputed[key] = value as ErrorExtensionCopmputedFn<any>
288
+ } else if (kind === 'method') {
289
+ const key = keyOrValue as string
290
+ if (value === undefined) {
291
+ throw new Error('ExtensionError0.extend("method", key, value) requires value')
278
292
  }
293
+ nextMethods[key] = value as ErrorExtensionMethodFn<any, any[]>
294
+ } else {
295
+ nextRefine.push(keyOrValue as ErrorExtensionRefineFn<Error0, Record<string, unknown>>)
279
296
  }
280
- return hasExpectedTrue
297
+ return new ExtensionError0({
298
+ props: nextProps,
299
+ computed: nextComputed,
300
+ methods: nextMethods,
301
+ refine: nextRefine,
302
+ })
281
303
  }
304
+ }
282
305
 
283
- // getters
306
+ export type ClassError0<TExtensionsMap extends ErrorExtensionsMap = EmptyExtensionsMap> = {
307
+ new (message: string, input?: ErrorInput<TExtensionsMap>): Error0 & ErrorOutput<TExtensionsMap>
308
+ new (input: { message: string } & ErrorInput<TExtensionsMap>): Error0 & ErrorOutput<TExtensionsMap>
309
+ readonly __extensionsMap?: TExtensionsMap
310
+ from: (error: unknown) => Error0 & ErrorOutput<TExtensionsMap>
311
+ serialize: (error: unknown, isPublic?: boolean) => object
312
+ extend: {
313
+ <TBuilder extends ExtensionError0>(
314
+ extension: TBuilder,
315
+ ): ClassError0<ExtendErrorExtensionsMap<TExtensionsMap, ExtensionOfBuilder<TBuilder>>>
316
+ <TKey extends string, TInputValue, TOutputValue>(
317
+ kind: 'prop',
318
+ key: TKey,
319
+ value: ErrorExtensionPropOptions<TInputValue, TOutputValue, ErrorInstanceOfMap<TExtensionsMap>>,
320
+ ): ClassError0<ExtendErrorExtensionsMapWithProp<TExtensionsMap, TKey, TInputValue, TOutputValue>>
321
+ <TKey extends string, TOutputValue>(
322
+ kind: 'computed',
323
+ key: TKey,
324
+ value: ErrorExtensionCopmputedFn<TOutputValue, ErrorInstanceOfMap<TExtensionsMap>>,
325
+ ): ClassError0<ExtendErrorExtensionsMapWithComputed<TExtensionsMap, TKey, TOutputValue>>
326
+ <TKey extends string, TArgs extends unknown[], TOutputValue>(
327
+ kind: 'method',
328
+ key: TKey,
329
+ value: ErrorExtensionMethodFn<TOutputValue, TArgs, ErrorInstanceOfMap<TExtensionsMap>>,
330
+ ): ClassError0<ExtendErrorExtensionsMapWithMethod<TExtensionsMap, TKey, TArgs, TOutputValue>>
331
+ (
332
+ kind: 'refine',
333
+ value: ErrorExtensionRefineFn<ErrorInstanceOfMap<TExtensionsMap>, ErrorOutputProps<TExtensionsMap>>,
334
+ ): ClassError0<TExtensionsMap>
335
+ }
336
+ extension: () => ExtensionError0
337
+ } & ErrorStaticMethods<TExtensionsMap>
284
338
 
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))
350
- }
351
- return result
339
+ export class Error0 extends Error {
340
+ static readonly __extensionsMap?: EmptyExtensionsMap
341
+ protected static _extensions: ErrorExtension[] = []
342
+
343
+ private static readonly _emptyExtension: ErrorExtensionResolved = {
344
+ props: {},
345
+ computed: {},
346
+ methods: {},
347
+ refine: [],
352
348
  }
353
349
 
354
- public static _getCausesPropsFromUnknown(error: unknown, maxLevel: number): Error0GeneralProps[] {
355
- if (!error) {
356
- return []
357
- }
358
- const causeProps = this._getPropsFromUnknown(error)
359
- const causesProps: Error0GeneralProps[] = [causeProps]
360
- if (!causeProps.cause) {
361
- return causesProps
350
+ private static _getResolvedExtension(this: typeof Error0): ErrorExtensionResolved {
351
+ const resolved: ErrorExtensionResolved = {
352
+ props: {},
353
+ computed: {},
354
+ methods: {},
355
+ refine: [],
362
356
  }
363
- if (maxLevel > 0) {
364
- causesProps.push(...this._getCausesPropsFromUnknown(this._getPropsFromUnknown(causeProps.cause), maxLevel - 1))
357
+ for (const extension of this._extensions) {
358
+ Object.assign(resolved.props, extension.props ?? this._emptyExtension.props)
359
+ Object.assign(resolved.computed, extension.computed ?? this._emptyExtension.computed)
360
+ Object.assign(resolved.methods, extension.methods ?? this._emptyExtension.methods)
361
+ resolved.refine.push(...(extension.refine ?? this._emptyExtension.refine))
365
362
  }
366
- return causesProps
363
+ return resolved
367
364
  }
368
365
 
369
- public static _getCausesPropsFromError0Props(
370
- error0Props: Error0GeneralProps,
371
- maxLevel: number,
372
- ): Error0GeneralProps[] {
373
- return [error0Props, ...this._getCausesPropsFromUnknown(error0Props.cause, maxLevel - 1)]
374
- }
366
+ constructor(message: string, input?: ErrorInput<EmptyExtensionsMap>)
367
+ constructor(input: { message: string } & ErrorInput<EmptyExtensionsMap>)
368
+ constructor(
369
+ ...args:
370
+ | [message: string, input?: ErrorInput<EmptyExtensionsMap>]
371
+ | [{ message: string } & ErrorInput<EmptyExtensionsMap>]
372
+ ) {
373
+ const [first, second] = args
374
+ const input = typeof first === 'string' ? { message: first, ...(second ?? {}) } : first
375
+
376
+ super(input.message, { cause: input.cause })
377
+ this.name = 'Error0'
375
378
 
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]>
379
+ const ctor = this.constructor as typeof Error0
380
+ const extension = ctor._getResolvedExtension()
381
+
382
+ for (const [key, prop] of Object.entries(extension.props)) {
383
+ if (key in input) {
384
+ const ownValue = (input as Record<string, unknown>)[key]
385
+ ;(this as Record<string, unknown>)[key] = prop.input(ownValue)
386
+ } else {
387
+ Object.defineProperty(this, key, {
388
+ get: () => prop.output(this),
389
+ set: (value) => {
390
+ Object.defineProperty(this, key, {
391
+ value,
392
+ writable: true,
393
+ enumerable: true,
394
+ configurable: true,
395
+ })
396
+ },
397
+ enumerable: true,
398
+ configurable: true,
399
+ })
384
400
  }
385
401
  }
386
- return undefined
402
+
403
+ for (const [key, computed] of Object.entries(extension.computed)) {
404
+ Object.defineProperty(this, key, {
405
+ get: () => computed(this),
406
+ set: (value) => {
407
+ Object.defineProperty(this, key, {
408
+ value,
409
+ writable: true,
410
+ enumerable: true,
411
+ configurable: true,
412
+ })
413
+ },
414
+ enumerable: true,
415
+ configurable: true,
416
+ })
417
+ }
387
418
  }
388
419
 
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
- // }
401
-
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]>)
420
+ private static readonly isSelfProperty = (object: object, key: string): boolean => {
421
+ const d = Object.getOwnPropertyDescriptor(object, key)
422
+ if (!d) return false
423
+ if (typeof d.get === 'function' || typeof d.set === 'function') {
424
+ if ('name' in object && object.name === 'Error0') {
425
+ return false
426
+ } else {
427
+ return true
411
428
  }
412
429
  }
413
- return values
430
+ return true
414
431
  }
415
432
 
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))
433
+ static own(error: object, key: string): unknown {
434
+ if (this.isSelfProperty(error, key)) {
435
+ return (error as Record<string, unknown>)[key]
424
436
  }
437
+ return undefined
438
+ }
439
+ own(key: string): unknown {
440
+ const ctor = this.constructor as typeof Error0
441
+ return ctor.own(this, key)
425
442
  }
426
443
 
427
- // stack
444
+ static flow(error: object, key: string, policy: ErrorFlowPolicy = 'likeError0'): unknown[] {
445
+ return this.causes(error, policy).map((cause) => {
446
+ const causeRecord = cause as Record<string, unknown>
447
+ if (this.isSelfProperty(causeRecord, key)) {
448
+ return causeRecord[key]
449
+ }
450
+ return undefined
451
+ })
452
+ }
453
+ flow(key: string, policy: ErrorFlowPolicy = 'likeError0'): unknown[] {
454
+ const ctor = this.constructor as typeof Error0
455
+ return ctor.flow(this, key, policy)
456
+ }
428
457
 
429
- public static _removeConstructorStackPart(stack: Error0GeneralProps['stack']): Error0GeneralProps['stack'] {
430
- if (!stack) {
431
- return stack
432
- }
433
- let lines = stack.split('\n')
434
- const removeAllLinesContains = (search: string) => {
435
- lines = lines.filter((line) => !line.includes(search))
458
+ static causes(error: object, policy: ErrorFlowPolicy = 'all'): object[] {
459
+ const causes: object[] = []
460
+ let current: unknown = error
461
+ const maxDepth = 99
462
+ const seen = new Set<unknown>()
463
+
464
+ for (let depth = 0; depth < maxDepth; depth += 1) {
465
+ if (!current || typeof current !== 'object') {
466
+ break
467
+ }
468
+ if (seen.has(current)) {
469
+ break
470
+ }
471
+ if (policy === 'instance') {
472
+ if (!this.is(current)) {
473
+ break
474
+ }
475
+ } else if (policy === 'error0') {
476
+ if (!this.isError0(current)) {
477
+ break
478
+ }
479
+ } else if (policy === 'likeError0') {
480
+ if (!this.isLikeError0(current)) {
481
+ break
482
+ }
483
+ }
484
+ seen.add(current)
485
+ causes.push(current)
486
+ current = (current as { cause?: unknown }).cause
436
487
  }
437
- removeAllLinesContains('at new Error0')
438
- removeAllLinesContains('at _toError0')
439
- removeAllLinesContains('at Error0.from')
440
- removeAllLinesContains('at Error0._toError0')
441
- return lines.join('\n')
442
- }
443
488
 
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
489
+ return causes
490
+ }
491
+ causes(policy: ErrorFlowPolicy = 'all'): object[] {
492
+ const ctor = this.constructor as typeof Error0
493
+ return ctor.causes(this, policy)
449
494
  }
450
495
 
451
- // transformations
496
+ static is(error: unknown): error is Error0 {
497
+ return error instanceof this
498
+ }
452
499
 
453
500
  static isError0(error: unknown): error is Error0 {
454
501
  return error instanceof Error0
455
502
  }
456
503
 
457
- static isLikelyError0(error: unknown): error is Error0 {
458
- if (error instanceof Error0) {
459
- return true
504
+ static isLikeError0(error: unknown): error is Error0 | object {
505
+ return (
506
+ error instanceof Error0 ||
507
+ (typeof error === 'object' && error !== null && 'name' in error && error.name === 'Error0')
508
+ )
509
+ }
510
+
511
+ static from(error: unknown): Error0 {
512
+ if (this.is(error)) {
513
+ return error
460
514
  }
515
+ if (this.isLikeError0(error)) {
516
+ return this._fromLikeError0(error)
517
+ }
518
+ return this._fromNonError0(error)
519
+ }
461
520
 
462
- if (typeof error === 'object' && error !== null) {
463
- if ('__I_AM_ERROR_0' in error && error.__I_AM_ERROR_0 === true) {
464
- return true
521
+ private static _applyRefine(error: Error0): Error0 {
522
+ const extension = this._getResolvedExtension()
523
+ for (const refine of extension.refine) {
524
+ const refined = refine(error as any)
525
+ if (refined && typeof refined === 'object') {
526
+ Object.assign(error as unknown as Record<string, unknown>, refined)
465
527
  }
466
528
  }
467
-
468
- return false
529
+ return error
469
530
  }
470
531
 
471
- public static _toError0(error: unknown, inputOverride: Error0Input = {}): Error0 {
472
- if (error instanceof Error0) {
473
- return error
532
+ private static _fromLikeError0(error: unknown): Error0 {
533
+ const message = this._extractMessage(error)
534
+ if (typeof error !== 'object' || error === null) {
535
+ return this._applyRefine(new this(message, { cause: error }))
474
536
  }
475
537
 
476
- if (typeof error === 'string') {
477
- return new Error0(error, inputOverride)
538
+ const errorRecord = error as Record<string, unknown>
539
+ const recreated = new this(message)
540
+ const temp = new this(message, { cause: errorRecord })
541
+ const extension = this._getResolvedExtension()
542
+ for (const [key, prop] of Object.entries(extension.props)) {
543
+ const value = prop.output(temp)
544
+ if (value !== undefined) {
545
+ ;(recreated as unknown as Record<string, unknown>)[key] = value
546
+ }
478
547
  }
548
+ ;(recreated as unknown as { cause?: unknown }).cause = errorRecord.cause
549
+ if (typeof errorRecord.stack === 'string') {
550
+ recreated.stack = errorRecord.stack
551
+ }
552
+ return this._applyRefine(recreated)
553
+ }
479
554
 
480
- if (typeof error !== 'object' || error === null) {
481
- return new Error0({
482
- message: this.defaultMessage,
483
- ...inputOverride,
555
+ private static _fromNonError0(error: unknown): Error0 {
556
+ const message = this._extractMessage(error)
557
+ return this._applyRefine(new this(message, { cause: error }))
558
+ }
559
+
560
+ private static _extractMessage(error: unknown): string {
561
+ return (
562
+ (typeof error === 'string'
563
+ ? error
564
+ : typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string'
565
+ ? error.message
566
+ : undefined) || 'Unknown error'
567
+ )
568
+ }
569
+
570
+ private static _extendWithExtension(
571
+ this: typeof Error0,
572
+ extension: ErrorExtension<ErrorExtensionProps, ErrorExtensionComputed, ErrorExtensionMethods>,
573
+ ): ClassError0 {
574
+ const Base = this as unknown as typeof Error0
575
+ const Error0Extended = class Error0 extends Base {}
576
+ ;(Error0Extended as typeof Error0)._extensions = [...Base._extensions, extension]
577
+
578
+ const resolved = (Error0Extended as typeof Error0)._getResolvedExtension()
579
+ for (const [key, method] of Object.entries(resolved.methods)) {
580
+ Object.defineProperty((Error0Extended as typeof Error0).prototype, key, {
581
+ value: function (...args: unknown[]) {
582
+ return method(this as Error0, ...args)
583
+ },
584
+ writable: true,
585
+ enumerable: true,
586
+ configurable: true,
587
+ })
588
+ Object.defineProperty(Error0Extended, key, {
589
+ value: function (error: unknown, ...args: unknown[]) {
590
+ return method(this.from(error), ...args)
591
+ },
592
+ writable: true,
593
+ enumerable: true,
594
+ configurable: true,
484
595
  })
485
596
  }
486
597
 
487
- const inputFromData = get(error, 'data')
488
- if (inputFromData) {
489
- if (Error0.isLikelyError0(inputFromData)) {
490
- return this._toError0(inputFromData, inputOverride)
491
- }
598
+ return Error0Extended as unknown as ClassError0
599
+ }
600
+
601
+ private static _extensionFromBuilder(
602
+ extension: ExtensionError0,
603
+ ): ErrorExtension<ErrorExtensionProps, ErrorExtensionComputed, ErrorExtensionMethods> {
604
+ const extensionRecord = extension as unknown as {
605
+ _extension: ErrorExtension<ErrorExtensionProps, ErrorExtensionComputed, ErrorExtensionMethods>
606
+ }
607
+ return {
608
+ props: { ...(extensionRecord._extension.props ?? {}) },
609
+ computed: { ...(extensionRecord._extension.computed ?? {}) },
610
+ methods: { ...(extensionRecord._extension.methods ?? {}) },
611
+ refine: [...(extensionRecord._extension.refine ?? [])],
492
612
  }
613
+ }
493
614
 
494
- const inputFromDataError0 = get(error, 'data.error0')
495
- if (inputFromDataError0) {
496
- if (Error0.isLikelyError0(inputFromDataError0)) {
497
- return this._toError0(inputFromDataError0, inputOverride)
615
+ static extend<TThis extends typeof Error0, TBuilder extends ExtensionError0>(
616
+ this: TThis,
617
+ extension: TBuilder,
618
+ ): ClassError0<ExtendErrorExtensionsMap<ExtensionsMapOf<TThis>, ExtensionOfBuilder<TBuilder>>>
619
+ static extend<TThis extends typeof Error0, TKey extends string, TInputValue, TOutputValue>(
620
+ this: TThis,
621
+ kind: 'prop',
622
+ key: TKey,
623
+ value: ErrorExtensionPropOptions<TInputValue, TOutputValue, ErrorInstanceOfMap<ExtensionsMapOf<TThis>>>,
624
+ ): ClassError0<ExtendErrorExtensionsMapWithProp<ExtensionsMapOf<TThis>, TKey, TInputValue, TOutputValue>>
625
+ static extend<TThis extends typeof Error0, TKey extends string, TOutputValue>(
626
+ this: TThis,
627
+ kind: 'computed',
628
+ key: TKey,
629
+ value: ErrorExtensionCopmputedFn<TOutputValue, ErrorInstanceOfMap<ExtensionsMapOf<TThis>>>,
630
+ ): ClassError0<ExtendErrorExtensionsMapWithComputed<ExtensionsMapOf<TThis>, TKey, TOutputValue>>
631
+ static extend<TThis extends typeof Error0, TKey extends string, TArgs extends unknown[], TOutputValue>(
632
+ this: TThis,
633
+ kind: 'method',
634
+ key: TKey,
635
+ value: ErrorExtensionMethodFn<TOutputValue, TArgs, ErrorInstanceOfMap<ExtensionsMapOf<TThis>>>,
636
+ ): ClassError0<ExtendErrorExtensionsMapWithMethod<ExtensionsMapOf<TThis>, TKey, TArgs, TOutputValue>>
637
+ static extend<TThis extends typeof Error0>(
638
+ this: TThis,
639
+ kind: 'refine',
640
+ value: ErrorExtensionRefineFn<ErrorInstanceOfMap<ExtensionsMapOf<TThis>>, ErrorOutputProps<ExtensionsMapOf<TThis>>>,
641
+ ): ClassError0<ExtensionsMapOf<TThis>>
642
+ static extend(
643
+ this: typeof Error0,
644
+ first: ExtensionError0 | 'prop' | 'computed' | 'method' | 'refine',
645
+ key?: string | ErrorExtensionRefineFn<any, any>,
646
+ value?:
647
+ | ErrorExtensionPropOptions<unknown, unknown>
648
+ | ErrorExtensionCopmputedFn<unknown>
649
+ | ErrorExtensionMethodFn<unknown>,
650
+ ): ClassError0 {
651
+ if (first instanceof ExtensionError0) {
652
+ return this._extendWithExtension(this._extensionFromBuilder(first))
653
+ }
654
+ if (first === 'refine') {
655
+ if (typeof key !== 'function') {
656
+ throw new Error('Error0.extend("refine", value) requires refine function')
498
657
  }
658
+ return this._extendWithExtension({
659
+ refine: [key],
660
+ })
661
+ }
662
+ if (typeof key !== 'string' || value === undefined) {
663
+ throw new Error('Error0.extend(kind, key, value) requires key and value')
499
664
  }
500
665
 
501
- return new Error0(this._getPropsFromUnknown(error, inputOverride))
502
- }
503
-
504
- static from(error: unknown, inputOverride?: Error0Input): Error0 {
505
- return this._toError0(error, inputOverride)
506
- }
507
-
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)
666
+ if (first === 'prop') {
667
+ return this._extendWithExtension({
668
+ props: { [key]: value as ErrorExtensionPropOptions<unknown, unknown> },
669
+ })
670
+ }
671
+ if (first === 'computed') {
672
+ return this._extendWithExtension({
673
+ computed: { [key]: value as ErrorExtensionCopmputedFn<unknown> },
674
+ })
524
675
  }
676
+ return this._extendWithExtension({
677
+ methods: { [key]: value as ErrorExtensionMethodFn<unknown> },
678
+ })
525
679
  }
526
680
 
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
681
+ static extension(): ExtensionError0 {
682
+ return new ExtensionError0()
539
683
  }
540
684
 
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,
685
+ static serialize(error: unknown, isPublic = true): object {
686
+ const error0 = this.from(error)
687
+ const jsonWithUndefined: Record<string, unknown> = {
688
+ name: error0.name,
689
+ message: error0.message,
690
+ cause: error0.cause,
691
+ stack: error0.stack,
554
692
  }
693
+
694
+ const extension = this._getResolvedExtension()
695
+ for (const [key, prop] of Object.entries(extension.props)) {
696
+ const value = prop.output(error0)
697
+ const jsonValue = prop.serialize(value, error0, isPublic)
698
+ if (jsonValue !== undefined) {
699
+ jsonWithUndefined[key] = jsonValue
700
+ } else {
701
+ try {
702
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
703
+ delete jsonWithUndefined[key]
704
+ } catch {
705
+ // ignore
706
+ }
707
+ }
708
+ }
709
+ return Object.fromEntries(Object.entries(jsonWithUndefined).filter(([, value]) => value !== undefined)) as object
555
710
  }
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
- )
572
- }
573
- }
574
711
 
575
- export namespace Error0 {
576
- export type JSON = ReturnType<Error0['toJSON']>
577
- export type Collection = Record<string, typeof Error0>
712
+ serialize(isPublic = true): object {
713
+ const ctor = this.constructor as typeof Error0
714
+ return ctor.serialize(this, isPublic)
715
+ }
578
716
  }
579
-
580
- export const e0s = {
581
- Default: Error0,
582
- Expected: Error0.extend({
583
- defaultExpected: true,
584
- }) as typeof Error0,
585
- } satisfies Error0.Collection