@cleverbrush/schema 0.0.9 → 0.0.10
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 +5 -0
- package/dist/schemaValidator.d.ts +3 -0
- package/dist/schemaValidator.js +10 -0
- package/dist/validators/validateObject.js +14 -0
- package/package.json +2 -2
- package/src/index.ts +14 -0
- package/src/schemaValidator.test.ts +97 -0
- package/src/schemaValidator.ts +18 -0
- package/src/validators/validateObject.ts +24 -0
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ export declare type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'>
|
|
|
23
23
|
properties?: Partial<{
|
|
24
24
|
[S in keyof T]: Schema<PropType<T, S>>;
|
|
25
25
|
}>;
|
|
26
|
+
preprocessors?: Partial<{
|
|
27
|
+
[S in keyof T]: ((value: unknown) => PropType<T, S> | Promise<PropType<T, S>>) | string;
|
|
28
|
+
}>;
|
|
26
29
|
};
|
|
27
30
|
export declare type AliasSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
28
31
|
type: 'alias';
|
|
@@ -70,6 +73,8 @@ export interface ISchemasProvider<T = Record<string, never>> {
|
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
export interface ISchemaValidator<T = Record<string, never>> {
|
|
76
|
+
get preprocessors(): Map<string, (value: unknown) => unknown | Promise<unknown>>;
|
|
77
|
+
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T>;
|
|
73
78
|
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L | Array<Schema<any>>): SchemaValidator<T & {
|
|
74
79
|
[key in keyof K]: typeof schema;
|
|
75
80
|
}>;
|
|
@@ -2,6 +2,9 @@ import { ISchemasProvider, Schema, ISchemaActions, ObjectSchemaDefinitionParam,
|
|
|
2
2
|
export default class SchemaValidator<T = Record<string, never>> implements ISchemasProvider<T>, ISchemaValidator<T> {
|
|
3
3
|
private _schemasMap;
|
|
4
4
|
private _schemasCache;
|
|
5
|
+
private _preprocessorsMap;
|
|
6
|
+
get preprocessors(): Map<string, (value: unknown) => unknown>;
|
|
7
|
+
addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T>;
|
|
5
8
|
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L | Array<Schema<any>>): SchemaValidator<T & {
|
|
6
9
|
[key in keyof K]: L;
|
|
7
10
|
}>;
|
package/dist/schemaValidator.js
CHANGED
|
@@ -37,6 +37,16 @@ const isDefaultType = (name) => defaultSchemaNames.indexOf(name) !== -1;
|
|
|
37
37
|
class SchemaValidator {
|
|
38
38
|
_schemasMap = new Map();
|
|
39
39
|
_schemasCache = null;
|
|
40
|
+
_preprocessorsMap = new Map();
|
|
41
|
+
get preprocessors() {
|
|
42
|
+
return this._preprocessorsMap;
|
|
43
|
+
}
|
|
44
|
+
addPreprocessor(name, preprocessor) {
|
|
45
|
+
if (this._preprocessorsMap.has(name))
|
|
46
|
+
throw new Error(`Preprocessor '${name}' is already registered`);
|
|
47
|
+
this._preprocessorsMap.set(name, preprocessor);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
40
50
|
addSchemaType(name, schema) {
|
|
41
51
|
if (typeof name !== 'string' || !name)
|
|
42
52
|
throw new Error('Name is required');
|
|
@@ -17,6 +17,20 @@ const validateObject = async (obj, schema, validator) => {
|
|
|
17
17
|
]
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
if (typeof schema.preprocessors === 'object' && schema.preprocessors) {
|
|
21
|
+
await Promise.all(Object.keys(schema.preprocessors).map(async (key) => {
|
|
22
|
+
if (typeof schema.preprocessors[key] === 'function') {
|
|
23
|
+
obj[key] = await Promise.resolve(schema.preprocessors[key](obj[key]));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const preprocessor = validator.preprocessors.get(schema.preprocessors[key]);
|
|
27
|
+
if (typeof preprocessor !== 'function') {
|
|
28
|
+
throw new Error(`preprocessor '${schema.preprocessors[key]}' is unknown`);
|
|
29
|
+
}
|
|
30
|
+
obj[key] = await Promise.resolve(preprocessor(obj[key]));
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
20
34
|
if (typeof schema.properties === 'object' && schema.properties) {
|
|
21
35
|
const errors = (await Promise.all(Object.entries(schema.properties).map(async ([name, schema]) => {
|
|
22
36
|
const result = await validator.validate(schema, obj[name]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleverbrush/schema",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
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.10"
|
|
23
23
|
},
|
|
24
24
|
"types": "./dist/index.d.ts"
|
|
25
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,11 @@ export type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
|
39
39
|
properties?: Partial<{
|
|
40
40
|
[S in keyof T]: Schema<PropType<T, S>>;
|
|
41
41
|
}>;
|
|
42
|
+
preprocessors?: Partial<{
|
|
43
|
+
[S in keyof T]:
|
|
44
|
+
| ((value: unknown) => PropType<T, S> | Promise<PropType<T, S>>)
|
|
45
|
+
| string;
|
|
46
|
+
}>;
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
export type AliasSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
@@ -129,6 +134,15 @@ export interface ISchemasProvider<T = Record<string, never>> {
|
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
export interface ISchemaValidator<T = Record<string, never>> {
|
|
137
|
+
get preprocessors(): Map<
|
|
138
|
+
string,
|
|
139
|
+
(value: unknown) => unknown | Promise<unknown>
|
|
140
|
+
>;
|
|
141
|
+
addPreprocessor(
|
|
142
|
+
name: string,
|
|
143
|
+
preprocessor: (value: unknown) => unknown | Promise<unknown>
|
|
144
|
+
): SchemaValidator<T>;
|
|
145
|
+
|
|
132
146
|
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(
|
|
133
147
|
name: keyof K,
|
|
134
148
|
schema: L | Array<Schema<any>>
|
|
@@ -1202,3 +1202,100 @@ test('Not required alternative schema alias', async () => {
|
|
|
1202
1202
|
|
|
1203
1203
|
expect(result).toHaveProperty('valid', false);
|
|
1204
1204
|
});
|
|
1205
|
+
|
|
1206
|
+
test('Preprocessors - 1', async () => {
|
|
1207
|
+
const validator = new SchemaValidator().addSchemaType('Date', {
|
|
1208
|
+
validators: [
|
|
1209
|
+
(value) =>
|
|
1210
|
+
value instanceof Date && !Number.isNaN(value)
|
|
1211
|
+
? {
|
|
1212
|
+
valid: true
|
|
1213
|
+
}
|
|
1214
|
+
: {
|
|
1215
|
+
valid: false,
|
|
1216
|
+
errors: ['should be a valid Date object']
|
|
1217
|
+
}
|
|
1218
|
+
]
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
const schema: Schema<{ bornAt: Date }> = {
|
|
1222
|
+
type: 'object',
|
|
1223
|
+
properties: {
|
|
1224
|
+
bornAt: 'Date'
|
|
1225
|
+
},
|
|
1226
|
+
preprocessors: {
|
|
1227
|
+
bornAt: (value: unknown): Date => {
|
|
1228
|
+
const time = Date.parse(value.toString());
|
|
1229
|
+
if (Number.isNaN(time)) return undefined;
|
|
1230
|
+
return new Date(time);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
|
|
1235
|
+
const result = await validator.validate(schema, {
|
|
1236
|
+
bornAt: new Date().toJSON()
|
|
1237
|
+
});
|
|
1238
|
+
expect(result).toHaveProperty('valid', true);
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
test('Preprocessors - 2', async () => {
|
|
1242
|
+
const validator = new SchemaValidator().addSchemaType('Date', {
|
|
1243
|
+
validators: [
|
|
1244
|
+
(value) =>
|
|
1245
|
+
value instanceof Date && !Number.isNaN(value)
|
|
1246
|
+
? {
|
|
1247
|
+
valid: true
|
|
1248
|
+
}
|
|
1249
|
+
: {
|
|
1250
|
+
valid: false,
|
|
1251
|
+
errors: ['should be a valid Date object']
|
|
1252
|
+
}
|
|
1253
|
+
]
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
let schema: Schema<{ bornAt: Date; diedAt?: Date }> = {
|
|
1257
|
+
type: 'object',
|
|
1258
|
+
properties: {
|
|
1259
|
+
bornAt: 'Date'
|
|
1260
|
+
},
|
|
1261
|
+
preprocessors: {
|
|
1262
|
+
bornAt: 'StringToDate'
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
validator.addPreprocessor('StringToDate', (value: unknown): Date => {
|
|
1267
|
+
const time = Date.parse(value.toString());
|
|
1268
|
+
if (Number.isNaN(time)) return undefined;
|
|
1269
|
+
return new Date(time);
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
expect(() =>
|
|
1273
|
+
validator.addPreprocessor('StringToDate', (value: unknown): Date => {
|
|
1274
|
+
const time = Date.parse(value.toString());
|
|
1275
|
+
if (Number.isNaN(time)) return undefined;
|
|
1276
|
+
return new Date(time);
|
|
1277
|
+
})
|
|
1278
|
+
).toThrow();
|
|
1279
|
+
|
|
1280
|
+
const result = await validator.validate(schema, {
|
|
1281
|
+
bornAt: new Date().toJSON()
|
|
1282
|
+
});
|
|
1283
|
+
expect(result).toHaveProperty('valid', true);
|
|
1284
|
+
|
|
1285
|
+
schema = {
|
|
1286
|
+
type: 'object',
|
|
1287
|
+
properties: {
|
|
1288
|
+
bornAt: 'Date',
|
|
1289
|
+
diedAt: 'Date'
|
|
1290
|
+
},
|
|
1291
|
+
preprocessors: {
|
|
1292
|
+
bornAt: 'StringToDate',
|
|
1293
|
+
diedAt: 'Unregistered'
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
expect(async () => {
|
|
1297
|
+
await validator.validate(schema, {
|
|
1298
|
+
bornAt: new Date().toJSON()
|
|
1299
|
+
});
|
|
1300
|
+
}).rejects.toBeInstanceOf(Error);
|
|
1301
|
+
});
|
package/src/schemaValidator.ts
CHANGED
|
@@ -70,6 +70,24 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
70
70
|
{
|
|
71
71
|
private _schemasMap = new Map<string, Schema<any>>();
|
|
72
72
|
private _schemasCache: { [K in keyof T]: ISchemaActions<T, K> } = null;
|
|
73
|
+
private _preprocessorsMap = new Map<
|
|
74
|
+
string,
|
|
75
|
+
(value: unknown) => unknown | Promise<unknown>
|
|
76
|
+
>();
|
|
77
|
+
|
|
78
|
+
public get preprocessors(): Map<string, (value: unknown) => unknown> {
|
|
79
|
+
return this._preprocessorsMap;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public addPreprocessor(
|
|
83
|
+
name: string,
|
|
84
|
+
preprocessor: (value: unknown) => unknown | Promise<unknown>
|
|
85
|
+
): SchemaValidator<T> {
|
|
86
|
+
if (this._preprocessorsMap.has(name))
|
|
87
|
+
throw new Error(`Preprocessor '${name}' is already registered`);
|
|
88
|
+
this._preprocessorsMap.set(name, preprocessor);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
73
91
|
|
|
74
92
|
public addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(
|
|
75
93
|
name: keyof K,
|
|
@@ -28,6 +28,30 @@ export const validateObject = async (
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
if (typeof schema.preprocessors === 'object' && schema.preprocessors) {
|
|
32
|
+
await Promise.all(
|
|
33
|
+
Object.keys(schema.preprocessors).map(async (key: string) => {
|
|
34
|
+
if (typeof schema.preprocessors[key] === 'function') {
|
|
35
|
+
obj[key] = await Promise.resolve(
|
|
36
|
+
(schema.preprocessors[key] as (unknown) => unknown)(
|
|
37
|
+
obj[key]
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
const preprocessor = validator.preprocessors.get(
|
|
42
|
+
schema.preprocessors[key] as string
|
|
43
|
+
);
|
|
44
|
+
if (typeof preprocessor !== 'function') {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`preprocessor '${schema.preprocessors[key]}' is unknown`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
obj[key] = await Promise.resolve(preprocessor(obj[key]));
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
31
55
|
if (typeof schema.properties === 'object' && schema.properties) {
|
|
32
56
|
const errors = (
|
|
33
57
|
await Promise.all(
|