ejv 1.1.10 → 1.1.11
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/CHANGELOG.md +67 -67
- package/LICENSE +21 -21
- package/README-KR.md +591 -591
- package/README.md +595 -595
- package/build/constants.d.ts +104 -104
- package/build/constants.js +118 -118
- package/build/ejv.d.ts +2 -2
- package/build/ejv.js +686 -684
- package/build/ejv.js.map +1 -1
- package/build/interfaces.d.ts +38 -38
- package/build/interfaces.js +14 -14
- package/build/public_api.d.ts +3 -3
- package/build/public_api.js +13 -13
- package/build/tester.d.ts +34 -34
- package/build/tester.js +268 -268
- package/build/tester.js.map +1 -1
- package/build/util.d.ts +1 -1
- package/build/util.js +67 -65
- package/build/util.js.map +1 -1
- package/package.json +39 -39
- package/spec/common-test-runner.ts +17 -17
- package/spec/ejv.spec.ts +4634 -4634
- package/spec/testers.spec.ts +832 -832
- package/src/constants.ts +155 -155
- package/src/ejv.ts +1071 -1071
- package/src/interfaces.ts +63 -63
- package/src/public_api.ts +2 -2
- package/src/tester.ts +302 -302
- package/src/util.ts +58 -58
- package/tsconfig.json +18 -18
- package/tsconfig.spec.json +18 -18
package/src/ejv.ts
CHANGED
|
@@ -1,1071 +1,1071 @@
|
|
|
1
|
-
import { EjvError, InternalOptions, Options, Scheme } from './interfaces';
|
|
2
|
-
import { DataType, ErrorMsg, ErrorMsgCursorA, ErrorType, NumberFormat, StringFormat } from './constants';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
arrayTester,
|
|
6
|
-
arrayTypeOfTester,
|
|
7
|
-
booleanTester,
|
|
8
|
-
dateFormatTester,
|
|
9
|
-
dateTester,
|
|
10
|
-
dateTimeFormatTester,
|
|
11
|
-
definedTester,
|
|
12
|
-
emailTester,
|
|
13
|
-
enumTester,
|
|
14
|
-
exclusiveMaxDateTester,
|
|
15
|
-
exclusiveMaxNumberTester,
|
|
16
|
-
exclusiveMinDateTester,
|
|
17
|
-
exclusiveMinNumberTester,
|
|
18
|
-
hasPropertyTester,
|
|
19
|
-
indexTester,
|
|
20
|
-
integerTester,
|
|
21
|
-
lengthTester,
|
|
22
|
-
maxDateTester,
|
|
23
|
-
maxLengthTester,
|
|
24
|
-
maxNumberTester,
|
|
25
|
-
minDateTester,
|
|
26
|
-
minLengthTester,
|
|
27
|
-
minNumberTester,
|
|
28
|
-
numberTester,
|
|
29
|
-
objectTester,
|
|
30
|
-
regExpTester,
|
|
31
|
-
stringRegExpTester,
|
|
32
|
-
stringTester,
|
|
33
|
-
timeFormatTester,
|
|
34
|
-
typeTester,
|
|
35
|
-
uniqueItemsTester
|
|
36
|
-
} from './tester';
|
|
37
|
-
import { clone } from './util';
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
interface AnyObject {
|
|
41
|
-
[key : string] : any;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const _ejv = <T>(data : T, schemes : Scheme[], options : InternalOptions = {
|
|
46
|
-
path : []
|
|
47
|
-
}) : null | EjvError => {
|
|
48
|
-
// check schemes
|
|
49
|
-
if (!arrayTester(schemes)) {
|
|
50
|
-
throw new Error(ErrorMsg.NO_ARRAY_SCHEME);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!arrayTypeOfTester(schemes, DataType.OBJECT)) {
|
|
54
|
-
throw new Error(ErrorMsg.NO_OBJECT_ARRAY_SCHEME);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!minLengthTester(schemes, 1)) {
|
|
58
|
-
throw new Error(ErrorMsg.EMPTY_SCHEME);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// check data by schemes
|
|
62
|
-
let result : EjvError | null = null;
|
|
63
|
-
|
|
64
|
-
// use for() instead of forEach() to stop
|
|
65
|
-
const schemeLength : number = schemes.length;
|
|
66
|
-
|
|
67
|
-
for (let i = 0; i < schemeLength; i++) {
|
|
68
|
-
const _options : InternalOptions = clone(options); // divide instance
|
|
69
|
-
|
|
70
|
-
if (!definedTester(_options.path)) {
|
|
71
|
-
_options.path = [];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const scheme : Scheme = schemes[i];
|
|
75
|
-
const key : keyof T = scheme.key as keyof T;
|
|
76
|
-
|
|
77
|
-
let value : any;
|
|
78
|
-
|
|
79
|
-
if (!!key) {
|
|
80
|
-
value = data[key];
|
|
81
|
-
|
|
82
|
-
_options.path.push(key as string);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let types : DataType[];
|
|
86
|
-
|
|
87
|
-
if (!definedTester(scheme.type)) {
|
|
88
|
-
throw new Error(ErrorMsg.SCHEMES_SHOULD_HAVE_TYPE);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!arrayTester(scheme.type)) {
|
|
92
|
-
types = [scheme.type as DataType];
|
|
93
|
-
} else {
|
|
94
|
-
types = scheme.type as DataType[];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const allDataType : DataType[] = Object.values(DataType);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const errorType : string | undefined = types.find(type => {
|
|
101
|
-
return !(stringTester(type) && enumTester(type, allDataType));
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
if (!!errorType) {
|
|
105
|
-
throw new Error(ErrorMsg.SCHEMES_HAS_INVALID_TYPE.replace(ErrorMsgCursorA, errorType));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!uniqueItemsTester(types)) {
|
|
109
|
-
throw new Error(ErrorMsg.SCHEMES_HAS_DUPLICATED_TYPE);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!definedTester(value)) {
|
|
113
|
-
if (scheme.optional !== true) {
|
|
114
|
-
result = new EjvError(
|
|
115
|
-
ErrorType.REQUIRED,
|
|
116
|
-
ErrorMsg.REQUIRED,
|
|
117
|
-
_options.path,
|
|
118
|
-
data,
|
|
119
|
-
value
|
|
120
|
-
);
|
|
121
|
-
break;
|
|
122
|
-
} else {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (value === null) {
|
|
128
|
-
if (scheme.nullable !== true) {
|
|
129
|
-
result = new EjvError(
|
|
130
|
-
ErrorType.REQUIRED,
|
|
131
|
-
ErrorMsg.REQUIRED,
|
|
132
|
-
_options.path,
|
|
133
|
-
data,
|
|
134
|
-
value
|
|
135
|
-
);
|
|
136
|
-
break;
|
|
137
|
-
} else {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const typeResolved : DataType | undefined = types.find(type => {
|
|
143
|
-
return typeTester(value, type);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
if (!typeResolved) {
|
|
147
|
-
if (!arrayTester(scheme.type)) {
|
|
148
|
-
result = new EjvError(
|
|
149
|
-
ErrorType.TYPE_MISMATCH,
|
|
150
|
-
ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, scheme.type as DataType),
|
|
151
|
-
_options.path,
|
|
152
|
-
data,
|
|
153
|
-
value
|
|
154
|
-
);
|
|
155
|
-
} else {
|
|
156
|
-
result = new EjvError(
|
|
157
|
-
ErrorType.TYPE_MISMATCH_ONE_OF,
|
|
158
|
-
ErrorMsg.TYPE_MISMATCH_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.type)),
|
|
159
|
-
_options.path,
|
|
160
|
-
data,
|
|
161
|
-
value
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// additional check for type resolved
|
|
168
|
-
switch (typeResolved) {
|
|
169
|
-
case DataType.NUMBER:
|
|
170
|
-
const valueAsNumber : number = value as unknown as number;
|
|
171
|
-
|
|
172
|
-
if (definedTester(scheme.enum)) {
|
|
173
|
-
if (!arrayTester(scheme.enum)) {
|
|
174
|
-
throw new Error(ErrorMsg.ENUM_SHOULD_BE_ARRAY);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const enumArr : number[] = scheme.enum as number[];
|
|
178
|
-
|
|
179
|
-
if (!arrayTypeOfTester(enumArr, DataType.NUMBER)) {
|
|
180
|
-
throw new Error(ErrorMsg.ENUM_SHOULD_BE_NUMBERS);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (!enumTester(valueAsNumber, enumArr)) {
|
|
184
|
-
result = new EjvError(
|
|
185
|
-
ErrorType.ONE_OF,
|
|
186
|
-
ErrorMsg.ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumArr)),
|
|
187
|
-
_options.path,
|
|
188
|
-
data,
|
|
189
|
-
value
|
|
190
|
-
);
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (definedTester(scheme.enumReverse)) {
|
|
196
|
-
const enumReverseArr : number[] = scheme.enumReverse as number[];
|
|
197
|
-
|
|
198
|
-
if (!arrayTester(enumReverseArr)) {
|
|
199
|
-
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_ARRAY);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (!arrayTypeOfTester(enumReverseArr, DataType.NUMBER)) {
|
|
203
|
-
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_NUMBERS);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (enumTester(valueAsNumber, enumReverseArr)) {
|
|
207
|
-
result = new EjvError(
|
|
208
|
-
ErrorType.NOT_ONE_OF,
|
|
209
|
-
ErrorMsg.NOT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumReverseArr)),
|
|
210
|
-
_options.path,
|
|
211
|
-
data,
|
|
212
|
-
value
|
|
213
|
-
);
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (definedTester(scheme.min)) {
|
|
219
|
-
if (!numberTester(scheme.min)) {
|
|
220
|
-
throw new Error(ErrorMsg.MIN_SHOULD_BE_NUMBER);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (definedTester(scheme.exclusiveMin)) {
|
|
224
|
-
if (!booleanTester(scheme.exclusiveMin)) {
|
|
225
|
-
throw new Error(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (scheme.exclusiveMin === true) {
|
|
229
|
-
if (!exclusiveMinNumberTester(valueAsNumber, scheme.min as number)) {
|
|
230
|
-
result = new EjvError(
|
|
231
|
-
ErrorType.GREATER_THAN,
|
|
232
|
-
ErrorMsg.GREATER_THAN.replace(ErrorMsgCursorA, '' + scheme.min),
|
|
233
|
-
_options.path,
|
|
234
|
-
data,
|
|
235
|
-
value
|
|
236
|
-
);
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
if (!minNumberTester(valueAsNumber, scheme.min as number)) {
|
|
241
|
-
result = new EjvError(
|
|
242
|
-
ErrorType.GREATER_THAN_OR_EQUAL,
|
|
243
|
-
ErrorMsg.GREATER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.min),
|
|
244
|
-
_options.path,
|
|
245
|
-
data,
|
|
246
|
-
value
|
|
247
|
-
);
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
} else {
|
|
252
|
-
if (!minNumberTester(valueAsNumber, scheme.min as number)) {
|
|
253
|
-
result = new EjvError(
|
|
254
|
-
ErrorType.GREATER_THAN_OR_EQUAL,
|
|
255
|
-
ErrorMsg.GREATER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.min),
|
|
256
|
-
_options.path,
|
|
257
|
-
data,
|
|
258
|
-
value
|
|
259
|
-
);
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (definedTester(scheme.max)) {
|
|
266
|
-
if (!numberTester(scheme.max)) {
|
|
267
|
-
throw new Error(ErrorMsg.MAX_SHOULD_BE_NUMBER);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (definedTester(scheme.exclusiveMax)) {
|
|
271
|
-
if (!booleanTester(scheme.exclusiveMax)) {
|
|
272
|
-
throw new Error(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (scheme.exclusiveMax === true) {
|
|
276
|
-
if (!exclusiveMaxNumberTester(valueAsNumber, scheme.max as number)) {
|
|
277
|
-
result = new EjvError(
|
|
278
|
-
ErrorType.SMALLER_THAN,
|
|
279
|
-
ErrorMsg.SMALLER_THAN.replace(ErrorMsgCursorA, '' + scheme.max),
|
|
280
|
-
_options.path,
|
|
281
|
-
data,
|
|
282
|
-
value
|
|
283
|
-
);
|
|
284
|
-
break;
|
|
285
|
-
}
|
|
286
|
-
} else {
|
|
287
|
-
if (!maxNumberTester(valueAsNumber, scheme.max as number)) {
|
|
288
|
-
result = new EjvError(
|
|
289
|
-
ErrorType.SMALLER_THAN_OR_EQUAL,
|
|
290
|
-
ErrorMsg.SMALLER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.max),
|
|
291
|
-
_options.path,
|
|
292
|
-
data,
|
|
293
|
-
value
|
|
294
|
-
);
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
} else {
|
|
299
|
-
if (!maxNumberTester(valueAsNumber, scheme.max as number)) {
|
|
300
|
-
result = new EjvError(
|
|
301
|
-
ErrorType.SMALLER_THAN_OR_EQUAL,
|
|
302
|
-
ErrorMsg.SMALLER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.max),
|
|
303
|
-
_options.path,
|
|
304
|
-
data,
|
|
305
|
-
value
|
|
306
|
-
);
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if (definedTester(scheme.format)) {
|
|
313
|
-
let formats : NumberFormat[];
|
|
314
|
-
|
|
315
|
-
const allNumberFormat : NumberFormat[] = Object.values(NumberFormat);
|
|
316
|
-
|
|
317
|
-
if (!arrayTester(scheme.format)) {
|
|
318
|
-
const formatAsString : NumberFormat = scheme.format as NumberFormat;
|
|
319
|
-
|
|
320
|
-
if (!enumTester(formatAsString, allNumberFormat)) {
|
|
321
|
-
throw new Error(ErrorMsg.INVALID_NUMBER_FORMAT.replace(ErrorMsgCursorA, formatAsString));
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
formats = [scheme.format as NumberFormat];
|
|
325
|
-
} else {
|
|
326
|
-
const formatAsArray : NumberFormat[] = scheme.format as NumberFormat[];
|
|
327
|
-
|
|
328
|
-
const errorFormat : string | undefined = formatAsArray.find(format => {
|
|
329
|
-
return !enumTester(format, allNumberFormat);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
if (!!errorFormat) {
|
|
333
|
-
throw new Error(ErrorMsg.INVALID_NUMBER_FORMAT.replace(ErrorMsgCursorA, errorFormat));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
formats = scheme.format as NumberFormat[];
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (!formats.some(format => {
|
|
340
|
-
let valid : boolean = false;
|
|
341
|
-
|
|
342
|
-
switch (format) {
|
|
343
|
-
case NumberFormat.INTEGER:
|
|
344
|
-
valid = integerTester(value);
|
|
345
|
-
break;
|
|
346
|
-
|
|
347
|
-
case NumberFormat.INDEX:
|
|
348
|
-
valid = indexTester(value);
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return valid;
|
|
353
|
-
})) {
|
|
354
|
-
if (!arrayTester(scheme.format)) {
|
|
355
|
-
result = new EjvError(
|
|
356
|
-
ErrorType.FORMAT,
|
|
357
|
-
ErrorMsg.FORMAT.replace(ErrorMsgCursorA, scheme.format as NumberFormat),
|
|
358
|
-
_options.path,
|
|
359
|
-
data,
|
|
360
|
-
value
|
|
361
|
-
);
|
|
362
|
-
} else {
|
|
363
|
-
result = new EjvError(
|
|
364
|
-
ErrorType.FORMAT_ONE_OF,
|
|
365
|
-
ErrorMsg.FORMAT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.format)),
|
|
366
|
-
_options.path,
|
|
367
|
-
data,
|
|
368
|
-
value
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
break;
|
|
375
|
-
|
|
376
|
-
case DataType.STRING:
|
|
377
|
-
const valueAsString : string = value as unknown as string;
|
|
378
|
-
|
|
379
|
-
if (definedTester(scheme.enum)) {
|
|
380
|
-
if (!arrayTester(scheme.enum)) {
|
|
381
|
-
throw new Error(ErrorMsg.ENUM_SHOULD_BE_ARRAY);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const enumArr : string[] = scheme.enum as string[];
|
|
385
|
-
|
|
386
|
-
if (!arrayTypeOfTester(enumArr, DataType.STRING)) {
|
|
387
|
-
throw new Error(ErrorMsg.ENUM_SHOULD_BE_STRINGS);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (!enumTester(valueAsString, enumArr)) {
|
|
391
|
-
result = new EjvError(
|
|
392
|
-
ErrorType.ONE_OF,
|
|
393
|
-
ErrorMsg.ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.enum)),
|
|
394
|
-
_options.path,
|
|
395
|
-
data,
|
|
396
|
-
value
|
|
397
|
-
);
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (definedTester(scheme.enumReverse)) {
|
|
403
|
-
if (!arrayTester(scheme.enumReverse)) {
|
|
404
|
-
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_ARRAY);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const enumReverseArr : string[] = scheme.enumReverse as string[];
|
|
408
|
-
|
|
409
|
-
if (!arrayTypeOfTester(enumReverseArr, DataType.STRING)) {
|
|
410
|
-
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_STRINGS);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (enumTester(valueAsString, enumReverseArr)) {
|
|
414
|
-
result = new EjvError(
|
|
415
|
-
ErrorType.NOT_ONE_OF,
|
|
416
|
-
ErrorMsg.NOT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumReverseArr)),
|
|
417
|
-
_options.path,
|
|
418
|
-
data,
|
|
419
|
-
value
|
|
420
|
-
);
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (definedTester(scheme.length)) {
|
|
426
|
-
const length : number = scheme.length as number;
|
|
427
|
-
|
|
428
|
-
if (!(numberTester(length) && integerTester(length))) {
|
|
429
|
-
throw new Error(ErrorMsg.LENGTH_SHOULD_BE_INTEGER);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (!lengthTester(valueAsString, length)) {
|
|
433
|
-
result = new EjvError(
|
|
434
|
-
ErrorType.LENGTH,
|
|
435
|
-
ErrorMsg.LENGTH.replace(ErrorMsgCursorA, '' + length),
|
|
436
|
-
_options.path,
|
|
437
|
-
data,
|
|
438
|
-
value
|
|
439
|
-
);
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (definedTester(scheme.minLength)) {
|
|
445
|
-
const minLength : number = scheme.minLength as number;
|
|
446
|
-
|
|
447
|
-
if (!(numberTester(minLength) && integerTester(minLength))) {
|
|
448
|
-
throw new Error(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (!minLengthTester(valueAsString, minLength)) {
|
|
452
|
-
result = new EjvError(
|
|
453
|
-
ErrorType.MIN_LENGTH,
|
|
454
|
-
ErrorMsg.MIN_LENGTH.replace(ErrorMsgCursorA, '' + minLength),
|
|
455
|
-
_options.path,
|
|
456
|
-
data,
|
|
457
|
-
value
|
|
458
|
-
);
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
if (definedTester(scheme.maxLength)) {
|
|
464
|
-
const maxLength : number = scheme.maxLength as number;
|
|
465
|
-
|
|
466
|
-
if (!(numberTester(maxLength) && integerTester(maxLength))) {
|
|
467
|
-
throw new Error(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if (!maxLengthTester(valueAsString, maxLength)) {
|
|
471
|
-
result = new EjvError(
|
|
472
|
-
ErrorType.MAX_LENGTH,
|
|
473
|
-
ErrorMsg.MAX_LENGTH.replace(ErrorMsgCursorA, '' + maxLength),
|
|
474
|
-
_options.path,
|
|
475
|
-
data,
|
|
476
|
-
value
|
|
477
|
-
);
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (definedTester(scheme.format)) {
|
|
483
|
-
let formats : StringFormat[];
|
|
484
|
-
|
|
485
|
-
const allStringFormat : StringFormat[] = Object.values(StringFormat);
|
|
486
|
-
|
|
487
|
-
if (!arrayTester(scheme.format)) {
|
|
488
|
-
const formatAsString : string = scheme.format as string;
|
|
489
|
-
|
|
490
|
-
if (!enumTester(formatAsString, allStringFormat)) {
|
|
491
|
-
throw new Error(ErrorMsg.INVALID_STRING_FORMAT.replace(ErrorMsgCursorA, formatAsString));
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
formats = [scheme.format] as StringFormat[];
|
|
495
|
-
} else {
|
|
496
|
-
const formatAsArray : string[] = scheme.format as string[];
|
|
497
|
-
const errorFormat : string | undefined = formatAsArray.find(format => {
|
|
498
|
-
return !enumTester(format, allStringFormat);
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
if (!!errorFormat) {
|
|
502
|
-
throw new Error(ErrorMsg.INVALID_STRING_FORMAT.replace(ErrorMsgCursorA, errorFormat));
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
formats = scheme.format as StringFormat[];
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (!formats.some(format => {
|
|
509
|
-
let valid : boolean = false;
|
|
510
|
-
|
|
511
|
-
switch (format) {
|
|
512
|
-
case StringFormat.EMAIL:
|
|
513
|
-
valid = emailTester(value);
|
|
514
|
-
break;
|
|
515
|
-
|
|
516
|
-
case StringFormat.DATE:
|
|
517
|
-
valid = dateFormatTester(value);
|
|
518
|
-
break;
|
|
519
|
-
|
|
520
|
-
case StringFormat.TIME:
|
|
521
|
-
valid = timeFormatTester(value);
|
|
522
|
-
break;
|
|
523
|
-
|
|
524
|
-
case StringFormat.DATE_TIME:
|
|
525
|
-
valid = dateTimeFormatTester(value);
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
return valid;
|
|
530
|
-
})) {
|
|
531
|
-
if (!arrayTester(scheme.format)) {
|
|
532
|
-
result = new EjvError(
|
|
533
|
-
ErrorType.FORMAT,
|
|
534
|
-
ErrorMsg.FORMAT.replace(ErrorMsgCursorA, scheme.format as StringFormat),
|
|
535
|
-
_options.path,
|
|
536
|
-
data,
|
|
537
|
-
value
|
|
538
|
-
);
|
|
539
|
-
} else {
|
|
540
|
-
result = new EjvError(
|
|
541
|
-
ErrorType.FORMAT_ONE_OF,
|
|
542
|
-
ErrorMsg.FORMAT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.format)),
|
|
543
|
-
_options.path,
|
|
544
|
-
data,
|
|
545
|
-
value
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
break;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
if (definedTester(scheme.pattern)) {
|
|
553
|
-
// check parameter
|
|
554
|
-
if (scheme.pattern === null) {
|
|
555
|
-
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
556
|
-
.replace(ErrorMsgCursorA, 'null'));
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
const isValidPattern = (pattern : string | RegExp) : boolean => {
|
|
560
|
-
return (stringTester(pattern) && minLengthTester(pattern as string, 1))
|
|
561
|
-
|| (regExpTester(pattern) && pattern.toString() !== '/(?:)/' && pattern.toString() !== '/null/');
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
const patternToString = (pattern : string | RegExp) : string => {
|
|
565
|
-
let regExpStr : string;
|
|
566
|
-
|
|
567
|
-
if (pattern === null) {
|
|
568
|
-
regExpStr = '/null/';
|
|
569
|
-
} else if (stringTester(pattern)) {
|
|
570
|
-
if (minLengthTester(pattern as string, 1)) {
|
|
571
|
-
regExpStr = new RegExp(pattern as string).toString();
|
|
572
|
-
} else {
|
|
573
|
-
regExpStr = '//';
|
|
574
|
-
}
|
|
575
|
-
} else {
|
|
576
|
-
regExpStr = pattern.toString();
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// empty regular expression
|
|
580
|
-
if (regExpStr === '/(?:)/') {
|
|
581
|
-
regExpStr = '//';
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
return regExpStr;
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
const createArrayErrorMsg = (patternsAsArray : (string | RegExp)[]) : string => {
|
|
588
|
-
return '[' + patternsAsArray.map(onePattern => {
|
|
589
|
-
return patternToString(onePattern);
|
|
590
|
-
}).join(', ') + ']';
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
if (arrayTester(scheme.pattern)) {
|
|
594
|
-
const patternsAsArray : (string | RegExp)[] = scheme.pattern as (string | RegExp)[];
|
|
595
|
-
|
|
596
|
-
if (!minLengthTester(patternsAsArray, 1)) { // empty array
|
|
597
|
-
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
598
|
-
.replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)));
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const regExpPatterns : RegExp[] = patternsAsArray.map(pattern => {
|
|
602
|
-
if (!isValidPattern(pattern)) {
|
|
603
|
-
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
604
|
-
.replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)));
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
return new RegExp(pattern);
|
|
608
|
-
}) as RegExp[];
|
|
609
|
-
|
|
610
|
-
// check value
|
|
611
|
-
if (!regExpPatterns.some((regexp : RegExp) => {
|
|
612
|
-
return stringRegExpTester(value, regexp);
|
|
613
|
-
})) {
|
|
614
|
-
result = new EjvError(
|
|
615
|
-
ErrorType.PATTERN_ONE_OF,
|
|
616
|
-
ErrorMsg.PATTERN_ONE_OF
|
|
617
|
-
.replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)),
|
|
618
|
-
_options.path,
|
|
619
|
-
data,
|
|
620
|
-
value
|
|
621
|
-
);
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
} else {
|
|
626
|
-
const patternAsOne : string | RegExp = scheme.pattern as string | RegExp;
|
|
627
|
-
|
|
628
|
-
if (!isValidPattern(patternAsOne)) {
|
|
629
|
-
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
630
|
-
.replace(ErrorMsgCursorA, patternToString(patternAsOne)));
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// check value
|
|
634
|
-
const regExp : RegExp = new RegExp(patternAsOne);
|
|
635
|
-
|
|
636
|
-
if (!stringRegExpTester(valueAsString, regExp)) {
|
|
637
|
-
result = new EjvError(
|
|
638
|
-
ErrorType.PATTERN,
|
|
639
|
-
ErrorMsg.PATTERN.replace(ErrorMsgCursorA, patternToString(patternAsOne)),
|
|
640
|
-
_options.path,
|
|
641
|
-
data,
|
|
642
|
-
value
|
|
643
|
-
);
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
break;
|
|
649
|
-
|
|
650
|
-
case DataType.OBJECT:
|
|
651
|
-
const valueAsObject = value as unknown as { [key : string] : any };
|
|
652
|
-
|
|
653
|
-
if (definedTester(scheme.allowNoProperty)) {
|
|
654
|
-
if (!booleanTester(scheme.allowNoProperty)) {
|
|
655
|
-
throw new Error(ErrorMsg.ALLOW_NO_PROPERTY_SHOULD_BE_BOOLEAN);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
if (scheme.allowNoProperty !== true && !hasPropertyTester(valueAsObject)) {
|
|
659
|
-
result = new EjvError(
|
|
660
|
-
ErrorType.NO_PROPERTY,
|
|
661
|
-
ErrorMsg.NO_PROPERTY,
|
|
662
|
-
_options.path,
|
|
663
|
-
data,
|
|
664
|
-
value
|
|
665
|
-
);
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if (definedTester(scheme.properties)) {
|
|
671
|
-
if (!arrayTester(scheme.properties)) {
|
|
672
|
-
throw new Error(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
const properties : Scheme[] = scheme.properties as Scheme[];
|
|
676
|
-
|
|
677
|
-
if (!minLengthTester(properties, 1)) {
|
|
678
|
-
throw new Error(ErrorMsg.PROPERTIES_SHOULD_HAVE_ITEMS);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
if (!arrayTypeOfTester(properties, DataType.OBJECT)) {
|
|
682
|
-
throw new Error(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY_OF_OBJECT);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
if (!objectTester(value)) {
|
|
686
|
-
result = new EjvError(
|
|
687
|
-
ErrorType.TYPE_MISMATCH,
|
|
688
|
-
ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, 'object'),
|
|
689
|
-
_options.path,
|
|
690
|
-
data,
|
|
691
|
-
value
|
|
692
|
-
);
|
|
693
|
-
break;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const partialData : T[keyof T] = data[key];
|
|
697
|
-
const partialScheme : Scheme[] = scheme.properties as Scheme[];
|
|
698
|
-
|
|
699
|
-
// call recursively
|
|
700
|
-
result = _ejv(partialData, partialScheme, _options);
|
|
701
|
-
|
|
702
|
-
if (!!result) {
|
|
703
|
-
// inject original data
|
|
704
|
-
result.data = data;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
break;
|
|
708
|
-
|
|
709
|
-
case DataType.DATE:
|
|
710
|
-
const valueAsDate : Date = value as unknown as Date;
|
|
711
|
-
|
|
712
|
-
if (definedTester(scheme.min)) {
|
|
713
|
-
if (!(
|
|
714
|
-
(stringTester(scheme.min) && (dateFormatTester(scheme.min as string) || dateTimeFormatTester(scheme.min as string)))
|
|
715
|
-
|| dateTester(scheme.min)
|
|
716
|
-
)) {
|
|
717
|
-
throw new Error(ErrorMsg.MIN_DATE_SHOULD_BE_DATE_OR_STRING);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
if (definedTester(scheme.exclusiveMin) && !booleanTester(scheme.exclusiveMin)) {
|
|
721
|
-
throw new Error(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
let minDate : Date = new Date(scheme.min as string | Date);
|
|
725
|
-
|
|
726
|
-
// adjust timezone
|
|
727
|
-
if (stringTester(scheme.min)) {
|
|
728
|
-
// by minutes
|
|
729
|
-
const timezoneOffset : number = minDate.getTimezoneOffset();
|
|
730
|
-
|
|
731
|
-
minDate = new Date(+minDate + (timezoneOffset * 60 * 1000));
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (scheme.exclusiveMin !== true) {
|
|
735
|
-
if (!minDateTester(valueAsDate, minDate)) {
|
|
736
|
-
result = new EjvError(
|
|
737
|
-
ErrorType.AFTER_OR_SAME_DATE,
|
|
738
|
-
ErrorMsg.AFTER_OR_SAME_DATE.replace(ErrorMsgCursorA, minDate.toISOString()),
|
|
739
|
-
_options.path,
|
|
740
|
-
data,
|
|
741
|
-
value
|
|
742
|
-
);
|
|
743
|
-
break;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
} else {
|
|
747
|
-
if (!exclusiveMinDateTester(valueAsDate, minDate)) {
|
|
748
|
-
result = new EjvError(
|
|
749
|
-
ErrorType.AFTER_DATE,
|
|
750
|
-
ErrorMsg.AFTER_DATE.replace(ErrorMsgCursorA, minDate.toISOString()),
|
|
751
|
-
_options.path,
|
|
752
|
-
data,
|
|
753
|
-
value
|
|
754
|
-
);
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
if (definedTester(scheme.max)) {
|
|
761
|
-
if (!(
|
|
762
|
-
(stringTester(scheme.max) && (dateFormatTester(scheme.max as string) || dateTimeFormatTester(scheme.max as string)))
|
|
763
|
-
|| dateTester(scheme.max)
|
|
764
|
-
)) {
|
|
765
|
-
throw new Error(ErrorMsg.MAX_DATE_SHOULD_BE_DATE_OR_STRING);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
if (definedTester(scheme.exclusiveMax) && !booleanTester(scheme.exclusiveMax)) {
|
|
769
|
-
throw new Error(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
let maxDate : Date = new Date(scheme.max as string | Date);
|
|
773
|
-
|
|
774
|
-
// adjust timezone
|
|
775
|
-
if (stringTester(scheme.max)) {
|
|
776
|
-
// by minutes
|
|
777
|
-
const timezoneOffset : number = maxDate.getTimezoneOffset();
|
|
778
|
-
|
|
779
|
-
maxDate = new Date(+maxDate + (timezoneOffset * 60 * 1000));
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
if (scheme.exclusiveMax !== true) {
|
|
783
|
-
if (!maxDateTester(valueAsDate, maxDate)) {
|
|
784
|
-
result = new EjvError(
|
|
785
|
-
ErrorType.BEFORE_OR_SAME_DATE,
|
|
786
|
-
ErrorMsg.BEFORE_OR_SAME_DATE.replace(ErrorMsgCursorA, maxDate.toISOString()),
|
|
787
|
-
_options.path,
|
|
788
|
-
data,
|
|
789
|
-
value
|
|
790
|
-
);
|
|
791
|
-
break;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
} else {
|
|
795
|
-
if (!exclusiveMaxDateTester(valueAsDate, maxDate)) {
|
|
796
|
-
result = new EjvError(
|
|
797
|
-
ErrorType.BEFORE_DATE,
|
|
798
|
-
ErrorMsg.BEFORE_DATE.replace(ErrorMsgCursorA, maxDate.toISOString()),
|
|
799
|
-
_options.path,
|
|
800
|
-
data,
|
|
801
|
-
value
|
|
802
|
-
);
|
|
803
|
-
break;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
break;
|
|
808
|
-
|
|
809
|
-
case DataType.ARRAY:
|
|
810
|
-
const valueAsArray : any[] = value as unknown as any[];
|
|
811
|
-
|
|
812
|
-
if (definedTester(scheme.length)) {
|
|
813
|
-
const length : number = scheme.length as number;
|
|
814
|
-
|
|
815
|
-
if (!(numberTester(length) && integerTester(length))) {
|
|
816
|
-
throw new Error(ErrorMsg.LENGTH_SHOULD_BE_INTEGER);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
if (!lengthTester(valueAsArray, length)) {
|
|
820
|
-
result = new EjvError(
|
|
821
|
-
ErrorType.LENGTH,
|
|
822
|
-
ErrorMsg.LENGTH.replace(ErrorMsgCursorA, '' + length),
|
|
823
|
-
_options.path,
|
|
824
|
-
data,
|
|
825
|
-
value
|
|
826
|
-
);
|
|
827
|
-
break;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
if (definedTester(scheme.minLength)) {
|
|
832
|
-
const minLength : number = scheme.minLength as number;
|
|
833
|
-
|
|
834
|
-
if (!(numberTester(scheme.minLength) && integerTester(minLength))) {
|
|
835
|
-
throw new Error(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if (!minLengthTester(valueAsArray, minLength)) {
|
|
839
|
-
result = new EjvError(
|
|
840
|
-
ErrorType.MIN_LENGTH,
|
|
841
|
-
ErrorMsg.MIN_LENGTH.replace(ErrorMsgCursorA, '' + minLength),
|
|
842
|
-
_options.path,
|
|
843
|
-
data,
|
|
844
|
-
value
|
|
845
|
-
);
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
if (definedTester(scheme.maxLength)) {
|
|
851
|
-
const maxLength : number = scheme.maxLength as number;
|
|
852
|
-
|
|
853
|
-
if (!(numberTester(scheme.maxLength) && integerTester(maxLength))) {
|
|
854
|
-
throw new Error(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
if (!maxLengthTester(valueAsArray, maxLength)) {
|
|
858
|
-
result = new EjvError(
|
|
859
|
-
ErrorType.MAX_LENGTH,
|
|
860
|
-
ErrorMsg.MAX_LENGTH.replace(ErrorMsgCursorA, '' + maxLength),
|
|
861
|
-
_options.path,
|
|
862
|
-
data,
|
|
863
|
-
value
|
|
864
|
-
);
|
|
865
|
-
break;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
if (definedTester(scheme.unique)) {
|
|
870
|
-
if (!booleanTester(scheme.unique)) {
|
|
871
|
-
throw new Error(ErrorMsg.UNIQUE_SHOULD_BE_BOOLEAN);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
if (scheme.unique === true && !uniqueItemsTester(valueAsArray)) {
|
|
875
|
-
result = new EjvError(
|
|
876
|
-
ErrorType.UNIQUE_ITEMS,
|
|
877
|
-
ErrorMsg.UNIQUE_ITEMS,
|
|
878
|
-
_options.path,
|
|
879
|
-
data,
|
|
880
|
-
value
|
|
881
|
-
);
|
|
882
|
-
break;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (definedTester(scheme.items)) {
|
|
887
|
-
// convert array to object
|
|
888
|
-
if (valueAsArray.length > 0) {
|
|
889
|
-
const now : Date = new Date;
|
|
890
|
-
const tempKeyArr : string[] = valueAsArray.map((value : any, i : number) => {
|
|
891
|
-
return '' + (+now + i);
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
if (stringTester(scheme.items) // by DataType
|
|
895
|
-
|| (arrayTester(scheme.items) && arrayTypeOfTester(scheme.items as DataType[], DataType.STRING)) // by DataType[]
|
|
896
|
-
) {
|
|
897
|
-
const itemTypes : DataType[] = (arrayTester(scheme.items) ? scheme.items : [scheme.items]) as DataType[];
|
|
898
|
-
|
|
899
|
-
const partialData : AnyObject = {};
|
|
900
|
-
const partialSchemes : Scheme[] = [];
|
|
901
|
-
|
|
902
|
-
tempKeyArr.forEach((tempKey : string, i : number) => {
|
|
903
|
-
partialData[tempKey] = valueAsArray[i];
|
|
904
|
-
partialSchemes.push({
|
|
905
|
-
key : tempKey,
|
|
906
|
-
type : itemTypes
|
|
907
|
-
});
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
// call recursively
|
|
911
|
-
const partialResult : EjvError | null = _ejv(partialData, partialSchemes, _options);
|
|
912
|
-
|
|
913
|
-
// convert new EjvError
|
|
914
|
-
if (!!partialResult) {
|
|
915
|
-
let errorMsg : string;
|
|
916
|
-
|
|
917
|
-
if (arrayTester(scheme.items)) {
|
|
918
|
-
errorMsg = ErrorMsg.ITEMS_TYPE.replace(ErrorMsgCursorA, JSON.stringify(itemTypes));
|
|
919
|
-
} else {
|
|
920
|
-
errorMsg = ErrorMsg.ITEMS_TYPE.replace(ErrorMsgCursorA, scheme.items as string);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const partialKeys : string[] = partialResult.path.split('/');
|
|
924
|
-
const partialKey : string = partialKeys[partialKeys.length - 1];
|
|
925
|
-
|
|
926
|
-
const partialScheme : Scheme = partialSchemes.find(scheme => {
|
|
927
|
-
return scheme.key === partialKey;
|
|
928
|
-
}) as Scheme;
|
|
929
|
-
|
|
930
|
-
const partialKeyIndex : number = partialSchemes.indexOf(partialScheme);
|
|
931
|
-
|
|
932
|
-
result = new EjvError(
|
|
933
|
-
ErrorType.ITEMS_TYPE,
|
|
934
|
-
errorMsg,
|
|
935
|
-
[..._options.path, '' + partialKeyIndex],
|
|
936
|
-
data,
|
|
937
|
-
partialData[partialKey]
|
|
938
|
-
);
|
|
939
|
-
}
|
|
940
|
-
break;
|
|
941
|
-
} else if ((objectTester(scheme.items) && scheme.items !== null) // by Scheme
|
|
942
|
-
|| (arrayTester(scheme.items) && arrayTypeOfTester(scheme.items as Scheme[], DataType.OBJECT)) // by Scheme[]
|
|
943
|
-
) {
|
|
944
|
-
const itemsAsSchemes : Scheme[] = (arrayTester(scheme.items) ? scheme.items : [scheme.items]) as Scheme[];
|
|
945
|
-
|
|
946
|
-
let partialError : EjvError | null | undefined = null;
|
|
947
|
-
|
|
948
|
-
// use for() instead of forEach() to break
|
|
949
|
-
const valueLength : number = valueAsArray.length;
|
|
950
|
-
|
|
951
|
-
for (let arrIndex = 0; arrIndex < valueLength; arrIndex++) {
|
|
952
|
-
const oneValue : any = valueAsArray[arrIndex];
|
|
953
|
-
|
|
954
|
-
const partialData : AnyObject = {};
|
|
955
|
-
const partialSchemes : Scheme[] = [];
|
|
956
|
-
|
|
957
|
-
const tempKeyForThisValue : string = tempKeyArr[arrIndex];
|
|
958
|
-
|
|
959
|
-
partialData[tempKeyForThisValue] = oneValue;
|
|
960
|
-
|
|
961
|
-
partialSchemes.push(...itemsAsSchemes.map((oneScheme : Scheme) => {
|
|
962
|
-
const newScheme : Scheme = clone(oneScheme); // divide instance
|
|
963
|
-
|
|
964
|
-
newScheme.key = tempKeyForThisValue;
|
|
965
|
-
|
|
966
|
-
return newScheme;
|
|
967
|
-
}));
|
|
968
|
-
|
|
969
|
-
const partialResults : (EjvError | null)[] = partialSchemes.map((partialScheme : Scheme) => {
|
|
970
|
-
// call recursively
|
|
971
|
-
const partialResult : EjvError | null = _ejv(partialData, [partialScheme], _options);
|
|
972
|
-
|
|
973
|
-
if (!!partialResult) {
|
|
974
|
-
partialResult.path = partialResult.path.replace(tempKeyForThisValue, '' + arrIndex);
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
return partialResult;
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
if (!partialResults.some(oneResult => oneResult === null)) {
|
|
981
|
-
partialError = partialResults.find(oneResult => {
|
|
982
|
-
return !!oneResult;
|
|
983
|
-
});
|
|
984
|
-
break;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
if (!!partialError) {
|
|
989
|
-
let errorType : ErrorType;
|
|
990
|
-
let errorMsg : string;
|
|
991
|
-
|
|
992
|
-
if (!!itemsAsSchemes && itemsAsSchemes.length > 1) {
|
|
993
|
-
errorType = ErrorType.ITEMS_SCHEMES;
|
|
994
|
-
errorMsg = ErrorMsg.ITEMS_SCHEMES.replace(ErrorMsgCursorA, JSON.stringify(itemsAsSchemes));
|
|
995
|
-
} else {
|
|
996
|
-
errorType = partialError.type;
|
|
997
|
-
errorMsg = partialError.message;
|
|
998
|
-
|
|
999
|
-
if (errorType === ErrorType.REQUIRED) {
|
|
1000
|
-
// REQUIRED in array is TYPE_MISMATCH except with nullable === true
|
|
1001
|
-
errorType = ErrorType.TYPE_MISMATCH;
|
|
1002
|
-
errorMsg = ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, JSON.stringify(scheme.items));
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
result = new EjvError(
|
|
1007
|
-
errorType,
|
|
1008
|
-
errorMsg,
|
|
1009
|
-
partialError.path.split('/'),
|
|
1010
|
-
data,
|
|
1011
|
-
partialError.errorData
|
|
1012
|
-
);
|
|
1013
|
-
break;
|
|
1014
|
-
}
|
|
1015
|
-
} else {
|
|
1016
|
-
throw new Error(ErrorMsg.INVALID_ITEMS_SCHEME.replace(ErrorMsgCursorA, JSON.stringify(scheme.items)));
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
break;
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
if (!!result) {
|
|
1024
|
-
break;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
if (definedTester(result) && definedTester(options.customErrorMsg)) {
|
|
1029
|
-
const resultAsNotNull : EjvError = result as NonNullable<EjvError>;
|
|
1030
|
-
const customErrorMsgObj : {
|
|
1031
|
-
[key in ErrorType]? : string;
|
|
1032
|
-
} = options.customErrorMsg as {
|
|
1033
|
-
[key in ErrorType]? : string;
|
|
1034
|
-
};
|
|
1035
|
-
|
|
1036
|
-
// override error message
|
|
1037
|
-
const customMsg : string = customErrorMsgObj[resultAsNotNull.type] as string;
|
|
1038
|
-
|
|
1039
|
-
if (definedTester(customMsg)) {
|
|
1040
|
-
resultAsNotNull.message = customMsg;
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
return result;
|
|
1045
|
-
};
|
|
1046
|
-
|
|
1047
|
-
export const ejv = (data : object, schemes : Scheme[], options? : Options) : null | EjvError => {
|
|
1048
|
-
// check data itself
|
|
1049
|
-
if (!definedTester(data) || !objectTester(data) || data === null) {
|
|
1050
|
-
return new EjvError(ErrorType.REQUIRED, ErrorMsg.NO_DATA, ['/'], data,
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// check schemes itself
|
|
1054
|
-
if (!definedTester(schemes) || schemes === null) {
|
|
1055
|
-
throw new Error(ErrorMsg.NO_SCHEME);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
if (!arrayTester(schemes)) {
|
|
1059
|
-
throw new Error(ErrorMsg.NO_ARRAY_SCHEME);
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (!arrayTypeOfTester(schemes, DataType.OBJECT)) {
|
|
1063
|
-
throw new Error(ErrorMsg.NO_OBJECT_ARRAY_SCHEME);
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
if (!minLengthTester(schemes, 1)) {
|
|
1067
|
-
throw new Error(ErrorMsg.EMPTY_SCHEME);
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
return _ejv(data, schemes, options as InternalOptions);
|
|
1071
|
-
};
|
|
1
|
+
import { EjvError, InternalOptions, Options, Scheme } from './interfaces';
|
|
2
|
+
import { DataType, ErrorMsg, ErrorMsgCursorA, ErrorType, NumberFormat, StringFormat } from './constants';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
arrayTester,
|
|
6
|
+
arrayTypeOfTester,
|
|
7
|
+
booleanTester,
|
|
8
|
+
dateFormatTester,
|
|
9
|
+
dateTester,
|
|
10
|
+
dateTimeFormatTester,
|
|
11
|
+
definedTester,
|
|
12
|
+
emailTester,
|
|
13
|
+
enumTester,
|
|
14
|
+
exclusiveMaxDateTester,
|
|
15
|
+
exclusiveMaxNumberTester,
|
|
16
|
+
exclusiveMinDateTester,
|
|
17
|
+
exclusiveMinNumberTester,
|
|
18
|
+
hasPropertyTester,
|
|
19
|
+
indexTester,
|
|
20
|
+
integerTester,
|
|
21
|
+
lengthTester,
|
|
22
|
+
maxDateTester,
|
|
23
|
+
maxLengthTester,
|
|
24
|
+
maxNumberTester,
|
|
25
|
+
minDateTester,
|
|
26
|
+
minLengthTester,
|
|
27
|
+
minNumberTester,
|
|
28
|
+
numberTester,
|
|
29
|
+
objectTester,
|
|
30
|
+
regExpTester,
|
|
31
|
+
stringRegExpTester,
|
|
32
|
+
stringTester,
|
|
33
|
+
timeFormatTester,
|
|
34
|
+
typeTester,
|
|
35
|
+
uniqueItemsTester
|
|
36
|
+
} from './tester';
|
|
37
|
+
import { clone } from './util';
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
interface AnyObject {
|
|
41
|
+
[key : string] : any;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
const _ejv = <T>(data : T, schemes : Scheme[], options : InternalOptions = {
|
|
46
|
+
path : []
|
|
47
|
+
}) : null | EjvError => {
|
|
48
|
+
// check schemes
|
|
49
|
+
if (!arrayTester(schemes)) {
|
|
50
|
+
throw new Error(ErrorMsg.NO_ARRAY_SCHEME);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!arrayTypeOfTester(schemes, DataType.OBJECT)) {
|
|
54
|
+
throw new Error(ErrorMsg.NO_OBJECT_ARRAY_SCHEME);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!minLengthTester(schemes, 1)) {
|
|
58
|
+
throw new Error(ErrorMsg.EMPTY_SCHEME);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// check data by schemes
|
|
62
|
+
let result : EjvError | null = null;
|
|
63
|
+
|
|
64
|
+
// use for() instead of forEach() to stop
|
|
65
|
+
const schemeLength : number = schemes.length;
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < schemeLength; i++) {
|
|
68
|
+
const _options : InternalOptions = clone(options); // divide instance
|
|
69
|
+
|
|
70
|
+
if (!definedTester(_options.path)) {
|
|
71
|
+
_options.path = [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const scheme : Scheme = schemes[i];
|
|
75
|
+
const key : keyof T = scheme.key as keyof T;
|
|
76
|
+
|
|
77
|
+
let value : any;
|
|
78
|
+
|
|
79
|
+
if (!!key) {
|
|
80
|
+
value = data[key];
|
|
81
|
+
|
|
82
|
+
_options.path.push(key as string);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let types : DataType[];
|
|
86
|
+
|
|
87
|
+
if (!definedTester(scheme.type)) {
|
|
88
|
+
throw new Error(ErrorMsg.SCHEMES_SHOULD_HAVE_TYPE);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!arrayTester(scheme.type)) {
|
|
92
|
+
types = [scheme.type as DataType];
|
|
93
|
+
} else {
|
|
94
|
+
types = scheme.type as DataType[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const allDataType : DataType[] = Object.values(DataType);
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const errorType : string | undefined = types.find(type => {
|
|
101
|
+
return !(stringTester(type) && enumTester(type, allDataType));
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (!!errorType) {
|
|
105
|
+
throw new Error(ErrorMsg.SCHEMES_HAS_INVALID_TYPE.replace(ErrorMsgCursorA, errorType));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!uniqueItemsTester(types)) {
|
|
109
|
+
throw new Error(ErrorMsg.SCHEMES_HAS_DUPLICATED_TYPE);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!definedTester(value)) {
|
|
113
|
+
if (scheme.optional !== true) {
|
|
114
|
+
result = new EjvError(
|
|
115
|
+
ErrorType.REQUIRED,
|
|
116
|
+
ErrorMsg.REQUIRED,
|
|
117
|
+
_options.path,
|
|
118
|
+
data,
|
|
119
|
+
value
|
|
120
|
+
);
|
|
121
|
+
break;
|
|
122
|
+
} else {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (value === null) {
|
|
128
|
+
if (scheme.nullable !== true) {
|
|
129
|
+
result = new EjvError(
|
|
130
|
+
ErrorType.REQUIRED,
|
|
131
|
+
ErrorMsg.REQUIRED,
|
|
132
|
+
_options.path,
|
|
133
|
+
data,
|
|
134
|
+
value
|
|
135
|
+
);
|
|
136
|
+
break;
|
|
137
|
+
} else {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const typeResolved : DataType | undefined = types.find(type => {
|
|
143
|
+
return typeTester(value, type);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (!typeResolved) {
|
|
147
|
+
if (!arrayTester(scheme.type)) {
|
|
148
|
+
result = new EjvError(
|
|
149
|
+
ErrorType.TYPE_MISMATCH,
|
|
150
|
+
ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, scheme.type as DataType),
|
|
151
|
+
_options.path,
|
|
152
|
+
data,
|
|
153
|
+
value
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
result = new EjvError(
|
|
157
|
+
ErrorType.TYPE_MISMATCH_ONE_OF,
|
|
158
|
+
ErrorMsg.TYPE_MISMATCH_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.type)),
|
|
159
|
+
_options.path,
|
|
160
|
+
data,
|
|
161
|
+
value
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// additional check for type resolved
|
|
168
|
+
switch (typeResolved) {
|
|
169
|
+
case DataType.NUMBER:
|
|
170
|
+
const valueAsNumber : number = value as unknown as number;
|
|
171
|
+
|
|
172
|
+
if (definedTester(scheme.enum)) {
|
|
173
|
+
if (!arrayTester(scheme.enum)) {
|
|
174
|
+
throw new Error(ErrorMsg.ENUM_SHOULD_BE_ARRAY);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const enumArr : number[] = scheme.enum as number[];
|
|
178
|
+
|
|
179
|
+
if (!arrayTypeOfTester(enumArr, DataType.NUMBER)) {
|
|
180
|
+
throw new Error(ErrorMsg.ENUM_SHOULD_BE_NUMBERS);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!enumTester(valueAsNumber, enumArr)) {
|
|
184
|
+
result = new EjvError(
|
|
185
|
+
ErrorType.ONE_OF,
|
|
186
|
+
ErrorMsg.ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumArr)),
|
|
187
|
+
_options.path,
|
|
188
|
+
data,
|
|
189
|
+
value
|
|
190
|
+
);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (definedTester(scheme.enumReverse)) {
|
|
196
|
+
const enumReverseArr : number[] = scheme.enumReverse as number[];
|
|
197
|
+
|
|
198
|
+
if (!arrayTester(enumReverseArr)) {
|
|
199
|
+
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_ARRAY);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!arrayTypeOfTester(enumReverseArr, DataType.NUMBER)) {
|
|
203
|
+
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_NUMBERS);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (enumTester(valueAsNumber, enumReverseArr)) {
|
|
207
|
+
result = new EjvError(
|
|
208
|
+
ErrorType.NOT_ONE_OF,
|
|
209
|
+
ErrorMsg.NOT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumReverseArr)),
|
|
210
|
+
_options.path,
|
|
211
|
+
data,
|
|
212
|
+
value
|
|
213
|
+
);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (definedTester(scheme.min)) {
|
|
219
|
+
if (!numberTester(scheme.min)) {
|
|
220
|
+
throw new Error(ErrorMsg.MIN_SHOULD_BE_NUMBER);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (definedTester(scheme.exclusiveMin)) {
|
|
224
|
+
if (!booleanTester(scheme.exclusiveMin)) {
|
|
225
|
+
throw new Error(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (scheme.exclusiveMin === true) {
|
|
229
|
+
if (!exclusiveMinNumberTester(valueAsNumber, scheme.min as number)) {
|
|
230
|
+
result = new EjvError(
|
|
231
|
+
ErrorType.GREATER_THAN,
|
|
232
|
+
ErrorMsg.GREATER_THAN.replace(ErrorMsgCursorA, '' + scheme.min),
|
|
233
|
+
_options.path,
|
|
234
|
+
data,
|
|
235
|
+
value
|
|
236
|
+
);
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
if (!minNumberTester(valueAsNumber, scheme.min as number)) {
|
|
241
|
+
result = new EjvError(
|
|
242
|
+
ErrorType.GREATER_THAN_OR_EQUAL,
|
|
243
|
+
ErrorMsg.GREATER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.min),
|
|
244
|
+
_options.path,
|
|
245
|
+
data,
|
|
246
|
+
value
|
|
247
|
+
);
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
if (!minNumberTester(valueAsNumber, scheme.min as number)) {
|
|
253
|
+
result = new EjvError(
|
|
254
|
+
ErrorType.GREATER_THAN_OR_EQUAL,
|
|
255
|
+
ErrorMsg.GREATER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.min),
|
|
256
|
+
_options.path,
|
|
257
|
+
data,
|
|
258
|
+
value
|
|
259
|
+
);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (definedTester(scheme.max)) {
|
|
266
|
+
if (!numberTester(scheme.max)) {
|
|
267
|
+
throw new Error(ErrorMsg.MAX_SHOULD_BE_NUMBER);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (definedTester(scheme.exclusiveMax)) {
|
|
271
|
+
if (!booleanTester(scheme.exclusiveMax)) {
|
|
272
|
+
throw new Error(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (scheme.exclusiveMax === true) {
|
|
276
|
+
if (!exclusiveMaxNumberTester(valueAsNumber, scheme.max as number)) {
|
|
277
|
+
result = new EjvError(
|
|
278
|
+
ErrorType.SMALLER_THAN,
|
|
279
|
+
ErrorMsg.SMALLER_THAN.replace(ErrorMsgCursorA, '' + scheme.max),
|
|
280
|
+
_options.path,
|
|
281
|
+
data,
|
|
282
|
+
value
|
|
283
|
+
);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
if (!maxNumberTester(valueAsNumber, scheme.max as number)) {
|
|
288
|
+
result = new EjvError(
|
|
289
|
+
ErrorType.SMALLER_THAN_OR_EQUAL,
|
|
290
|
+
ErrorMsg.SMALLER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.max),
|
|
291
|
+
_options.path,
|
|
292
|
+
data,
|
|
293
|
+
value
|
|
294
|
+
);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
if (!maxNumberTester(valueAsNumber, scheme.max as number)) {
|
|
300
|
+
result = new EjvError(
|
|
301
|
+
ErrorType.SMALLER_THAN_OR_EQUAL,
|
|
302
|
+
ErrorMsg.SMALLER_THAN_OR_EQUAL.replace(ErrorMsgCursorA, '' + scheme.max),
|
|
303
|
+
_options.path,
|
|
304
|
+
data,
|
|
305
|
+
value
|
|
306
|
+
);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (definedTester(scheme.format)) {
|
|
313
|
+
let formats : NumberFormat[];
|
|
314
|
+
|
|
315
|
+
const allNumberFormat : NumberFormat[] = Object.values(NumberFormat);
|
|
316
|
+
|
|
317
|
+
if (!arrayTester(scheme.format)) {
|
|
318
|
+
const formatAsString : NumberFormat = scheme.format as NumberFormat;
|
|
319
|
+
|
|
320
|
+
if (!enumTester(formatAsString, allNumberFormat)) {
|
|
321
|
+
throw new Error(ErrorMsg.INVALID_NUMBER_FORMAT.replace(ErrorMsgCursorA, formatAsString));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
formats = [scheme.format as NumberFormat];
|
|
325
|
+
} else {
|
|
326
|
+
const formatAsArray : NumberFormat[] = scheme.format as NumberFormat[];
|
|
327
|
+
|
|
328
|
+
const errorFormat : string | undefined = formatAsArray.find(format => {
|
|
329
|
+
return !enumTester(format, allNumberFormat);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (!!errorFormat) {
|
|
333
|
+
throw new Error(ErrorMsg.INVALID_NUMBER_FORMAT.replace(ErrorMsgCursorA, errorFormat));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
formats = scheme.format as NumberFormat[];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (!formats.some(format => {
|
|
340
|
+
let valid : boolean = false;
|
|
341
|
+
|
|
342
|
+
switch (format) {
|
|
343
|
+
case NumberFormat.INTEGER:
|
|
344
|
+
valid = integerTester(value);
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case NumberFormat.INDEX:
|
|
348
|
+
valid = indexTester(value);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return valid;
|
|
353
|
+
})) {
|
|
354
|
+
if (!arrayTester(scheme.format)) {
|
|
355
|
+
result = new EjvError(
|
|
356
|
+
ErrorType.FORMAT,
|
|
357
|
+
ErrorMsg.FORMAT.replace(ErrorMsgCursorA, scheme.format as NumberFormat),
|
|
358
|
+
_options.path,
|
|
359
|
+
data,
|
|
360
|
+
value
|
|
361
|
+
);
|
|
362
|
+
} else {
|
|
363
|
+
result = new EjvError(
|
|
364
|
+
ErrorType.FORMAT_ONE_OF,
|
|
365
|
+
ErrorMsg.FORMAT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.format)),
|
|
366
|
+
_options.path,
|
|
367
|
+
data,
|
|
368
|
+
value
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
375
|
+
|
|
376
|
+
case DataType.STRING:
|
|
377
|
+
const valueAsString : string = value as unknown as string;
|
|
378
|
+
|
|
379
|
+
if (definedTester(scheme.enum)) {
|
|
380
|
+
if (!arrayTester(scheme.enum)) {
|
|
381
|
+
throw new Error(ErrorMsg.ENUM_SHOULD_BE_ARRAY);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const enumArr : string[] = scheme.enum as string[];
|
|
385
|
+
|
|
386
|
+
if (!arrayTypeOfTester(enumArr, DataType.STRING)) {
|
|
387
|
+
throw new Error(ErrorMsg.ENUM_SHOULD_BE_STRINGS);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (!enumTester(valueAsString, enumArr)) {
|
|
391
|
+
result = new EjvError(
|
|
392
|
+
ErrorType.ONE_OF,
|
|
393
|
+
ErrorMsg.ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.enum)),
|
|
394
|
+
_options.path,
|
|
395
|
+
data,
|
|
396
|
+
value
|
|
397
|
+
);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (definedTester(scheme.enumReverse)) {
|
|
403
|
+
if (!arrayTester(scheme.enumReverse)) {
|
|
404
|
+
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_ARRAY);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const enumReverseArr : string[] = scheme.enumReverse as string[];
|
|
408
|
+
|
|
409
|
+
if (!arrayTypeOfTester(enumReverseArr, DataType.STRING)) {
|
|
410
|
+
throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_STRINGS);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (enumTester(valueAsString, enumReverseArr)) {
|
|
414
|
+
result = new EjvError(
|
|
415
|
+
ErrorType.NOT_ONE_OF,
|
|
416
|
+
ErrorMsg.NOT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumReverseArr)),
|
|
417
|
+
_options.path,
|
|
418
|
+
data,
|
|
419
|
+
value
|
|
420
|
+
);
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (definedTester(scheme.length)) {
|
|
426
|
+
const length : number = scheme.length as number;
|
|
427
|
+
|
|
428
|
+
if (!(numberTester(length) && integerTester(length))) {
|
|
429
|
+
throw new Error(ErrorMsg.LENGTH_SHOULD_BE_INTEGER);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (!lengthTester(valueAsString, length)) {
|
|
433
|
+
result = new EjvError(
|
|
434
|
+
ErrorType.LENGTH,
|
|
435
|
+
ErrorMsg.LENGTH.replace(ErrorMsgCursorA, '' + length),
|
|
436
|
+
_options.path,
|
|
437
|
+
data,
|
|
438
|
+
value
|
|
439
|
+
);
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (definedTester(scheme.minLength)) {
|
|
445
|
+
const minLength : number = scheme.minLength as number;
|
|
446
|
+
|
|
447
|
+
if (!(numberTester(minLength) && integerTester(minLength))) {
|
|
448
|
+
throw new Error(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (!minLengthTester(valueAsString, minLength)) {
|
|
452
|
+
result = new EjvError(
|
|
453
|
+
ErrorType.MIN_LENGTH,
|
|
454
|
+
ErrorMsg.MIN_LENGTH.replace(ErrorMsgCursorA, '' + minLength),
|
|
455
|
+
_options.path,
|
|
456
|
+
data,
|
|
457
|
+
value
|
|
458
|
+
);
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (definedTester(scheme.maxLength)) {
|
|
464
|
+
const maxLength : number = scheme.maxLength as number;
|
|
465
|
+
|
|
466
|
+
if (!(numberTester(maxLength) && integerTester(maxLength))) {
|
|
467
|
+
throw new Error(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (!maxLengthTester(valueAsString, maxLength)) {
|
|
471
|
+
result = new EjvError(
|
|
472
|
+
ErrorType.MAX_LENGTH,
|
|
473
|
+
ErrorMsg.MAX_LENGTH.replace(ErrorMsgCursorA, '' + maxLength),
|
|
474
|
+
_options.path,
|
|
475
|
+
data,
|
|
476
|
+
value
|
|
477
|
+
);
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (definedTester(scheme.format)) {
|
|
483
|
+
let formats : StringFormat[];
|
|
484
|
+
|
|
485
|
+
const allStringFormat : StringFormat[] = Object.values(StringFormat);
|
|
486
|
+
|
|
487
|
+
if (!arrayTester(scheme.format)) {
|
|
488
|
+
const formatAsString : string = scheme.format as string;
|
|
489
|
+
|
|
490
|
+
if (!enumTester(formatAsString, allStringFormat)) {
|
|
491
|
+
throw new Error(ErrorMsg.INVALID_STRING_FORMAT.replace(ErrorMsgCursorA, formatAsString));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
formats = [scheme.format] as StringFormat[];
|
|
495
|
+
} else {
|
|
496
|
+
const formatAsArray : string[] = scheme.format as string[];
|
|
497
|
+
const errorFormat : string | undefined = formatAsArray.find(format => {
|
|
498
|
+
return !enumTester(format, allStringFormat);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
if (!!errorFormat) {
|
|
502
|
+
throw new Error(ErrorMsg.INVALID_STRING_FORMAT.replace(ErrorMsgCursorA, errorFormat));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
formats = scheme.format as StringFormat[];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (!formats.some(format => {
|
|
509
|
+
let valid : boolean = false;
|
|
510
|
+
|
|
511
|
+
switch (format) {
|
|
512
|
+
case StringFormat.EMAIL:
|
|
513
|
+
valid = emailTester(value);
|
|
514
|
+
break;
|
|
515
|
+
|
|
516
|
+
case StringFormat.DATE:
|
|
517
|
+
valid = dateFormatTester(value);
|
|
518
|
+
break;
|
|
519
|
+
|
|
520
|
+
case StringFormat.TIME:
|
|
521
|
+
valid = timeFormatTester(value);
|
|
522
|
+
break;
|
|
523
|
+
|
|
524
|
+
case StringFormat.DATE_TIME:
|
|
525
|
+
valid = dateTimeFormatTester(value);
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return valid;
|
|
530
|
+
})) {
|
|
531
|
+
if (!arrayTester(scheme.format)) {
|
|
532
|
+
result = new EjvError(
|
|
533
|
+
ErrorType.FORMAT,
|
|
534
|
+
ErrorMsg.FORMAT.replace(ErrorMsgCursorA, scheme.format as StringFormat),
|
|
535
|
+
_options.path,
|
|
536
|
+
data,
|
|
537
|
+
value
|
|
538
|
+
);
|
|
539
|
+
} else {
|
|
540
|
+
result = new EjvError(
|
|
541
|
+
ErrorType.FORMAT_ONE_OF,
|
|
542
|
+
ErrorMsg.FORMAT_ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(scheme.format)),
|
|
543
|
+
_options.path,
|
|
544
|
+
data,
|
|
545
|
+
value
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (definedTester(scheme.pattern)) {
|
|
553
|
+
// check parameter
|
|
554
|
+
if (scheme.pattern === null) {
|
|
555
|
+
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
556
|
+
.replace(ErrorMsgCursorA, 'null'));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const isValidPattern = (pattern : string | RegExp) : boolean => {
|
|
560
|
+
return (stringTester(pattern) && minLengthTester(pattern as string, 1))
|
|
561
|
+
|| (regExpTester(pattern) && pattern.toString() !== '/(?:)/' && pattern.toString() !== '/null/');
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const patternToString = (pattern : string | RegExp) : string => {
|
|
565
|
+
let regExpStr : string;
|
|
566
|
+
|
|
567
|
+
if (pattern === null) {
|
|
568
|
+
regExpStr = '/null/';
|
|
569
|
+
} else if (stringTester(pattern)) {
|
|
570
|
+
if (minLengthTester(pattern as string, 1)) {
|
|
571
|
+
regExpStr = new RegExp(pattern as string).toString();
|
|
572
|
+
} else {
|
|
573
|
+
regExpStr = '//';
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
regExpStr = pattern.toString();
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// empty regular expression
|
|
580
|
+
if (regExpStr === '/(?:)/') {
|
|
581
|
+
regExpStr = '//';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return regExpStr;
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const createArrayErrorMsg = (patternsAsArray : (string | RegExp)[]) : string => {
|
|
588
|
+
return '[' + patternsAsArray.map(onePattern => {
|
|
589
|
+
return patternToString(onePattern);
|
|
590
|
+
}).join(', ') + ']';
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
if (arrayTester(scheme.pattern)) {
|
|
594
|
+
const patternsAsArray : (string | RegExp)[] = scheme.pattern as (string | RegExp)[];
|
|
595
|
+
|
|
596
|
+
if (!minLengthTester(patternsAsArray, 1)) { // empty array
|
|
597
|
+
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
598
|
+
.replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const regExpPatterns : RegExp[] = patternsAsArray.map(pattern => {
|
|
602
|
+
if (!isValidPattern(pattern)) {
|
|
603
|
+
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
604
|
+
.replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return new RegExp(pattern);
|
|
608
|
+
}) as RegExp[];
|
|
609
|
+
|
|
610
|
+
// check value
|
|
611
|
+
if (!regExpPatterns.some((regexp : RegExp) => {
|
|
612
|
+
return stringRegExpTester(value, regexp);
|
|
613
|
+
})) {
|
|
614
|
+
result = new EjvError(
|
|
615
|
+
ErrorType.PATTERN_ONE_OF,
|
|
616
|
+
ErrorMsg.PATTERN_ONE_OF
|
|
617
|
+
.replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)),
|
|
618
|
+
_options.path,
|
|
619
|
+
data,
|
|
620
|
+
value
|
|
621
|
+
);
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
} else {
|
|
626
|
+
const patternAsOne : string | RegExp = scheme.pattern as string | RegExp;
|
|
627
|
+
|
|
628
|
+
if (!isValidPattern(patternAsOne)) {
|
|
629
|
+
throw new Error(ErrorMsg.INVALID_STRING_PATTERN
|
|
630
|
+
.replace(ErrorMsgCursorA, patternToString(patternAsOne)));
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// check value
|
|
634
|
+
const regExp : RegExp = new RegExp(patternAsOne);
|
|
635
|
+
|
|
636
|
+
if (!stringRegExpTester(valueAsString, regExp)) {
|
|
637
|
+
result = new EjvError(
|
|
638
|
+
ErrorType.PATTERN,
|
|
639
|
+
ErrorMsg.PATTERN.replace(ErrorMsgCursorA, patternToString(patternAsOne)),
|
|
640
|
+
_options.path,
|
|
641
|
+
data,
|
|
642
|
+
value
|
|
643
|
+
);
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
|
|
650
|
+
case DataType.OBJECT:
|
|
651
|
+
const valueAsObject = value as unknown as { [key : string] : any };
|
|
652
|
+
|
|
653
|
+
if (definedTester(scheme.allowNoProperty)) {
|
|
654
|
+
if (!booleanTester(scheme.allowNoProperty)) {
|
|
655
|
+
throw new Error(ErrorMsg.ALLOW_NO_PROPERTY_SHOULD_BE_BOOLEAN);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (scheme.allowNoProperty !== true && !hasPropertyTester(valueAsObject)) {
|
|
659
|
+
result = new EjvError(
|
|
660
|
+
ErrorType.NO_PROPERTY,
|
|
661
|
+
ErrorMsg.NO_PROPERTY,
|
|
662
|
+
_options.path,
|
|
663
|
+
data,
|
|
664
|
+
value
|
|
665
|
+
);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (definedTester(scheme.properties)) {
|
|
671
|
+
if (!arrayTester(scheme.properties)) {
|
|
672
|
+
throw new Error(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const properties : Scheme[] = scheme.properties as Scheme[];
|
|
676
|
+
|
|
677
|
+
if (!minLengthTester(properties, 1)) {
|
|
678
|
+
throw new Error(ErrorMsg.PROPERTIES_SHOULD_HAVE_ITEMS);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (!arrayTypeOfTester(properties, DataType.OBJECT)) {
|
|
682
|
+
throw new Error(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY_OF_OBJECT);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (!objectTester(value)) {
|
|
686
|
+
result = new EjvError(
|
|
687
|
+
ErrorType.TYPE_MISMATCH,
|
|
688
|
+
ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, 'object'),
|
|
689
|
+
_options.path,
|
|
690
|
+
data,
|
|
691
|
+
value
|
|
692
|
+
);
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const partialData : T[keyof T] = data[key];
|
|
697
|
+
const partialScheme : Scheme[] = scheme.properties as Scheme[];
|
|
698
|
+
|
|
699
|
+
// call recursively
|
|
700
|
+
result = _ejv(partialData, partialScheme, _options);
|
|
701
|
+
|
|
702
|
+
if (!!result) {
|
|
703
|
+
// inject original data
|
|
704
|
+
result.data = data;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
break;
|
|
708
|
+
|
|
709
|
+
case DataType.DATE:
|
|
710
|
+
const valueAsDate : Date = value as unknown as Date;
|
|
711
|
+
|
|
712
|
+
if (definedTester(scheme.min)) {
|
|
713
|
+
if (!(
|
|
714
|
+
(stringTester(scheme.min) && (dateFormatTester(scheme.min as string) || dateTimeFormatTester(scheme.min as string)))
|
|
715
|
+
|| dateTester(scheme.min)
|
|
716
|
+
)) {
|
|
717
|
+
throw new Error(ErrorMsg.MIN_DATE_SHOULD_BE_DATE_OR_STRING);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (definedTester(scheme.exclusiveMin) && !booleanTester(scheme.exclusiveMin)) {
|
|
721
|
+
throw new Error(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
let minDate : Date = new Date(scheme.min as string | Date);
|
|
725
|
+
|
|
726
|
+
// adjust timezone
|
|
727
|
+
if (stringTester(scheme.min)) {
|
|
728
|
+
// by minutes
|
|
729
|
+
const timezoneOffset : number = minDate.getTimezoneOffset();
|
|
730
|
+
|
|
731
|
+
minDate = new Date(+minDate + (timezoneOffset * 60 * 1000));
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (scheme.exclusiveMin !== true) {
|
|
735
|
+
if (!minDateTester(valueAsDate, minDate)) {
|
|
736
|
+
result = new EjvError(
|
|
737
|
+
ErrorType.AFTER_OR_SAME_DATE,
|
|
738
|
+
ErrorMsg.AFTER_OR_SAME_DATE.replace(ErrorMsgCursorA, minDate.toISOString()),
|
|
739
|
+
_options.path,
|
|
740
|
+
data,
|
|
741
|
+
value
|
|
742
|
+
);
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
} else {
|
|
747
|
+
if (!exclusiveMinDateTester(valueAsDate, minDate)) {
|
|
748
|
+
result = new EjvError(
|
|
749
|
+
ErrorType.AFTER_DATE,
|
|
750
|
+
ErrorMsg.AFTER_DATE.replace(ErrorMsgCursorA, minDate.toISOString()),
|
|
751
|
+
_options.path,
|
|
752
|
+
data,
|
|
753
|
+
value
|
|
754
|
+
);
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (definedTester(scheme.max)) {
|
|
761
|
+
if (!(
|
|
762
|
+
(stringTester(scheme.max) && (dateFormatTester(scheme.max as string) || dateTimeFormatTester(scheme.max as string)))
|
|
763
|
+
|| dateTester(scheme.max)
|
|
764
|
+
)) {
|
|
765
|
+
throw new Error(ErrorMsg.MAX_DATE_SHOULD_BE_DATE_OR_STRING);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (definedTester(scheme.exclusiveMax) && !booleanTester(scheme.exclusiveMax)) {
|
|
769
|
+
throw new Error(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
let maxDate : Date = new Date(scheme.max as string | Date);
|
|
773
|
+
|
|
774
|
+
// adjust timezone
|
|
775
|
+
if (stringTester(scheme.max)) {
|
|
776
|
+
// by minutes
|
|
777
|
+
const timezoneOffset : number = maxDate.getTimezoneOffset();
|
|
778
|
+
|
|
779
|
+
maxDate = new Date(+maxDate + (timezoneOffset * 60 * 1000));
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (scheme.exclusiveMax !== true) {
|
|
783
|
+
if (!maxDateTester(valueAsDate, maxDate)) {
|
|
784
|
+
result = new EjvError(
|
|
785
|
+
ErrorType.BEFORE_OR_SAME_DATE,
|
|
786
|
+
ErrorMsg.BEFORE_OR_SAME_DATE.replace(ErrorMsgCursorA, maxDate.toISOString()),
|
|
787
|
+
_options.path,
|
|
788
|
+
data,
|
|
789
|
+
value
|
|
790
|
+
);
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
} else {
|
|
795
|
+
if (!exclusiveMaxDateTester(valueAsDate, maxDate)) {
|
|
796
|
+
result = new EjvError(
|
|
797
|
+
ErrorType.BEFORE_DATE,
|
|
798
|
+
ErrorMsg.BEFORE_DATE.replace(ErrorMsgCursorA, maxDate.toISOString()),
|
|
799
|
+
_options.path,
|
|
800
|
+
data,
|
|
801
|
+
value
|
|
802
|
+
);
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
break;
|
|
808
|
+
|
|
809
|
+
case DataType.ARRAY:
|
|
810
|
+
const valueAsArray : any[] = value as unknown as any[];
|
|
811
|
+
|
|
812
|
+
if (definedTester(scheme.length)) {
|
|
813
|
+
const length : number = scheme.length as number;
|
|
814
|
+
|
|
815
|
+
if (!(numberTester(length) && integerTester(length))) {
|
|
816
|
+
throw new Error(ErrorMsg.LENGTH_SHOULD_BE_INTEGER);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (!lengthTester(valueAsArray, length)) {
|
|
820
|
+
result = new EjvError(
|
|
821
|
+
ErrorType.LENGTH,
|
|
822
|
+
ErrorMsg.LENGTH.replace(ErrorMsgCursorA, '' + length),
|
|
823
|
+
_options.path,
|
|
824
|
+
data,
|
|
825
|
+
value
|
|
826
|
+
);
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (definedTester(scheme.minLength)) {
|
|
832
|
+
const minLength : number = scheme.minLength as number;
|
|
833
|
+
|
|
834
|
+
if (!(numberTester(scheme.minLength) && integerTester(minLength))) {
|
|
835
|
+
throw new Error(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (!minLengthTester(valueAsArray, minLength)) {
|
|
839
|
+
result = new EjvError(
|
|
840
|
+
ErrorType.MIN_LENGTH,
|
|
841
|
+
ErrorMsg.MIN_LENGTH.replace(ErrorMsgCursorA, '' + minLength),
|
|
842
|
+
_options.path,
|
|
843
|
+
data,
|
|
844
|
+
value
|
|
845
|
+
);
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (definedTester(scheme.maxLength)) {
|
|
851
|
+
const maxLength : number = scheme.maxLength as number;
|
|
852
|
+
|
|
853
|
+
if (!(numberTester(scheme.maxLength) && integerTester(maxLength))) {
|
|
854
|
+
throw new Error(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (!maxLengthTester(valueAsArray, maxLength)) {
|
|
858
|
+
result = new EjvError(
|
|
859
|
+
ErrorType.MAX_LENGTH,
|
|
860
|
+
ErrorMsg.MAX_LENGTH.replace(ErrorMsgCursorA, '' + maxLength),
|
|
861
|
+
_options.path,
|
|
862
|
+
data,
|
|
863
|
+
value
|
|
864
|
+
);
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (definedTester(scheme.unique)) {
|
|
870
|
+
if (!booleanTester(scheme.unique)) {
|
|
871
|
+
throw new Error(ErrorMsg.UNIQUE_SHOULD_BE_BOOLEAN);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (scheme.unique === true && !uniqueItemsTester(valueAsArray)) {
|
|
875
|
+
result = new EjvError(
|
|
876
|
+
ErrorType.UNIQUE_ITEMS,
|
|
877
|
+
ErrorMsg.UNIQUE_ITEMS,
|
|
878
|
+
_options.path,
|
|
879
|
+
data,
|
|
880
|
+
value
|
|
881
|
+
);
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (definedTester(scheme.items)) {
|
|
887
|
+
// convert array to object
|
|
888
|
+
if (valueAsArray.length > 0) {
|
|
889
|
+
const now : Date = new Date;
|
|
890
|
+
const tempKeyArr : string[] = valueAsArray.map((value : any, i : number) => {
|
|
891
|
+
return '' + (+now + i);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
if (stringTester(scheme.items) // by DataType
|
|
895
|
+
|| (arrayTester(scheme.items) && arrayTypeOfTester(scheme.items as DataType[], DataType.STRING)) // by DataType[]
|
|
896
|
+
) {
|
|
897
|
+
const itemTypes : DataType[] = (arrayTester(scheme.items) ? scheme.items : [scheme.items]) as DataType[];
|
|
898
|
+
|
|
899
|
+
const partialData : AnyObject = {};
|
|
900
|
+
const partialSchemes : Scheme[] = [];
|
|
901
|
+
|
|
902
|
+
tempKeyArr.forEach((tempKey : string, i : number) => {
|
|
903
|
+
partialData[tempKey] = valueAsArray[i];
|
|
904
|
+
partialSchemes.push({
|
|
905
|
+
key : tempKey,
|
|
906
|
+
type : itemTypes
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
// call recursively
|
|
911
|
+
const partialResult : EjvError | null = _ejv(partialData, partialSchemes, _options);
|
|
912
|
+
|
|
913
|
+
// convert new EjvError
|
|
914
|
+
if (!!partialResult) {
|
|
915
|
+
let errorMsg : string;
|
|
916
|
+
|
|
917
|
+
if (arrayTester(scheme.items)) {
|
|
918
|
+
errorMsg = ErrorMsg.ITEMS_TYPE.replace(ErrorMsgCursorA, JSON.stringify(itemTypes));
|
|
919
|
+
} else {
|
|
920
|
+
errorMsg = ErrorMsg.ITEMS_TYPE.replace(ErrorMsgCursorA, scheme.items as string);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
const partialKeys : string[] = partialResult.path.split('/');
|
|
924
|
+
const partialKey : string = partialKeys[partialKeys.length - 1];
|
|
925
|
+
|
|
926
|
+
const partialScheme : Scheme = partialSchemes.find(scheme => {
|
|
927
|
+
return scheme.key === partialKey;
|
|
928
|
+
}) as Scheme;
|
|
929
|
+
|
|
930
|
+
const partialKeyIndex : number = partialSchemes.indexOf(partialScheme);
|
|
931
|
+
|
|
932
|
+
result = new EjvError(
|
|
933
|
+
ErrorType.ITEMS_TYPE,
|
|
934
|
+
errorMsg,
|
|
935
|
+
[..._options.path, '' + partialKeyIndex],
|
|
936
|
+
data,
|
|
937
|
+
partialData[partialKey]
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
break;
|
|
941
|
+
} else if ((objectTester(scheme.items) && scheme.items !== null) // by Scheme
|
|
942
|
+
|| (arrayTester(scheme.items) && arrayTypeOfTester(scheme.items as Scheme[], DataType.OBJECT)) // by Scheme[]
|
|
943
|
+
) {
|
|
944
|
+
const itemsAsSchemes : Scheme[] = (arrayTester(scheme.items) ? scheme.items : [scheme.items]) as Scheme[];
|
|
945
|
+
|
|
946
|
+
let partialError : EjvError | null | undefined = null;
|
|
947
|
+
|
|
948
|
+
// use for() instead of forEach() to break
|
|
949
|
+
const valueLength : number = valueAsArray.length;
|
|
950
|
+
|
|
951
|
+
for (let arrIndex = 0; arrIndex < valueLength; arrIndex++) {
|
|
952
|
+
const oneValue : any = valueAsArray[arrIndex];
|
|
953
|
+
|
|
954
|
+
const partialData : AnyObject = {};
|
|
955
|
+
const partialSchemes : Scheme[] = [];
|
|
956
|
+
|
|
957
|
+
const tempKeyForThisValue : string = tempKeyArr[arrIndex];
|
|
958
|
+
|
|
959
|
+
partialData[tempKeyForThisValue] = oneValue;
|
|
960
|
+
|
|
961
|
+
partialSchemes.push(...itemsAsSchemes.map((oneScheme : Scheme) => {
|
|
962
|
+
const newScheme : Scheme = clone(oneScheme); // divide instance
|
|
963
|
+
|
|
964
|
+
newScheme.key = tempKeyForThisValue;
|
|
965
|
+
|
|
966
|
+
return newScheme;
|
|
967
|
+
}));
|
|
968
|
+
|
|
969
|
+
const partialResults : (EjvError | null)[] = partialSchemes.map((partialScheme : Scheme) => {
|
|
970
|
+
// call recursively
|
|
971
|
+
const partialResult : EjvError | null = _ejv(partialData, [partialScheme], _options);
|
|
972
|
+
|
|
973
|
+
if (!!partialResult) {
|
|
974
|
+
partialResult.path = partialResult.path.replace(tempKeyForThisValue, '' + arrIndex);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return partialResult;
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
if (!partialResults.some(oneResult => oneResult === null)) {
|
|
981
|
+
partialError = partialResults.find(oneResult => {
|
|
982
|
+
return !!oneResult;
|
|
983
|
+
});
|
|
984
|
+
break;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
if (!!partialError) {
|
|
989
|
+
let errorType : ErrorType;
|
|
990
|
+
let errorMsg : string;
|
|
991
|
+
|
|
992
|
+
if (!!itemsAsSchemes && itemsAsSchemes.length > 1) {
|
|
993
|
+
errorType = ErrorType.ITEMS_SCHEMES;
|
|
994
|
+
errorMsg = ErrorMsg.ITEMS_SCHEMES.replace(ErrorMsgCursorA, JSON.stringify(itemsAsSchemes));
|
|
995
|
+
} else {
|
|
996
|
+
errorType = partialError.type;
|
|
997
|
+
errorMsg = partialError.message;
|
|
998
|
+
|
|
999
|
+
if (errorType === ErrorType.REQUIRED) {
|
|
1000
|
+
// REQUIRED in array is TYPE_MISMATCH except with nullable === true
|
|
1001
|
+
errorType = ErrorType.TYPE_MISMATCH;
|
|
1002
|
+
errorMsg = ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, JSON.stringify(scheme.items));
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
result = new EjvError(
|
|
1007
|
+
errorType,
|
|
1008
|
+
errorMsg,
|
|
1009
|
+
partialError.path.split('/'),
|
|
1010
|
+
data,
|
|
1011
|
+
partialError.errorData
|
|
1012
|
+
);
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
} else {
|
|
1016
|
+
throw new Error(ErrorMsg.INVALID_ITEMS_SCHEME.replace(ErrorMsgCursorA, JSON.stringify(scheme.items)));
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (!!result) {
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (definedTester(result) && definedTester(options.customErrorMsg)) {
|
|
1029
|
+
const resultAsNotNull : EjvError = result as NonNullable<EjvError>;
|
|
1030
|
+
const customErrorMsgObj : {
|
|
1031
|
+
[key in ErrorType]? : string;
|
|
1032
|
+
} = options.customErrorMsg as {
|
|
1033
|
+
[key in ErrorType]? : string;
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
// override error message
|
|
1037
|
+
const customMsg : string = customErrorMsgObj[resultAsNotNull.type] as string;
|
|
1038
|
+
|
|
1039
|
+
if (definedTester(customMsg)) {
|
|
1040
|
+
resultAsNotNull.message = customMsg;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return result;
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
export const ejv = (data : object, schemes : Scheme[], options? : Options) : null | EjvError => {
|
|
1048
|
+
// check data itself
|
|
1049
|
+
if (!definedTester(data) || !objectTester(data) || data === null) {
|
|
1050
|
+
return new EjvError(ErrorType.REQUIRED, ErrorMsg.NO_DATA, ['/'], data, data);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// check schemes itself
|
|
1054
|
+
if (!definedTester(schemes) || schemes === null) {
|
|
1055
|
+
throw new Error(ErrorMsg.NO_SCHEME);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (!arrayTester(schemes)) {
|
|
1059
|
+
throw new Error(ErrorMsg.NO_ARRAY_SCHEME);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (!arrayTypeOfTester(schemes, DataType.OBJECT)) {
|
|
1063
|
+
throw new Error(ErrorMsg.NO_OBJECT_ARRAY_SCHEME);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (!minLengthTester(schemes, 1)) {
|
|
1067
|
+
throw new Error(ErrorMsg.EMPTY_SCHEME);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
return _ejv(data, schemes, options as InternalOptions);
|
|
1071
|
+
};
|