@naturalcycles/js-lib 15.20.0 → 15.22.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/cfg/frontend/tsconfig.json +2 -2
- package/dist/datetime/localTime.d.ts +1 -1
- package/dist/validation/validation.d.ts +10 -0
- package/dist/zod/zod.shared.schemas.d.ts +35 -32
- package/dist/zod/zod.shared.schemas.js +100 -65
- package/dist/zod/zod.util.d.ts +2 -1
- package/dist/zod/zod.util.js +10 -3
- package/package.json +2 -2
- package/src/datetime/localTime.ts +1 -1
- package/src/validation/validation.ts +10 -0
- package/src/zod/zod.shared.schemas.ts +85 -46
- package/src/zod/zod.util.ts +16 -18
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"jsx": "preserve",
|
|
60
60
|
"pretty": true,
|
|
61
61
|
"newLine": "lf",
|
|
62
|
-
"experimentalDecorators": true
|
|
62
|
+
"experimentalDecorators": true
|
|
63
63
|
// "emitDecoratorMetadata": true // use if needed
|
|
64
|
-
}
|
|
64
|
+
}
|
|
65
65
|
// Need to be specified in the project tsconfig
|
|
66
66
|
// "include": ["src"],
|
|
67
67
|
// "exclude": ["**/__exclude", "**/@linked"]
|
|
@@ -291,7 +291,7 @@ declare class LocalTimeFactory {
|
|
|
291
291
|
/**
|
|
292
292
|
* Throws on invalid value.
|
|
293
293
|
*/
|
|
294
|
-
|
|
294
|
+
validateDateTimeObject(o: DateTimeObject): void;
|
|
295
295
|
isDateTimeObjectValid(o: DateTimeObject): boolean;
|
|
296
296
|
isTimeObjectValid({ hour, minute, second }: TimeObject): boolean;
|
|
297
297
|
fromDate(date: Date): LocalTime;
|
|
@@ -17,6 +17,16 @@ import type { AppError } from '../error/error.util.js';
|
|
|
17
17
|
export type ValidationFunction<T, ERR extends AppError> = (input: T, opt?: ValidationFunctionOptions) => ValidationFunctionResult<T, ERR>;
|
|
18
18
|
export type ValidationFunctionResult<T, ERR extends AppError> = [err: ERR | null, output: T];
|
|
19
19
|
export interface ValidationFunctionOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Defaults to false,
|
|
22
|
+
* which means that the ValidationFunction IS NOT ALLOWED to mutate the input.
|
|
23
|
+
* If set to true - the ValidationFunction HAS TO mutate the input
|
|
24
|
+
* if it needs to apply transformations, such as:
|
|
25
|
+
* - stripping unknown properties
|
|
26
|
+
* - converting types (e.g. string to number)
|
|
27
|
+
* - applying transformations (which as string trim, toLowerCase, etc)
|
|
28
|
+
*/
|
|
29
|
+
mutateInput?: boolean;
|
|
20
30
|
/**
|
|
21
31
|
* E.g User
|
|
22
32
|
* Used for error message printing.
|
|
@@ -1,44 +1,47 @@
|
|
|
1
|
+
import type { ZodString } from 'zod';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import type { IsoDate, UnixTimestamp, UnixTimestampMillis } from '../types.js';
|
|
3
4
|
type ZodBranded<T, B> = T & Record<'_zod', Record<'output', number & B>>;
|
|
4
5
|
export type ZodBrandedString<B> = ZodBranded<z.ZodString, B>;
|
|
5
6
|
export type ZodBrandedInt<B> = ZodBranded<z.ZodInt, B>;
|
|
6
7
|
export type ZodBrandedNumber<B> = ZodBranded<z.ZodNumber, B>;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export declare const BASE64URL_REGEX: RegExp;
|
|
19
|
-
export declare const zBase62: () => z.ZodString;
|
|
20
|
-
export declare const zBase64: () => z.ZodString;
|
|
21
|
-
export declare const zBase64Url: () => z.ZodString;
|
|
22
|
-
export declare const JWT_REGEX: RegExp;
|
|
23
|
-
export declare const zJwt: () => z.ZodString;
|
|
8
|
+
declare function unixTimestamp(): ZodBrandedInt<UnixTimestamp>;
|
|
9
|
+
declare function unixTimestamp2000(): ZodBrandedInt<UnixTimestamp>;
|
|
10
|
+
declare function unixTimestampMillis(): ZodBranded<z.ZodNumber, UnixTimestampMillis>;
|
|
11
|
+
declare function unixTimestampMillis2000(): ZodBrandedInt<UnixTimestampMillis>;
|
|
12
|
+
declare function semVer(): z.ZodString;
|
|
13
|
+
declare function isoDate(): ZodBrandedString<IsoDate>;
|
|
14
|
+
declare function email(): z.ZodEmail;
|
|
15
|
+
declare function base62(): z.ZodString;
|
|
16
|
+
declare function base64(): z.ZodString;
|
|
17
|
+
declare function base64Url(): z.ZodString;
|
|
18
|
+
declare function jwt(): z.ZodString;
|
|
24
19
|
/**
|
|
25
20
|
* "Slug" - a valid URL, filename, etc.
|
|
26
21
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
declare function slug(): z.ZodString;
|
|
23
|
+
declare function ianaTimezone(): z.ZodEnum;
|
|
24
|
+
type BaseDBEntityZodShape = {
|
|
25
|
+
id: ZodString;
|
|
26
|
+
created: ZodBrandedInt<UnixTimestamp>;
|
|
27
|
+
updated: ZodBrandedInt<UnixTimestamp>;
|
|
28
|
+
};
|
|
29
|
+
declare function dbEntity(): z.ZodObject<BaseDBEntityZodShape>;
|
|
30
|
+
declare function dbEntity<T extends z.ZodRawShape>(shape: T): z.ZodObject<BaseDBEntityZodShape & T>;
|
|
29
31
|
export declare const customZodSchemas: {
|
|
30
|
-
base62:
|
|
31
|
-
base64:
|
|
32
|
-
base64Url:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
base62: typeof base62;
|
|
33
|
+
base64: typeof base64;
|
|
34
|
+
base64Url: typeof base64Url;
|
|
35
|
+
dbEntity: typeof dbEntity;
|
|
36
|
+
email: typeof email;
|
|
37
|
+
ianaTimezone: typeof ianaTimezone;
|
|
38
|
+
isoDate: typeof isoDate;
|
|
39
|
+
jwt: typeof jwt;
|
|
40
|
+
slug: typeof slug;
|
|
41
|
+
semver: typeof semVer;
|
|
42
|
+
unixTimestamp: typeof unixTimestamp;
|
|
43
|
+
unixTimestamp2000: typeof unixTimestamp2000;
|
|
44
|
+
unixTimestampMillis: typeof unixTimestampMillis;
|
|
45
|
+
unixTimestampMillis2000: typeof unixTimestampMillis2000;
|
|
43
46
|
};
|
|
44
47
|
export {};
|
|
@@ -1,71 +1,106 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
2
|
+
const TS_2500 = 16725225600; // 2500-01-01
|
|
3
|
+
const TS_2000 = 946684800; // 2000-01-01
|
|
4
|
+
function unixTimestamp() {
|
|
5
|
+
return z
|
|
6
|
+
.number()
|
|
7
|
+
.int()
|
|
8
|
+
.min(0)
|
|
9
|
+
.max(TS_2500, 'Must be a UnixTimestamp number')
|
|
10
|
+
.describe('UnixTimestamp');
|
|
11
|
+
}
|
|
12
|
+
function unixTimestamp2000() {
|
|
13
|
+
return z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.min(TS_2000)
|
|
17
|
+
.max(TS_2500, 'Must be a UnixTimestamp number after 2000-01-01')
|
|
18
|
+
.describe('UnixTimestamp2000');
|
|
19
|
+
}
|
|
20
|
+
function unixTimestampMillis() {
|
|
21
|
+
return z
|
|
22
|
+
.number()
|
|
23
|
+
.int()
|
|
24
|
+
.min(0)
|
|
25
|
+
.max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number')
|
|
26
|
+
.describe('UnixTimestampMillis');
|
|
27
|
+
}
|
|
28
|
+
function unixTimestampMillis2000() {
|
|
29
|
+
return z
|
|
30
|
+
.number()
|
|
31
|
+
.int()
|
|
32
|
+
.min(TS_2000 * 1000)
|
|
33
|
+
.max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number after 2000-01-01')
|
|
34
|
+
.describe('UnixTimestampMillis2000');
|
|
35
|
+
}
|
|
36
|
+
function semVer() {
|
|
37
|
+
return z
|
|
38
|
+
.string()
|
|
39
|
+
.regex(/^[0-9]+\.[0-9]+\.[0-9]+$/, 'Must be a SemVer string')
|
|
40
|
+
.describe('SemVer');
|
|
41
|
+
}
|
|
42
|
+
function isoDate() {
|
|
43
|
+
return z
|
|
44
|
+
.string()
|
|
45
|
+
.refine(v => {
|
|
46
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(v);
|
|
47
|
+
}, 'Must be an IsoDateString')
|
|
48
|
+
.describe('IsoDateString');
|
|
49
|
+
}
|
|
50
|
+
function email() {
|
|
51
|
+
return z.email().describe('Email');
|
|
52
|
+
}
|
|
53
|
+
const BASE62_REGEX = /^[a-zA-Z0-9]+$/;
|
|
54
|
+
const BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
55
|
+
const BASE64URL_REGEX = /^[\w\-/]+$/;
|
|
56
|
+
function base62() {
|
|
57
|
+
return z.string().regex(BASE62_REGEX, 'Must be a base62 string').describe('Base62String');
|
|
58
|
+
}
|
|
59
|
+
function base64() {
|
|
60
|
+
return z.string().regex(BASE64_REGEX, 'Must be a base64 string').describe('Base64String');
|
|
61
|
+
}
|
|
62
|
+
function base64Url() {
|
|
63
|
+
return z.string().regex(BASE64URL_REGEX, 'Must be a base64url string').describe('Base64UrlString');
|
|
64
|
+
}
|
|
65
|
+
const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/;
|
|
66
|
+
function jwt() {
|
|
67
|
+
return z.string().regex(JWT_REGEX, 'Must be a JWT string').describe('JWTString');
|
|
68
|
+
}
|
|
47
69
|
/**
|
|
48
70
|
* "Slug" - a valid URL, filename, etc.
|
|
49
71
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
function slug() {
|
|
73
|
+
return z
|
|
74
|
+
.string()
|
|
75
|
+
.regex(/^[a-z0-9-]{1,255}$/, 'Must be a slug string')
|
|
76
|
+
.describe('Slug');
|
|
77
|
+
}
|
|
78
|
+
function ianaTimezone() {
|
|
79
|
+
return (z
|
|
80
|
+
// UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
|
|
81
|
+
.enum([...Intl.supportedValuesOf('timeZone'), 'UTC']));
|
|
82
|
+
}
|
|
83
|
+
const baseDBEntitySchema = z.object({
|
|
84
|
+
id: z.string(),
|
|
85
|
+
created: unixTimestamp2000(),
|
|
86
|
+
updated: unixTimestamp2000(),
|
|
87
|
+
});
|
|
88
|
+
function dbEntity(shape) {
|
|
89
|
+
return baseDBEntitySchema.extend(shape ?? {});
|
|
90
|
+
}
|
|
57
91
|
export const customZodSchemas = {
|
|
58
|
-
base62
|
|
59
|
-
base64
|
|
60
|
-
base64Url
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
92
|
+
base62,
|
|
93
|
+
base64,
|
|
94
|
+
base64Url,
|
|
95
|
+
dbEntity,
|
|
96
|
+
email,
|
|
97
|
+
ianaTimezone,
|
|
98
|
+
isoDate,
|
|
99
|
+
jwt,
|
|
100
|
+
slug,
|
|
101
|
+
semver: semVer,
|
|
102
|
+
unixTimestamp,
|
|
103
|
+
unixTimestamp2000,
|
|
104
|
+
unixTimestampMillis,
|
|
105
|
+
unixTimestampMillis2000,
|
|
71
106
|
};
|
package/dist/zod/zod.util.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ZodError, ZodType } from 'zod';
|
|
2
2
|
import type { ErrorData } from '../error/error.model.js';
|
|
3
3
|
import { AppError } from '../error/error.util.js';
|
|
4
|
-
import type { ValidationFunctionResult } from '../validation/validation.js';
|
|
4
|
+
import type { ValidationFunction, ValidationFunctionResult } from '../validation/validation.js';
|
|
5
|
+
export declare function getZodValidationFunction<T>(schema: ZodType<T>): ValidationFunction<T, ZodValidationError>;
|
|
5
6
|
export declare function zIsValid<T>(value: T, schema: ZodType<T>): boolean;
|
|
6
7
|
export declare function zValidate<T>(value: T, schema: ZodType<T>): T;
|
|
7
8
|
export declare function zSafeValidate<T>(input: T, schema: ZodType<T>): ValidationFunctionResult<T, ZodValidationError>;
|
package/dist/zod/zod.util.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { _assert } from '../error/assert.js';
|
|
1
2
|
import { AppError } from '../error/error.util.js';
|
|
2
3
|
import { _stringify } from '../string/stringify.js';
|
|
4
|
+
export function getZodValidationFunction(schema) {
|
|
5
|
+
return (input, opt) => {
|
|
6
|
+
_assert(!opt?.mutateInput, 'mutateInput=true is not yet supported with Zod');
|
|
7
|
+
return zSafeValidate(input, schema);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
3
10
|
export function zIsValid(value, schema) {
|
|
4
11
|
const { success } = schema.safeParse(value);
|
|
5
12
|
return success;
|
|
@@ -27,9 +34,9 @@ export class ZodValidationError extends AppError {
|
|
|
27
34
|
function createZodErrorMessage(err, schema, value) {
|
|
28
35
|
let objectTitle = schema.description;
|
|
29
36
|
if (typeof value === 'object' && value) {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
objectTitle = [
|
|
37
|
+
const inputName = schema.description || value.constructor?.name;
|
|
38
|
+
const inputId = value['id'];
|
|
39
|
+
objectTitle = [inputName, inputId].filter(Boolean).join('.');
|
|
33
40
|
}
|
|
34
41
|
objectTitle ||= 'data';
|
|
35
42
|
return [
|
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.22.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"tslib": "^2",
|
|
7
7
|
"zod": "^4"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@types/semver": "^7",
|
|
13
13
|
"crypto-js": "^4",
|
|
14
14
|
"dayjs": "^1",
|
|
15
|
-
"@naturalcycles/dev-lib": "19.
|
|
15
|
+
"@naturalcycles/dev-lib": "19.22.0"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./dist/index.js",
|
|
@@ -932,7 +932,7 @@ class LocalTimeFactory {
|
|
|
932
932
|
/**
|
|
933
933
|
* Throws on invalid value.
|
|
934
934
|
*/
|
|
935
|
-
|
|
935
|
+
validateDateTimeObject(o: DateTimeObject): void {
|
|
936
936
|
_assert(
|
|
937
937
|
this.isDateTimeObjectValid(o),
|
|
938
938
|
`Cannot construct LocalTime from: ${o.year}-${o.month}-${o.day} ${o.hour}:${o.minute}:${o.second}`,
|
|
@@ -23,6 +23,16 @@ export type ValidationFunction<T, ERR extends AppError> = (
|
|
|
23
23
|
export type ValidationFunctionResult<T, ERR extends AppError> = [err: ERR | null, output: T]
|
|
24
24
|
|
|
25
25
|
export interface ValidationFunctionOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Defaults to false,
|
|
28
|
+
* which means that the ValidationFunction IS NOT ALLOWED to mutate the input.
|
|
29
|
+
* If set to true - the ValidationFunction HAS TO mutate the input
|
|
30
|
+
* if it needs to apply transformations, such as:
|
|
31
|
+
* - stripping unknown properties
|
|
32
|
+
* - converting types (e.g. string to number)
|
|
33
|
+
* - applying transformations (which as string trim, toLowerCase, etc)
|
|
34
|
+
*/
|
|
35
|
+
mutateInput?: boolean
|
|
26
36
|
/**
|
|
27
37
|
* E.g User
|
|
28
38
|
* Used for error message printing.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ZodString } from 'zod'
|
|
1
2
|
import { z } from 'zod'
|
|
2
3
|
import type { IsoDate, UnixTimestamp, UnixTimestampMillis } from '../types.js'
|
|
3
4
|
|
|
@@ -6,100 +7,138 @@ export type ZodBrandedString<B> = ZodBranded<z.ZodString, B>
|
|
|
6
7
|
export type ZodBrandedInt<B> = ZodBranded<z.ZodInt, B>
|
|
7
8
|
export type ZodBrandedNumber<B> = ZodBranded<z.ZodNumber, B>
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const TS_2500 = 16725225600 // 2500-01-01
|
|
11
|
+
const TS_2000 = 946684800 // 2000-01-01
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
z
|
|
13
|
+
function unixTimestamp(): ZodBrandedInt<UnixTimestamp> {
|
|
14
|
+
return z
|
|
14
15
|
.number()
|
|
15
16
|
.int()
|
|
16
17
|
.min(0)
|
|
17
18
|
.max(TS_2500, 'Must be a UnixTimestamp number')
|
|
18
19
|
.describe('UnixTimestamp') as ZodBrandedInt<UnixTimestamp>
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
z
|
|
22
|
+
function unixTimestamp2000(): ZodBrandedInt<UnixTimestamp> {
|
|
23
|
+
return z
|
|
22
24
|
.number()
|
|
23
25
|
.int()
|
|
24
26
|
.min(TS_2000)
|
|
25
27
|
.max(TS_2500, 'Must be a UnixTimestamp number after 2000-01-01')
|
|
26
28
|
.describe('UnixTimestamp2000') as ZodBrandedInt<UnixTimestamp>
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
z
|
|
31
|
+
function unixTimestampMillis(): ZodBranded<z.ZodNumber, UnixTimestampMillis> {
|
|
32
|
+
return z
|
|
30
33
|
.number()
|
|
31
34
|
.int()
|
|
32
35
|
.min(0)
|
|
33
36
|
.max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number')
|
|
34
37
|
.describe('UnixTimestampMillis') as ZodBrandedInt<UnixTimestampMillis>
|
|
38
|
+
}
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
z
|
|
40
|
+
function unixTimestampMillis2000(): ZodBrandedInt<UnixTimestampMillis> {
|
|
41
|
+
return z
|
|
38
42
|
.number()
|
|
39
43
|
.int()
|
|
40
44
|
.min(TS_2000 * 1000)
|
|
41
45
|
.max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number after 2000-01-01')
|
|
42
46
|
.describe('UnixTimestampMillis2000') as ZodBrandedInt<UnixTimestampMillis>
|
|
47
|
+
}
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
z
|
|
49
|
+
function semVer(): z.ZodString {
|
|
50
|
+
return z
|
|
46
51
|
.string()
|
|
47
52
|
.regex(/^[0-9]+\.[0-9]+\.[0-9]+$/, 'Must be a SemVer string')
|
|
48
53
|
.describe('SemVer')
|
|
54
|
+
}
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
z
|
|
56
|
+
function isoDate(): ZodBrandedString<IsoDate> {
|
|
57
|
+
return z
|
|
52
58
|
.string()
|
|
53
59
|
.refine(v => {
|
|
54
60
|
return /^\d{4}-\d{2}-\d{2}$/.test(v)
|
|
55
61
|
}, 'Must be an IsoDateString')
|
|
56
62
|
.describe('IsoDateString') as ZodBrandedString<IsoDate>
|
|
63
|
+
}
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
function email(): z.ZodEmail {
|
|
66
|
+
return z.email().describe('Email')
|
|
67
|
+
}
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
const BASE62_REGEX = /^[a-zA-Z0-9]+$/
|
|
70
|
+
const BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/
|
|
71
|
+
const BASE64URL_REGEX = /^[\w\-/]+$/
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
z.string().regex(BASE62_REGEX, 'Must be a base62 string').describe('Base62String')
|
|
73
|
+
function base62(): z.ZodString {
|
|
74
|
+
return z.string().regex(BASE62_REGEX, 'Must be a base62 string').describe('Base62String')
|
|
75
|
+
}
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
z.string().regex(BASE64_REGEX, 'Must be a base64 string').describe('Base64String')
|
|
77
|
+
function base64(): z.ZodString {
|
|
78
|
+
return z.string().regex(BASE64_REGEX, 'Must be a base64 string').describe('Base64String')
|
|
79
|
+
}
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
z.string().regex(BASE64URL_REGEX, 'Must be a base64url string').describe('Base64UrlString')
|
|
81
|
+
function base64Url(): z.ZodString {
|
|
82
|
+
return z.string().regex(BASE64URL_REGEX, 'Must be a base64url string').describe('Base64UrlString')
|
|
83
|
+
}
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/
|
|
86
|
+
|
|
87
|
+
function jwt(): z.ZodString {
|
|
88
|
+
return z.string().regex(JWT_REGEX, 'Must be a JWT string').describe('JWTString')
|
|
89
|
+
}
|
|
76
90
|
|
|
77
91
|
/**
|
|
78
92
|
* "Slug" - a valid URL, filename, etc.
|
|
79
93
|
*/
|
|
80
|
-
|
|
81
|
-
z
|
|
94
|
+
function slug(): z.ZodString {
|
|
95
|
+
return z
|
|
82
96
|
.string()
|
|
83
97
|
.regex(/^[a-z0-9-]{1,255}$/, 'Must be a slug string')
|
|
84
98
|
.describe('Slug')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function ianaTimezone(): z.ZodEnum {
|
|
102
|
+
return (
|
|
103
|
+
z
|
|
104
|
+
// UTC is added to assist unit-testing, which uses UTC by default (not technically a valid Iana timezone identifier)
|
|
105
|
+
.enum([...Intl.supportedValuesOf('timeZone'), 'UTC'])
|
|
106
|
+
)
|
|
107
|
+
}
|
|
85
108
|
|
|
86
|
-
|
|
87
|
-
z
|
|
88
|
-
|
|
89
|
-
|
|
109
|
+
const baseDBEntitySchema = z.object({
|
|
110
|
+
id: z.string(),
|
|
111
|
+
created: unixTimestamp2000(),
|
|
112
|
+
updated: unixTimestamp2000(),
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
|
116
|
+
type BaseDBEntityZodShape = {
|
|
117
|
+
id: ZodString
|
|
118
|
+
created: ZodBrandedInt<UnixTimestamp>
|
|
119
|
+
updated: ZodBrandedInt<UnixTimestamp>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function dbEntity(): z.ZodObject<BaseDBEntityZodShape>
|
|
123
|
+
function dbEntity<T extends z.ZodRawShape>(shape: T): z.ZodObject<BaseDBEntityZodShape & T>
|
|
124
|
+
|
|
125
|
+
function dbEntity<T extends z.ZodRawShape>(shape?: T): z.ZodObject<BaseDBEntityZodShape & T> {
|
|
126
|
+
return baseDBEntitySchema.extend(shape ?? {}) as z.ZodObject<BaseDBEntityZodShape & T>
|
|
127
|
+
}
|
|
90
128
|
|
|
91
129
|
export const customZodSchemas = {
|
|
92
|
-
base62
|
|
93
|
-
base64
|
|
94
|
-
base64Url
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
base62,
|
|
131
|
+
base64,
|
|
132
|
+
base64Url,
|
|
133
|
+
dbEntity,
|
|
134
|
+
email,
|
|
135
|
+
ianaTimezone,
|
|
136
|
+
isoDate,
|
|
137
|
+
jwt,
|
|
138
|
+
slug,
|
|
139
|
+
semver: semVer,
|
|
140
|
+
unixTimestamp,
|
|
141
|
+
unixTimestamp2000,
|
|
142
|
+
unixTimestampMillis,
|
|
143
|
+
unixTimestampMillis2000,
|
|
105
144
|
}
|
package/src/zod/zod.util.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import type { ZodError, ZodType } from 'zod'
|
|
2
|
+
import { _assert } from '../error/assert.js'
|
|
2
3
|
import type { ErrorData } from '../error/error.model.js'
|
|
3
4
|
import { AppError } from '../error/error.util.js'
|
|
4
5
|
import { _stringify } from '../string/stringify.js'
|
|
5
|
-
import type { ValidationFunctionResult } from '../validation/validation.js'
|
|
6
|
+
import type { ValidationFunction, ValidationFunctionResult } from '../validation/validation.js'
|
|
7
|
+
|
|
8
|
+
export function getZodValidationFunction<T>(
|
|
9
|
+
schema: ZodType<T>,
|
|
10
|
+
): ValidationFunction<T, ZodValidationError> {
|
|
11
|
+
return (input, opt) => {
|
|
12
|
+
_assert(!opt?.mutateInput, 'mutateInput=true is not yet supported with Zod')
|
|
13
|
+
return zSafeValidate(input, schema)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
6
16
|
|
|
7
17
|
export function zIsValid<T>(value: T, schema: ZodType<T>): boolean {
|
|
8
18
|
const { success } = schema.safeParse(value)
|
|
@@ -18,7 +28,7 @@ export function zValidate<T>(value: T, schema: ZodType<T>): T {
|
|
|
18
28
|
export function zSafeValidate<T>(
|
|
19
29
|
input: T,
|
|
20
30
|
schema: ZodType<T>,
|
|
21
|
-
//
|
|
31
|
+
// inputName?: string,
|
|
22
32
|
): ValidationFunctionResult<T, ZodValidationError> {
|
|
23
33
|
const r = schema.safeParse(input)
|
|
24
34
|
if (r.success) {
|
|
@@ -28,19 +38,7 @@ export function zSafeValidate<T>(
|
|
|
28
38
|
return [new ZodValidationError(r.error, input, schema), r.data ?? input]
|
|
29
39
|
}
|
|
30
40
|
|
|
31
|
-
export interface ZodValidationErrorData extends ErrorData {
|
|
32
|
-
// issues: $ZodIssue[]
|
|
33
|
-
// joiValidationObjectName?: string
|
|
34
|
-
// joiValidationObjectId?: string
|
|
35
|
-
/**
|
|
36
|
-
* Error "annotation" is stripped in Error.message.
|
|
37
|
-
* This field contains the "full" annotation.
|
|
38
|
-
*
|
|
39
|
-
* This field is non-enumerable, won't be printed or included in JSON by default,
|
|
40
|
-
* but still accessible programmatically (via `err.data.annotation`) when needed!
|
|
41
|
-
*/
|
|
42
|
-
// annotation?: string
|
|
43
|
-
}
|
|
41
|
+
export interface ZodValidationErrorData extends ErrorData {}
|
|
44
42
|
|
|
45
43
|
export class ZodValidationError extends AppError<ZodValidationErrorData> {
|
|
46
44
|
constructor(zodError: ZodError, value: any, schema: ZodType) {
|
|
@@ -54,9 +52,9 @@ function createZodErrorMessage<T>(err: ZodError<T>, schema: ZodType<T>, value: T
|
|
|
54
52
|
let objectTitle = schema.description
|
|
55
53
|
|
|
56
54
|
if (typeof value === 'object' && value) {
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
objectTitle = [
|
|
55
|
+
const inputName = schema.description || value.constructor?.name
|
|
56
|
+
const inputId = (value as any)['id'] as string
|
|
57
|
+
objectTitle = [inputName, inputId].filter(Boolean).join('.')
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
objectTitle ||= 'data'
|