@cleverbrush/schema 0.0.10 → 0.0.13
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 +2 -1
- package/dist/schemaValidator.d.ts +1 -1
- package/dist/validators/validateArray.js +11 -0
- package/dist/validators/validateObject.js +15 -0
- package/package.json +2 -2
- package/src/index.ts +8 -3
- package/src/schemaValidator.test.ts +159 -1
- package/src/schemaValidator.ts +5 -4
- package/src/validators/validateArray.ts +13 -0
- package/src/validators/validateObject.ts +19 -0
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export declare type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'>
|
|
|
24
24
|
[S in keyof T]: Schema<PropType<T, S>>;
|
|
25
25
|
}>;
|
|
26
26
|
preprocessors?: Partial<{
|
|
27
|
-
[S in keyof T]: ((value: unknown) => PropType<T, S> | Promise<PropType<T, S>>) | string
|
|
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
28
|
}>;
|
|
29
29
|
};
|
|
30
30
|
export declare type AliasSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
@@ -56,6 +56,7 @@ export declare type StringSchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, '
|
|
|
56
56
|
};
|
|
57
57
|
export declare type ArraySchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, 'type'> & {
|
|
58
58
|
type: 'array';
|
|
59
|
+
preprocessor?: ((value: unknown) => unknown | Promise<unknown>) | string;
|
|
59
60
|
ofType?: Schema<any>;
|
|
60
61
|
minLength?: number;
|
|
61
62
|
maxLength?: number;
|
|
@@ -13,5 +13,5 @@ export default class SchemaValidator<T = Record<string, never>> implements ISche
|
|
|
13
13
|
};
|
|
14
14
|
private validateDefaultType;
|
|
15
15
|
private checkValidators;
|
|
16
|
-
validate(schema: keyof T | DefaultSchemaType | Schema<
|
|
16
|
+
validate<K>(schema: keyof T | DefaultSchemaType | Schema<K>, obj: any): Promise<ValidationResult>;
|
|
17
17
|
}
|
|
@@ -14,6 +14,17 @@ const validateArray = async (obj, schema, validator) => {
|
|
|
14
14
|
valid: false,
|
|
15
15
|
errors: ['expected type array']
|
|
16
16
|
};
|
|
17
|
+
if (schema.preprocessor) {
|
|
18
|
+
const preprocessor = typeof schema.preprocessor === 'function'
|
|
19
|
+
? schema.preprocessor
|
|
20
|
+
: validator.preprocessors.get(schema.preprocessor);
|
|
21
|
+
if (typeof preprocessor !== 'function') {
|
|
22
|
+
throw new Error(`unknown preprocessor '${schema.preprocessor}'`);
|
|
23
|
+
}
|
|
24
|
+
for (let i = 0; i < obj.length; i++) {
|
|
25
|
+
obj[i] = await Promise.resolve(preprocessor(obj[i]));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
17
28
|
if (typeof schema.minLength === 'number' && obj.length < schema.minLength) {
|
|
18
29
|
return {
|
|
19
30
|
valid: false,
|
|
@@ -19,6 +19,8 @@ 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
25
|
obj[key] = await Promise.resolve(schema.preprocessors[key](obj[key]));
|
|
24
26
|
}
|
|
@@ -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.13",
|
|
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.13"
|
|
23
23
|
},
|
|
24
24
|
"types": "./dist/index.d.ts"
|
|
25
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -40,9 +40,13 @@ export type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
|
40
40
|
[S in keyof T]: Schema<PropType<T, S>>;
|
|
41
41
|
}>;
|
|
42
42
|
preprocessors?: Partial<{
|
|
43
|
-
[S in keyof T]:
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
[S in keyof T | '*']: S extends keyof T
|
|
44
|
+
?
|
|
45
|
+
| ((
|
|
46
|
+
value: unknown
|
|
47
|
+
) => PropType<T, S> | Promise<PropType<T, S>>)
|
|
48
|
+
| string
|
|
49
|
+
: (value: T) => void | Promise<void>;
|
|
46
50
|
}>;
|
|
47
51
|
};
|
|
48
52
|
|
|
@@ -96,6 +100,7 @@ export type ArraySchemaDefinition<TObj> = Omit<
|
|
|
96
100
|
'type'
|
|
97
101
|
> & {
|
|
98
102
|
type: 'array';
|
|
103
|
+
preprocessor?: ((value: unknown) => unknown | Promise<unknown>) | string;
|
|
99
104
|
ofType?: Schema<any>;
|
|
100
105
|
minLength?: number;
|
|
101
106
|
maxLength?: number;
|
|
@@ -4,6 +4,7 @@ import { ValidationResult } from '../dist/index.js';
|
|
|
4
4
|
import {
|
|
5
5
|
NumberSchemaDefinition,
|
|
6
6
|
ObjectSchemaDefinitionParam,
|
|
7
|
+
ObjectSchemaDefinition,
|
|
7
8
|
Schema
|
|
8
9
|
} from './index';
|
|
9
10
|
import SchemaValidator from './schemaValidator';
|
|
@@ -829,7 +830,7 @@ test('Validate - object - 1', async () => {
|
|
|
829
830
|
b: 'number'
|
|
830
831
|
},
|
|
831
832
|
validators: [
|
|
832
|
-
(value) => {
|
|
833
|
+
(value: { a: number; b: number }) => {
|
|
833
834
|
if (value.a + value.b === 5) {
|
|
834
835
|
return {
|
|
835
836
|
valid: true
|
|
@@ -1299,3 +1300,160 @@ test('Preprocessors - 2', async () => {
|
|
|
1299
1300
|
});
|
|
1300
1301
|
}).rejects.toBeInstanceOf(Error);
|
|
1301
1302
|
});
|
|
1303
|
+
|
|
1304
|
+
test('Preprocessors - 3', async () => {
|
|
1305
|
+
const validator = new SchemaValidator().addSchemaType('Date', {
|
|
1306
|
+
validators: [
|
|
1307
|
+
(value) =>
|
|
1308
|
+
value instanceof Date && !Number.isNaN(value)
|
|
1309
|
+
? {
|
|
1310
|
+
valid: true
|
|
1311
|
+
}
|
|
1312
|
+
: {
|
|
1313
|
+
valid: false,
|
|
1314
|
+
errors: ['should be a valid Date object']
|
|
1315
|
+
}
|
|
1316
|
+
]
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
validator.addPreprocessor('StringToDate', (value: unknown): Date => {
|
|
1320
|
+
const time = Date.parse(value.toString());
|
|
1321
|
+
if (Number.isNaN(time)) return undefined;
|
|
1322
|
+
return new Date(time);
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
let obj = [new Date().toJSON()];
|
|
1326
|
+
|
|
1327
|
+
let result = await validator.validate(
|
|
1328
|
+
{
|
|
1329
|
+
preprocessor: 'StringToDate',
|
|
1330
|
+
type: 'array',
|
|
1331
|
+
ofType: 'Date'
|
|
1332
|
+
},
|
|
1333
|
+
obj
|
|
1334
|
+
);
|
|
1335
|
+
expect(result).toHaveProperty('valid', true);
|
|
1336
|
+
|
|
1337
|
+
obj = [new Date().toJSON()];
|
|
1338
|
+
result = await validator.validate(
|
|
1339
|
+
{
|
|
1340
|
+
preprocessor: (value: unknown): Date => {
|
|
1341
|
+
const time = Date.parse(value.toString());
|
|
1342
|
+
if (Number.isNaN(time)) return undefined;
|
|
1343
|
+
return new Date(time);
|
|
1344
|
+
},
|
|
1345
|
+
type: 'array',
|
|
1346
|
+
ofType: 'Date'
|
|
1347
|
+
},
|
|
1348
|
+
obj
|
|
1349
|
+
);
|
|
1350
|
+
expect(result).toHaveProperty('valid', true);
|
|
1351
|
+
|
|
1352
|
+
obj = ['sdfsdf12', new Date().toJSON()];
|
|
1353
|
+
result = await validator.validate(
|
|
1354
|
+
{
|
|
1355
|
+
preprocessor: (value: unknown): Date => {
|
|
1356
|
+
const time = Date.parse(value.toString());
|
|
1357
|
+
if (Number.isNaN(time)) return undefined;
|
|
1358
|
+
return new Date(time);
|
|
1359
|
+
},
|
|
1360
|
+
type: 'array',
|
|
1361
|
+
ofType: 'Date'
|
|
1362
|
+
},
|
|
1363
|
+
obj
|
|
1364
|
+
);
|
|
1365
|
+
expect(result).toHaveProperty('valid', false);
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
test('Preprocessors - 3', async () => {
|
|
1369
|
+
const validator = new SchemaValidator();
|
|
1370
|
+
|
|
1371
|
+
type SomeType = { age: number; marriedAt: number };
|
|
1372
|
+
|
|
1373
|
+
const schema: ObjectSchemaDefinition<SomeType> = {
|
|
1374
|
+
type: 'object',
|
|
1375
|
+
properties: {
|
|
1376
|
+
age: 'number',
|
|
1377
|
+
marriedAt: 'number'
|
|
1378
|
+
},
|
|
1379
|
+
preprocessors: {
|
|
1380
|
+
age: (value): number => 40,
|
|
1381
|
+
'*': (value: SomeType): void => {
|
|
1382
|
+
if (value.marriedAt > value.age) {
|
|
1383
|
+
value.marriedAt = value.age;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
const obj = {
|
|
1390
|
+
age: 50,
|
|
1391
|
+
marriedAt: 80
|
|
1392
|
+
};
|
|
1393
|
+
await validator.validate(schema, obj);
|
|
1394
|
+
expect(obj).toHaveProperty('marriedAt', 40);
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
test('Preprocessors - 4', async () => {
|
|
1398
|
+
const validator = new SchemaValidator().addSchemaType('Date', {
|
|
1399
|
+
validators: [
|
|
1400
|
+
(value) =>
|
|
1401
|
+
value instanceof Date && !Number.isNaN(value)
|
|
1402
|
+
? {
|
|
1403
|
+
valid: true
|
|
1404
|
+
}
|
|
1405
|
+
: {
|
|
1406
|
+
valid: false,
|
|
1407
|
+
errors: ['should be a valid Date object']
|
|
1408
|
+
}
|
|
1409
|
+
]
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
validator.addPreprocessor('StringToDate', (value: unknown): Date => {
|
|
1413
|
+
const time = Date.parse(value.toString());
|
|
1414
|
+
if (Number.isNaN(time)) return undefined;
|
|
1415
|
+
return new Date(time);
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
let obj = [new Date().toJSON()];
|
|
1419
|
+
|
|
1420
|
+
let result = await validator.validate(
|
|
1421
|
+
{
|
|
1422
|
+
preprocessor: 'StringToDate',
|
|
1423
|
+
type: 'array',
|
|
1424
|
+
ofType: 'Date'
|
|
1425
|
+
},
|
|
1426
|
+
obj
|
|
1427
|
+
);
|
|
1428
|
+
expect(result).toHaveProperty('valid', true);
|
|
1429
|
+
|
|
1430
|
+
obj = [new Date().toJSON()];
|
|
1431
|
+
result = await validator.validate(
|
|
1432
|
+
{
|
|
1433
|
+
preprocessor: (value: unknown): Date => {
|
|
1434
|
+
const time = Date.parse(value.toString());
|
|
1435
|
+
if (Number.isNaN(time)) return undefined;
|
|
1436
|
+
return new Date(time);
|
|
1437
|
+
},
|
|
1438
|
+
type: 'array',
|
|
1439
|
+
ofType: 'Date'
|
|
1440
|
+
},
|
|
1441
|
+
obj
|
|
1442
|
+
);
|
|
1443
|
+
expect(result).toHaveProperty('valid', true);
|
|
1444
|
+
|
|
1445
|
+
obj = ['sdfsdf12', new Date().toJSON()];
|
|
1446
|
+
result = await validator.validate(
|
|
1447
|
+
{
|
|
1448
|
+
preprocessor: (value: unknown): Date => {
|
|
1449
|
+
const time = Date.parse(value.toString());
|
|
1450
|
+
if (Number.isNaN(time)) return undefined;
|
|
1451
|
+
return new Date(time);
|
|
1452
|
+
},
|
|
1453
|
+
type: 'array',
|
|
1454
|
+
ofType: 'Date'
|
|
1455
|
+
},
|
|
1456
|
+
obj
|
|
1457
|
+
);
|
|
1458
|
+
expect(result).toHaveProperty('valid', false);
|
|
1459
|
+
});
|
package/src/schemaValidator.ts
CHANGED
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
ArraySchemaDefinition,
|
|
13
13
|
ISchemaValidator,
|
|
14
14
|
DefaultSchemaType,
|
|
15
|
-
ObjectSchemaDefinition
|
|
15
|
+
ObjectSchemaDefinition,
|
|
16
|
+
SchemaDefintion
|
|
16
17
|
} from './index';
|
|
17
18
|
import { validateNumber } from './validators/validateNumber';
|
|
18
19
|
import { validateString } from './validators/validateString';
|
|
@@ -213,8 +214,8 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
213
214
|
};
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
public async validate(
|
|
217
|
-
schema: keyof T | DefaultSchemaType | Schema<
|
|
217
|
+
public async validate<K>(
|
|
218
|
+
schema: keyof T | DefaultSchemaType | Schema<K>,
|
|
218
219
|
obj: any
|
|
219
220
|
): Promise<ValidationResult> {
|
|
220
221
|
if (!schema) throw new Error('schemaName is required');
|
|
@@ -293,7 +294,7 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
293
294
|
'it is only possible to use a full schema schema definition as alias'
|
|
294
295
|
);
|
|
295
296
|
|
|
296
|
-
const schemaToMerge = { ...schema };
|
|
297
|
+
const schemaToMerge = { ...(schema as any) };
|
|
297
298
|
delete schemaToMerge.type;
|
|
298
299
|
delete schemaToMerge.schemaName;
|
|
299
300
|
const finalSchema: Schema<any> = deepExtend(
|
|
@@ -24,6 +24,19 @@ export const validateArray = async (
|
|
|
24
24
|
errors: ['expected type array']
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
if (schema.preprocessor) {
|
|
28
|
+
const preprocessor =
|
|
29
|
+
typeof schema.preprocessor === 'function'
|
|
30
|
+
? schema.preprocessor
|
|
31
|
+
: validator.preprocessors.get(schema.preprocessor);
|
|
32
|
+
if (typeof preprocessor !== 'function') {
|
|
33
|
+
throw new Error(`unknown preprocessor '${schema.preprocessor}'`);
|
|
34
|
+
}
|
|
35
|
+
for (let i = 0; i < obj.length; i++) {
|
|
36
|
+
obj[i] = await Promise.resolve(preprocessor(obj[i]));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
if (typeof schema.minLength === 'number' && obj.length < schema.minLength) {
|
|
28
41
|
return {
|
|
29
42
|
valid: false,
|
|
@@ -31,6 +31,7 @@ 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)(
|
|
@@ -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) {
|