@cleverbrush/schema 0.0.12 → 0.0.15
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/index.d.ts +22 -13
- package/dist/schemaValidator.d.ts +7 -8
- package/dist/schemaValidator.js +28 -10
- package/dist/validators/validateBoolean.d.ts +2 -0
- package/dist/validators/validateBoolean.js +33 -0
- package/dist/validators/validateObject.d.ts +1 -1
- package/dist/validators/validateObject.js +16 -1
- package/package.json +2 -2
- package/src/index.ts +47 -12
- package/src/schemaValidator.test.ts +197 -46
- package/src/schemaValidator.ts +77 -36
- package/src/validators/validateBoolean.ts +45 -0
- package/src/validators/validateObject.ts +21 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { Merge } from '@cleverbrush/deep';
|
|
1
2
|
import SchemaValidator from './schemaValidator';
|
|
2
|
-
export declare type DefaultSchemaType = 'object' | 'function' | 'number' | 'string' | 'array' | 'alias';
|
|
3
|
+
export declare type DefaultSchemaType = 'object' | 'boolean' | 'function' | 'number' | 'string' | 'array' | 'alias';
|
|
3
4
|
export declare type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];
|
|
4
5
|
export declare type DefaultPropertyDefinition = {
|
|
5
6
|
isRequired: boolean;
|
|
@@ -24,7 +25,7 @@ export declare type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'>
|
|
|
24
25
|
[S in keyof T]: Schema<PropType<T, S>>;
|
|
25
26
|
}>;
|
|
26
27
|
preprocessors?: Partial<{
|
|
27
|
-
[S in keyof T | '*']: S extends keyof T ? ((value: unknown) => PropType<T, S> | Promise<PropType<T, S>>) | string : (value: T) => void | Promise<void>;
|
|
28
|
+
[S in keyof T | '*']: S extends keyof T ? ((value: unknown) => undefined | PropType<T, S> | Promise<PropType<T, S>>) | string : (value: T) => void | Promise<void>;
|
|
28
29
|
}>;
|
|
29
30
|
};
|
|
30
31
|
export declare type AliasSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
@@ -39,6 +40,10 @@ export declare type FunctionSchemaDefinition<T extends (...args: any) => any> =
|
|
|
39
40
|
type: 'function';
|
|
40
41
|
params?: ParamsValidators<T>;
|
|
41
42
|
};
|
|
43
|
+
export declare type BooleanSchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, 'type'> & {
|
|
44
|
+
type: 'boolean';
|
|
45
|
+
equals?: boolean;
|
|
46
|
+
};
|
|
42
47
|
export declare type NumberSchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, 'type'> & {
|
|
43
48
|
type: 'number';
|
|
44
49
|
min?: number;
|
|
@@ -61,25 +66,29 @@ export declare type ArraySchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, 't
|
|
|
61
66
|
minLength?: number;
|
|
62
67
|
maxLength?: number;
|
|
63
68
|
};
|
|
64
|
-
export declare type CompositeSchema<TObj> = TObj extends (...args: any) => any ? FunctionSchemaDefinition<TObj> : TObj extends number ? NumberSchemaDefinition<TObj> : TObj extends string ? StringSchemaDefinition<TObj> | 'string' : NumberSchemaDefinition<TObj> | StringSchemaDefinition<TObj> | ArraySchemaDefinition<TObj> | ObjectSchemaDefinition<TObj> | AliasSchemaDefinition<TObj>;
|
|
69
|
+
export declare type CompositeSchema<TObj> = TObj extends (...args: any) => any ? FunctionSchemaDefinition<TObj> : TObj extends number ? NumberSchemaDefinition<TObj> : TObj extends string ? StringSchemaDefinition<TObj> | 'string' : BooleanSchemaDefinition<TObj> | NumberSchemaDefinition<TObj> | StringSchemaDefinition<TObj> | ArraySchemaDefinition<TObj> | ObjectSchemaDefinition<TObj> | AliasSchemaDefinition<TObj>;
|
|
65
70
|
export declare type SingleSchema<TObj = Record<string, never>> = number | string | DefaultSchemaType | CompositeSchema<TObj>;
|
|
66
71
|
export declare type Schema<TObj> = SingleSchema<TObj> | Array<SingleSchema<TObj>>;
|
|
67
|
-
export
|
|
72
|
+
export declare type Cons<H, T extends unknown[] = []> = T['length'] extends 0 ? [H] : ((h: H, ...t: T) => void) extends (...r: infer R) => void ? R : never;
|
|
73
|
+
export interface ISchemaActions<S> {
|
|
68
74
|
validate(value: any): Promise<ValidationResult>;
|
|
69
|
-
schema:
|
|
75
|
+
schema: S;
|
|
70
76
|
}
|
|
71
|
-
export interface ISchemasProvider<T
|
|
72
|
-
schemas:
|
|
73
|
-
[K in keyof T]: ISchemaActions<T, K>;
|
|
74
|
-
};
|
|
77
|
+
export interface ISchemasProvider<T extends unknown[]> {
|
|
78
|
+
schemas: Merge<T>;
|
|
75
79
|
}
|
|
76
|
-
export interface ISchemaValidator<T = Record<string, never
|
|
80
|
+
export interface ISchemaValidator<T extends Record<string, never> = Record<string, never>, SchemaTypesStructures extends unknown[] = []> {
|
|
77
81
|
get preprocessors(): Map<string, (value: unknown) => unknown | Promise<unknown>>;
|
|
78
|
-
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T>;
|
|
79
|
-
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M
|
|
82
|
+
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T, SchemaTypesStructures>;
|
|
83
|
+
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>, M = any>(name: keyof K, schema: L): SchemaValidator<T & {
|
|
80
84
|
[key in keyof K]: typeof schema;
|
|
81
|
-
}
|
|
85
|
+
}, Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>>;
|
|
82
86
|
validate(schema: keyof T | DefaultSchemaType | Schema<any>, obj: any): Promise<ValidationResult>;
|
|
83
87
|
}
|
|
88
|
+
export declare type Unfold<T, K> = T extends string ? T extends `${infer F}.${infer L}` ? {
|
|
89
|
+
[k in F]: Unfold<L, K>;
|
|
90
|
+
} : {
|
|
91
|
+
[k in T]: K;
|
|
92
|
+
} : never;
|
|
84
93
|
export { SchemaValidator };
|
|
85
94
|
export default SchemaValidator;
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { Merge } from '@cleverbrush/deep';
|
|
2
|
+
import { ISchemasProvider, Schema, ISchemaActions, ObjectSchemaDefinitionParam, ValidationResult, ISchemaValidator, DefaultSchemaType, Cons, Unfold } from './index';
|
|
3
|
+
export default class SchemaValidator<T extends Record<string, never> = Record<string, never>, SchemaTypesStructures extends unknown[] = []> implements ISchemasProvider<SchemaTypesStructures>, ISchemaValidator<T, SchemaTypesStructures> {
|
|
3
4
|
private _schemasMap;
|
|
4
5
|
private _schemasCache;
|
|
5
6
|
private _preprocessorsMap;
|
|
6
7
|
get preprocessors(): Map<string, (value: unknown) => unknown>;
|
|
7
|
-
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T>;
|
|
8
|
-
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M
|
|
8
|
+
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T, SchemaTypesStructures>;
|
|
9
|
+
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>, M = any>(name: keyof K, schema: L): SchemaValidator<T & {
|
|
9
10
|
[key in keyof K]: L;
|
|
10
|
-
}
|
|
11
|
-
get schemas():
|
|
12
|
-
[K in keyof T]: ISchemaActions<T, K>;
|
|
13
|
-
};
|
|
11
|
+
}, Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>>;
|
|
12
|
+
get schemas(): Merge<SchemaTypesStructures>;
|
|
14
13
|
private validateDefaultType;
|
|
15
14
|
private checkValidators;
|
|
16
15
|
validate<K>(schema: keyof T | DefaultSchemaType | Schema<K>, obj: any): Promise<ValidationResult>;
|
package/dist/schemaValidator.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const deep_1 = require("@cleverbrush/deep");
|
|
4
4
|
const validateNumber_1 = require("./validators/validateNumber");
|
|
5
|
+
const validateBoolean_1 = require("./validators/validateBoolean");
|
|
5
6
|
const validateString_1 = require("./validators/validateString");
|
|
6
7
|
const validateArray_1 = require("./validators/validateArray");
|
|
7
8
|
const validateObject_1 = require("./validators/validateObject");
|
|
8
|
-
const defaultSchemaNames = ['string', 'number', 'date', 'array'];
|
|
9
|
+
const defaultSchemaNames = ['string', 'boolean', 'number', 'date', 'array'];
|
|
9
10
|
const defaultSchemas = {
|
|
10
11
|
number: {
|
|
11
12
|
type: 'number',
|
|
@@ -14,6 +15,11 @@ const defaultSchemas = {
|
|
|
14
15
|
ensureNotNaN: true,
|
|
15
16
|
ensureIsFinite: true
|
|
16
17
|
},
|
|
18
|
+
boolean: {
|
|
19
|
+
type: 'boolean',
|
|
20
|
+
isRequired: true,
|
|
21
|
+
isNullable: false
|
|
22
|
+
},
|
|
17
23
|
string: {
|
|
18
24
|
type: 'string',
|
|
19
25
|
isNullable: false,
|
|
@@ -30,6 +36,7 @@ const defaultSchemas = {
|
|
|
30
36
|
};
|
|
31
37
|
const defaultSchemasValidationStrategies = {
|
|
32
38
|
number: (obj, schema, validator) => (0, validateNumber_1.validateNumber)(obj, schema, validator),
|
|
39
|
+
boolean: (obj, schema, validator) => (0, validateBoolean_1.validateBoolean)(obj, schema, validator),
|
|
33
40
|
string: (obj, schema, validator) => (0, validateString_1.validateString)(obj, schema, validator),
|
|
34
41
|
array: (obj, schema, validator) => (0, validateArray_1.validateArray)(obj, schema, validator)
|
|
35
42
|
};
|
|
@@ -74,10 +81,22 @@ class SchemaValidator {
|
|
|
74
81
|
return this._schemasCache;
|
|
75
82
|
const res = {};
|
|
76
83
|
for (const key of this._schemasMap.keys()) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
const parts = key.split('.');
|
|
85
|
+
let curr = res;
|
|
86
|
+
for (let i = 0; i < parts.length; i++) {
|
|
87
|
+
if (i === parts.length - 1) {
|
|
88
|
+
curr[parts[i]] = {
|
|
89
|
+
validate: (value) => this.validate(this._schemasMap.get(key), value),
|
|
90
|
+
schema: this._schemasMap.get(key)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
if (typeof curr[parts[i]] === 'undefined') {
|
|
95
|
+
curr[parts[i]] = {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
curr = curr[parts[i]];
|
|
99
|
+
}
|
|
81
100
|
}
|
|
82
101
|
this._schemasCache = res;
|
|
83
102
|
return this._schemasCache;
|
|
@@ -142,12 +161,11 @@ class SchemaValidator {
|
|
|
142
161
|
if (isDefaultType(schema)) {
|
|
143
162
|
return await this.validateDefaultType(schema, obj);
|
|
144
163
|
}
|
|
145
|
-
else if (typeof this.
|
|
146
|
-
if (Array.isArray(this.
|
|
147
|
-
return this.validate(this.
|
|
148
|
-
.schema, obj);
|
|
164
|
+
else if (typeof this._schemasMap.get(schema) !== 'undefined') {
|
|
165
|
+
if (Array.isArray(this._schemasMap.get(schema))) {
|
|
166
|
+
return this.validate(this._schemasMap.get(schema), obj);
|
|
149
167
|
}
|
|
150
|
-
const objSchema = (0, deep_1.deepExtend)(defaultSchemas.object, this.
|
|
168
|
+
const objSchema = (0, deep_1.deepExtend)(defaultSchemas.object, this._schemasMap.get(schema));
|
|
151
169
|
const res = await (0, validateObject_1.validateObject)(obj, objSchema, this);
|
|
152
170
|
if (!res.valid)
|
|
153
171
|
return res;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateBoolean = void 0;
|
|
4
|
+
const validateBoolean = async (obj, schema, validator) => {
|
|
5
|
+
if (typeof obj === 'undefined' &&
|
|
6
|
+
typeof schema === 'object' &&
|
|
7
|
+
schema.isRequired === false) {
|
|
8
|
+
return {
|
|
9
|
+
valid: true
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
if (typeof obj !== 'boolean')
|
|
13
|
+
return {
|
|
14
|
+
valid: false,
|
|
15
|
+
errors: [`expected type boolean, but saw ${typeof obj}`]
|
|
16
|
+
};
|
|
17
|
+
if (typeof schema === 'boolean') {
|
|
18
|
+
return { valid: true };
|
|
19
|
+
}
|
|
20
|
+
const bool = obj;
|
|
21
|
+
if (typeof schema.equals === 'boolean') {
|
|
22
|
+
return schema.equals === bool
|
|
23
|
+
? { valid: true }
|
|
24
|
+
: {
|
|
25
|
+
valid: false,
|
|
26
|
+
errors: [`must be equal to ${schema.equals}`]
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
valid: true
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
exports.validateBoolean = validateBoolean;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ValidationResult, ObjectSchemaDefinition, ISchemaValidator } from '../index';
|
|
2
|
-
export declare const validateObject: (obj: any, schema: ObjectSchemaDefinition<any>, validator: ISchemaValidator<any>) => Promise<ValidationResult>;
|
|
2
|
+
export declare const validateObject: (obj: any, schema: ObjectSchemaDefinition<any>, validator: ISchemaValidator<any, unknown[]>) => Promise<ValidationResult>;
|
|
@@ -19,8 +19,10 @@ const validateObject = async (obj, schema, validator) => {
|
|
|
19
19
|
}
|
|
20
20
|
if (typeof schema.preprocessors === 'object' && schema.preprocessors) {
|
|
21
21
|
await Promise.all(Object.keys(schema.preprocessors).map(async (key) => {
|
|
22
|
+
if (key === '*')
|
|
23
|
+
return;
|
|
22
24
|
if (typeof schema.preprocessors[key] === 'function') {
|
|
23
|
-
obj[key] = await Promise.resolve(schema.preprocessors[key](
|
|
25
|
+
obj[key] = await Promise.resolve(schema.preprocessors[key](obj[key]));
|
|
24
26
|
}
|
|
25
27
|
else {
|
|
26
28
|
const preprocessor = validator.preprocessors.get(schema.preprocessors[key]);
|
|
@@ -30,6 +32,19 @@ const validateObject = async (obj, schema, validator) => {
|
|
|
30
32
|
obj[key] = await Promise.resolve(preprocessor(obj[key]));
|
|
31
33
|
}
|
|
32
34
|
}));
|
|
35
|
+
if ('*' in schema.preprocessors) {
|
|
36
|
+
const objectPreprocessor = schema.preprocessors['*'];
|
|
37
|
+
if (typeof objectPreprocessor === 'function') {
|
|
38
|
+
await Promise.resolve(objectPreprocessor(obj));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const preprocessor = validator.preprocessors.get(objectPreprocessor);
|
|
42
|
+
if (typeof preprocessor !== 'function') {
|
|
43
|
+
throw new Error(`preprocessor '${objectPreprocessor}' is unknown`);
|
|
44
|
+
}
|
|
45
|
+
await Promise.resolve(preprocessor(obj));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
33
48
|
}
|
|
34
49
|
if (typeof schema.properties === 'object' && schema.properties) {
|
|
35
50
|
const errors = (await Promise.all(Object.entries(schema.properties).map(async ([name, schema]) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleverbrush/schema",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"object schema validator",
|
|
6
6
|
"schema",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"build": "tsc"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@cleverbrush/deep": "0.0.
|
|
22
|
+
"@cleverbrush/deep": "0.0.15"
|
|
23
23
|
},
|
|
24
24
|
"types": "./dist/index.d.ts"
|
|
25
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Merge } from '@cleverbrush/deep';
|
|
1
2
|
import SchemaValidator from './schemaValidator';
|
|
2
3
|
|
|
3
4
|
export type DefaultSchemaType =
|
|
4
5
|
| 'object'
|
|
6
|
+
| 'boolean'
|
|
5
7
|
| 'function'
|
|
6
8
|
| 'number'
|
|
7
9
|
| 'string'
|
|
@@ -44,7 +46,7 @@ export type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
|
44
46
|
?
|
|
45
47
|
| ((
|
|
46
48
|
value: unknown
|
|
47
|
-
) => PropType<T, S> | Promise<PropType<T, S>>)
|
|
49
|
+
) => undefined | PropType<T, S> | Promise<PropType<T, S>>)
|
|
48
50
|
| string
|
|
49
51
|
: (value: T) => void | Promise<void>;
|
|
50
52
|
}>;
|
|
@@ -72,6 +74,14 @@ export type FunctionSchemaDefinition<T extends (...args: any) => any> = Omit<
|
|
|
72
74
|
params?: ParamsValidators<T>;
|
|
73
75
|
};
|
|
74
76
|
|
|
77
|
+
export type BooleanSchemaDefinition<TObj> = Omit<
|
|
78
|
+
SchemaDefintion<TObj>,
|
|
79
|
+
'type'
|
|
80
|
+
> & {
|
|
81
|
+
type: 'boolean';
|
|
82
|
+
equals?: boolean;
|
|
83
|
+
};
|
|
84
|
+
|
|
75
85
|
export type NumberSchemaDefinition<TObj> = Omit<
|
|
76
86
|
SchemaDefintion<TObj>,
|
|
77
87
|
'type'
|
|
@@ -113,6 +123,7 @@ export type CompositeSchema<TObj> = TObj extends (...args: any) => any
|
|
|
113
123
|
: TObj extends string
|
|
114
124
|
? StringSchemaDefinition<TObj> | 'string'
|
|
115
125
|
:
|
|
126
|
+
| BooleanSchemaDefinition<TObj>
|
|
116
127
|
| NumberSchemaDefinition<TObj>
|
|
117
128
|
| StringSchemaDefinition<TObj>
|
|
118
129
|
| ArraySchemaDefinition<TObj>
|
|
@@ -127,18 +138,25 @@ export type SingleSchema<TObj = Record<string, never>> =
|
|
|
127
138
|
|
|
128
139
|
export type Schema<TObj> = SingleSchema<TObj> | Array<SingleSchema<TObj>>;
|
|
129
140
|
|
|
130
|
-
export
|
|
141
|
+
export type Cons<H, T extends unknown[] = []> = T['length'] extends 0
|
|
142
|
+
? [H]
|
|
143
|
+
: ((h: H, ...t: T) => void) extends (...r: infer R) => void
|
|
144
|
+
? R
|
|
145
|
+
: never;
|
|
146
|
+
|
|
147
|
+
export interface ISchemaActions<S> {
|
|
131
148
|
validate(value: any): Promise<ValidationResult>;
|
|
132
|
-
schema:
|
|
149
|
+
schema: S;
|
|
133
150
|
}
|
|
134
151
|
|
|
135
|
-
export interface ISchemasProvider<T
|
|
136
|
-
schemas:
|
|
137
|
-
[K in keyof T]: ISchemaActions<T, K>;
|
|
138
|
-
};
|
|
152
|
+
export interface ISchemasProvider<T extends unknown[]> {
|
|
153
|
+
schemas: Merge<T>;
|
|
139
154
|
}
|
|
140
155
|
|
|
141
|
-
export interface ISchemaValidator<
|
|
156
|
+
export interface ISchemaValidator<
|
|
157
|
+
T extends Record<string, never> = Record<string, never>,
|
|
158
|
+
SchemaTypesStructures extends unknown[] = []
|
|
159
|
+
> {
|
|
142
160
|
get preprocessors(): Map<
|
|
143
161
|
string,
|
|
144
162
|
(value: unknown) => unknown | Promise<unknown>
|
|
@@ -146,12 +164,19 @@ export interface ISchemaValidator<T = Record<string, never>> {
|
|
|
146
164
|
addPreprocessor(
|
|
147
165
|
name: string,
|
|
148
166
|
preprocessor: (value: unknown) => unknown | Promise<unknown>
|
|
149
|
-
): SchemaValidator<T>;
|
|
167
|
+
): SchemaValidator<T, SchemaTypesStructures>;
|
|
150
168
|
|
|
151
|
-
addSchemaType<
|
|
169
|
+
addSchemaType<
|
|
170
|
+
K,
|
|
171
|
+
L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>,
|
|
172
|
+
M = any
|
|
173
|
+
>(
|
|
152
174
|
name: keyof K,
|
|
153
|
-
schema: L
|
|
154
|
-
): SchemaValidator<
|
|
175
|
+
schema: L
|
|
176
|
+
): SchemaValidator<
|
|
177
|
+
T & { [key in keyof K]: typeof schema },
|
|
178
|
+
Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
|
|
179
|
+
>;
|
|
155
180
|
|
|
156
181
|
validate(
|
|
157
182
|
schema: keyof T | DefaultSchemaType | Schema<any>,
|
|
@@ -159,5 +184,15 @@ export interface ISchemaValidator<T = Record<string, never>> {
|
|
|
159
184
|
): Promise<ValidationResult>;
|
|
160
185
|
}
|
|
161
186
|
|
|
187
|
+
export type Unfold<T, K> = T extends string
|
|
188
|
+
? T extends `${infer F}.${infer L}`
|
|
189
|
+
? {
|
|
190
|
+
[k in F]: Unfold<L, K>;
|
|
191
|
+
}
|
|
192
|
+
: {
|
|
193
|
+
[k in T]: K;
|
|
194
|
+
}
|
|
195
|
+
: never;
|
|
196
|
+
|
|
162
197
|
export { SchemaValidator };
|
|
163
198
|
export default SchemaValidator;
|
|
@@ -170,7 +170,7 @@ test('Validate - no schema', async () => {
|
|
|
170
170
|
const validator = new SchemaValidator();
|
|
171
171
|
const cth = jest.fn();
|
|
172
172
|
validator
|
|
173
|
-
.validate(null, 10)
|
|
173
|
+
.validate(null as any, 10)
|
|
174
174
|
.catch(cth)
|
|
175
175
|
.then(() => {
|
|
176
176
|
expect(cth).toBeCalled();
|
|
@@ -554,6 +554,82 @@ test('Validate - number by schema - custom validators - 2', async () => {
|
|
|
554
554
|
expect(result).toHaveProperty('valid', false);
|
|
555
555
|
});
|
|
556
556
|
|
|
557
|
+
test('Validate - boolean - 1', async () => {
|
|
558
|
+
const validator = new SchemaValidator();
|
|
559
|
+
const result = await validator.validate(
|
|
560
|
+
{
|
|
561
|
+
type: 'boolean'
|
|
562
|
+
},
|
|
563
|
+
300
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
expect(result).toHaveProperty('valid', false);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test('Validate - boolean - 2', async () => {
|
|
570
|
+
const validator = new SchemaValidator();
|
|
571
|
+
const result = await validator.validate(
|
|
572
|
+
{
|
|
573
|
+
type: 'boolean'
|
|
574
|
+
},
|
|
575
|
+
true
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
expect(result).toHaveProperty('valid', true);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test('Validate - boolean - 3', async () => {
|
|
582
|
+
const validator = new SchemaValidator();
|
|
583
|
+
const result = await validator.validate(
|
|
584
|
+
{
|
|
585
|
+
type: 'boolean'
|
|
586
|
+
},
|
|
587
|
+
false
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
expect(result).toHaveProperty('valid', true);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test('Validate - boolean - 4', async () => {
|
|
594
|
+
const validator = new SchemaValidator();
|
|
595
|
+
const result = await validator.validate(
|
|
596
|
+
{
|
|
597
|
+
type: 'boolean',
|
|
598
|
+
equals: true
|
|
599
|
+
},
|
|
600
|
+
false
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
expect(result).toHaveProperty('valid', false);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
test('Validate - boolean - 5', async () => {
|
|
607
|
+
const validator = new SchemaValidator();
|
|
608
|
+
const result = await validator.validate(
|
|
609
|
+
{
|
|
610
|
+
type: 'boolean',
|
|
611
|
+
equals: false
|
|
612
|
+
},
|
|
613
|
+
false
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
expect(result).toHaveProperty('valid', true);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test('Validate - boolean - 6', async () => {
|
|
620
|
+
const validator = new SchemaValidator();
|
|
621
|
+
const result = await validator.validate('boolean', false);
|
|
622
|
+
|
|
623
|
+
expect(result).toHaveProperty('valid', true);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test('Validate - boolean - 7', async () => {
|
|
627
|
+
const validator = new SchemaValidator();
|
|
628
|
+
const result = await validator.validate('boolean', '123');
|
|
629
|
+
|
|
630
|
+
expect(result).toHaveProperty('valid', false);
|
|
631
|
+
});
|
|
632
|
+
|
|
557
633
|
test('Validate - string - 1', async () => {
|
|
558
634
|
const validator = new SchemaValidator();
|
|
559
635
|
const result = await validator.validate('string', '12345');
|
|
@@ -868,7 +944,7 @@ test('Validate - object - 2', async () => {
|
|
|
868
944
|
};
|
|
869
945
|
}
|
|
870
946
|
]
|
|
871
|
-
})
|
|
947
|
+
}) as any as ObjectSchemaDefinition<any>
|
|
872
948
|
);
|
|
873
949
|
const user: User = {
|
|
874
950
|
id: 1,
|
|
@@ -1123,19 +1199,30 @@ test('Validate schema - 1', async () => {
|
|
|
1123
1199
|
});
|
|
1124
1200
|
|
|
1125
1201
|
test('Validate schema - 2', async () => {
|
|
1126
|
-
const validator = new SchemaValidator()
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1202
|
+
const validator = new SchemaValidator()
|
|
1203
|
+
.addSchemaType('Date', {
|
|
1204
|
+
validators: [
|
|
1205
|
+
(value) =>
|
|
1206
|
+
value instanceof Date && !Number.isNaN(value)
|
|
1207
|
+
? {
|
|
1208
|
+
valid: true
|
|
1209
|
+
}
|
|
1210
|
+
: {
|
|
1211
|
+
valid: false,
|
|
1212
|
+
errors: ['should be a valid Date object']
|
|
1213
|
+
}
|
|
1214
|
+
]
|
|
1215
|
+
})
|
|
1216
|
+
.addSchemaType('Module.Schema1', {
|
|
1217
|
+
properties: {
|
|
1218
|
+
a: 'string'
|
|
1219
|
+
}
|
|
1220
|
+
})
|
|
1221
|
+
.addSchemaType('Module.Schema2', {
|
|
1222
|
+
properties: {
|
|
1223
|
+
b: 'number'
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1139
1226
|
|
|
1140
1227
|
const result = await validator.validate(
|
|
1141
1228
|
{
|
|
@@ -1225,8 +1312,10 @@ test('Preprocessors - 1', async () => {
|
|
|
1225
1312
|
bornAt: 'Date'
|
|
1226
1313
|
},
|
|
1227
1314
|
preprocessors: {
|
|
1228
|
-
bornAt: (value: unknown): Date => {
|
|
1229
|
-
const time = Date.parse(
|
|
1315
|
+
bornAt: (value: unknown): Date | undefined => {
|
|
1316
|
+
const time = Date.parse(
|
|
1317
|
+
(value as Record<string, unknown>).toString()
|
|
1318
|
+
);
|
|
1230
1319
|
if (Number.isNaN(time)) return undefined;
|
|
1231
1320
|
return new Date(time);
|
|
1232
1321
|
}
|
|
@@ -1264,18 +1353,28 @@ test('Preprocessors - 2', async () => {
|
|
|
1264
1353
|
}
|
|
1265
1354
|
};
|
|
1266
1355
|
|
|
1267
|
-
validator.addPreprocessor(
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
expect(() =>
|
|
1274
|
-
validator.addPreprocessor('StringToDate', (value: unknown): Date => {
|
|
1275
|
-
const time = Date.parse(value.toString());
|
|
1356
|
+
validator.addPreprocessor(
|
|
1357
|
+
'StringToDate',
|
|
1358
|
+
(value: unknown): Date | undefined => {
|
|
1359
|
+
const time = Date.parse(
|
|
1360
|
+
(value as Record<string, unknown>).toString()
|
|
1361
|
+
);
|
|
1276
1362
|
if (Number.isNaN(time)) return undefined;
|
|
1277
1363
|
return new Date(time);
|
|
1278
|
-
}
|
|
1364
|
+
}
|
|
1365
|
+
);
|
|
1366
|
+
|
|
1367
|
+
expect(() =>
|
|
1368
|
+
validator.addPreprocessor(
|
|
1369
|
+
'StringToDate',
|
|
1370
|
+
(value: unknown): Date | undefined => {
|
|
1371
|
+
const time = Date.parse(
|
|
1372
|
+
(value as Record<string, unknown>).toString()
|
|
1373
|
+
);
|
|
1374
|
+
if (Number.isNaN(time)) return undefined;
|
|
1375
|
+
return new Date(time);
|
|
1376
|
+
}
|
|
1377
|
+
)
|
|
1279
1378
|
).toThrow();
|
|
1280
1379
|
|
|
1281
1380
|
const result = await validator.validate(schema, {
|
|
@@ -1316,11 +1415,16 @@ test('Preprocessors - 3', async () => {
|
|
|
1316
1415
|
]
|
|
1317
1416
|
});
|
|
1318
1417
|
|
|
1319
|
-
validator.addPreprocessor(
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1418
|
+
validator.addPreprocessor(
|
|
1419
|
+
'StringToDate',
|
|
1420
|
+
(value: unknown): Date | undefined => {
|
|
1421
|
+
const time = Date.parse(
|
|
1422
|
+
(value as Record<string, unknown>).toString()
|
|
1423
|
+
);
|
|
1424
|
+
if (Number.isNaN(time)) return undefined;
|
|
1425
|
+
return new Date(time);
|
|
1426
|
+
}
|
|
1427
|
+
);
|
|
1324
1428
|
|
|
1325
1429
|
let obj = [new Date().toJSON()];
|
|
1326
1430
|
|
|
@@ -1337,8 +1441,10 @@ test('Preprocessors - 3', async () => {
|
|
|
1337
1441
|
obj = [new Date().toJSON()];
|
|
1338
1442
|
result = await validator.validate(
|
|
1339
1443
|
{
|
|
1340
|
-
preprocessor: (value: unknown): Date => {
|
|
1341
|
-
const time = Date.parse(
|
|
1444
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1445
|
+
const time = Date.parse(
|
|
1446
|
+
(value as Record<string, unknown>).toString()
|
|
1447
|
+
);
|
|
1342
1448
|
if (Number.isNaN(time)) return undefined;
|
|
1343
1449
|
return new Date(time);
|
|
1344
1450
|
},
|
|
@@ -1352,8 +1458,10 @@ test('Preprocessors - 3', async () => {
|
|
|
1352
1458
|
obj = ['sdfsdf12', new Date().toJSON()];
|
|
1353
1459
|
result = await validator.validate(
|
|
1354
1460
|
{
|
|
1355
|
-
preprocessor: (value: unknown): Date => {
|
|
1356
|
-
const time = Date.parse(
|
|
1461
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1462
|
+
const time = Date.parse(
|
|
1463
|
+
(value as Record<string, unknown>).toString()
|
|
1464
|
+
);
|
|
1357
1465
|
if (Number.isNaN(time)) return undefined;
|
|
1358
1466
|
return new Date(time);
|
|
1359
1467
|
},
|
|
@@ -1377,6 +1485,7 @@ test('Preprocessors - 3', async () => {
|
|
|
1377
1485
|
marriedAt: 'number'
|
|
1378
1486
|
},
|
|
1379
1487
|
preprocessors: {
|
|
1488
|
+
age: (value): number => 40,
|
|
1380
1489
|
'*': (value: SomeType): void => {
|
|
1381
1490
|
if (value.marriedAt > value.age) {
|
|
1382
1491
|
value.marriedAt = value.age;
|
|
@@ -1390,7 +1499,7 @@ test('Preprocessors - 3', async () => {
|
|
|
1390
1499
|
marriedAt: 80
|
|
1391
1500
|
};
|
|
1392
1501
|
await validator.validate(schema, obj);
|
|
1393
|
-
expect(obj).toHaveProperty('marriedAt',
|
|
1502
|
+
expect(obj).toHaveProperty('marriedAt', 40);
|
|
1394
1503
|
});
|
|
1395
1504
|
|
|
1396
1505
|
test('Preprocessors - 4', async () => {
|
|
@@ -1408,11 +1517,16 @@ test('Preprocessors - 4', async () => {
|
|
|
1408
1517
|
]
|
|
1409
1518
|
});
|
|
1410
1519
|
|
|
1411
|
-
validator.addPreprocessor(
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1520
|
+
validator.addPreprocessor(
|
|
1521
|
+
'StringToDate',
|
|
1522
|
+
(value: unknown): Date | undefined => {
|
|
1523
|
+
const time = Date.parse(
|
|
1524
|
+
(value as Record<string, unknown>).toString()
|
|
1525
|
+
);
|
|
1526
|
+
if (Number.isNaN(time)) return undefined;
|
|
1527
|
+
return new Date(time);
|
|
1528
|
+
}
|
|
1529
|
+
);
|
|
1416
1530
|
|
|
1417
1531
|
let obj = [new Date().toJSON()];
|
|
1418
1532
|
|
|
@@ -1429,8 +1543,10 @@ test('Preprocessors - 4', async () => {
|
|
|
1429
1543
|
obj = [new Date().toJSON()];
|
|
1430
1544
|
result = await validator.validate(
|
|
1431
1545
|
{
|
|
1432
|
-
preprocessor: (value: unknown): Date => {
|
|
1433
|
-
const time = Date.parse(
|
|
1546
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1547
|
+
const time = Date.parse(
|
|
1548
|
+
(value as Record<string, unknown>).toString()
|
|
1549
|
+
);
|
|
1434
1550
|
if (Number.isNaN(time)) return undefined;
|
|
1435
1551
|
return new Date(time);
|
|
1436
1552
|
},
|
|
@@ -1444,8 +1560,10 @@ test('Preprocessors - 4', async () => {
|
|
|
1444
1560
|
obj = ['sdfsdf12', new Date().toJSON()];
|
|
1445
1561
|
result = await validator.validate(
|
|
1446
1562
|
{
|
|
1447
|
-
preprocessor: (value: unknown): Date => {
|
|
1448
|
-
const time = Date.parse(
|
|
1563
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1564
|
+
const time = Date.parse(
|
|
1565
|
+
(value as Record<string, unknown>).toString()
|
|
1566
|
+
);
|
|
1449
1567
|
if (Number.isNaN(time)) return undefined;
|
|
1450
1568
|
return new Date(time);
|
|
1451
1569
|
},
|
|
@@ -1456,3 +1574,36 @@ test('Preprocessors - 4', async () => {
|
|
|
1456
1574
|
);
|
|
1457
1575
|
expect(result).toHaveProperty('valid', false);
|
|
1458
1576
|
});
|
|
1577
|
+
|
|
1578
|
+
test('Submodules - 1', async () => {
|
|
1579
|
+
const validator = new SchemaValidator().addSchemaType(
|
|
1580
|
+
'Module1.Schema1',
|
|
1581
|
+
{}
|
|
1582
|
+
);
|
|
1583
|
+
|
|
1584
|
+
const result = validator.schemas;
|
|
1585
|
+
|
|
1586
|
+
expect(result).toHaveProperty('Module1');
|
|
1587
|
+
expect(result).toHaveProperty('Module1.Schema1');
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
test('Submodules - 2', async () => {
|
|
1591
|
+
const validator = new SchemaValidator()
|
|
1592
|
+
.addSchemaType('Module1.Schema1', {})
|
|
1593
|
+
.addSchemaType('Module1.Schema2', {
|
|
1594
|
+
properties: {
|
|
1595
|
+
a: 'number'
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
const result = validator.schemas;
|
|
1600
|
+
|
|
1601
|
+
expect(result).toHaveProperty('Module1');
|
|
1602
|
+
expect(result).toHaveProperty('Module1.Schema1');
|
|
1603
|
+
expect(result).toHaveProperty('Module1.Schema2');
|
|
1604
|
+
|
|
1605
|
+
const result2 = await validator.schemas.Module1.Schema2.validate({
|
|
1606
|
+
a: 234
|
|
1607
|
+
});
|
|
1608
|
+
expect(result2).toHaveProperty('valid', true);
|
|
1609
|
+
});
|
package/src/schemaValidator.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepExtend } from '@cleverbrush/deep';
|
|
1
|
+
import { deepExtend, Merge } from '@cleverbrush/deep';
|
|
2
2
|
import {
|
|
3
3
|
ISchemasProvider,
|
|
4
4
|
Schema,
|
|
@@ -13,14 +13,17 @@ import {
|
|
|
13
13
|
ISchemaValidator,
|
|
14
14
|
DefaultSchemaType,
|
|
15
15
|
ObjectSchemaDefinition,
|
|
16
|
-
|
|
16
|
+
BooleanSchemaDefinition,
|
|
17
|
+
Cons,
|
|
18
|
+
Unfold
|
|
17
19
|
} from './index';
|
|
18
20
|
import { validateNumber } from './validators/validateNumber';
|
|
21
|
+
import { validateBoolean } from './validators/validateBoolean';
|
|
19
22
|
import { validateString } from './validators/validateString';
|
|
20
23
|
import { validateArray } from './validators/validateArray';
|
|
21
24
|
import { validateObject } from './validators/validateObject';
|
|
22
25
|
|
|
23
|
-
const defaultSchemaNames = ['string', 'number', 'date', 'array'];
|
|
26
|
+
const defaultSchemaNames = ['string', 'boolean', 'number', 'date', 'array'];
|
|
24
27
|
|
|
25
28
|
const defaultSchemas: { [key in DefaultSchemaType]?: Schema<any> } = {
|
|
26
29
|
number: {
|
|
@@ -30,6 +33,11 @@ const defaultSchemas: { [key in DefaultSchemaType]?: Schema<any> } = {
|
|
|
30
33
|
ensureNotNaN: true,
|
|
31
34
|
ensureIsFinite: true
|
|
32
35
|
},
|
|
36
|
+
boolean: {
|
|
37
|
+
type: 'boolean',
|
|
38
|
+
isRequired: true,
|
|
39
|
+
isNullable: false
|
|
40
|
+
},
|
|
33
41
|
string: {
|
|
34
42
|
type: 'string',
|
|
35
43
|
isNullable: false,
|
|
@@ -49,31 +57,40 @@ const defaultSchemasValidationStrategies = {
|
|
|
49
57
|
number: (
|
|
50
58
|
obj: any,
|
|
51
59
|
schema: NumberSchemaDefinition<any>,
|
|
52
|
-
validator: ISchemaValidator<any>
|
|
60
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
53
61
|
): Promise<ValidationResult> => validateNumber(obj, schema, validator),
|
|
62
|
+
boolean: (
|
|
63
|
+
obj: any,
|
|
64
|
+
schema: BooleanSchemaDefinition<any>,
|
|
65
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
66
|
+
): Promise<ValidationResult> => validateBoolean(obj, schema, validator),
|
|
54
67
|
string: (
|
|
55
68
|
obj: any,
|
|
56
69
|
schema: StringSchemaDefinition<any>,
|
|
57
|
-
validator: ISchemaValidator<any>
|
|
70
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
58
71
|
): Promise<ValidationResult> => validateString(obj, schema, validator),
|
|
59
72
|
array: (
|
|
60
73
|
obj: any,
|
|
61
74
|
schema: ArraySchemaDefinition<any>,
|
|
62
|
-
validator: ISchemaValidator<any>
|
|
75
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
63
76
|
): Promise<ValidationResult> => validateArray(obj, schema, validator)
|
|
64
77
|
};
|
|
65
78
|
|
|
66
79
|
const isDefaultType = (name: string): boolean =>
|
|
67
80
|
defaultSchemaNames.indexOf(name) !== -1;
|
|
68
81
|
|
|
69
|
-
export default class SchemaValidator<
|
|
70
|
-
|
|
82
|
+
export default class SchemaValidator<
|
|
83
|
+
T extends Record<string, never> = Record<string, never>,
|
|
84
|
+
SchemaTypesStructures extends unknown[] = []
|
|
85
|
+
> implements
|
|
86
|
+
ISchemasProvider<SchemaTypesStructures>,
|
|
87
|
+
ISchemaValidator<T, SchemaTypesStructures>
|
|
71
88
|
{
|
|
72
89
|
private _schemasMap = new Map<string, Schema<any>>();
|
|
73
|
-
private _schemasCache:
|
|
90
|
+
private _schemasCache: Merge<SchemaTypesStructures> = null;
|
|
74
91
|
private _preprocessorsMap = new Map<
|
|
75
92
|
string,
|
|
76
|
-
(value: unknown) => unknown | Promise<unknown>
|
|
93
|
+
(value: unknown) => unknown | Promise<unknown> | undefined
|
|
77
94
|
>();
|
|
78
95
|
|
|
79
96
|
public get preprocessors(): Map<string, (value: unknown) => unknown> {
|
|
@@ -83,17 +100,24 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
83
100
|
public addPreprocessor(
|
|
84
101
|
name: string,
|
|
85
102
|
preprocessor: (value: unknown) => unknown | Promise<unknown>
|
|
86
|
-
): SchemaValidator<T> {
|
|
103
|
+
): SchemaValidator<T, SchemaTypesStructures> {
|
|
87
104
|
if (this._preprocessorsMap.has(name))
|
|
88
105
|
throw new Error(`Preprocessor '${name}' is already registered`);
|
|
89
106
|
this._preprocessorsMap.set(name, preprocessor);
|
|
90
107
|
return this;
|
|
91
108
|
}
|
|
92
109
|
|
|
93
|
-
public addSchemaType<
|
|
110
|
+
public addSchemaType<
|
|
111
|
+
K,
|
|
112
|
+
L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>,
|
|
113
|
+
M = any
|
|
114
|
+
>(
|
|
94
115
|
name: keyof K,
|
|
95
|
-
schema: L
|
|
96
|
-
): SchemaValidator<
|
|
116
|
+
schema: L
|
|
117
|
+
): SchemaValidator<
|
|
118
|
+
T & { [key in keyof K]: L },
|
|
119
|
+
Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
|
|
120
|
+
> {
|
|
97
121
|
if (typeof name !== 'string' || !name)
|
|
98
122
|
throw new Error('Name is required');
|
|
99
123
|
|
|
@@ -108,7 +132,10 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
108
132
|
if (Array.isArray(schema)) {
|
|
109
133
|
this._schemasMap.set(name.toString(), schema as Schema<any>);
|
|
110
134
|
this._schemasCache = null;
|
|
111
|
-
return this as any as SchemaValidator<
|
|
135
|
+
return this as any as SchemaValidator<
|
|
136
|
+
T & { [key in keyof K]: L },
|
|
137
|
+
Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
|
|
138
|
+
>;
|
|
112
139
|
}
|
|
113
140
|
|
|
114
141
|
if (typeof schema !== 'object')
|
|
@@ -119,20 +146,34 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
119
146
|
} as Schema<any>);
|
|
120
147
|
this._schemasCache = null;
|
|
121
148
|
|
|
122
|
-
return this as any as SchemaValidator<
|
|
149
|
+
return this as any as SchemaValidator<
|
|
150
|
+
T & { [key in keyof K]: L },
|
|
151
|
+
Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
|
|
152
|
+
>;
|
|
123
153
|
}
|
|
124
154
|
|
|
125
|
-
public get schemas():
|
|
155
|
+
public get schemas(): Merge<SchemaTypesStructures> {
|
|
126
156
|
if (this._schemasCache) return this._schemasCache;
|
|
127
157
|
const res = {};
|
|
128
158
|
for (const key of this._schemasMap.keys()) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
const parts = key.split('.');
|
|
160
|
+
let curr = res;
|
|
161
|
+
for (let i = 0; i < parts.length; i++) {
|
|
162
|
+
if (i === parts.length - 1) {
|
|
163
|
+
curr[parts[i]] = {
|
|
164
|
+
validate: (value: any): Promise<any> =>
|
|
165
|
+
this.validate(this._schemasMap.get(key), value),
|
|
166
|
+
schema: this._schemasMap.get(key)
|
|
167
|
+
};
|
|
168
|
+
} else {
|
|
169
|
+
if (typeof curr[parts[i]] === 'undefined') {
|
|
170
|
+
curr[parts[i]] = {};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
curr = curr[parts[i]];
|
|
174
|
+
}
|
|
134
175
|
}
|
|
135
|
-
this._schemasCache = res as
|
|
176
|
+
this._schemasCache = res as Merge<SchemaTypesStructures>;
|
|
136
177
|
return this._schemasCache;
|
|
137
178
|
}
|
|
138
179
|
|
|
@@ -144,14 +185,14 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
144
185
|
const strategy = defaultSchemasValidationStrategies[name] as (
|
|
145
186
|
obj: any,
|
|
146
187
|
schema: Schema<any>,
|
|
147
|
-
validator: ISchemaValidator<any>
|
|
188
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
148
189
|
) => ValidationResult;
|
|
149
190
|
let finalSchema = defaultSchemas[name] as CompositeSchema<
|
|
150
191
|
Record<string, never>
|
|
151
192
|
>;
|
|
152
193
|
if (strategy) {
|
|
153
194
|
if (typeof mergeSchema === 'object') {
|
|
154
|
-
finalSchema = deepExtend(finalSchema, mergeSchema);
|
|
195
|
+
finalSchema = deepExtend(finalSchema, mergeSchema) as any;
|
|
155
196
|
}
|
|
156
197
|
if (typeof mergeSchema === 'number') {
|
|
157
198
|
finalSchema = {
|
|
@@ -161,7 +202,11 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
161
202
|
mergeSchema;
|
|
162
203
|
}
|
|
163
204
|
|
|
164
|
-
let preliminaryResult = await strategy(
|
|
205
|
+
let preliminaryResult = await strategy(
|
|
206
|
+
value,
|
|
207
|
+
finalSchema,
|
|
208
|
+
this as any
|
|
209
|
+
);
|
|
165
210
|
if (!preliminaryResult.valid) return preliminaryResult;
|
|
166
211
|
|
|
167
212
|
preliminaryResult = await this.checkValidators(finalSchema, value);
|
|
@@ -225,19 +270,15 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
225
270
|
schema as DefaultSchemaType,
|
|
226
271
|
obj
|
|
227
272
|
);
|
|
228
|
-
} else if (typeof this.
|
|
229
|
-
if (Array.isArray(this.
|
|
230
|
-
return this.validate(
|
|
231
|
-
this.schemas[schema as keyof T]
|
|
232
|
-
.schema as any as Schema<any>,
|
|
233
|
-
obj
|
|
234
|
-
);
|
|
273
|
+
} else if (typeof this._schemasMap.get(schema) !== 'undefined') {
|
|
274
|
+
if (Array.isArray(this._schemasMap.get(schema))) {
|
|
275
|
+
return this.validate(this._schemasMap.get(schema), obj);
|
|
235
276
|
}
|
|
236
277
|
const objSchema = deepExtend(
|
|
237
278
|
defaultSchemas.object,
|
|
238
|
-
this.
|
|
279
|
+
this._schemasMap.get(schema)
|
|
239
280
|
) as ObjectSchemaDefinition<Record<string, never>>;
|
|
240
|
-
const res = await validateObject(obj, objSchema, this);
|
|
281
|
+
const res = await validateObject(obj, objSchema, this as any);
|
|
241
282
|
if (!res.valid) return res;
|
|
242
283
|
return await this.checkValidators(objSchema, obj);
|
|
243
284
|
} else {
|
|
@@ -306,7 +347,7 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
306
347
|
const preliminaryResult = await validateObject(
|
|
307
348
|
obj,
|
|
308
349
|
schema,
|
|
309
|
-
this
|
|
350
|
+
this as any
|
|
310
351
|
);
|
|
311
352
|
if (!preliminaryResult.valid) return preliminaryResult;
|
|
312
353
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationResult,
|
|
3
|
+
BooleanSchemaDefinition,
|
|
4
|
+
ISchemaValidator
|
|
5
|
+
} from '../index';
|
|
6
|
+
|
|
7
|
+
export const validateBoolean = async (
|
|
8
|
+
obj: any,
|
|
9
|
+
schema: BooleanSchemaDefinition<any>,
|
|
10
|
+
validator: ISchemaValidator<any>
|
|
11
|
+
): Promise<ValidationResult> => {
|
|
12
|
+
if (
|
|
13
|
+
typeof obj === 'undefined' &&
|
|
14
|
+
typeof schema === 'object' &&
|
|
15
|
+
schema.isRequired === false
|
|
16
|
+
) {
|
|
17
|
+
return {
|
|
18
|
+
valid: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (typeof obj !== 'boolean')
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
errors: [`expected type boolean, but saw ${typeof obj}`]
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (typeof schema === 'boolean') {
|
|
28
|
+
return { valid: true };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const bool = obj as boolean;
|
|
32
|
+
|
|
33
|
+
if (typeof schema.equals === 'boolean') {
|
|
34
|
+
return schema.equals === bool
|
|
35
|
+
? { valid: true }
|
|
36
|
+
: {
|
|
37
|
+
valid: false,
|
|
38
|
+
errors: [`must be equal to ${schema.equals}`]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
valid: true
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
export const validateObject = async (
|
|
9
9
|
obj: any,
|
|
10
10
|
schema: ObjectSchemaDefinition<any>,
|
|
11
|
-
validator: ISchemaValidator<any>
|
|
11
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
12
12
|
): Promise<ValidationResult> => {
|
|
13
13
|
if (
|
|
14
14
|
typeof obj === 'undefined' &&
|
|
@@ -31,10 +31,11 @@ export const validateObject = async (
|
|
|
31
31
|
if (typeof schema.preprocessors === 'object' && schema.preprocessors) {
|
|
32
32
|
await Promise.all(
|
|
33
33
|
Object.keys(schema.preprocessors).map(async (key: string) => {
|
|
34
|
+
if (key === '*') return;
|
|
34
35
|
if (typeof schema.preprocessors[key] === 'function') {
|
|
35
36
|
obj[key] = await Promise.resolve(
|
|
36
37
|
(schema.preprocessors[key] as (unknown) => unknown)(
|
|
37
|
-
|
|
38
|
+
obj[key]
|
|
38
39
|
)
|
|
39
40
|
);
|
|
40
41
|
} else {
|
|
@@ -50,6 +51,24 @@ export const validateObject = async (
|
|
|
50
51
|
}
|
|
51
52
|
})
|
|
52
53
|
);
|
|
54
|
+
if ('*' in schema.preprocessors) {
|
|
55
|
+
const objectPreprocessor = schema.preprocessors['*'];
|
|
56
|
+
if (typeof objectPreprocessor === 'function') {
|
|
57
|
+
await Promise.resolve(
|
|
58
|
+
(objectPreprocessor as (unknown) => unknown)(obj)
|
|
59
|
+
);
|
|
60
|
+
} else {
|
|
61
|
+
const preprocessor = validator.preprocessors.get(
|
|
62
|
+
objectPreprocessor as string
|
|
63
|
+
);
|
|
64
|
+
if (typeof preprocessor !== 'function') {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`preprocessor '${objectPreprocessor}' is unknown`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
await Promise.resolve(preprocessor(obj));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
if (typeof schema.properties === 'object' && schema.properties) {
|