@cleverbrush/schema 0.0.7 → 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 +11 -2
- package/dist/schemaValidator.d.ts +3 -0
- package/dist/schemaValidator.js +35 -0
- package/dist/validators/validateObject.js +14 -0
- package/package.json +2 -2
- package/src/index.ts +23 -2
- package/src/schemaValidator.test.ts +180 -1
- package/src/schemaValidator.ts +50 -0
- package/src/validators/validateObject.ts +24 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import SchemaValidator from './schemaValidator';
|
|
2
|
-
export declare type DefaultSchemaType = 'object' | 'function' | 'number' | 'string' | 'array';
|
|
2
|
+
export declare type DefaultSchemaType = 'object' | 'function' | 'number' | 'string' | 'array' | 'alias';
|
|
3
3
|
export declare type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];
|
|
4
4
|
export declare type DefaultPropertyDefinition = {
|
|
5
5
|
isRequired: boolean;
|
|
@@ -23,6 +23,13 @@ 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
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
export declare type AliasSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
31
|
+
type: 'alias';
|
|
32
|
+
schemaName: string;
|
|
26
33
|
};
|
|
27
34
|
export declare type ObjectSchemaDefinitionParam<T> = Omit<ObjectSchemaDefinition<T>, 'type'>;
|
|
28
35
|
export declare type ParamsValidators<TFunc extends (...args: any) => any> = {
|
|
@@ -53,7 +60,7 @@ export declare type ArraySchemaDefinition<TObj> = Omit<SchemaDefintion<TObj>, 't
|
|
|
53
60
|
minLength?: number;
|
|
54
61
|
maxLength?: number;
|
|
55
62
|
};
|
|
56
|
-
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>;
|
|
63
|
+
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>;
|
|
57
64
|
export declare type SingleSchema<TObj = Record<string, never>> = number | string | DefaultSchemaType | CompositeSchema<TObj>;
|
|
58
65
|
export declare type Schema<TObj> = SingleSchema<TObj> | Array<SingleSchema<TObj>>;
|
|
59
66
|
export interface ISchemaActions<K, T extends keyof K> {
|
|
@@ -66,6 +73,8 @@ export interface ISchemasProvider<T = Record<string, never>> {
|
|
|
66
73
|
};
|
|
67
74
|
}
|
|
68
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>;
|
|
69
78
|
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(name: keyof K, schema: L | Array<Schema<any>>): SchemaValidator<T & {
|
|
70
79
|
[key in keyof K]: typeof schema;
|
|
71
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');
|
|
@@ -95,6 +105,11 @@ class SchemaValidator {
|
|
|
95
105
|
throw new Error('not implemented');
|
|
96
106
|
}
|
|
97
107
|
async checkValidators(schema, value) {
|
|
108
|
+
if (!schema.isRequired && typeof value === 'undefined') {
|
|
109
|
+
return {
|
|
110
|
+
valid: true
|
|
111
|
+
};
|
|
112
|
+
}
|
|
98
113
|
if (Array.isArray(schema.validators)) {
|
|
99
114
|
const validatorsResults = await Promise.allSettled(schema.validators.map((v) => Promise.resolve(v(value))));
|
|
100
115
|
const rejections = validatorsResults
|
|
@@ -166,6 +181,26 @@ class SchemaValidator {
|
|
|
166
181
|
if (isDefaultType(schema.type)) {
|
|
167
182
|
return await this.validateDefaultType(schema.type, obj, schema);
|
|
168
183
|
}
|
|
184
|
+
else if (schema.type === 'alias') {
|
|
185
|
+
const alias = this.schemas[schema.schemaName]
|
|
186
|
+
.schema;
|
|
187
|
+
if (Array.isArray(alias)) {
|
|
188
|
+
if ((!schema.isRequired && typeof obj === 'undefined') ||
|
|
189
|
+
(schema.isNullable && obj === null)) {
|
|
190
|
+
return {
|
|
191
|
+
valid: true
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return await this.validate(alias, obj);
|
|
195
|
+
}
|
|
196
|
+
if (typeof alias !== 'object')
|
|
197
|
+
throw new Error('it is only possible to use a full schema schema definition as alias');
|
|
198
|
+
const schemaToMerge = { ...schema };
|
|
199
|
+
delete schemaToMerge.type;
|
|
200
|
+
delete schemaToMerge.schemaName;
|
|
201
|
+
const finalSchema = (0, deep_1.deepExtend)(alias, schemaToMerge);
|
|
202
|
+
return await this.validate(finalSchema, obj);
|
|
203
|
+
}
|
|
169
204
|
else if (schema.type === 'object') {
|
|
170
205
|
const preliminaryResult = await (0, validateObject_1.validateObject)(obj, schema, this);
|
|
171
206
|
if (!preliminaryResult.valid)
|
|
@@ -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
|
@@ -5,7 +5,8 @@ export type DefaultSchemaType =
|
|
|
5
5
|
| 'function'
|
|
6
6
|
| 'number'
|
|
7
7
|
| 'string'
|
|
8
|
-
| 'array'
|
|
8
|
+
| 'array'
|
|
9
|
+
| 'alias';
|
|
9
10
|
|
|
10
11
|
export type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];
|
|
11
12
|
|
|
@@ -38,6 +39,16 @@ export type ObjectSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
|
38
39
|
properties?: Partial<{
|
|
39
40
|
[S in keyof T]: Schema<PropType<T, S>>;
|
|
40
41
|
}>;
|
|
42
|
+
preprocessors?: Partial<{
|
|
43
|
+
[S in keyof T]:
|
|
44
|
+
| ((value: unknown) => PropType<T, S> | Promise<PropType<T, S>>)
|
|
45
|
+
| string;
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type AliasSchemaDefinition<T> = Omit<SchemaDefintion<T>, 'type'> & {
|
|
50
|
+
type: 'alias';
|
|
51
|
+
schemaName: string;
|
|
41
52
|
};
|
|
42
53
|
|
|
43
54
|
export type ObjectSchemaDefinitionParam<T> = Omit<
|
|
@@ -100,7 +111,8 @@ export type CompositeSchema<TObj> = TObj extends (...args: any) => any
|
|
|
100
111
|
| NumberSchemaDefinition<TObj>
|
|
101
112
|
| StringSchemaDefinition<TObj>
|
|
102
113
|
| ArraySchemaDefinition<TObj>
|
|
103
|
-
| ObjectSchemaDefinition<TObj
|
|
114
|
+
| ObjectSchemaDefinition<TObj>
|
|
115
|
+
| AliasSchemaDefinition<TObj>;
|
|
104
116
|
|
|
105
117
|
export type SingleSchema<TObj = Record<string, never>> =
|
|
106
118
|
| number
|
|
@@ -122,6 +134,15 @@ export interface ISchemasProvider<T = Record<string, never>> {
|
|
|
122
134
|
}
|
|
123
135
|
|
|
124
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
|
+
|
|
125
146
|
addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(
|
|
126
147
|
name: keyof K,
|
|
127
148
|
schema: L | Array<Schema<any>>
|
|
@@ -913,7 +913,7 @@ test('Validate - one of - 1', async () => {
|
|
|
913
913
|
expect(result).toHaveProperty('valid', true);
|
|
914
914
|
});
|
|
915
915
|
|
|
916
|
-
test('Validate schema', async () => {
|
|
916
|
+
test('Validate schema - 1', async () => {
|
|
917
917
|
const authorsReportSpecificationSchema = {
|
|
918
918
|
properties: {
|
|
919
919
|
type: {
|
|
@@ -1120,3 +1120,182 @@ test('Validate schema', async () => {
|
|
|
1120
1120
|
|
|
1121
1121
|
expect(result).toHaveProperty('valid', true);
|
|
1122
1122
|
});
|
|
1123
|
+
|
|
1124
|
+
test('Validate schema - 2', async () => {
|
|
1125
|
+
const validator = new SchemaValidator().addSchemaType('Date', {
|
|
1126
|
+
validators: [
|
|
1127
|
+
(value) =>
|
|
1128
|
+
value instanceof Date && !Number.isNaN(value)
|
|
1129
|
+
? {
|
|
1130
|
+
valid: true
|
|
1131
|
+
}
|
|
1132
|
+
: {
|
|
1133
|
+
valid: false,
|
|
1134
|
+
errors: ['should be a valid Date object']
|
|
1135
|
+
}
|
|
1136
|
+
]
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
const result = await validator.validate(
|
|
1140
|
+
{
|
|
1141
|
+
type: 'object',
|
|
1142
|
+
properties: {
|
|
1143
|
+
date: {
|
|
1144
|
+
type: 'alias',
|
|
1145
|
+
schemaName: 'Date',
|
|
1146
|
+
isRequired: false
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
{}
|
|
1151
|
+
);
|
|
1152
|
+
|
|
1153
|
+
expect(result).toHaveProperty('valid', true);
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
test('Not required alternative schema alias', async () => {
|
|
1157
|
+
const validator = new SchemaValidator()
|
|
1158
|
+
.addSchemaType('Alternate1', {
|
|
1159
|
+
properties: {
|
|
1160
|
+
a1: 'string'
|
|
1161
|
+
}
|
|
1162
|
+
})
|
|
1163
|
+
.addSchemaType('Alternate2', {
|
|
1164
|
+
properties: {
|
|
1165
|
+
a2: 'string'
|
|
1166
|
+
}
|
|
1167
|
+
})
|
|
1168
|
+
.addSchemaType('Alternate', ['Alternate1', 'Alternate2']);
|
|
1169
|
+
|
|
1170
|
+
let result = await validator.validate(
|
|
1171
|
+
{
|
|
1172
|
+
type: 'alias',
|
|
1173
|
+
isRequired: false,
|
|
1174
|
+
schemaName: 'Alternate'
|
|
1175
|
+
},
|
|
1176
|
+
undefined
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
expect(result).toHaveProperty('valid', true);
|
|
1180
|
+
|
|
1181
|
+
result = await validator.validate(
|
|
1182
|
+
{
|
|
1183
|
+
type: 'alias',
|
|
1184
|
+
isRequired: false,
|
|
1185
|
+
schemaName: 'Alternate'
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
a2: 'something'
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
|
|
1192
|
+
expect(result).toHaveProperty('valid', true);
|
|
1193
|
+
|
|
1194
|
+
result = await validator.validate(
|
|
1195
|
+
{
|
|
1196
|
+
type: 'alias',
|
|
1197
|
+
isRequired: false,
|
|
1198
|
+
schemaName: 'Alternate'
|
|
1199
|
+
},
|
|
1200
|
+
'invalid'
|
|
1201
|
+
);
|
|
1202
|
+
|
|
1203
|
+
expect(result).toHaveProperty('valid', false);
|
|
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,
|
|
@@ -156,6 +174,11 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
156
174
|
schema: CompositeSchema<Record<string, never>>,
|
|
157
175
|
value: any
|
|
158
176
|
): Promise<ValidationResult> {
|
|
177
|
+
if (!schema.isRequired && typeof value === 'undefined') {
|
|
178
|
+
return {
|
|
179
|
+
valid: true
|
|
180
|
+
};
|
|
181
|
+
}
|
|
159
182
|
if (Array.isArray(schema.validators)) {
|
|
160
183
|
const validatorsResults = await Promise.allSettled(
|
|
161
184
|
schema.validators.map((v) => Promise.resolve(v(value)))
|
|
@@ -251,6 +274,33 @@ export default class SchemaValidator<T = Record<string, never>>
|
|
|
251
274
|
obj,
|
|
252
275
|
schema
|
|
253
276
|
);
|
|
277
|
+
} else if (schema.type === 'alias') {
|
|
278
|
+
const alias = this.schemas[schema.schemaName]
|
|
279
|
+
.schema as Schema<any>;
|
|
280
|
+
if (Array.isArray(alias)) {
|
|
281
|
+
if (
|
|
282
|
+
(!schema.isRequired && typeof obj === 'undefined') ||
|
|
283
|
+
(schema.isNullable && obj === null)
|
|
284
|
+
) {
|
|
285
|
+
return {
|
|
286
|
+
valid: true
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return await this.validate(alias, obj);
|
|
290
|
+
}
|
|
291
|
+
if (typeof alias !== 'object')
|
|
292
|
+
throw new Error(
|
|
293
|
+
'it is only possible to use a full schema schema definition as alias'
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const schemaToMerge = { ...schema };
|
|
297
|
+
delete schemaToMerge.type;
|
|
298
|
+
delete schemaToMerge.schemaName;
|
|
299
|
+
const finalSchema: Schema<any> = deepExtend(
|
|
300
|
+
alias,
|
|
301
|
+
schemaToMerge
|
|
302
|
+
);
|
|
303
|
+
return await this.validate(finalSchema, obj);
|
|
254
304
|
} else if (schema.type === 'object') {
|
|
255
305
|
const preliminaryResult = await validateObject(
|
|
256
306
|
obj,
|
|
@@ -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(
|