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