@naturalcycles/js-lib 14.128.1 → 14.130.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/decorators/asyncMemo.decorator.d.ts +2 -2
- package/dist/decorators/createPromiseDecorator.d.ts +6 -6
- package/dist/decorators/debounce.d.ts +2 -2
- package/dist/decorators/memo.decorator.d.ts +2 -2
- package/dist/decorators/memo.util.d.ts +7 -7
- package/dist/error/app.error.d.ts +1 -1
- package/dist/error/app.error.js +2 -2
- package/dist/error/assert.d.ts +0 -1
- package/dist/error/assert.js +0 -3
- package/dist/error/error.model.d.ts +1 -0
- package/dist/error/error.util.d.ts +4 -4
- package/dist/error/error.util.js +26 -13
- package/dist/error/errorMode.d.ts +1 -1
- package/dist/error/errorMode.js +1 -1
- package/dist/error/http.error.d.ts +1 -1
- package/dist/error/http.error.js +2 -2
- package/dist/error/tryCatch.d.ts +1 -1
- package/dist/http/fetcher.js +4 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +9 -4
- package/dist/json-schema/jsonSchemaBuilder.d.ts +1 -1
- package/dist/promise/pDefer.d.ts +2 -2
- package/dist/promise/pMap.js +3 -4
- package/dist/promise/pRetry.d.ts +1 -1
- package/dist/promise/pTimeout.d.ts +1 -1
- package/dist/string/pupa.d.ts +2 -2
- package/dist/string/readingTime.d.ts +1 -1
- package/dist/string/stringifyAny.d.ts +6 -0
- package/dist/string/stringifyAny.js +13 -1
- package/dist/zod/zod.shared.schemas.d.ts +52 -0
- package/dist/zod/zod.shared.schemas.js +94 -0
- package/dist/zod/zod.util.d.ts +21 -0
- package/dist/zod/zod.util.js +59 -0
- package/dist-esm/error/app.error.js +2 -2
- package/dist-esm/error/assert.js +0 -3
- package/dist-esm/error/error.util.js +26 -11
- package/dist-esm/error/errorMode.js +1 -1
- package/dist-esm/error/http.error.js +2 -2
- package/dist-esm/http/fetcher.js +4 -3
- package/dist-esm/index.js +5 -3
- package/dist-esm/promise/pMap.js +3 -4
- package/dist-esm/string/stringifyAny.js +13 -1
- package/dist-esm/zod/zod.shared.schemas.js +91 -0
- package/dist-esm/zod/zod.util.js +53 -0
- package/package.json +3 -2
- package/src/decorators/asyncMemo.decorator.ts +2 -2
- package/src/decorators/createPromiseDecorator.ts +4 -4
- package/src/decorators/debounce.ts +2 -2
- package/src/decorators/memo.decorator.ts +2 -2
- package/src/decorators/memo.util.ts +7 -7
- package/src/error/app.error.ts +2 -2
- package/src/error/assert.ts +1 -5
- package/src/error/error.model.ts +10 -0
- package/src/error/error.util.ts +26 -21
- package/src/error/errorMode.ts +1 -1
- package/src/error/http.error.ts +2 -2
- package/src/error/tryCatch.ts +1 -1
- package/src/http/fetcher.ts +5 -8
- package/src/index.ts +6 -3
- package/src/json-schema/jsonSchemaBuilder.ts +1 -1
- package/src/promise/pDefer.ts +2 -2
- package/src/promise/pMap.ts +3 -4
- package/src/promise/pRetry.ts +1 -1
- package/src/promise/pTimeout.ts +1 -1
- package/src/string/pupa.ts +1 -1
- package/src/string/readingTime.ts +1 -1
- package/src/string/stringifyAny.ts +23 -1
- package/src/zod/zod.shared.schemas.ts +102 -0
- package/src/zod/zod.util.ts +77 -0
- package/dist/promise/AggregatedError.d.ts +0 -10
- package/dist/promise/AggregatedError.js +0 -34
- package/dist-esm/promise/AggregatedError.js +0 -30
- package/src/promise/AggregatedError.ts +0 -36
|
@@ -10,15 +10,15 @@ export const jsonMemoSerializer: MemoSerializer = args => {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface MemoCache<KEY = any, VALUE = any> {
|
|
13
|
-
has(k: KEY)
|
|
14
|
-
get(k: KEY)
|
|
15
|
-
set(k: KEY, v: VALUE | Error)
|
|
13
|
+
has: (k: KEY) => boolean
|
|
14
|
+
get: (k: KEY) => VALUE | Error | undefined
|
|
15
|
+
set: (k: KEY, v: VALUE | Error) => void
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Clear is only called when `.dropCache()` is called.
|
|
19
19
|
* Otherwise the Cache is "persistent" (never cleared).
|
|
20
20
|
*/
|
|
21
|
-
clear()
|
|
21
|
+
clear: () => void
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface AsyncMemoCache<KEY = any, VALUE = any> {
|
|
@@ -29,14 +29,14 @@ export interface AsyncMemoCache<KEY = any, VALUE = any> {
|
|
|
29
29
|
* This also means that you CANNOT store `undefined` value in the Cache, as it'll be treated as a MISS.
|
|
30
30
|
* You CAN store `null` value instead, it will be treated as a HIT.
|
|
31
31
|
*/
|
|
32
|
-
get(k: KEY)
|
|
33
|
-
set(k: KEY, v: VALUE | Error)
|
|
32
|
+
get: (k: KEY) => Promisable<VALUE | Error | undefined>
|
|
33
|
+
set: (k: KEY, v: VALUE | Error) => Promisable<void>
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Clear is only called when `.dropCache()` is called.
|
|
37
37
|
* Otherwise the Cache is "persistent" (never cleared).
|
|
38
38
|
*/
|
|
39
|
-
clear()
|
|
39
|
+
clear: () => Promisable<void>
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// SingleValueMemoCache and ObjectMemoCache are example-only, not used in production code
|
package/src/error/app.error.ts
CHANGED
|
@@ -12,8 +12,8 @@ import type { ErrorData } from './error.model'
|
|
|
12
12
|
export class AppError<DATA_TYPE extends ErrorData = ErrorData> extends Error {
|
|
13
13
|
data!: DATA_TYPE
|
|
14
14
|
|
|
15
|
-
constructor(message: string, data = {} as DATA_TYPE) {
|
|
16
|
-
super(message)
|
|
15
|
+
constructor(message: string, data = {} as DATA_TYPE, opt?: ErrorOptions) {
|
|
16
|
+
super(message, opt)
|
|
17
17
|
|
|
18
18
|
Object.defineProperty(this, 'name', {
|
|
19
19
|
value: this.constructor.name,
|
package/src/error/assert.ts
CHANGED
|
@@ -123,8 +123,4 @@ export function _assertTypeOf<T>(v: any, expectedType: string, message?: string)
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
export class AssertionError extends AppError {
|
|
127
|
-
constructor(message: string, data = {} as ErrorData) {
|
|
128
|
-
super(message, data)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
126
|
+
export class AssertionError extends AppError {}
|
package/src/error/error.model.ts
CHANGED
|
@@ -76,6 +76,14 @@ export interface Admin403ErrorData extends HttpErrorData {
|
|
|
76
76
|
adminPermissionsRequired: string[]
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// export interface ErrorLike {
|
|
80
|
+
// name: string
|
|
81
|
+
// message: string
|
|
82
|
+
// stack?: string
|
|
83
|
+
// data?: any
|
|
84
|
+
// cause?: any
|
|
85
|
+
// }
|
|
86
|
+
|
|
79
87
|
/**
|
|
80
88
|
* Portable object that represents Error.
|
|
81
89
|
* Has extendable generic `data` property.
|
|
@@ -114,6 +122,8 @@ export interface ErrorObject<DATA_TYPE extends ErrorData = ErrorData> {
|
|
|
114
122
|
* It's non-optional, to save some null-checks.
|
|
115
123
|
*/
|
|
116
124
|
data: DATA_TYPE
|
|
125
|
+
|
|
126
|
+
cause?: any
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
/**
|
package/src/error/error.util.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ErrorData,
|
|
3
|
-
ErrorObject,
|
|
4
|
-
HttpErrorData,
|
|
5
|
-
HttpErrorResponse,
|
|
6
|
-
StringifyAnyOptions,
|
|
7
|
-
Class,
|
|
8
|
-
} from '..'
|
|
1
|
+
import type { ErrorData, ErrorObject, HttpErrorData, HttpErrorResponse, Class } from '..'
|
|
9
2
|
import { AppError, _jsonParseIfPossible, _stringifyAny } from '..'
|
|
10
3
|
|
|
11
4
|
/**
|
|
@@ -20,7 +13,6 @@ export function _anyToError<ERROR_TYPE extends Error = Error>(
|
|
|
20
13
|
o: any,
|
|
21
14
|
errorClass: Class<ERROR_TYPE> = Error as any,
|
|
22
15
|
errorData?: ErrorData,
|
|
23
|
-
opt?: StringifyAnyOptions,
|
|
24
16
|
): ERROR_TYPE {
|
|
25
17
|
let e: ERROR_TYPE
|
|
26
18
|
|
|
@@ -29,8 +21,8 @@ export function _anyToError<ERROR_TYPE extends Error = Error>(
|
|
|
29
21
|
} else {
|
|
30
22
|
// If it's an instance of Error, but ErrorClass is something else (e.g AppError) - it'll be "repacked" into AppError
|
|
31
23
|
|
|
32
|
-
const errorObject =
|
|
33
|
-
e = _errorObjectToError(errorObject, errorClass)
|
|
24
|
+
const errorObject = _anyToErrorObject(o)
|
|
25
|
+
e = _errorObjectToError(errorObject, errorClass)
|
|
34
26
|
}
|
|
35
27
|
|
|
36
28
|
if (errorData) {
|
|
@@ -52,12 +44,11 @@ export function _anyToError<ERROR_TYPE extends Error = Error>(
|
|
|
52
44
|
export function _anyToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
|
|
53
45
|
o: any,
|
|
54
46
|
errorData?: Partial<DATA_TYPE>,
|
|
55
|
-
opt?: StringifyAnyOptions,
|
|
56
47
|
): ErrorObject<DATA_TYPE> {
|
|
57
48
|
let eo: ErrorObject<DATA_TYPE>
|
|
58
49
|
|
|
59
50
|
if (o instanceof Error) {
|
|
60
|
-
eo = _errorToErrorObject<DATA_TYPE>(o
|
|
51
|
+
eo = _errorToErrorObject<DATA_TYPE>(o)
|
|
61
52
|
} else {
|
|
62
53
|
o = _jsonParseIfPossible(o)
|
|
63
54
|
|
|
@@ -70,10 +61,7 @@ export function _anyToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
|
|
|
70
61
|
// so, fair to return `data: {}` in the end
|
|
71
62
|
// Also we're sure it includes no "error name", e.g no `Error: ...`,
|
|
72
63
|
// so, fair to include `name: 'Error'`
|
|
73
|
-
const message = _stringifyAny(o
|
|
74
|
-
includeErrorData: true, // cause we're returning an ErrorObject, not a stringified error (yet)
|
|
75
|
-
...opt,
|
|
76
|
-
})
|
|
64
|
+
const message = _stringifyAny(o)
|
|
77
65
|
|
|
78
66
|
eo = {
|
|
79
67
|
name: 'Error',
|
|
@@ -89,16 +77,16 @@ export function _anyToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
|
|
|
89
77
|
|
|
90
78
|
export function _errorToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
|
|
91
79
|
e: AppError<DATA_TYPE> | Error,
|
|
92
|
-
includeErrorStack = true,
|
|
93
80
|
): ErrorObject<DATA_TYPE> {
|
|
94
81
|
const obj: ErrorObject<DATA_TYPE> = {
|
|
95
82
|
name: e.name,
|
|
96
83
|
message: e.message,
|
|
97
84
|
data: { ...(e as any).data }, // empty by default
|
|
85
|
+
stack: e.stack,
|
|
98
86
|
}
|
|
99
87
|
|
|
100
|
-
if (
|
|
101
|
-
obj.
|
|
88
|
+
if (e.cause) {
|
|
89
|
+
obj.cause = _anyToErrorObject(e.cause)
|
|
102
90
|
}
|
|
103
91
|
|
|
104
92
|
return obj
|
|
@@ -138,6 +126,15 @@ export function _errorObjectToError<DATA_TYPE extends ErrorData, ERROR_TYPE exte
|
|
|
138
126
|
})
|
|
139
127
|
}
|
|
140
128
|
|
|
129
|
+
if (o.cause) {
|
|
130
|
+
Object.defineProperty(err, 'cause', {
|
|
131
|
+
value: o.cause,
|
|
132
|
+
writable: true,
|
|
133
|
+
configurable: true,
|
|
134
|
+
enumerable: false,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
141
138
|
return err
|
|
142
139
|
}
|
|
143
140
|
|
|
@@ -159,10 +156,18 @@ export function _isHttpErrorObject(o: any): o is ErrorObject<HttpErrorData> {
|
|
|
159
156
|
*/
|
|
160
157
|
export function _isErrorObject(o: any): o is ErrorObject {
|
|
161
158
|
return (
|
|
162
|
-
!!o &&
|
|
159
|
+
!!o &&
|
|
160
|
+
typeof o === 'object' &&
|
|
161
|
+
typeof o.name === 'string' &&
|
|
162
|
+
typeof o.message === 'string' &&
|
|
163
|
+
typeof o.data === 'object'
|
|
163
164
|
)
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
// export function _isErrorLike(o: any): o is ErrorLike {
|
|
168
|
+
// return !!o && typeof o === 'object' && typeof o.name === 'string' && typeof o.message === 'string'
|
|
169
|
+
// }
|
|
170
|
+
|
|
166
171
|
/**
|
|
167
172
|
* Convenience function to safely add properties to Error's `data` object
|
|
168
173
|
* (even if it wasn't previously existing)
|
package/src/error/errorMode.ts
CHANGED
|
@@ -10,7 +10,7 @@ export enum ErrorMode {
|
|
|
10
10
|
THROW_IMMEDIATELY = 'THROW_IMMEDIATELY',
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Don't throw on errors, but collect them and throw as
|
|
13
|
+
* Don't throw on errors, but collect them and throw as AggregateError in the end.
|
|
14
14
|
*/
|
|
15
15
|
THROW_AGGREGATED = 'THROW_AGGREGATED',
|
|
16
16
|
|
package/src/error/http.error.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { HttpErrorData } from './error.model'
|
|
|
7
7
|
export class HttpError<
|
|
8
8
|
DATA_TYPE extends HttpErrorData = HttpErrorData,
|
|
9
9
|
> extends AppError<DATA_TYPE> {
|
|
10
|
-
constructor(message: string, data: DATA_TYPE) {
|
|
11
|
-
super(message, data)
|
|
10
|
+
constructor(message: string, data: DATA_TYPE, opt?: ErrorOptions) {
|
|
11
|
+
super(message, data, opt)
|
|
12
12
|
}
|
|
13
13
|
}
|
package/src/error/tryCatch.ts
CHANGED
package/src/http/fetcher.ts
CHANGED
|
@@ -219,15 +219,12 @@ export class Fetcher {
|
|
|
219
219
|
res.body = text
|
|
220
220
|
res.body = JSON.parse(text, req.jsonReviver)
|
|
221
221
|
} catch (err) {
|
|
222
|
+
const { message } = _anyToError(err)
|
|
223
|
+
res.err = new HttpError([signature, message].join('\n'), {
|
|
224
|
+
httpStatusCode: 0,
|
|
225
|
+
url: req.url,
|
|
226
|
+
})
|
|
222
227
|
res.ok = false
|
|
223
|
-
res.err = _anyToError(
|
|
224
|
-
err,
|
|
225
|
-
HttpError,
|
|
226
|
-
_filterNullishValues({
|
|
227
|
-
httpStatusCode: 0,
|
|
228
|
-
url: req.url,
|
|
229
|
-
}),
|
|
230
|
-
)
|
|
231
228
|
}
|
|
232
229
|
} else {
|
|
233
230
|
// Body had a '' (empty string)
|
package/src/index.ts
CHANGED
|
@@ -37,7 +37,6 @@ export * from './object/deepEquals'
|
|
|
37
37
|
export * from './object/object.util'
|
|
38
38
|
export * from './object/sortObject'
|
|
39
39
|
export * from './object/sortObjectDeep'
|
|
40
|
-
export * from './promise/AggregatedError'
|
|
41
40
|
export * from './promise/pDefer'
|
|
42
41
|
export * from './promise/pDelay'
|
|
43
42
|
export * from './promise/pFilter'
|
|
@@ -59,7 +58,6 @@ export * from './is.util'
|
|
|
59
58
|
export * from './typeFest'
|
|
60
59
|
export * from './types'
|
|
61
60
|
export * from './unit/size.util'
|
|
62
|
-
import { is } from './vendor/is'
|
|
63
61
|
export * from './log/commonLogger'
|
|
64
62
|
export * from './string/safeJsonStringify'
|
|
65
63
|
export * from './promise/pQueue'
|
|
@@ -77,5 +75,10 @@ export * from './datetime/timeInterval'
|
|
|
77
75
|
export * from './http/http.model'
|
|
78
76
|
export * from './http/fetcher'
|
|
79
77
|
export * from './http/fetcher.model'
|
|
78
|
+
export * from './zod/zod.util'
|
|
79
|
+
export * from './zod/zod.shared.schemas'
|
|
80
|
+
import { z, ZodSchema, ZodError, ZodIssue } from 'zod'
|
|
81
|
+
import { is } from './vendor/is'
|
|
80
82
|
|
|
81
|
-
export { is }
|
|
83
|
+
export { is, z, ZodSchema, ZodError }
|
|
84
|
+
export type { ZodIssue }
|
package/src/promise/pDefer.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Similar to Deferred object, which is also a promise itself (instead of deferred.promise).
|
|
3
3
|
*/
|
|
4
4
|
export interface DeferredPromise<T = void> extends Promise<T> {
|
|
5
|
-
resolve(a?: T)
|
|
6
|
-
reject(e?: Error)
|
|
5
|
+
resolve: (a?: T) => void
|
|
6
|
+
reject: (e?: Error) => void
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/* eslint-disable @typescript-eslint/promise-function-async */
|
package/src/promise/pMap.ts
CHANGED
|
@@ -9,7 +9,6 @@ Improvements:
|
|
|
9
9
|
|
|
10
10
|
import type { AbortableAsyncMapper } from '..'
|
|
11
11
|
import { END, ErrorMode, SKIP } from '..'
|
|
12
|
-
import { AggregatedError } from './AggregatedError'
|
|
13
12
|
|
|
14
13
|
export interface PMapOptions {
|
|
15
14
|
/**
|
|
@@ -93,7 +92,7 @@ export async function pMap<IN, OUT>(
|
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
if (errors.length) {
|
|
96
|
-
throw new
|
|
95
|
+
throw new AggregateError(errors, `pMap resulted in ${errors.length} error(s)`)
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
return ret as OUT[]
|
|
@@ -115,7 +114,7 @@ export async function pMap<IN, OUT>(
|
|
|
115
114
|
})
|
|
116
115
|
|
|
117
116
|
if (errors.length) {
|
|
118
|
-
throw new
|
|
117
|
+
throw new AggregateError(errors, `pMap resulted in ${errors.length} error(s)`)
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
return ret as OUT[]
|
|
@@ -135,7 +134,7 @@ export async function pMap<IN, OUT>(
|
|
|
135
134
|
isSettled = true
|
|
136
135
|
const r = ret.filter(r => r !== SKIP) as OUT[]
|
|
137
136
|
if (errors.length) {
|
|
138
|
-
reject(new
|
|
137
|
+
reject(new AggregateError(errors, `pMap resulted in ${errors.length} error(s)`))
|
|
139
138
|
} else {
|
|
140
139
|
resolve(r)
|
|
141
140
|
}
|
package/src/promise/pRetry.ts
CHANGED
|
@@ -43,7 +43,7 @@ export interface PRetryOptions {
|
|
|
43
43
|
*
|
|
44
44
|
* @default () => true
|
|
45
45
|
*/
|
|
46
|
-
predicate
|
|
46
|
+
predicate?: (err: Error, attempt: number, maxAttempts: number) => boolean
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Log the first attempt (which is not a "retry" yet).
|
package/src/promise/pTimeout.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface PTimeoutOptions {
|
|
|
23
23
|
* err (which is TimeoutError) is passed as an argument for convenience, so it can
|
|
24
24
|
* be logged or such. You don't have to consume it in any way though.
|
|
25
25
|
*/
|
|
26
|
-
onTimeout
|
|
26
|
+
onTimeout?: (err: TimeoutError) => any
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Defaults to true.
|
package/src/string/pupa.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface PupaOptions {
|
|
|
31
31
|
/**
|
|
32
32
|
* Performs arbitrary operation for each interpolation. If the returned value was `undefined`, it behaves differently depending on the `ignoreMissing` option. Otherwise, the returned value will be interpolated into a string (and escaped when double-braced) and embedded into the template.
|
|
33
33
|
*/
|
|
34
|
-
transform
|
|
34
|
+
transform?: (data: { value: any; key: string }) => unknown
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -16,7 +16,7 @@ export interface ReadingTimeOptions {
|
|
|
16
16
|
* A function that returns a boolean value depending on if a character is considered as a word bound.
|
|
17
17
|
* Default: spaces, new lines and tabs
|
|
18
18
|
*/
|
|
19
|
-
wordBound
|
|
19
|
+
wordBound?: (char: string) => boolean
|
|
20
20
|
/**
|
|
21
21
|
* Default 200
|
|
22
22
|
*/
|
|
@@ -3,6 +3,8 @@ import type { Reviver } from '../types'
|
|
|
3
3
|
import { _jsonParseIfPossible } from './json.util'
|
|
4
4
|
import { _safeJsonStringify } from './safeJsonStringify'
|
|
5
5
|
|
|
6
|
+
const supportsAggregateError = typeof globalThis.AggregateError === 'function'
|
|
7
|
+
|
|
6
8
|
let globalStringifyFunction: JsonStringifyFunction = _safeJsonStringify
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -45,6 +47,13 @@ export interface StringifyAnyOptions {
|
|
|
45
47
|
*/
|
|
46
48
|
includeErrorStack?: boolean
|
|
47
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Set to false to skip including Error.cause.
|
|
52
|
+
*
|
|
53
|
+
* @default true
|
|
54
|
+
*/
|
|
55
|
+
includeErrorCause?: boolean
|
|
56
|
+
|
|
48
57
|
/**
|
|
49
58
|
* Allows to pass custom "stringify function".
|
|
50
59
|
* E.g in Node.js you can pass `util.inspect` instead.
|
|
@@ -91,6 +100,7 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
if (obj instanceof Error || _isErrorObject(obj)) {
|
|
103
|
+
const { includeErrorCause = true } = opt
|
|
94
104
|
//
|
|
95
105
|
// Error or ErrorObject
|
|
96
106
|
//
|
|
@@ -100,7 +110,7 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
100
110
|
// if (obj?.name === 'Error') {
|
|
101
111
|
// s = obj.message
|
|
102
112
|
// }
|
|
103
|
-
s = [obj.name, obj.message].join(': ')
|
|
113
|
+
s = [obj.name, obj.message].filter(Boolean).join(': ')
|
|
104
114
|
|
|
105
115
|
if (opt.includeErrorStack && obj.stack) {
|
|
106
116
|
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
@@ -128,6 +138,18 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
128
138
|
// Error that has no `data`, but has `code` property
|
|
129
139
|
s = [s, `code: ${(obj as any).code}`].join('\n')
|
|
130
140
|
}
|
|
141
|
+
|
|
142
|
+
if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {
|
|
143
|
+
s = [
|
|
144
|
+
s,
|
|
145
|
+
`${obj.errors.length} error(s):`,
|
|
146
|
+
...obj.errors.map((err, i) => `${i + 1}. ${_stringifyAny(err, opt)}`),
|
|
147
|
+
].join('\n')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (obj.cause && includeErrorCause) {
|
|
151
|
+
s = s + '\ncaused by: ' + _stringifyAny(obj.cause, opt)
|
|
152
|
+
}
|
|
131
153
|
} else if (typeof obj === 'string') {
|
|
132
154
|
//
|
|
133
155
|
// String
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const TS_2500 = 16725225600 // 2500-01-01
|
|
4
|
+
export const TS_2000 = 946684800 // 2000-01-01
|
|
5
|
+
|
|
6
|
+
export const zUnixTimestamp = z
|
|
7
|
+
.number()
|
|
8
|
+
.int()
|
|
9
|
+
.min(0)
|
|
10
|
+
.max(TS_2500, 'Must be a UnixTimestamp number')
|
|
11
|
+
.describe('UnixTimestamp')
|
|
12
|
+
export const zUnixTimestamp2000 = z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(TS_2000)
|
|
16
|
+
.max(TS_2500, 'Must be a UnixTimestamp number after 2000-01-01')
|
|
17
|
+
.describe('UnixTimestamp2000')
|
|
18
|
+
export const zUnixTimestampMillis = z
|
|
19
|
+
.number()
|
|
20
|
+
.int()
|
|
21
|
+
.min(0)
|
|
22
|
+
.max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number')
|
|
23
|
+
.describe('UnixTimestampMillis')
|
|
24
|
+
export const zUnixTimestampMillis2000 = z
|
|
25
|
+
.number()
|
|
26
|
+
.int()
|
|
27
|
+
.min(TS_2000 * 1000)
|
|
28
|
+
.max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number after 2000-01-01')
|
|
29
|
+
.describe('UnixTimestampMillis2000')
|
|
30
|
+
|
|
31
|
+
export const zSemVer = z
|
|
32
|
+
.string()
|
|
33
|
+
.regex(/^[0-9]+\.[0-9]+\.[0-9]+$/, 'Must be a SemVer string')
|
|
34
|
+
.describe('SemVer')
|
|
35
|
+
|
|
36
|
+
export const zIsoDateString = z
|
|
37
|
+
.string()
|
|
38
|
+
.refine(v => {
|
|
39
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(v)
|
|
40
|
+
}, 'Must be an IsoDateString')
|
|
41
|
+
.describe('IsoDateString')
|
|
42
|
+
|
|
43
|
+
export const zEmail = z
|
|
44
|
+
.string()
|
|
45
|
+
.trim()
|
|
46
|
+
.email()
|
|
47
|
+
.transform(s => s.toLowerCase())
|
|
48
|
+
.describe('Email')
|
|
49
|
+
|
|
50
|
+
export const BASE62_REGEX = /^[a-zA-Z0-9]+$/
|
|
51
|
+
export const BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/
|
|
52
|
+
export const BASE64URL_REGEX = /^[\w-/]+$/
|
|
53
|
+
export const zBase62 = z
|
|
54
|
+
.string()
|
|
55
|
+
.regex(BASE62_REGEX, 'Must be a base62 string')
|
|
56
|
+
.describe('Base62String')
|
|
57
|
+
export const zBase64 = z
|
|
58
|
+
.string()
|
|
59
|
+
.regex(BASE64_REGEX, 'Must be a base64 string')
|
|
60
|
+
.describe('Base64String')
|
|
61
|
+
export const zBase64Url = z
|
|
62
|
+
.string()
|
|
63
|
+
.regex(BASE64URL_REGEX, 'Must be a base64url string')
|
|
64
|
+
.describe('Base64UrlString')
|
|
65
|
+
|
|
66
|
+
export const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/
|
|
67
|
+
export const zJwt = z.string().regex(JWT_REGEX, 'Must be a JWT string').describe('JWTString')
|
|
68
|
+
|
|
69
|
+
export const zId = z
|
|
70
|
+
.string()
|
|
71
|
+
.regex(/^[a-zA-Z0-9_]{6,64}$/, 'Must be an id string')
|
|
72
|
+
.describe('IdString')
|
|
73
|
+
export const zIdBase62 = z
|
|
74
|
+
.string()
|
|
75
|
+
.regex(/^[a-zA-Z0-9]{8,64}$/, 'Must be a base62 id string')
|
|
76
|
+
.describe('Base62Id')
|
|
77
|
+
export const zIdBase64 = z
|
|
78
|
+
.string()
|
|
79
|
+
.regex(/^[A-Za-z0-9+/]{6,62}={0,2}$/, 'Must be a base64 id string')
|
|
80
|
+
.describe('Base64Id')
|
|
81
|
+
export const zIdBase64Url = z
|
|
82
|
+
.string()
|
|
83
|
+
.regex(/^[\w-/]{8,64}$/, 'Must be a base64url id string')
|
|
84
|
+
.describe('Base64UrlId')
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* "Slug" - a valid URL, filename, etc.
|
|
88
|
+
*/
|
|
89
|
+
export const zSlug = z
|
|
90
|
+
.string()
|
|
91
|
+
.regex(/^[a-z0-9-]{1,255}$/, 'Must be a slug string')
|
|
92
|
+
.describe('Slug')
|
|
93
|
+
|
|
94
|
+
export const zBaseDBEntity = z
|
|
95
|
+
.object({
|
|
96
|
+
id: z.string().optional(),
|
|
97
|
+
created: zUnixTimestamp2000.optional(),
|
|
98
|
+
updated: zUnixTimestamp2000.optional(),
|
|
99
|
+
})
|
|
100
|
+
.describe('BaseDBEntity')
|
|
101
|
+
|
|
102
|
+
export const zSavedDBEntity = zBaseDBEntity.required().describe('SavedDBEntity')
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ZodError, ZodSchema, ZodIssue } from 'zod'
|
|
2
|
+
import { _stringifyAny } from '../string/stringifyAny'
|
|
3
|
+
|
|
4
|
+
export interface ZodErrorResult<T> {
|
|
5
|
+
success: false
|
|
6
|
+
data?: T
|
|
7
|
+
error: ZodValidationError<T>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ZodSuccessResult<T> {
|
|
11
|
+
success: true
|
|
12
|
+
data: T
|
|
13
|
+
error?: ZodValidationError<T>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function zIsValid<T>(value: T, schema: ZodSchema<T>): boolean {
|
|
17
|
+
const { success } = schema.safeParse(value)
|
|
18
|
+
return success
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function zValidate<T>(value: T, schema: ZodSchema<T>): T {
|
|
22
|
+
const r = zSafeValidate(value, schema)
|
|
23
|
+
if (r.success) {
|
|
24
|
+
return r.data
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
throw r.error
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function zSafeValidate<T>(
|
|
31
|
+
value: T,
|
|
32
|
+
schema: ZodSchema<T>,
|
|
33
|
+
// objectName?: string,
|
|
34
|
+
): ZodSuccessResult<T> | ZodErrorResult<T> {
|
|
35
|
+
const r = schema.safeParse(value)
|
|
36
|
+
if (r.success) {
|
|
37
|
+
return r
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
error: new ZodValidationError<T>(r.error.issues, value, schema),
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ZodValidationError<T> extends ZodError<T> {
|
|
47
|
+
constructor(issues: ZodIssue[], public value: T, public schema: ZodSchema<T>) {
|
|
48
|
+
super(issues)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override get message(): string {
|
|
52
|
+
return this.annotate()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
annotate(): string {
|
|
56
|
+
let objectTitle = this.schema.description
|
|
57
|
+
|
|
58
|
+
if (typeof this.value === 'object' && this.value) {
|
|
59
|
+
const objectName = this.schema.description || this.value.constructor?.name
|
|
60
|
+
const objectId = (this.value as any)['id'] as string
|
|
61
|
+
objectTitle = [objectName, objectId].filter(Boolean).join('.')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
objectTitle ||= 'data'
|
|
65
|
+
|
|
66
|
+
return [
|
|
67
|
+
`Invalid ${objectTitle}`,
|
|
68
|
+
'',
|
|
69
|
+
'Input:',
|
|
70
|
+
_stringifyAny(this.value),
|
|
71
|
+
this.issues.length > 1 ? `\n${this.issues.length} issues:` : '',
|
|
72
|
+
...this.issues.slice(0, 100).map(i => {
|
|
73
|
+
return [i.path.join('.'), i.message].filter(Boolean).join(': ')
|
|
74
|
+
}),
|
|
75
|
+
].join('\n')
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error that aggregates a number of other errors.
|
|
3
|
-
* .errors contain raw original errors to be accessed if needed.
|
|
4
|
-
* .results contain the results of some batch operation (if needed).
|
|
5
|
-
*/
|
|
6
|
-
export declare class AggregatedError<RESULT = any> extends Error {
|
|
7
|
-
errors: Error[];
|
|
8
|
-
results: RESULT[];
|
|
9
|
-
constructor(errors: Error[], results?: RESULT[]);
|
|
10
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AggregatedError = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Error that aggregates a number of other errors.
|
|
6
|
-
* .errors contain raw original errors to be accessed if needed.
|
|
7
|
-
* .results contain the results of some batch operation (if needed).
|
|
8
|
-
*/
|
|
9
|
-
class AggregatedError extends Error {
|
|
10
|
-
constructor(errors, results = []) {
|
|
11
|
-
const message = [
|
|
12
|
-
`${errors.length} errors:`,
|
|
13
|
-
...errors.map((e, i) => `${i + 1}. ${e.message}`),
|
|
14
|
-
].join('\n');
|
|
15
|
-
super(message);
|
|
16
|
-
this.errors = errors;
|
|
17
|
-
this.results = results;
|
|
18
|
-
Object.defineProperty(this, 'name', {
|
|
19
|
-
value: this.constructor.name,
|
|
20
|
-
configurable: true,
|
|
21
|
-
});
|
|
22
|
-
if (Error.captureStackTrace) {
|
|
23
|
-
Error.captureStackTrace(this, this.constructor);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
Object.defineProperty(this, 'stack', {
|
|
27
|
-
value: new Error().stack,
|
|
28
|
-
writable: true,
|
|
29
|
-
configurable: true,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
exports.AggregatedError = AggregatedError;
|