@naturalcycles/js-lib 15.2.0 → 15.4.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/browser/analytics.util.js +2 -2
- package/dist/decorators/logMethod.decorator.js +1 -1
- package/dist/decorators/timeout.decorator.js +1 -1
- package/dist/error/assert.js +0 -1
- package/dist/string/slugify.js +0 -1
- package/dist/zod/index.d.ts +2 -4
- package/dist/zod/index.js +2 -2
- package/dist/zod/zod.shared.schemas.d.ts +4 -12
- package/dist/zod/zod.shared.schemas.js +2 -2
- package/dist/zod/zod.util.d.ts +12 -12
- package/dist/zod/zod.util.js +25 -30
- package/package.json +10 -4
- package/readme.md +1 -1
- package/src/browser/analytics.util.ts +2 -2
- package/src/decorators/logMethod.decorator.ts +2 -2
- package/src/decorators/timeout.decorator.ts +2 -2
- package/src/error/assert.ts +0 -1
- package/src/string/slugify.ts +0 -1
- package/src/zod/index.ts +2 -4
- package/src/zod/zod.shared.schemas.ts +2 -2
- package/src/zod/zod.util.ts +46 -36
|
@@ -10,7 +10,7 @@ export async function loadGTag(gtagId, enabled = true) {
|
|
|
10
10
|
window.dataLayer ||= [];
|
|
11
11
|
window.gtag ||= function gtag() {
|
|
12
12
|
// biome-ignore lint/complexity/useArrowFunction: ok
|
|
13
|
-
// biome-ignore lint/
|
|
13
|
+
// biome-ignore lint/complexity/noArguments: ok
|
|
14
14
|
window.dataLayer.push(arguments);
|
|
15
15
|
};
|
|
16
16
|
window.gtag('js', new Date());
|
|
@@ -39,7 +39,7 @@ export function loadHotjar(hjid) {
|
|
|
39
39
|
h.hj =
|
|
40
40
|
h.hj ||
|
|
41
41
|
function hj() {
|
|
42
|
-
// biome-ignore lint/
|
|
42
|
+
// biome-ignore lint/complexity/noArguments: ok
|
|
43
43
|
;
|
|
44
44
|
(h.hj.q = h.hj.q || []).push(arguments);
|
|
45
45
|
};
|
|
@@ -16,7 +16,7 @@ import { _getArgsSignature, _getMethodSignature } from './decorator.util.js';
|
|
|
16
16
|
*/
|
|
17
17
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
18
18
|
export function _LogMethod(opt = {}) {
|
|
19
|
-
return (
|
|
19
|
+
return (_target, key, descriptor) => {
|
|
20
20
|
_assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods');
|
|
21
21
|
const originalFn = descriptor.value;
|
|
22
22
|
const keyStr = String(key);
|
|
@@ -3,7 +3,7 @@ import { pTimeout } from '../promise/pTimeout.js';
|
|
|
3
3
|
import { _getMethodSignature } from './decorator.util.js';
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
5
5
|
export function _Timeout(opt) {
|
|
6
|
-
return (
|
|
6
|
+
return (_target, key, descriptor) => {
|
|
7
7
|
_assert(typeof descriptor.value === 'function', '@_Timeout can be applied only to methods');
|
|
8
8
|
if (!opt.timeout)
|
|
9
9
|
return descriptor;
|
package/dist/error/assert.js
CHANGED
|
@@ -93,7 +93,6 @@ export function _assertIsNumber(v, message) {
|
|
|
93
93
|
_assertTypeOf(v, 'number', message);
|
|
94
94
|
}
|
|
95
95
|
export function _assertTypeOf(v, expectedType, message) {
|
|
96
|
-
// biome-ignore lint/suspicious/useValidTypeof: ok
|
|
97
96
|
if (typeof v !== expectedType) {
|
|
98
97
|
const msg = message || `Expected typeof ${expectedType}, actual typeof: ${typeof v}`;
|
|
99
98
|
throw new AssertionError(msg);
|
package/dist/string/slugify.js
CHANGED
|
@@ -17,7 +17,6 @@ export function _slugify(s, opt = {}) {
|
|
|
17
17
|
// based on https://stackoverflow.com/a/23633850/4919972
|
|
18
18
|
// Combining Diacritical Marks
|
|
19
19
|
// https://www.unicode.org/charts/PDF/U0300.pdf
|
|
20
|
-
// biome-ignore lint/suspicious/noMisleadingCharacterClass: ok
|
|
21
20
|
s = s.normalize('NFKD').replaceAll(/[\u0300-\u036F]/g, '');
|
|
22
21
|
// Detect contractions/possessives by looking for any word followed by a `'t`
|
|
23
22
|
// or `'s` in isolation and then remove it.
|
package/dist/zod/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export * from './zod.shared.schemas.js';
|
|
2
2
|
export * from './zod.util.js';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
export { z, ZodError, ZodSchema };
|
|
6
|
-
export type { ZodIssue };
|
|
3
|
+
import { z, ZodType } from 'zod/v4';
|
|
4
|
+
export { z, ZodType };
|
package/dist/zod/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
2
|
export declare const TS_2500 = 16725225600;
|
|
3
3
|
export declare const TS_2000 = 946684800;
|
|
4
4
|
export declare const zUnixTimestamp: z.ZodNumber;
|
|
@@ -6,8 +6,8 @@ export declare const zUnixTimestamp2000: z.ZodNumber;
|
|
|
6
6
|
export declare const zUnixTimestampMillis: z.ZodNumber;
|
|
7
7
|
export declare const zUnixTimestampMillis2000: z.ZodNumber;
|
|
8
8
|
export declare const zSemVer: z.ZodString;
|
|
9
|
-
export declare const zIsoDateString: z.
|
|
10
|
-
export declare const zEmail: z.
|
|
9
|
+
export declare const zIsoDateString: z.ZodString;
|
|
10
|
+
export declare const zEmail: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
11
11
|
export declare const BASE62_REGEX: RegExp;
|
|
12
12
|
export declare const BASE64_REGEX: RegExp;
|
|
13
13
|
export declare const BASE64URL_REGEX: RegExp;
|
|
@@ -28,12 +28,4 @@ export declare const zBaseDBEntity: z.ZodObject<{
|
|
|
28
28
|
id: z.ZodString;
|
|
29
29
|
created: z.ZodOptional<z.ZodNumber>;
|
|
30
30
|
updated: z.ZodOptional<z.ZodNumber>;
|
|
31
|
-
},
|
|
32
|
-
id: string;
|
|
33
|
-
created?: number | undefined;
|
|
34
|
-
updated?: number | undefined;
|
|
35
|
-
}, {
|
|
36
|
-
id: string;
|
|
37
|
-
created?: number | undefined;
|
|
38
|
-
updated?: number | undefined;
|
|
39
|
-
}>;
|
|
31
|
+
}, z.core.$strip>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
2
|
export const TS_2500 = 16725225600; // 2500-01-01
|
|
3
3
|
export const TS_2000 = 946684800; // 2000-01-01
|
|
4
4
|
export const zUnixTimestamp = z
|
|
@@ -38,7 +38,7 @@ export const zIsoDateString = z
|
|
|
38
38
|
export const zEmail = z
|
|
39
39
|
.string()
|
|
40
40
|
.trim()
|
|
41
|
-
.email()
|
|
41
|
+
.email() // keeping as-is, so trim happens before email validation
|
|
42
42
|
.transform(s => s.toLowerCase())
|
|
43
43
|
.describe('Email');
|
|
44
44
|
export const BASE62_REGEX = /^[a-zA-Z0-9]+$/;
|
package/dist/zod/zod.util.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { ZodError,
|
|
1
|
+
import type { ZodError, ZodType } from 'zod/v4';
|
|
2
|
+
import type { ErrorData } from '../error/error.model.js';
|
|
3
|
+
import { AppError } from '../error/error.util.js';
|
|
2
4
|
export interface ZodErrorResult<T> {
|
|
3
5
|
success: false;
|
|
4
6
|
data?: T;
|
|
5
|
-
error: ZodValidationError
|
|
7
|
+
error: ZodValidationError;
|
|
6
8
|
}
|
|
7
9
|
export interface ZodSuccessResult<T> {
|
|
8
10
|
success: true;
|
|
9
11
|
data: T;
|
|
10
|
-
error?: ZodValidationError
|
|
12
|
+
error?: ZodValidationError;
|
|
11
13
|
}
|
|
12
|
-
export declare function zIsValid<T>(value: T, schema:
|
|
13
|
-
export declare function zValidate<T>(value: T, schema:
|
|
14
|
-
export declare function zSafeValidate<T>(value: T, schema:
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
constructor(
|
|
19
|
-
get message(): string;
|
|
20
|
-
annotate(): string;
|
|
14
|
+
export declare function zIsValid<T>(value: T, schema: ZodType<T>): boolean;
|
|
15
|
+
export declare function zValidate<T>(value: T, schema: ZodType<T>): T;
|
|
16
|
+
export declare function zSafeValidate<T>(value: T, schema: ZodType<T>): ZodSuccessResult<T> | ZodErrorResult<T>;
|
|
17
|
+
export interface ZodValidationErrorData extends ErrorData {
|
|
18
|
+
}
|
|
19
|
+
export declare class ZodValidationError extends AppError<ZodValidationErrorData> {
|
|
20
|
+
constructor(zodError: ZodError, value: any, schema: ZodType);
|
|
21
21
|
}
|
package/dist/zod/zod.util.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AppError } from '../error/error.util.js';
|
|
2
2
|
import { _stringify } from '../string/stringify.js';
|
|
3
3
|
export function zIsValid(value, schema) {
|
|
4
4
|
const { success } = schema.safeParse(value);
|
|
@@ -18,37 +18,32 @@ export function zSafeValidate(value, schema) {
|
|
|
18
18
|
}
|
|
19
19
|
return {
|
|
20
20
|
success: false,
|
|
21
|
-
error: new ZodValidationError(r.error
|
|
21
|
+
error: new ZodValidationError(r.error, value, schema),
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
export class ZodValidationError extends
|
|
25
|
-
value
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
super(
|
|
29
|
-
this.value = value;
|
|
30
|
-
this.schema = schema;
|
|
24
|
+
export class ZodValidationError extends AppError {
|
|
25
|
+
constructor(zodError, value, schema) {
|
|
26
|
+
const message = createZodErrorMessage(zodError, schema, value);
|
|
27
|
+
// const message = z.prettifyError(zodError) // todo: consider adopting it instead
|
|
28
|
+
super(message, {}, { name: 'ZodValidationError' });
|
|
31
29
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const objectId = this.value['id'];
|
|
40
|
-
objectTitle = [objectName, objectId].filter(Boolean).join('.');
|
|
41
|
-
}
|
|
42
|
-
objectTitle ||= 'data';
|
|
43
|
-
return [
|
|
44
|
-
`Invalid ${objectTitle}`,
|
|
45
|
-
'',
|
|
46
|
-
'Input:',
|
|
47
|
-
_stringify(this.value),
|
|
48
|
-
this.issues.length > 1 ? `\n${this.issues.length} issues:` : '',
|
|
49
|
-
...this.issues.slice(0, 100).map(i => {
|
|
50
|
-
return [i.path.join('.'), i.message].filter(Boolean).join(': ');
|
|
51
|
-
}),
|
|
52
|
-
].join('\n');
|
|
30
|
+
}
|
|
31
|
+
function createZodErrorMessage(err, schema, value) {
|
|
32
|
+
let objectTitle = schema.description;
|
|
33
|
+
if (typeof value === 'object' && value) {
|
|
34
|
+
const objectName = schema.description || value.constructor?.name;
|
|
35
|
+
const objectId = value['id'];
|
|
36
|
+
objectTitle = [objectName, objectId].filter(Boolean).join('.');
|
|
53
37
|
}
|
|
38
|
+
objectTitle ||= 'data';
|
|
39
|
+
return [
|
|
40
|
+
`Invalid ${objectTitle}`,
|
|
41
|
+
'',
|
|
42
|
+
'Input:',
|
|
43
|
+
_stringify(value),
|
|
44
|
+
err.issues.length > 1 ? `\n${err.issues.length} issues:` : '',
|
|
45
|
+
...err.issues.slice(0, 100).map(i => {
|
|
46
|
+
return [i.path.join('.'), i.message].filter(Boolean).join(': ');
|
|
47
|
+
}),
|
|
48
|
+
].join('\n');
|
|
54
49
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/js-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.4.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"tslib": "^2",
|
|
7
7
|
"zod": "^3"
|
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@naturalcycles/dev-lib": "*",
|
|
11
11
|
"@types/crypto-js": "^4",
|
|
12
|
-
"@types/node": "^
|
|
12
|
+
"@types/node": "^24",
|
|
13
13
|
"@types/semver": "^7",
|
|
14
14
|
"crypto-js": "^4",
|
|
15
15
|
"dayjs": "^1"
|
|
16
16
|
},
|
|
17
|
+
"exports": {
|
|
18
|
+
"./cfg/frontend/tsconfig.json": "./cfg/frontend/tsconfig.json",
|
|
19
|
+
".": "./dist/index.js",
|
|
20
|
+
"./zod": "./dist/zod/index.js"
|
|
21
|
+
},
|
|
17
22
|
"files": [
|
|
18
23
|
"dist",
|
|
19
24
|
"cfg",
|
|
@@ -51,8 +56,9 @@
|
|
|
51
56
|
"test": "dev-lib test",
|
|
52
57
|
"lint": "dev-lib lint",
|
|
53
58
|
"bt": "dev-lib bt",
|
|
54
|
-
"
|
|
55
|
-
"check": "dev-lib
|
|
59
|
+
"typecheck": "dev-lib typecheck",
|
|
60
|
+
"check": "dev-lib check",
|
|
61
|
+
"clean": "dev-lib clean",
|
|
56
62
|
"test-tz1": "TZ=Europe/Stockholm yarn test local",
|
|
57
63
|
"test-tz2": "TZ=JST-9 yarn test local",
|
|
58
64
|
"test-ny": "TZ=GMT-0500 yarn test localTime"
|
package/readme.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://codeclimate.com/github/NaturalCycles/js-lib/test_coverage)
|
|
11
11
|
[](https://github.com/prettier/prettier)
|
|
12
12
|
|
|
13
|
-
# [Documentation](https://naturalcycles.github.io/js-
|
|
13
|
+
# [Documentation](https://naturalcycles.github.io/js-libs/)
|
|
14
14
|
|
|
15
15
|
# Packaging
|
|
16
16
|
|
|
@@ -19,7 +19,7 @@ export async function loadGTag(gtagId: string, enabled = true): Promise<void> {
|
|
|
19
19
|
window.dataLayer ||= []
|
|
20
20
|
window.gtag ||= function gtag() {
|
|
21
21
|
// biome-ignore lint/complexity/useArrowFunction: ok
|
|
22
|
-
// biome-ignore lint/
|
|
22
|
+
// biome-ignore lint/complexity/noArguments: ok
|
|
23
23
|
window.dataLayer.push(arguments)
|
|
24
24
|
}
|
|
25
25
|
window.gtag('js', new Date())
|
|
@@ -54,7 +54,7 @@ export function loadHotjar(hjid: number): void {
|
|
|
54
54
|
h.hj =
|
|
55
55
|
h.hj ||
|
|
56
56
|
function hj() {
|
|
57
|
-
// biome-ignore lint/
|
|
57
|
+
// biome-ignore lint/complexity/noArguments: ok
|
|
58
58
|
;(h.hj.q = h.hj.q || []).push(arguments)
|
|
59
59
|
}
|
|
60
60
|
h._hjSettings = { hjid, hjsv: 6 }
|
|
@@ -71,7 +71,7 @@ export interface LogMethodOptions {
|
|
|
71
71
|
*/
|
|
72
72
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
73
73
|
export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
74
|
-
return (
|
|
74
|
+
return (_target, key, descriptor) => {
|
|
75
75
|
_assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods')
|
|
76
76
|
|
|
77
77
|
const originalFn = descriptor.value
|
|
@@ -97,7 +97,7 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
|
97
97
|
const sma = avg ? new SimpleMovingAverage(avg) : undefined
|
|
98
98
|
let count = 0
|
|
99
99
|
|
|
100
|
-
descriptor.value = function (this: typeof
|
|
100
|
+
descriptor.value = function (this: typeof _target, ...args: any[]) {
|
|
101
101
|
const started = Date.now()
|
|
102
102
|
const ctx = this
|
|
103
103
|
|
|
@@ -5,7 +5,7 @@ import { _getMethodSignature } from './decorator.util.js'
|
|
|
5
5
|
|
|
6
6
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
7
7
|
export function _Timeout(opt: PTimeoutOptions): MethodDecorator {
|
|
8
|
-
return (
|
|
8
|
+
return (_target, key, descriptor) => {
|
|
9
9
|
_assert(typeof descriptor.value === 'function', '@_Timeout can be applied only to methods')
|
|
10
10
|
|
|
11
11
|
if (!opt.timeout) return descriptor
|
|
@@ -13,7 +13,7 @@ export function _Timeout(opt: PTimeoutOptions): MethodDecorator {
|
|
|
13
13
|
const originalFn = descriptor.value
|
|
14
14
|
const keyStr = String(key)
|
|
15
15
|
|
|
16
|
-
descriptor.value = async function (this: typeof
|
|
16
|
+
descriptor.value = async function (this: typeof _target, ...args: any[]) {
|
|
17
17
|
const ctx = this
|
|
18
18
|
opt.name ||= _getMethodSignature(ctx, keyStr)
|
|
19
19
|
return await pTimeout(() => originalFn.apply(this, args), opt)
|
package/src/error/assert.ts
CHANGED
|
@@ -137,7 +137,6 @@ export function _assertIsNumber(v: any, message?: string): asserts v is number {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export function _assertTypeOf<T>(v: any, expectedType: string, message?: string): asserts v is T {
|
|
140
|
-
// biome-ignore lint/suspicious/useValidTypeof: ok
|
|
141
140
|
if (typeof v !== expectedType) {
|
|
142
141
|
const msg = message || `Expected typeof ${expectedType}, actual typeof: ${typeof v}`
|
|
143
142
|
throw new AssertionError(msg)
|
package/src/string/slugify.ts
CHANGED
|
@@ -41,7 +41,6 @@ export function _slugify(s: string, opt: SlugifyOptions = {}): string {
|
|
|
41
41
|
// based on https://stackoverflow.com/a/23633850/4919972
|
|
42
42
|
// Combining Diacritical Marks
|
|
43
43
|
// https://www.unicode.org/charts/PDF/U0300.pdf
|
|
44
|
-
// biome-ignore lint/suspicious/noMisleadingCharacterClass: ok
|
|
45
44
|
s = s.normalize('NFKD').replaceAll(/[\u0300-\u036F]/g, '')
|
|
46
45
|
|
|
47
46
|
// Detect contractions/possessives by looking for any word followed by a `'t`
|
package/src/zod/index.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export * from './zod.shared.schemas.js'
|
|
2
2
|
export * from './zod.util.js'
|
|
3
|
-
import
|
|
4
|
-
import { z, ZodError, ZodSchema } from 'zod'
|
|
3
|
+
import { z, ZodType } from 'zod/v4'
|
|
5
4
|
|
|
6
|
-
export { z,
|
|
7
|
-
export type { ZodIssue }
|
|
5
|
+
export { z, ZodType }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
1
|
+
import { z } from 'zod/v4'
|
|
2
2
|
|
|
3
3
|
export const TS_2500 = 16725225600 // 2500-01-01
|
|
4
4
|
export const TS_2000 = 946684800 // 2000-01-01
|
|
@@ -43,7 +43,7 @@ export const zIsoDateString = z
|
|
|
43
43
|
export const zEmail = z
|
|
44
44
|
.string()
|
|
45
45
|
.trim()
|
|
46
|
-
.email()
|
|
46
|
+
.email() // keeping as-is, so trim happens before email validation
|
|
47
47
|
.transform(s => s.toLowerCase())
|
|
48
48
|
.describe('Email')
|
|
49
49
|
|
package/src/zod/zod.util.ts
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import { ZodError,
|
|
1
|
+
import type { ZodError, ZodType } from 'zod/v4'
|
|
2
|
+
import type { ErrorData } from '../error/error.model.js'
|
|
3
|
+
import { AppError } from '../error/error.util.js'
|
|
2
4
|
import { _stringify } from '../string/stringify.js'
|
|
3
5
|
|
|
4
6
|
export interface ZodErrorResult<T> {
|
|
5
7
|
success: false
|
|
6
8
|
data?: T
|
|
7
|
-
error: ZodValidationError
|
|
9
|
+
error: ZodValidationError
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export interface ZodSuccessResult<T> {
|
|
11
13
|
success: true
|
|
12
14
|
data: T
|
|
13
|
-
error?: ZodValidationError
|
|
15
|
+
error?: ZodValidationError
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
export function zIsValid<T>(value: T, schema:
|
|
18
|
+
export function zIsValid<T>(value: T, schema: ZodType<T>): boolean {
|
|
17
19
|
const { success } = schema.safeParse(value)
|
|
18
20
|
return success
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
export function zValidate<T>(value: T, schema:
|
|
23
|
+
export function zValidate<T>(value: T, schema: ZodType<T>): T {
|
|
22
24
|
const r = zSafeValidate(value, schema)
|
|
23
25
|
if (r.success) {
|
|
24
26
|
return r.data
|
|
@@ -29,7 +31,7 @@ export function zValidate<T>(value: T, schema: ZodSchema<T>): T {
|
|
|
29
31
|
|
|
30
32
|
export function zSafeValidate<T>(
|
|
31
33
|
value: T,
|
|
32
|
-
schema:
|
|
34
|
+
schema: ZodType<T>,
|
|
33
35
|
// objectName?: string,
|
|
34
36
|
): ZodSuccessResult<T> | ZodErrorResult<T> {
|
|
35
37
|
const r = schema.safeParse(value)
|
|
@@ -39,43 +41,51 @@ export function zSafeValidate<T>(
|
|
|
39
41
|
|
|
40
42
|
return {
|
|
41
43
|
success: false,
|
|
42
|
-
error: new ZodValidationError
|
|
44
|
+
error: new ZodValidationError(r.error, value, schema),
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
export interface ZodValidationErrorData extends ErrorData {
|
|
49
|
+
// issues: $ZodIssue[]
|
|
50
|
+
// joiValidationObjectName?: string
|
|
51
|
+
// joiValidationObjectId?: string
|
|
52
|
+
/**
|
|
53
|
+
* Error "annotation" is stripped in Error.message.
|
|
54
|
+
* This field contains the "full" annotation.
|
|
55
|
+
*
|
|
56
|
+
* This field is non-enumerable, won't be printed or included in JSON by default,
|
|
57
|
+
* but still accessible programmatically (via `err.data.annotation`) when needed!
|
|
58
|
+
*/
|
|
59
|
+
// annotation?: string
|
|
60
|
+
}
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
export class ZodValidationError extends AppError<ZodValidationErrorData> {
|
|
63
|
+
constructor(zodError: ZodError, value: any, schema: ZodType) {
|
|
64
|
+
const message = createZodErrorMessage(zodError, schema, value)
|
|
65
|
+
// const message = z.prettifyError(zodError) // todo: consider adopting it instead
|
|
66
|
+
super(message, {}, { name: 'ZodValidationError' })
|
|
57
67
|
}
|
|
68
|
+
}
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
function createZodErrorMessage<T>(err: ZodError<T>, schema: ZodType<T>, value: T): string {
|
|
71
|
+
let objectTitle = schema.description
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
if (typeof value === 'object' && value) {
|
|
74
|
+
const objectName = schema.description || value.constructor?.name
|
|
75
|
+
const objectId = (value as any)['id'] as string
|
|
76
|
+
objectTitle = [objectName, objectId].filter(Boolean).join('.')
|
|
77
|
+
}
|
|
67
78
|
|
|
68
|
-
|
|
79
|
+
objectTitle ||= 'data'
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
+
return [
|
|
82
|
+
`Invalid ${objectTitle}`,
|
|
83
|
+
'',
|
|
84
|
+
'Input:',
|
|
85
|
+
_stringify(value),
|
|
86
|
+
err.issues.length > 1 ? `\n${err.issues.length} issues:` : '',
|
|
87
|
+
...err.issues.slice(0, 100).map(i => {
|
|
88
|
+
return [i.path.join('.'), i.message].filter(Boolean).join(': ')
|
|
89
|
+
}),
|
|
90
|
+
].join('\n')
|
|
81
91
|
}
|