@cleverbrush/schema 0.0.14 → 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 +16 -11
- package/dist/schemaValidator.d.ts +7 -8
- package/dist/schemaValidator.js +20 -9
- package/dist/validators/validateObject.d.ts +1 -1
- package/package.json +2 -2
- package/src/index.ts +37 -12
- package/src/schemaValidator.test.ts +119 -45
- package/src/schemaValidator.ts +66 -36
- package/src/validators/validateObject.ts +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Merge } from '@cleverbrush/deep';
|
|
1
2
|
import SchemaValidator from './schemaValidator';
|
|
2
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];
|
|
@@ -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'> & {
|
|
@@ -68,22 +69,26 @@ export declare type ArraySchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, 't
|
|
|
68
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>;
|
|
69
70
|
export declare type SingleSchema<TObj = Record<string, never>> = number | string | DefaultSchemaType | CompositeSchema<TObj>;
|
|
70
71
|
export declare type Schema<TObj> = SingleSchema<TObj> | Array<SingleSchema<TObj>>;
|
|
71
|
-
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> {
|
|
72
74
|
validate(value: any): Promise<ValidationResult>;
|
|
73
|
-
schema:
|
|
75
|
+
schema: S;
|
|
74
76
|
}
|
|
75
|
-
export interface ISchemasProvider<T
|
|
76
|
-
schemas:
|
|
77
|
-
[K in keyof T]: ISchemaActions<T, K>;
|
|
78
|
-
};
|
|
77
|
+
export interface ISchemasProvider<T extends unknown[]> {
|
|
78
|
+
schemas: Merge<T>;
|
|
79
79
|
}
|
|
80
|
-
export interface ISchemaValidator<T = Record<string, never
|
|
80
|
+
export interface ISchemaValidator<T extends Record<string, never> = Record<string, never>, SchemaTypesStructures extends unknown[] = []> {
|
|
81
81
|
get preprocessors(): Map<string, (value: unknown) => unknown | Promise<unknown>>;
|
|
82
|
-
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T>;
|
|
83
|
-
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 & {
|
|
84
84
|
[key in keyof K]: typeof schema;
|
|
85
|
-
}
|
|
85
|
+
}, Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>>;
|
|
86
86
|
validate(schema: keyof T | DefaultSchemaType | Schema<any>, obj: any): Promise<ValidationResult>;
|
|
87
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;
|
|
88
93
|
export { SchemaValidator };
|
|
89
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
|
@@ -81,10 +81,22 @@ class SchemaValidator {
|
|
|
81
81
|
return this._schemasCache;
|
|
82
82
|
const res = {};
|
|
83
83
|
for (const key of this._schemasMap.keys()) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
}
|
|
88
100
|
}
|
|
89
101
|
this._schemasCache = res;
|
|
90
102
|
return this._schemasCache;
|
|
@@ -149,12 +161,11 @@ class SchemaValidator {
|
|
|
149
161
|
if (isDefaultType(schema)) {
|
|
150
162
|
return await this.validateDefaultType(schema, obj);
|
|
151
163
|
}
|
|
152
|
-
else if (typeof this.
|
|
153
|
-
if (Array.isArray(this.
|
|
154
|
-
return this.validate(this.
|
|
155
|
-
.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);
|
|
156
167
|
}
|
|
157
|
-
const objSchema = (0, deep_1.deepExtend)(defaultSchemas.object, this.
|
|
168
|
+
const objSchema = (0, deep_1.deepExtend)(defaultSchemas.object, this._schemasMap.get(schema));
|
|
158
169
|
const res = await (0, validateObject_1.validateObject)(obj, objSchema, this);
|
|
159
170
|
if (!res.valid)
|
|
160
171
|
return res;
|
|
@@ -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>;
|
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,3 +1,4 @@
|
|
|
1
|
+
import { Merge } from '@cleverbrush/deep';
|
|
1
2
|
import SchemaValidator from './schemaValidator';
|
|
2
3
|
|
|
3
4
|
export type DefaultSchemaType =
|
|
@@ -45,7 +46,7 @@ export type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
|
45
46
|
?
|
|
46
47
|
| ((
|
|
47
48
|
value: unknown
|
|
48
|
-
) => PropType<T, S> | Promise<PropType<T, S>>)
|
|
49
|
+
) => undefined | PropType<T, S> | Promise<PropType<T, S>>)
|
|
49
50
|
| string
|
|
50
51
|
: (value: T) => void | Promise<void>;
|
|
51
52
|
}>;
|
|
@@ -137,18 +138,25 @@ export type SingleSchema<TObj = Record<string, never>> =
|
|
|
137
138
|
|
|
138
139
|
export type Schema<TObj> = SingleSchema<TObj> | Array<SingleSchema<TObj>>;
|
|
139
140
|
|
|
140
|
-
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> {
|
|
141
148
|
validate(value: any): Promise<ValidationResult>;
|
|
142
|
-
schema:
|
|
149
|
+
schema: S;
|
|
143
150
|
}
|
|
144
151
|
|
|
145
|
-
export interface ISchemasProvider<T
|
|
146
|
-
schemas:
|
|
147
|
-
[K in keyof T]: ISchemaActions<T, K>;
|
|
148
|
-
};
|
|
152
|
+
export interface ISchemasProvider<T extends unknown[]> {
|
|
153
|
+
schemas: Merge<T>;
|
|
149
154
|
}
|
|
150
155
|
|
|
151
|
-
export interface ISchemaValidator<
|
|
156
|
+
export interface ISchemaValidator<
|
|
157
|
+
T extends Record<string, never> = Record<string, never>,
|
|
158
|
+
SchemaTypesStructures extends unknown[] = []
|
|
159
|
+
> {
|
|
152
160
|
get preprocessors(): Map<
|
|
153
161
|
string,
|
|
154
162
|
(value: unknown) => unknown | Promise<unknown>
|
|
@@ -156,12 +164,19 @@ export interface ISchemaValidator<T = Record<string, never>> {
|
|
|
156
164
|
addPreprocessor(
|
|
157
165
|
name: string,
|
|
158
166
|
preprocessor: (value: unknown) => unknown | Promise<unknown>
|
|
159
|
-
): SchemaValidator<T>;
|
|
167
|
+
): SchemaValidator<T, SchemaTypesStructures>;
|
|
160
168
|
|
|
161
|
-
addSchemaType<
|
|
169
|
+
addSchemaType<
|
|
170
|
+
K,
|
|
171
|
+
L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>,
|
|
172
|
+
M = any
|
|
173
|
+
>(
|
|
162
174
|
name: keyof K,
|
|
163
|
-
schema: L
|
|
164
|
-
): SchemaValidator<
|
|
175
|
+
schema: L
|
|
176
|
+
): SchemaValidator<
|
|
177
|
+
T & { [key in keyof K]: typeof schema },
|
|
178
|
+
Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
|
|
179
|
+
>;
|
|
165
180
|
|
|
166
181
|
validate(
|
|
167
182
|
schema: keyof T | DefaultSchemaType | Schema<any>,
|
|
@@ -169,5 +184,15 @@ export interface ISchemaValidator<T = Record<string, never>> {
|
|
|
169
184
|
): Promise<ValidationResult>;
|
|
170
185
|
}
|
|
171
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
|
+
|
|
172
197
|
export { SchemaValidator };
|
|
173
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();
|
|
@@ -944,7 +944,7 @@ test('Validate - object - 2', async () => {
|
|
|
944
944
|
};
|
|
945
945
|
}
|
|
946
946
|
]
|
|
947
|
-
})
|
|
947
|
+
}) as any as ObjectSchemaDefinition<any>
|
|
948
948
|
);
|
|
949
949
|
const user: User = {
|
|
950
950
|
id: 1,
|
|
@@ -1199,19 +1199,30 @@ test('Validate schema - 1', async () => {
|
|
|
1199
1199
|
});
|
|
1200
1200
|
|
|
1201
1201
|
test('Validate schema - 2', async () => {
|
|
1202
|
-
const validator = new SchemaValidator()
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
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
|
+
});
|
|
1215
1226
|
|
|
1216
1227
|
const result = await validator.validate(
|
|
1217
1228
|
{
|
|
@@ -1301,8 +1312,10 @@ test('Preprocessors - 1', async () => {
|
|
|
1301
1312
|
bornAt: 'Date'
|
|
1302
1313
|
},
|
|
1303
1314
|
preprocessors: {
|
|
1304
|
-
bornAt: (value: unknown): Date => {
|
|
1305
|
-
const time = Date.parse(
|
|
1315
|
+
bornAt: (value: unknown): Date | undefined => {
|
|
1316
|
+
const time = Date.parse(
|
|
1317
|
+
(value as Record<string, unknown>).toString()
|
|
1318
|
+
);
|
|
1306
1319
|
if (Number.isNaN(time)) return undefined;
|
|
1307
1320
|
return new Date(time);
|
|
1308
1321
|
}
|
|
@@ -1340,18 +1353,28 @@ test('Preprocessors - 2', async () => {
|
|
|
1340
1353
|
}
|
|
1341
1354
|
};
|
|
1342
1355
|
|
|
1343
|
-
validator.addPreprocessor(
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
expect(() =>
|
|
1350
|
-
validator.addPreprocessor('StringToDate', (value: unknown): Date => {
|
|
1351
|
-
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
|
+
);
|
|
1352
1362
|
if (Number.isNaN(time)) return undefined;
|
|
1353
1363
|
return new Date(time);
|
|
1354
|
-
}
|
|
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
|
+
)
|
|
1355
1378
|
).toThrow();
|
|
1356
1379
|
|
|
1357
1380
|
const result = await validator.validate(schema, {
|
|
@@ -1392,11 +1415,16 @@ test('Preprocessors - 3', async () => {
|
|
|
1392
1415
|
]
|
|
1393
1416
|
});
|
|
1394
1417
|
|
|
1395
|
-
validator.addPreprocessor(
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
+
);
|
|
1400
1428
|
|
|
1401
1429
|
let obj = [new Date().toJSON()];
|
|
1402
1430
|
|
|
@@ -1413,8 +1441,10 @@ test('Preprocessors - 3', async () => {
|
|
|
1413
1441
|
obj = [new Date().toJSON()];
|
|
1414
1442
|
result = await validator.validate(
|
|
1415
1443
|
{
|
|
1416
|
-
preprocessor: (value: unknown): Date => {
|
|
1417
|
-
const time = Date.parse(
|
|
1444
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1445
|
+
const time = Date.parse(
|
|
1446
|
+
(value as Record<string, unknown>).toString()
|
|
1447
|
+
);
|
|
1418
1448
|
if (Number.isNaN(time)) return undefined;
|
|
1419
1449
|
return new Date(time);
|
|
1420
1450
|
},
|
|
@@ -1428,8 +1458,10 @@ test('Preprocessors - 3', async () => {
|
|
|
1428
1458
|
obj = ['sdfsdf12', new Date().toJSON()];
|
|
1429
1459
|
result = await validator.validate(
|
|
1430
1460
|
{
|
|
1431
|
-
preprocessor: (value: unknown): Date => {
|
|
1432
|
-
const time = Date.parse(
|
|
1461
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1462
|
+
const time = Date.parse(
|
|
1463
|
+
(value as Record<string, unknown>).toString()
|
|
1464
|
+
);
|
|
1433
1465
|
if (Number.isNaN(time)) return undefined;
|
|
1434
1466
|
return new Date(time);
|
|
1435
1467
|
},
|
|
@@ -1485,11 +1517,16 @@ test('Preprocessors - 4', async () => {
|
|
|
1485
1517
|
]
|
|
1486
1518
|
});
|
|
1487
1519
|
|
|
1488
|
-
validator.addPreprocessor(
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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
|
+
);
|
|
1493
1530
|
|
|
1494
1531
|
let obj = [new Date().toJSON()];
|
|
1495
1532
|
|
|
@@ -1506,8 +1543,10 @@ test('Preprocessors - 4', async () => {
|
|
|
1506
1543
|
obj = [new Date().toJSON()];
|
|
1507
1544
|
result = await validator.validate(
|
|
1508
1545
|
{
|
|
1509
|
-
preprocessor: (value: unknown): Date => {
|
|
1510
|
-
const time = Date.parse(
|
|
1546
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1547
|
+
const time = Date.parse(
|
|
1548
|
+
(value as Record<string, unknown>).toString()
|
|
1549
|
+
);
|
|
1511
1550
|
if (Number.isNaN(time)) return undefined;
|
|
1512
1551
|
return new Date(time);
|
|
1513
1552
|
},
|
|
@@ -1521,8 +1560,10 @@ test('Preprocessors - 4', async () => {
|
|
|
1521
1560
|
obj = ['sdfsdf12', new Date().toJSON()];
|
|
1522
1561
|
result = await validator.validate(
|
|
1523
1562
|
{
|
|
1524
|
-
preprocessor: (value: unknown): Date => {
|
|
1525
|
-
const time = Date.parse(
|
|
1563
|
+
preprocessor: (value: unknown): Date | undefined => {
|
|
1564
|
+
const time = Date.parse(
|
|
1565
|
+
(value as Record<string, unknown>).toString()
|
|
1566
|
+
);
|
|
1526
1567
|
if (Number.isNaN(time)) return undefined;
|
|
1527
1568
|
return new Date(time);
|
|
1528
1569
|
},
|
|
@@ -1533,3 +1574,36 @@ test('Preprocessors - 4', async () => {
|
|
|
1533
1574
|
);
|
|
1534
1575
|
expect(result).toHaveProperty('valid', false);
|
|
1535
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,7 +13,9 @@ import {
|
|
|
13
13
|
ISchemaValidator,
|
|
14
14
|
DefaultSchemaType,
|
|
15
15
|
ObjectSchemaDefinition,
|
|
16
|
-
BooleanSchemaDefinition
|
|
16
|
+
BooleanSchemaDefinition,
|
|
17
|
+
Cons,
|
|
18
|
+
Unfold
|
|
17
19
|
} from './index';
|
|
18
20
|
import { validateNumber } from './validators/validateNumber';
|
|
19
21
|
import { validateBoolean } from './validators/validateBoolean';
|
|
@@ -55,36 +57,40 @@ const defaultSchemasValidationStrategies = {
|
|
|
55
57
|
number: (
|
|
56
58
|
obj: any,
|
|
57
59
|
schema: NumberSchemaDefinition<any>,
|
|
58
|
-
validator: ISchemaValidator<any>
|
|
60
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
59
61
|
): Promise<ValidationResult> => validateNumber(obj, schema, validator),
|
|
60
62
|
boolean: (
|
|
61
63
|
obj: any,
|
|
62
64
|
schema: BooleanSchemaDefinition<any>,
|
|
63
|
-
validator: ISchemaValidator<any>
|
|
65
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
64
66
|
): Promise<ValidationResult> => validateBoolean(obj, schema, validator),
|
|
65
67
|
string: (
|
|
66
68
|
obj: any,
|
|
67
69
|
schema: StringSchemaDefinition<any>,
|
|
68
|
-
validator: ISchemaValidator<any>
|
|
70
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
69
71
|
): Promise<ValidationResult> => validateString(obj, schema, validator),
|
|
70
72
|
array: (
|
|
71
73
|
obj: any,
|
|
72
74
|
schema: ArraySchemaDefinition<any>,
|
|
73
|
-
validator: ISchemaValidator<any>
|
|
75
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
74
76
|
): Promise<ValidationResult> => validateArray(obj, schema, validator)
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
const isDefaultType = (name: string): boolean =>
|
|
78
80
|
defaultSchemaNames.indexOf(name) !== -1;
|
|
79
81
|
|
|
80
|
-
export default class SchemaValidator<
|
|
81
|
-
|
|
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>
|
|
82
88
|
{
|
|
83
89
|
private _schemasMap = new Map<string, Schema<any>>();
|
|
84
|
-
private _schemasCache:
|
|
90
|
+
private _schemasCache: Merge<SchemaTypesStructures> = null;
|
|
85
91
|
private _preprocessorsMap = new Map<
|
|
86
92
|
string,
|
|
87
|
-
(value: unknown) => unknown | Promise<unknown>
|
|
93
|
+
(value: unknown) => unknown | Promise<unknown> | undefined
|
|
88
94
|
>();
|
|
89
95
|
|
|
90
96
|
public get preprocessors(): Map<string, (value: unknown) => unknown> {
|
|
@@ -94,17 +100,24 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
94
100
|
public addPreprocessor(
|
|
95
101
|
name: string,
|
|
96
102
|
preprocessor: (value: unknown) => unknown | Promise<unknown>
|
|
97
|
-
): SchemaValidator<T> {
|
|
103
|
+
): SchemaValidator<T, SchemaTypesStructures> {
|
|
98
104
|
if (this._preprocessorsMap.has(name))
|
|
99
105
|
throw new Error(`Preprocessor '${name}' is already registered`);
|
|
100
106
|
this._preprocessorsMap.set(name, preprocessor);
|
|
101
107
|
return this;
|
|
102
108
|
}
|
|
103
109
|
|
|
104
|
-
public addSchemaType<
|
|
110
|
+
public addSchemaType<
|
|
111
|
+
K,
|
|
112
|
+
L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>,
|
|
113
|
+
M = any
|
|
114
|
+
>(
|
|
105
115
|
name: keyof K,
|
|
106
|
-
schema: L
|
|
107
|
-
): SchemaValidator<
|
|
116
|
+
schema: L
|
|
117
|
+
): SchemaValidator<
|
|
118
|
+
T & { [key in keyof K]: L },
|
|
119
|
+
Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
|
|
120
|
+
> {
|
|
108
121
|
if (typeof name !== 'string' || !name)
|
|
109
122
|
throw new Error('Name is required');
|
|
110
123
|
|
|
@@ -119,7 +132,10 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
119
132
|
if (Array.isArray(schema)) {
|
|
120
133
|
this._schemasMap.set(name.toString(), schema as Schema<any>);
|
|
121
134
|
this._schemasCache = null;
|
|
122
|
-
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
|
+
>;
|
|
123
139
|
}
|
|
124
140
|
|
|
125
141
|
if (typeof schema !== 'object')
|
|
@@ -130,20 +146,34 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
130
146
|
} as Schema<any>);
|
|
131
147
|
this._schemasCache = null;
|
|
132
148
|
|
|
133
|
-
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
|
+
>;
|
|
134
153
|
}
|
|
135
154
|
|
|
136
|
-
public get schemas():
|
|
155
|
+
public get schemas(): Merge<SchemaTypesStructures> {
|
|
137
156
|
if (this._schemasCache) return this._schemasCache;
|
|
138
157
|
const res = {};
|
|
139
158
|
for (const key of this._schemasMap.keys()) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
}
|
|
145
175
|
}
|
|
146
|
-
this._schemasCache = res as
|
|
176
|
+
this._schemasCache = res as Merge<SchemaTypesStructures>;
|
|
147
177
|
return this._schemasCache;
|
|
148
178
|
}
|
|
149
179
|
|
|
@@ -155,14 +185,14 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
155
185
|
const strategy = defaultSchemasValidationStrategies[name] as (
|
|
156
186
|
obj: any,
|
|
157
187
|
schema: Schema<any>,
|
|
158
|
-
validator: ISchemaValidator<any>
|
|
188
|
+
validator: ISchemaValidator<any, unknown[]>
|
|
159
189
|
) => ValidationResult;
|
|
160
190
|
let finalSchema = defaultSchemas[name] as CompositeSchema<
|
|
161
191
|
Record<string, never>
|
|
162
192
|
>;
|
|
163
193
|
if (strategy) {
|
|
164
194
|
if (typeof mergeSchema === 'object') {
|
|
165
|
-
finalSchema = deepExtend(finalSchema, mergeSchema);
|
|
195
|
+
finalSchema = deepExtend(finalSchema, mergeSchema) as any;
|
|
166
196
|
}
|
|
167
197
|
if (typeof mergeSchema === 'number') {
|
|
168
198
|
finalSchema = {
|
|
@@ -172,7 +202,11 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
172
202
|
mergeSchema;
|
|
173
203
|
}
|
|
174
204
|
|
|
175
|
-
let preliminaryResult = await strategy(
|
|
205
|
+
let preliminaryResult = await strategy(
|
|
206
|
+
value,
|
|
207
|
+
finalSchema,
|
|
208
|
+
this as any
|
|
209
|
+
);
|
|
176
210
|
if (!preliminaryResult.valid) return preliminaryResult;
|
|
177
211
|
|
|
178
212
|
preliminaryResult = await this.checkValidators(finalSchema, value);
|
|
@@ -236,19 +270,15 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
236
270
|
schema as DefaultSchemaType,
|
|
237
271
|
obj
|
|
238
272
|
);
|
|
239
|
-
} else if (typeof this.
|
|
240
|
-
if (Array.isArray(this.
|
|
241
|
-
return this.validate(
|
|
242
|
-
this.schemas[schema as keyof T]
|
|
243
|
-
.schema as any as Schema<any>,
|
|
244
|
-
obj
|
|
245
|
-
);
|
|
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);
|
|
246
276
|
}
|
|
247
277
|
const objSchema = deepExtend(
|
|
248
278
|
defaultSchemas.object,
|
|
249
|
-
this.
|
|
279
|
+
this._schemasMap.get(schema)
|
|
250
280
|
) as ObjectSchemaDefinition<Record<string, never>>;
|
|
251
|
-
const res = await validateObject(obj, objSchema, this);
|
|
281
|
+
const res = await validateObject(obj, objSchema, this as any);
|
|
252
282
|
if (!res.valid) return res;
|
|
253
283
|
return await this.checkValidators(objSchema, obj);
|
|
254
284
|
} else {
|
|
@@ -317,7 +347,7 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
317
347
|
const preliminaryResult = await validateObject(
|
|
318
348
|
obj,
|
|
319
349
|
schema,
|
|
320
|
-
this
|
|
350
|
+
this as any
|
|
321
351
|
);
|
|
322
352
|
if (!preliminaryResult.valid) return preliminaryResult;
|
|
323
353
|
|