ejv 1.1.10 → 2.0.0

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.
Files changed (75) hide show
  1. package/.eslintrc.json +88 -0
  2. package/.mocharc.json +8 -0
  3. package/CHANGELOG.md +70 -29
  4. package/README-KR.md +6 -2
  5. package/README.md +6 -2
  6. package/build/{constants.js → cjs/constants.js} +117 -118
  7. package/build/cjs/constants.js.map +1 -0
  8. package/build/cjs/ejv.js +1263 -0
  9. package/build/cjs/ejv.js.map +1 -0
  10. package/build/{public_api.js → cjs/index.js} +14 -14
  11. package/build/cjs/index.js.map +1 -0
  12. package/build/cjs/interfaces.js +29 -0
  13. package/build/cjs/interfaces.js.map +1 -0
  14. package/build/cjs/package.json +1 -0
  15. package/build/{tester.js → cjs/tester.js} +273 -268
  16. package/build/cjs/tester.js.map +1 -0
  17. package/build/cjs/util.js +103 -0
  18. package/build/cjs/util.js.map +1 -0
  19. package/build/constants.d.ts +101 -104
  20. package/build/ejv.d.ts +2 -2
  21. package/build/esm/constants.js +115 -0
  22. package/build/esm/constants.js.map +1 -0
  23. package/build/esm/ejv.js +1261 -0
  24. package/build/esm/ejv.js.map +1 -0
  25. package/build/esm/index.js +4 -0
  26. package/build/esm/index.js.map +1 -0
  27. package/build/esm/interfaces.js +33 -0
  28. package/build/esm/interfaces.js.map +1 -0
  29. package/build/esm/package.json +1 -0
  30. package/build/esm/tester.js +240 -0
  31. package/build/esm/tester.js.map +1 -0
  32. package/build/esm/util.js +96 -0
  33. package/build/esm/util.js.map +1 -0
  34. package/build/index.d.ts +3 -0
  35. package/build/interfaces.d.ts +78 -38
  36. package/build/scripts/add-js-extensions.js +46 -0
  37. package/build/scripts/add-js-extensions.js.map +1 -0
  38. package/build/tester.d.ts +33 -34
  39. package/build/util.d.ts +7 -1
  40. package/package.json +48 -37
  41. package/scripts/add-js-extensions.ts +59 -0
  42. package/spec/ArrayScheme.ts +1021 -0
  43. package/spec/CommonScheme.ts +251 -0
  44. package/spec/DateScheme.ts +472 -0
  45. package/spec/NumberScheme.ts +1032 -0
  46. package/spec/ObjectScheme.ts +499 -0
  47. package/spec/RegExpScheme.ts +112 -0
  48. package/spec/StringScheme.ts +1239 -0
  49. package/spec/common-test-util.ts +63 -0
  50. package/spec/ejv.spec.ts +133 -4558
  51. package/spec/testers.spec.ts +17 -16
  52. package/src/constants.ts +41 -42
  53. package/src/ejv.ts +1141 -564
  54. package/src/index.ts +14 -0
  55. package/src/interfaces.ts +127 -41
  56. package/src/tester.ts +75 -69
  57. package/src/util.ts +106 -41
  58. package/tsconfig.cjs.json +8 -0
  59. package/tsconfig.esm.json +7 -0
  60. package/tsconfig.json +21 -18
  61. package/tsconfig.scripts.json +14 -0
  62. package/tsconfig.types.json +9 -0
  63. package/build/constants.js.map +0 -1
  64. package/build/ejv.js +0 -685
  65. package/build/ejv.js.map +0 -1
  66. package/build/interfaces.js +0 -15
  67. package/build/interfaces.js.map +0 -1
  68. package/build/public_api.d.ts +0 -3
  69. package/build/public_api.js.map +0 -1
  70. package/build/tester.js.map +0 -1
  71. package/build/util.js +0 -66
  72. package/build/util.js.map +0 -1
  73. package/spec/common-test-runner.ts +0 -17
  74. package/src/public_api.ts +0 -3
  75. package/tsconfig.spec.json +0 -19
package/src/ejv.ts CHANGED
@@ -1,5 +1,17 @@
1
- import { EjvError, InternalOptions, Options, Scheme } from './interfaces';
2
- import { DataType, ErrorMsg, ErrorMsgCursorA, ErrorType, NumberFormat, StringFormat } from './constants';
1
+ import {
2
+ AllDataType,
3
+ AnyObject,
4
+ ArrayScheme,
5
+ DateScheme,
6
+ EjvError,
7
+ InternalOptions,
8
+ NumberScheme,
9
+ ObjectScheme,
10
+ Options,
11
+ Scheme,
12
+ StringScheme
13
+ } from './interfaces';
14
+ import { DataType, ErrorMsg, ErrorType, NumberFormat, StringFormat } from './constants';
3
15
 
4
16
  import {
5
17
  arrayTester,
@@ -34,545 +46,830 @@ import {
34
46
  typeTester,
35
47
  uniqueItemsTester
36
48
  } from './tester';
37
- import { clone } from './util';
49
+ import { clone, createErrorMsg, sift } from './util';
38
50
 
39
51
 
40
- interface AnyObject {
41
- [key : string] : any;
42
- }
52
+ function _getEffectiveTypes (scheme: Scheme): DataType[] | undefined {
53
+ let result: DataType[] | undefined;
43
54
 
55
+ if (definedTester(scheme.type)) {
56
+ result = (arrayTester(scheme.type)
57
+ ? scheme.type
58
+ : [scheme.type]) as DataType[];
59
+ }
60
+ else if (definedTester(scheme.parent)) {
61
+ result = _getEffectiveTypes(scheme.parent);
62
+ }
44
63
 
45
- const _ejv = <T>(data : T, schemes : Scheme[], options : InternalOptions = {
46
- path : []
47
- }) : null | EjvError => {
64
+ return result;
65
+ }
66
+
67
+ const _ejv = <T> (data: T, schemes: Scheme[], options: InternalOptions): null | EjvError => {
48
68
  // check schemes
69
+ if (!definedTester(schemes) || schemes === null) {
70
+ return new EjvError({
71
+ type: ErrorType.NO_SCHEME,
72
+ message: ErrorMsg.NO_SCHEME,
73
+
74
+ data: data,
75
+
76
+ errorScheme: schemes,
77
+ isSchemeError: true
78
+ });
79
+ }
80
+
49
81
  if (!arrayTester(schemes)) {
50
- throw new Error(ErrorMsg.NO_ARRAY_SCHEME);
82
+ return new EjvError({
83
+ type: ErrorType.INVALID_SCHEMES,
84
+ message: ErrorMsg.NO_ARRAY_SCHEME,
85
+
86
+ data: data,
87
+
88
+ errorScheme: schemes,
89
+ isSchemeError: true
90
+ });
51
91
  }
52
92
 
53
93
  if (!arrayTypeOfTester(schemes, DataType.OBJECT)) {
54
- throw new Error(ErrorMsg.NO_OBJECT_ARRAY_SCHEME);
94
+ return new EjvError({
95
+ type: ErrorType.INVALID_SCHEMES,
96
+ message: ErrorMsg.NO_OBJECT_ARRAY_SCHEME,
97
+
98
+ data: data,
99
+
100
+ errorScheme: schemes,
101
+ isSchemeError: true
102
+ });
55
103
  }
56
104
 
57
105
  if (!minLengthTester(schemes, 1)) {
58
- throw new Error(ErrorMsg.EMPTY_SCHEME);
106
+ return new EjvError({
107
+ type: ErrorType.INVALID_SCHEMES,
108
+ message: ErrorMsg.EMPTY_SCHEME,
109
+
110
+ data: data,
111
+
112
+ errorScheme: schemes,
113
+ isSchemeError: true
114
+ });
59
115
  }
60
116
 
61
117
  // check data by schemes
62
- let result : EjvError | null = null;
118
+ let result: EjvError | null = null;
63
119
 
64
120
  // use for() instead of forEach() to stop
65
- const schemeLength : number = schemes.length;
121
+ for (const scheme of schemes) {
122
+ const key: keyof T = scheme.key as keyof T;
66
123
 
67
- for (let i = 0; i < schemeLength; i++) {
68
- const _options : InternalOptions = clone(options); // divide instance
124
+ const _options: InternalOptions = clone(options); // divide instance
69
125
 
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;
126
+ let value: unknown;
78
127
 
79
- if (!!key) {
128
+ if (key) {
80
129
  value = data[key];
81
130
 
82
131
  _options.path.push(key as string);
83
132
  }
84
133
 
85
- let types : DataType[];
134
+ const types: DataType[] | undefined = _getEffectiveTypes(scheme);
86
135
 
87
- if (!definedTester(scheme.type)) {
88
- throw new Error(ErrorMsg.SCHEMES_SHOULD_HAVE_TYPE);
89
- }
136
+ if (!definedTester(types)) {
137
+ return new EjvError({
138
+ type: ErrorType.INVALID_SCHEMES,
139
+ message: ErrorMsg.SCHEMES_SHOULD_HAVE_TYPE,
140
+
141
+ data: data,
90
142
 
91
- if (!arrayTester(scheme.type)) {
92
- types = [scheme.type as DataType];
93
- } else {
94
- types = scheme.type as DataType[];
143
+ errorScheme: scheme,
144
+ isSchemeError: true
145
+ });
95
146
  }
96
147
 
97
- const allDataType : DataType[] = Object.values(DataType);
98
148
 
149
+ const allDataType: DataType[] = Object.values(DataType);
99
150
 
100
- const errorType : string | undefined = types.find(type => {
101
- return !(stringTester(type) && enumTester(type, allDataType));
151
+ const typeError: string | undefined = types.find((type: DataType): boolean => {
152
+ return !definedTester(type)
153
+ || !stringTester(type)
154
+ || !enumTester(type, allDataType);
102
155
  });
103
156
 
104
- if (!!errorType) {
105
- throw new Error(ErrorMsg.SCHEMES_HAS_INVALID_TYPE.replace(ErrorMsgCursorA, errorType));
157
+ if (typeError) {
158
+ return new EjvError({
159
+ type: ErrorType.INVALID_SCHEMES,
160
+ message: createErrorMsg(ErrorMsg.SCHEMES_HAS_INVALID_TYPE, {
161
+ placeholders: [typeError]
162
+ }),
163
+
164
+ data: data,
165
+
166
+ errorScheme: scheme,
167
+ isSchemeError: true
168
+ });
106
169
  }
107
170
 
108
171
  if (!uniqueItemsTester(types)) {
109
- throw new Error(ErrorMsg.SCHEMES_HAS_DUPLICATED_TYPE);
172
+ const notUniqueItems: string[] = types.filter((type: string): boolean => {
173
+ return types.filter((_type: unknown): boolean => _type === type).length > 1;
174
+ });
175
+
176
+ const notUniqueItemsSifted: string[] = sift(notUniqueItems);
177
+
178
+ return new EjvError({
179
+ type: ErrorType.INVALID_SCHEMES,
180
+ message: createErrorMsg(ErrorMsg.SCHEMES_HAS_DUPLICATED_TYPE, {
181
+ placeholders: [notUniqueItemsSifted.join(', ')]
182
+ }),
183
+
184
+ data: data,
185
+
186
+ errorScheme: scheme,
187
+ isSchemeError: true
188
+ });
110
189
  }
111
190
 
112
191
  if (!definedTester(value)) {
113
192
  if (scheme.optional !== true) {
114
- result = new EjvError(
115
- ErrorType.REQUIRED,
116
- ErrorMsg.REQUIRED,
117
- _options.path,
193
+ result = new EjvError({
194
+ type: ErrorType.REQUIRED,
195
+ message: createErrorMsg(ErrorMsg.REQUIRED),
196
+
118
197
  data,
119
- value
120
- );
198
+ path: _options.path,
199
+
200
+ errorScheme: scheme,
201
+ errorData: value
202
+ });
121
203
  break;
122
- } else {
204
+ }
205
+ else {
123
206
  continue;
124
207
  }
125
208
  }
126
209
 
127
210
  if (value === null) {
128
211
  if (scheme.nullable !== true) {
129
- result = new EjvError(
130
- ErrorType.REQUIRED,
131
- ErrorMsg.REQUIRED,
132
- _options.path,
212
+ result = new EjvError({
213
+ type: ErrorType.REQUIRED,
214
+ message: createErrorMsg(ErrorMsg.REQUIRED),
215
+
133
216
  data,
134
- value
135
- );
217
+ path: _options.path,
218
+
219
+ errorScheme: scheme,
220
+ errorData: value
221
+ });
136
222
  break;
137
- } else {
223
+ }
224
+ else {
138
225
  continue;
139
226
  }
140
227
  }
141
228
 
142
- const typeResolved : DataType | undefined = types.find(type => {
229
+ const typeResolved: DataType | undefined = types.find((type: DataType): type is DataType => {
143
230
  return typeTester(value, type);
144
231
  });
145
232
 
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,
233
+ if (typeResolved) {
234
+ if (definedTester(scheme.type)) {
235
+ if (!arrayTester(scheme.type)) {
236
+ if (scheme.type !== typeResolved) {
237
+ result = new EjvError({
238
+ type: ErrorType.TYPE_MISMATCH,
239
+ message: createErrorMsg(ErrorMsg.TYPE_MISMATCH, {
240
+ placeholders: [scheme.type]
241
+ }),
242
+
243
+ data,
244
+ path: _options.path,
245
+
246
+ errorScheme: scheme,
247
+ errorData: value
248
+ });
249
+ }
250
+ }
251
+ else {
252
+ if (!scheme.type.includes(typeResolved)) {
253
+ result = new EjvError({
254
+ type: ErrorType.TYPE_MISMATCH_ONE_OF,
255
+ message: createErrorMsg(ErrorMsg.TYPE_MISMATCH_ONE_OF, {
256
+ placeholders: [JSON.stringify(scheme.type)]
257
+ }),
258
+
259
+ data,
260
+ path: _options.path,
261
+
262
+ errorScheme: scheme,
263
+ errorData: value
264
+ });
265
+ }
266
+ }
267
+ }
268
+ // else do additional validation
269
+ }
270
+ else {
271
+ // type is not resolved
272
+ const typesForMsg: AllDataType = scheme.type || scheme.parent?.type as AllDataType;
273
+
274
+ if (!arrayTester(typesForMsg)) {
275
+ result = new EjvError({
276
+ type: ErrorType.TYPE_MISMATCH,
277
+ message: createErrorMsg(ErrorMsg.TYPE_MISMATCH, {
278
+ placeholders: [typesForMsg]
279
+ }),
280
+
152
281
  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,
282
+ path: _options.path,
283
+
284
+ errorScheme: scheme,
285
+ errorData: value
286
+ });
287
+ }
288
+ else {
289
+ result = new EjvError({
290
+ type: ErrorType.TYPE_MISMATCH_ONE_OF,
291
+ message: createErrorMsg(ErrorMsg.TYPE_MISMATCH_ONE_OF, {
292
+ placeholders: [JSON.stringify(typesForMsg)]
293
+ }),
294
+
160
295
  data,
161
- value
162
- );
296
+ path: _options.path,
297
+
298
+ errorScheme: scheme,
299
+ errorData: value
300
+ });
163
301
  }
302
+
164
303
  break;
165
304
  }
166
305
 
167
306
  // additional check for type resolved
168
307
  switch (typeResolved) {
169
- case DataType.NUMBER:
170
- const valueAsNumber : number = value as unknown as number;
308
+ case DataType.NUMBER: {
309
+ const valueAsNumber: number = value as unknown as number;
310
+ const numberScheme: NumberScheme = scheme as NumberScheme;
311
+
312
+ if (definedTester(numberScheme.enum)) {
313
+ if (!arrayTester(numberScheme.enum)) {
314
+ return new EjvError({
315
+ type: ErrorType.INVALID_SCHEMES,
316
+ message: createErrorMsg(ErrorMsg.ENUM_SHOULD_BE_ARRAY),
317
+
318
+ data: data,
171
319
 
172
- if (definedTester(scheme.enum)) {
173
- if (!arrayTester(scheme.enum)) {
174
- throw new Error(ErrorMsg.ENUM_SHOULD_BE_ARRAY);
320
+ errorScheme: numberScheme,
321
+ isSchemeError: true
322
+ });
175
323
  }
176
324
 
177
- const enumArr : number[] = scheme.enum as number[];
325
+ const enumArr: number[] = numberScheme.enum;
178
326
 
179
327
  if (!arrayTypeOfTester(enumArr, DataType.NUMBER)) {
180
- throw new Error(ErrorMsg.ENUM_SHOULD_BE_NUMBERS);
328
+ return new EjvError({
329
+ type: ErrorType.INVALID_SCHEMES,
330
+ message: createErrorMsg(ErrorMsg.ENUM_SHOULD_BE_NUMBERS),
331
+
332
+ data: data,
333
+
334
+ errorScheme: numberScheme,
335
+ isSchemeError: true
336
+ });
181
337
  }
182
338
 
183
339
  if (!enumTester(valueAsNumber, enumArr)) {
184
- result = new EjvError(
185
- ErrorType.ONE_OF,
186
- ErrorMsg.ONE_OF.replace(ErrorMsgCursorA, JSON.stringify(enumArr)),
187
- _options.path,
340
+ result = new EjvError({
341
+ type: ErrorType.ONE_OF,
342
+ message: createErrorMsg(ErrorMsg.ONE_VALUE_OF, {
343
+ placeholders: [JSON.stringify(enumArr)]
344
+ }),
345
+
188
346
  data,
189
- value
190
- );
347
+ path: _options.path,
348
+
349
+ errorScheme: numberScheme,
350
+ errorData: value
351
+ });
191
352
  break;
192
353
  }
193
354
  }
194
355
 
195
- if (definedTester(scheme.enumReverse)) {
196
- const enumReverseArr : number[] = scheme.enumReverse as number[];
356
+ if (
357
+ definedTester(numberScheme.min)
358
+ || definedTester((scheme.parent as NumberScheme)?.min)
359
+ ) {
360
+ const effectiveMin: number = numberScheme.min || (scheme.parent as NumberScheme)?.min as number;
197
361
 
198
- if (!arrayTester(enumReverseArr)) {
199
- throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_ARRAY);
200
- }
362
+ if (!numberTester(effectiveMin)) {
363
+ return new EjvError({
364
+ type: ErrorType.INVALID_SCHEMES,
365
+ message: createErrorMsg(ErrorMsg.MIN_SHOULD_BE_NUMBER),
201
366
 
202
- if (!arrayTypeOfTester(enumReverseArr, DataType.NUMBER)) {
203
- throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_NUMBERS);
204
- }
367
+ data: data,
205
368
 
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;
369
+ errorScheme: numberScheme,
370
+ isSchemeError: true
371
+ });
215
372
  }
216
- }
217
373
 
218
- if (definedTester(scheme.min)) {
219
- if (!numberTester(scheme.min)) {
220
- throw new Error(ErrorMsg.MIN_SHOULD_BE_NUMBER);
221
- }
374
+ if (definedTester(numberScheme.exclusiveMin)) {
375
+ if (!booleanTester(numberScheme.exclusiveMin)) {
376
+ return new EjvError({
377
+ type: ErrorType.INVALID_SCHEMES,
378
+ message: createErrorMsg(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN),
379
+
380
+ data: data,
222
381
 
223
- if (definedTester(scheme.exclusiveMin)) {
224
- if (!booleanTester(scheme.exclusiveMin)) {
225
- throw new Error(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN);
382
+ errorScheme: numberScheme,
383
+ isSchemeError: true
384
+ });
226
385
  }
386
+ }
227
387
 
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
- }
388
+ if (numberScheme.exclusiveMin) {
389
+ if (!exclusiveMinNumberTester(valueAsNumber, effectiveMin)) {
390
+ result = new EjvError({
391
+ type: ErrorType.BIGGER_THAN,
392
+ message: createErrorMsg(ErrorMsg.BIGGER_THAN, {
393
+ placeholders: [effectiveMin]
394
+ }),
395
+
396
+ data,
397
+ path: _options.path,
398
+
399
+ errorScheme: numberScheme,
400
+ errorData: value
401
+ });
402
+ break;
250
403
  }
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,
404
+ }
405
+ else {
406
+ if (!minNumberTester(valueAsNumber, effectiveMin)) {
407
+ result = new EjvError({
408
+ type: ErrorType.BIGGER_THAN_OR_EQUAL,
409
+ message: createErrorMsg(ErrorMsg.BIGGER_THAN_OR_EQUAL, {
410
+ placeholders: [effectiveMin]
411
+ }),
412
+
257
413
  data,
258
- value
259
- );
414
+ path: _options.path,
415
+
416
+ errorScheme: numberScheme,
417
+ errorData: value
418
+ });
260
419
  break;
261
420
  }
262
421
  }
263
422
  }
264
423
 
265
- if (definedTester(scheme.max)) {
266
- if (!numberTester(scheme.max)) {
267
- throw new Error(ErrorMsg.MAX_SHOULD_BE_NUMBER);
424
+ if (definedTester(numberScheme.max)
425
+ || definedTester((scheme.parent as NumberScheme)?.max)) {
426
+ const effectiveMax: number = numberScheme.max || (scheme.parent as NumberScheme)?.max as number;
427
+
428
+ if (!numberTester(effectiveMax)) {
429
+ return new EjvError({
430
+ type: ErrorType.INVALID_SCHEMES,
431
+ message: createErrorMsg(ErrorMsg.MAX_SHOULD_BE_NUMBER),
432
+
433
+ data: data,
434
+
435
+ errorScheme: numberScheme,
436
+ isSchemeError: true
437
+ });
268
438
  }
269
439
 
270
- if (definedTester(scheme.exclusiveMax)) {
271
- if (!booleanTester(scheme.exclusiveMax)) {
272
- throw new Error(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN);
440
+ if (definedTester(numberScheme.exclusiveMax)) {
441
+ if (!booleanTester(numberScheme.exclusiveMax)) {
442
+ return new EjvError({
443
+ type: ErrorType.INVALID_SCHEMES,
444
+ message: createErrorMsg(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN),
445
+
446
+ data: data,
447
+
448
+ errorScheme: numberScheme,
449
+ isSchemeError: true
450
+ });
273
451
  }
452
+ }
274
453
 
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
- }
454
+ if (numberScheme.exclusiveMax) {
455
+ if (!exclusiveMaxNumberTester(valueAsNumber, effectiveMax)) {
456
+ result = new EjvError({
457
+ type: ErrorType.SMALLER_THAN,
458
+ message: createErrorMsg(ErrorMsg.SMALLER_THAN, {
459
+ placeholders: [effectiveMax]
460
+ }),
461
+
462
+ data,
463
+ path: _options.path,
464
+
465
+ errorScheme: numberScheme,
466
+ errorData: value
467
+ });
468
+ break;
297
469
  }
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,
470
+ }
471
+ else {
472
+ if (!maxNumberTester(valueAsNumber, effectiveMax)) {
473
+ result = new EjvError({
474
+ type: ErrorType.SMALLER_THAN_OR_EQUAL,
475
+ message: createErrorMsg(ErrorMsg.SMALLER_THAN_OR_EQUAL, {
476
+ placeholders: [effectiveMax]
477
+ }),
478
+
304
479
  data,
305
- value
306
- );
480
+ path: _options.path,
481
+
482
+ errorScheme: numberScheme,
483
+ errorData: value
484
+ });
307
485
  break;
308
486
  }
309
487
  }
310
488
  }
311
489
 
312
- if (definedTester(scheme.format)) {
313
- let formats : NumberFormat[];
490
+ if (definedTester(numberScheme.format)) {
491
+ let formats: NumberFormat[];
314
492
 
315
- const allNumberFormat : NumberFormat[] = Object.values(NumberFormat);
493
+ const allNumberFormat: NumberFormat[] = Object.values(NumberFormat);
316
494
 
317
- if (!arrayTester(scheme.format)) {
318
- const formatAsString : NumberFormat = scheme.format as NumberFormat;
495
+ if (!arrayTester(numberScheme.format)) {
496
+ const formatAsString: NumberFormat = numberScheme.format as NumberFormat;
319
497
 
320
498
  if (!enumTester(formatAsString, allNumberFormat)) {
321
- throw new Error(ErrorMsg.INVALID_NUMBER_FORMAT.replace(ErrorMsgCursorA, formatAsString));
499
+ return new EjvError({
500
+ type: ErrorType.INVALID_SCHEMES,
501
+ message: createErrorMsg(ErrorMsg.INVALID_NUMBER_FORMAT, {
502
+ placeholders: [formatAsString]
503
+ }),
504
+
505
+ data: data,
506
+
507
+ errorScheme: numberScheme,
508
+ isSchemeError: true
509
+ });
322
510
  }
323
511
 
324
- formats = [scheme.format as NumberFormat];
325
- } else {
326
- const formatAsArray : NumberFormat[] = scheme.format as NumberFormat[];
512
+ formats = [numberScheme.format as NumberFormat];
513
+ }
514
+ else {
515
+ const formatAsArray: NumberFormat[] = numberScheme.format as NumberFormat[];
327
516
 
328
- const errorFormat : string | undefined = formatAsArray.find(format => {
517
+ const errorFormat: string | undefined = formatAsArray.find((format: NumberFormat): boolean => {
329
518
  return !enumTester(format, allNumberFormat);
330
519
  });
331
520
 
332
- if (!!errorFormat) {
333
- throw new Error(ErrorMsg.INVALID_NUMBER_FORMAT.replace(ErrorMsgCursorA, errorFormat));
521
+ if (errorFormat) {
522
+ return new EjvError({
523
+ type: ErrorType.INVALID_SCHEMES,
524
+ message: createErrorMsg(ErrorMsg.INVALID_NUMBER_FORMAT, {
525
+ placeholders: [errorFormat]
526
+ }),
527
+
528
+ data: data,
529
+
530
+ errorScheme: numberScheme,
531
+ isSchemeError: true
532
+ });
334
533
  }
335
534
 
336
- formats = scheme.format as NumberFormat[];
535
+ formats = numberScheme.format as NumberFormat[];
337
536
  }
338
537
 
339
- if (!formats.some(format => {
340
- let valid : boolean = false;
538
+ const someFormatIsWrong: boolean = formats.some((format: NumberFormat): boolean => {
539
+ let valid: boolean = false;
341
540
 
342
541
  switch (format) {
343
542
  case NumberFormat.INTEGER:
344
- valid = integerTester(value);
543
+ valid = integerTester(valueAsNumber);
345
544
  break;
346
545
 
347
546
  case NumberFormat.INDEX:
348
- valid = indexTester(value);
547
+ valid = indexTester(valueAsNumber);
349
548
  break;
350
549
  }
351
550
 
352
551
  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,
552
+ });
553
+
554
+ if (!someFormatIsWrong) {
555
+ if (!arrayTester(numberScheme.format)) {
556
+ result = new EjvError({
557
+ type: ErrorType.FORMAT,
558
+ message: createErrorMsg(ErrorMsg.FORMAT, {
559
+ placeholders: [numberScheme.format]
560
+ }),
561
+
359
562
  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,
563
+ path: _options.path,
564
+
565
+ errorScheme: numberScheme,
566
+ errorData: value
567
+ });
568
+ }
569
+ else {
570
+ result = new EjvError({
571
+ type: ErrorType.FORMAT_ONE_OF,
572
+ message: createErrorMsg(ErrorMsg.FORMAT_ONE_OF, {
573
+ placeholders: [JSON.stringify(numberScheme.format)]
574
+ }),
575
+
367
576
  data,
368
- value
369
- );
577
+ path: _options.path,
578
+
579
+ errorScheme: numberScheme,
580
+ errorData: value
581
+ });
370
582
  }
371
583
  break;
372
584
  }
373
585
  }
374
586
  break;
587
+ }
375
588
 
376
- case DataType.STRING:
377
- const valueAsString : string = value as unknown as string;
589
+ case DataType.STRING: {
590
+ const valueAsString: string = value as unknown as string;
591
+ const stringScheme: StringScheme = scheme as StringScheme;
378
592
 
379
- if (definedTester(scheme.enum)) {
380
- if (!arrayTester(scheme.enum)) {
381
- throw new Error(ErrorMsg.ENUM_SHOULD_BE_ARRAY);
382
- }
593
+ if (definedTester(stringScheme.enum)) {
594
+ if (!arrayTester(stringScheme.enum)) {
595
+ return new EjvError({
596
+ type: ErrorType.INVALID_SCHEMES,
597
+ message: createErrorMsg(ErrorMsg.ENUM_SHOULD_BE_ARRAY),
383
598
 
384
- const enumArr : string[] = scheme.enum as string[];
599
+ data: data,
385
600
 
386
- if (!arrayTypeOfTester(enumArr, DataType.STRING)) {
387
- throw new Error(ErrorMsg.ENUM_SHOULD_BE_STRINGS);
601
+ errorScheme: stringScheme,
602
+ isSchemeError: true
603
+ });
388
604
  }
389
605
 
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
- }
606
+ const enumArr: string[] = stringScheme.enum;
401
607
 
402
- if (definedTester(scheme.enumReverse)) {
403
- if (!arrayTester(scheme.enumReverse)) {
404
- throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_ARRAY);
405
- }
608
+ if (!arrayTypeOfTester(enumArr, DataType.STRING)) {
609
+ return new EjvError({
610
+ type: ErrorType.INVALID_SCHEMES,
611
+ message: createErrorMsg(ErrorMsg.ENUM_SHOULD_BE_STRINGS),
406
612
 
407
- const enumReverseArr : string[] = scheme.enumReverse as string[];
613
+ data: data,
408
614
 
409
- if (!arrayTypeOfTester(enumReverseArr, DataType.STRING)) {
410
- throw new Error(ErrorMsg.ENUM_REVERSE_SHOULD_BE_STRINGS);
615
+ errorScheme: stringScheme,
616
+ isSchemeError: true
617
+ });
411
618
  }
412
619
 
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,
620
+ if (!enumTester(valueAsString, enumArr)) {
621
+ result = new EjvError({
622
+ type: ErrorType.ONE_OF,
623
+ message: createErrorMsg(ErrorMsg.ONE_VALUE_OF, {
624
+ placeholders: [JSON.stringify(stringScheme.enum)]
625
+ }),
626
+
418
627
  data,
419
- value
420
- );
628
+ path: _options.path,
629
+
630
+ errorScheme: stringScheme,
631
+ errorData: value
632
+ });
421
633
  break;
422
634
  }
423
635
  }
424
636
 
425
- if (definedTester(scheme.length)) {
426
- const length : number = scheme.length as number;
637
+ if (definedTester(stringScheme.length)) {
638
+ const length: number = stringScheme.length;
427
639
 
428
640
  if (!(numberTester(length) && integerTester(length))) {
429
- throw new Error(ErrorMsg.LENGTH_SHOULD_BE_INTEGER);
641
+ return new EjvError({
642
+ type: ErrorType.INVALID_SCHEMES,
643
+ message: createErrorMsg(ErrorMsg.LENGTH_SHOULD_BE_INTEGER),
644
+
645
+ data: data,
646
+
647
+ errorScheme: stringScheme,
648
+ isSchemeError: true
649
+ });
430
650
  }
431
651
 
432
652
  if (!lengthTester(valueAsString, length)) {
433
- result = new EjvError(
434
- ErrorType.LENGTH,
435
- ErrorMsg.LENGTH.replace(ErrorMsgCursorA, '' + length),
436
- _options.path,
653
+ result = new EjvError({
654
+ type: ErrorType.LENGTH,
655
+ message: createErrorMsg(ErrorMsg.LENGTH, {
656
+ placeholders: [length]
657
+ }),
658
+
437
659
  data,
438
- value
439
- );
660
+ path: _options.path,
661
+
662
+ errorScheme: stringScheme,
663
+ errorData: value
664
+ });
440
665
  break;
441
666
  }
442
667
  }
443
668
 
444
- if (definedTester(scheme.minLength)) {
445
- const minLength : number = scheme.minLength as number;
669
+ if (definedTester(stringScheme.minLength)) {
670
+ const minLength: number = stringScheme.minLength;
446
671
 
447
672
  if (!(numberTester(minLength) && integerTester(minLength))) {
448
- throw new Error(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER);
673
+ return new EjvError({
674
+ type: ErrorType.INVALID_SCHEMES,
675
+ message: createErrorMsg(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER),
676
+
677
+ data: data,
678
+
679
+ errorScheme: stringScheme,
680
+ isSchemeError: true
681
+ });
449
682
  }
450
683
 
451
684
  if (!minLengthTester(valueAsString, minLength)) {
452
- result = new EjvError(
453
- ErrorType.MIN_LENGTH,
454
- ErrorMsg.MIN_LENGTH.replace(ErrorMsgCursorA, '' + minLength),
455
- _options.path,
685
+ result = new EjvError({
686
+ type: ErrorType.MIN_LENGTH,
687
+ message: createErrorMsg(ErrorMsg.MIN_LENGTH, {
688
+ placeholders: ['' + minLength]
689
+ }),
690
+
456
691
  data,
457
- value
458
- );
692
+ path: _options.path,
693
+
694
+ errorScheme: stringScheme,
695
+ errorData: value
696
+ });
459
697
  break;
460
698
  }
461
699
  }
462
700
 
463
- if (definedTester(scheme.maxLength)) {
464
- const maxLength : number = scheme.maxLength as number;
701
+ if (definedTester(stringScheme.maxLength)) {
702
+ const maxLength: number = stringScheme.maxLength;
465
703
 
466
704
  if (!(numberTester(maxLength) && integerTester(maxLength))) {
467
- throw new Error(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER);
705
+ return new EjvError({
706
+ type: ErrorType.INVALID_SCHEMES,
707
+ message: createErrorMsg(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER),
708
+
709
+ data: data,
710
+
711
+ errorScheme: stringScheme,
712
+ isSchemeError: true
713
+ });
468
714
  }
469
715
 
470
716
  if (!maxLengthTester(valueAsString, maxLength)) {
471
- result = new EjvError(
472
- ErrorType.MAX_LENGTH,
473
- ErrorMsg.MAX_LENGTH.replace(ErrorMsgCursorA, '' + maxLength),
474
- _options.path,
717
+ result = new EjvError({
718
+ type: ErrorType.MAX_LENGTH,
719
+ message: createErrorMsg(ErrorMsg.MAX_LENGTH, {
720
+ placeholders: ['' + maxLength]
721
+ }),
722
+
475
723
  data,
476
- value
477
- );
724
+ path: _options.path,
725
+
726
+ errorScheme: stringScheme,
727
+ errorData: value
728
+ });
478
729
  break;
479
730
  }
480
731
  }
481
732
 
482
- if (definedTester(scheme.format)) {
483
- let formats : StringFormat[];
733
+ if (definedTester(stringScheme.format)) {
734
+ let formats: StringFormat[];
484
735
 
485
- const allStringFormat : StringFormat[] = Object.values(StringFormat);
736
+ const allStringFormat: StringFormat[] = Object.values(StringFormat);
486
737
 
487
- if (!arrayTester(scheme.format)) {
488
- const formatAsString : string = scheme.format as string;
738
+ if (!arrayTester(stringScheme.format)) {
739
+ const formatAsString: string = stringScheme.format;
489
740
 
490
741
  if (!enumTester(formatAsString, allStringFormat)) {
491
- throw new Error(ErrorMsg.INVALID_STRING_FORMAT.replace(ErrorMsgCursorA, formatAsString));
742
+ return new EjvError({
743
+ type: ErrorType.INVALID_SCHEMES,
744
+ message: createErrorMsg(ErrorMsg.INVALID_STRING_FORMAT, {
745
+ placeholders: [formatAsString]
746
+ }),
747
+
748
+ data: data,
749
+
750
+ errorScheme: stringScheme,
751
+ isSchemeError: true
752
+ });
492
753
  }
493
754
 
494
- formats = [scheme.format] as StringFormat[];
495
- } else {
496
- const formatAsArray : string[] = scheme.format as string[];
497
- const errorFormat : string | undefined = formatAsArray.find(format => {
755
+ formats = [stringScheme.format] as StringFormat[];
756
+ }
757
+ else {
758
+ const formatAsArray: string[] = stringScheme.format;
759
+ const errorFormat: string | undefined = formatAsArray.find((format: string): boolean => {
498
760
  return !enumTester(format, allStringFormat);
499
761
  });
500
762
 
501
- if (!!errorFormat) {
502
- throw new Error(ErrorMsg.INVALID_STRING_FORMAT.replace(ErrorMsgCursorA, errorFormat));
763
+ if (errorFormat) {
764
+ return new EjvError({
765
+ type: ErrorType.INVALID_SCHEMES,
766
+ message: createErrorMsg(ErrorMsg.INVALID_STRING_FORMAT, {
767
+ placeholders: [errorFormat]
768
+ }),
769
+
770
+ data: data,
771
+
772
+ errorScheme: stringScheme,
773
+ isSchemeError: true
774
+ });
503
775
  }
504
776
 
505
- formats = scheme.format as StringFormat[];
777
+ formats = stringScheme.format as StringFormat[];
506
778
  }
507
779
 
508
- if (!formats.some(format => {
509
- let valid : boolean = false;
780
+ const foundFormatMatching: boolean = formats.some((format: StringFormat): boolean => {
781
+ let valid: boolean = false;
510
782
 
511
783
  switch (format) {
512
784
  case StringFormat.EMAIL:
513
- valid = emailTester(value);
785
+ valid = emailTester(valueAsString);
514
786
  break;
515
787
 
516
788
  case StringFormat.DATE:
517
- valid = dateFormatTester(value);
789
+ valid = dateFormatTester(valueAsString);
518
790
  break;
519
791
 
520
792
  case StringFormat.TIME:
521
- valid = timeFormatTester(value);
793
+ valid = timeFormatTester(valueAsString);
522
794
  break;
523
795
 
524
796
  case StringFormat.DATE_TIME:
525
- valid = dateTimeFormatTester(value);
797
+ valid = dateTimeFormatTester(valueAsString);
526
798
  break;
527
799
  }
528
800
 
529
801
  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,
802
+ });
803
+
804
+ if (!foundFormatMatching) {
805
+ if (!arrayTester(stringScheme.format)) {
806
+ result = new EjvError({
807
+ type: ErrorType.FORMAT,
808
+ message: createErrorMsg(ErrorMsg.FORMAT, {
809
+ placeholders: [stringScheme.format as StringFormat]
810
+ }),
811
+
536
812
  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,
813
+ path: _options.path,
814
+
815
+ errorScheme: stringScheme,
816
+ errorData: value
817
+ });
818
+ }
819
+ else {
820
+ result = new EjvError({
821
+ type: ErrorType.FORMAT_ONE_OF,
822
+ message: createErrorMsg(ErrorMsg.FORMAT_ONE_OF, {
823
+ placeholders: [JSON.stringify(stringScheme.format)]
824
+ }),
825
+
544
826
  data,
545
- value
546
- );
827
+ path: _options.path,
828
+
829
+ errorScheme: stringScheme,
830
+ errorData: value
831
+ });
547
832
  }
548
833
  break;
549
834
  }
550
835
  }
551
836
 
552
- if (definedTester(scheme.pattern)) {
837
+ if (definedTester(stringScheme.pattern)) {
553
838
  // check parameter
554
- if (scheme.pattern === null) {
555
- throw new Error(ErrorMsg.INVALID_STRING_PATTERN
556
- .replace(ErrorMsgCursorA, 'null'));
839
+ if (stringScheme.pattern === null) {
840
+ return new EjvError({
841
+ type: ErrorType.INVALID_SCHEMES,
842
+ message: createErrorMsg(ErrorMsg.INVALID_STRING_PATTERN, {
843
+ placeholders: ['null']
844
+ }),
845
+
846
+ data: data,
847
+
848
+ errorScheme: stringScheme,
849
+ isSchemeError: true
850
+ });
557
851
  }
558
852
 
559
- const isValidPattern = (pattern : string | RegExp) : boolean => {
853
+ const isValidPattern = (pattern: string | RegExp): boolean => {
560
854
  return (stringTester(pattern) && minLengthTester(pattern as string, 1))
561
855
  || (regExpTester(pattern) && pattern.toString() !== '/(?:)/' && pattern.toString() !== '/null/');
562
856
  };
563
857
 
564
- const patternToString = (pattern : string | RegExp) : string => {
565
- let regExpStr : string;
858
+ const patternToString = (pattern: string | RegExp): string => {
859
+ let regExpStr: string;
566
860
 
567
861
  if (pattern === null) {
568
862
  regExpStr = '/null/';
569
- } else if (stringTester(pattern)) {
863
+ }
864
+ else if (stringTester(pattern)) {
570
865
  if (minLengthTester(pattern as string, 1)) {
571
866
  regExpStr = new RegExp(pattern as string).toString();
572
- } else {
867
+ }
868
+ else {
573
869
  regExpStr = '//';
574
870
  }
575
- } else {
871
+ }
872
+ else {
576
873
  regExpStr = pattern.toString();
577
874
  }
578
875
 
@@ -584,488 +881,768 @@ const _ejv = <T>(data : T, schemes : Scheme[], options : InternalOptions = {
584
881
  return regExpStr;
585
882
  };
586
883
 
587
- const createArrayErrorMsg = (patternsAsArray : (string | RegExp)[]) : string => {
588
- return '[' + patternsAsArray.map(onePattern => {
884
+ const createArrayErrorMsg = (patternsAsArray: (string | RegExp)[]): string => {
885
+ return '[' + patternsAsArray.map((onePattern: string | RegExp): string => {
589
886
  return patternToString(onePattern);
590
887
  }).join(', ') + ']';
591
888
  };
592
889
 
593
- if (arrayTester(scheme.pattern)) {
594
- const patternsAsArray : (string | RegExp)[] = scheme.pattern as (string | RegExp)[];
890
+ if (arrayTester(stringScheme.pattern)) {
891
+ const patternsAsArray: (string | RegExp)[] = stringScheme.pattern as (string | RegExp)[];
595
892
 
596
893
  if (!minLengthTester(patternsAsArray, 1)) { // empty array
597
- throw new Error(ErrorMsg.INVALID_STRING_PATTERN
598
- .replace(ErrorMsgCursorA, createArrayErrorMsg(patternsAsArray)));
894
+ return new EjvError({
895
+ type: ErrorType.INVALID_SCHEMES,
896
+ message: createErrorMsg(ErrorMsg.INVALID_STRING_PATTERN, {
897
+ placeholders: [createArrayErrorMsg(patternsAsArray)]
898
+ }),
899
+
900
+ data: data,
901
+
902
+ errorScheme: stringScheme,
903
+ isSchemeError: true
904
+ });
599
905
  }
600
906
 
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
- }
907
+ try {
908
+ const regExpPatterns: RegExp[] = patternsAsArray.map((pattern: string | RegExp): RegExp => {
909
+ if (!isValidPattern(pattern)) {
910
+ throw new Error(createErrorMsg(ErrorMsg.INVALID_STRING_PATTERN, {
911
+ placeholders: [createArrayErrorMsg(patternsAsArray)]
912
+ }));
913
+ }
606
914
 
607
- return new RegExp(pattern);
608
- }) as RegExp[];
915
+ return new RegExp(pattern);
916
+ }) as RegExp[];
609
917
 
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;
918
+ // check value
919
+ const foundMatchPattern: boolean = regExpPatterns.some((regexp: RegExp): boolean => {
920
+ return stringRegExpTester(valueAsString, regexp);
921
+ });
922
+
923
+ if (!foundMatchPattern) {
924
+ result = new EjvError({
925
+ type: ErrorType.PATTERN_ONE_OF,
926
+ message: createErrorMsg(ErrorMsg.PATTERN_ONE_OF, {
927
+ placeholders: [createArrayErrorMsg(patternsAsArray)]
928
+ }),
929
+
930
+ data,
931
+ path: _options.path,
932
+
933
+ errorScheme: stringScheme,
934
+ errorData: value
935
+ });
936
+ break;
937
+ }
623
938
  }
939
+ catch (e: unknown) {
940
+ return new EjvError({
941
+ type: ErrorType.INVALID_SCHEMES,
942
+ message: createErrorMsg(ErrorMsg.INVALID_STRING_PATTERN, {
943
+ placeholders: [createArrayErrorMsg(patternsAsArray)]
944
+ }),
624
945
 
625
- } else {
626
- const patternAsOne : string | RegExp = scheme.pattern as string | RegExp;
946
+ data: data,
947
+
948
+ errorScheme: stringScheme,
949
+ isSchemeError: true
950
+ });
951
+ }
952
+ }
953
+ else {
954
+ const patternAsOne: string | RegExp = stringScheme.pattern as string | RegExp;
627
955
 
628
956
  if (!isValidPattern(patternAsOne)) {
629
- throw new Error(ErrorMsg.INVALID_STRING_PATTERN
630
- .replace(ErrorMsgCursorA, patternToString(patternAsOne)));
957
+ return new EjvError({
958
+ type: ErrorType.INVALID_SCHEMES,
959
+ message: createErrorMsg(ErrorMsg.INVALID_STRING_PATTERN, {
960
+ placeholders: [patternToString(patternAsOne)]
961
+ }),
962
+
963
+ data: data,
964
+
965
+ errorScheme: stringScheme,
966
+ isSchemeError: true
967
+ });
631
968
  }
632
969
 
633
970
  // check value
634
- const regExp : RegExp = new RegExp(patternAsOne);
971
+ const regExp: RegExp = new RegExp(patternAsOne);
635
972
 
636
973
  if (!stringRegExpTester(valueAsString, regExp)) {
637
- result = new EjvError(
638
- ErrorType.PATTERN,
639
- ErrorMsg.PATTERN.replace(ErrorMsgCursorA, patternToString(patternAsOne)),
640
- _options.path,
974
+ result = new EjvError({
975
+ type: ErrorType.PATTERN,
976
+ message: createErrorMsg(ErrorMsg.PATTERN, {
977
+ placeholders: [patternToString(patternAsOne)]
978
+ }),
979
+
641
980
  data,
642
- value
643
- );
981
+ path: _options.path,
982
+
983
+ errorScheme: stringScheme,
984
+ errorData: value
985
+ });
644
986
  break;
645
987
  }
646
988
  }
647
989
  }
648
990
  break;
991
+ }
992
+
993
+ case DataType.OBJECT: {
994
+ const valueAsObject: AnyObject = value as unknown as AnyObject;
995
+ const objectScheme: ObjectScheme = scheme as ObjectScheme;
996
+
997
+ if (definedTester(objectScheme.allowNoProperty)) {
998
+ if (!booleanTester(objectScheme.allowNoProperty)) {
999
+ return new EjvError({
1000
+ type: ErrorType.INVALID_SCHEMES,
1001
+ message: createErrorMsg(ErrorMsg.ALLOW_NO_PROPERTY_SHOULD_BE_BOOLEAN),
649
1002
 
650
- case DataType.OBJECT:
651
- const valueAsObject = value as unknown as { [key : string] : any };
1003
+ data: data,
652
1004
 
653
- if (definedTester(scheme.allowNoProperty)) {
654
- if (!booleanTester(scheme.allowNoProperty)) {
655
- throw new Error(ErrorMsg.ALLOW_NO_PROPERTY_SHOULD_BE_BOOLEAN);
1005
+ errorScheme: objectScheme,
1006
+ isSchemeError: true
1007
+ });
656
1008
  }
657
1009
 
658
- if (scheme.allowNoProperty !== true && !hasPropertyTester(valueAsObject)) {
659
- result = new EjvError(
660
- ErrorType.NO_PROPERTY,
661
- ErrorMsg.NO_PROPERTY,
662
- _options.path,
1010
+ if (!objectScheme.allowNoProperty && !hasPropertyTester(valueAsObject)) {
1011
+ result = new EjvError({
1012
+ type: ErrorType.PROPERTY,
1013
+ message: ErrorMsg.PROPERTY,
1014
+
663
1015
  data,
664
- value
665
- );
1016
+ path: _options.path,
1017
+
1018
+ errorScheme: objectScheme,
1019
+ errorData: value
1020
+ });
666
1021
  break;
667
1022
  }
668
1023
  }
669
1024
 
670
- if (definedTester(scheme.properties)) {
671
- if (!arrayTester(scheme.properties)) {
672
- throw new Error(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY);
1025
+ if (definedTester(objectScheme.properties)) {
1026
+ if (!arrayTester(objectScheme.properties)) {
1027
+ return new EjvError({
1028
+ type: ErrorType.INVALID_SCHEMES,
1029
+ message: createErrorMsg(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY),
1030
+
1031
+ data: data,
1032
+
1033
+ errorScheme: objectScheme,
1034
+ isSchemeError: true
1035
+ });
673
1036
  }
674
1037
 
675
- const properties : Scheme[] = scheme.properties as Scheme[];
1038
+ const properties: Scheme[] = objectScheme.properties as Scheme[];
676
1039
 
677
1040
  if (!minLengthTester(properties, 1)) {
678
- throw new Error(ErrorMsg.PROPERTIES_SHOULD_HAVE_ITEMS);
1041
+ return new EjvError({
1042
+ type: ErrorType.INVALID_SCHEMES,
1043
+ message: createErrorMsg(ErrorMsg.PROPERTIES_SHOULD_HAVE_ITEMS),
1044
+
1045
+ data: data,
1046
+
1047
+ errorScheme: objectScheme,
1048
+ isSchemeError: true
1049
+ });
679
1050
  }
680
1051
 
681
1052
  if (!arrayTypeOfTester(properties, DataType.OBJECT)) {
682
- throw new Error(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY_OF_OBJECT);
1053
+ return new EjvError({
1054
+ type: ErrorType.INVALID_SCHEMES,
1055
+ message: createErrorMsg(ErrorMsg.PROPERTIES_SHOULD_BE_ARRAY_OF_OBJECT),
1056
+
1057
+ data: data,
1058
+
1059
+ errorScheme: objectScheme,
1060
+ isSchemeError: true
1061
+ });
683
1062
  }
684
1063
 
685
1064
  if (!objectTester(value)) {
686
- result = new EjvError(
687
- ErrorType.TYPE_MISMATCH,
688
- ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, 'object'),
689
- _options.path,
1065
+ result = new EjvError({
1066
+ type: ErrorType.TYPE_MISMATCH,
1067
+ message: createErrorMsg(ErrorMsg.TYPE_MISMATCH, {
1068
+ placeholders: ['object']
1069
+ }),
1070
+
690
1071
  data,
691
- value
692
- );
1072
+ path: _options.path,
1073
+
1074
+ errorScheme: objectScheme,
1075
+ errorData: value
1076
+ });
693
1077
  break;
694
1078
  }
695
1079
 
696
- const partialData : T[keyof T] = data[key];
697
- const partialScheme : Scheme[] = scheme.properties as Scheme[];
1080
+ const partialData: T[keyof T] = data[key];
1081
+ const partialScheme: Scheme[] = objectScheme.properties as Scheme[];
1082
+ scheme.parent = objectScheme;
698
1083
 
699
1084
  // call recursively
700
1085
  result = _ejv(partialData, partialScheme, _options);
701
1086
 
702
- if (!!result) {
1087
+ if (result) {
703
1088
  // inject original data
704
1089
  result.data = data;
705
1090
  }
706
1091
  }
707
1092
  break;
1093
+ }
1094
+
1095
+ case DataType.DATE: {
1096
+ const valueAsDate: Date = value as unknown as Date;
1097
+ const dateScheme: DateScheme = scheme as DateScheme;
1098
+ const parentDateScheme: DateScheme | undefined = scheme.parent as DateScheme | undefined;
708
1099
 
709
- case DataType.DATE:
710
- const valueAsDate : Date = value as unknown as Date;
1100
+ if (definedTester(dateScheme.min)
1101
+ || definedTester(parentDateScheme?.min)) {
1102
+ const minDateCandidate: string | Date = dateScheme.min || parentDateScheme?.min as string | Date;
711
1103
 
712
- if (definedTester(scheme.min)) {
713
1104
  if (!(
714
- (stringTester(scheme.min) && (dateFormatTester(scheme.min as string) || dateTimeFormatTester(scheme.min as string)))
715
- || dateTester(scheme.min)
1105
+ (stringTester(minDateCandidate)
1106
+ && (
1107
+ dateFormatTester(minDateCandidate as string)
1108
+ || dateTimeFormatTester(minDateCandidate as string)
1109
+ )
1110
+ )
1111
+ || dateTester(minDateCandidate)
716
1112
  )) {
717
- throw new Error(ErrorMsg.MIN_DATE_SHOULD_BE_DATE_OR_STRING);
718
- }
1113
+ return new EjvError({
1114
+ type: ErrorType.INVALID_SCHEMES,
1115
+ message: createErrorMsg(ErrorMsg.MIN_DATE_SHOULD_BE_DATE_OR_STRING),
1116
+
1117
+ data: data,
719
1118
 
720
- if (definedTester(scheme.exclusiveMin) && !booleanTester(scheme.exclusiveMin)) {
721
- throw new Error(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN);
1119
+ errorScheme: dateScheme,
1120
+ isSchemeError: true
1121
+ });
722
1122
  }
723
1123
 
724
- let minDate : Date = new Date(scheme.min as string | Date);
1124
+ const effectiveMin: Date = new Date(minDateCandidate);
725
1125
 
726
- // adjust timezone
727
- if (stringTester(scheme.min)) {
728
- // by minutes
729
- const timezoneOffset : number = minDate.getTimezoneOffset();
1126
+ if (definedTester(dateScheme.exclusiveMin)) {
1127
+ if (!booleanTester(dateScheme.exclusiveMin)) {
1128
+ return new EjvError({
1129
+ type: ErrorType.INVALID_SCHEMES,
1130
+ message: createErrorMsg(ErrorMsg.EXCLUSIVE_MIN_SHOULD_BE_BOOLEAN),
730
1131
 
731
- minDate = new Date(+minDate + (timezoneOffset * 60 * 1000));
732
- }
1132
+ data: data,
733
1133
 
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;
1134
+ errorScheme: dateScheme,
1135
+ isSchemeError: true
1136
+ });
1137
+ }
1138
+
1139
+
1140
+ if (dateScheme.exclusiveMin) {
1141
+ if (!exclusiveMinDateTester(valueAsDate, effectiveMin)) {
1142
+ result = new EjvError({
1143
+ type: ErrorType.AFTER_DATE,
1144
+ message: createErrorMsg(ErrorMsg.AFTER_DATE, {
1145
+ placeholders: [effectiveMin.toISOString()]
1146
+ }),
1147
+
1148
+ data,
1149
+ path: _options.path,
1150
+
1151
+ errorScheme: dateScheme,
1152
+ errorData: value
1153
+ });
1154
+ break;
1155
+
1156
+ }
1157
+ }
1158
+ else {
1159
+ if (!minDateTester(valueAsDate, effectiveMin)) {
1160
+ result = new EjvError({
1161
+ type: ErrorType.AFTER_OR_SAME_DATE,
1162
+ message: createErrorMsg(ErrorMsg.AFTER_OR_SAME_DATE, {
1163
+ placeholders: [effectiveMin.toISOString()]
1164
+ }),
1165
+
1166
+ data,
1167
+ path: _options.path,
1168
+
1169
+ errorScheme: dateScheme,
1170
+ errorData: value
1171
+ });
1172
+ break;
1173
+ }
744
1174
  }
745
1175
 
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,
1176
+
1177
+ }
1178
+ else {
1179
+ if (!minDateTester(valueAsDate, effectiveMin)) {
1180
+ result = new EjvError({
1181
+ type: ErrorType.AFTER_OR_SAME_DATE,
1182
+ message: createErrorMsg(ErrorMsg.AFTER_OR_SAME_DATE, {
1183
+ placeholders: [effectiveMin.toISOString()]
1184
+ }),
1185
+
752
1186
  data,
753
- value
754
- );
1187
+ path: _options.path,
1188
+
1189
+ errorScheme: dateScheme,
1190
+ errorData: value
1191
+ });
755
1192
  break;
756
1193
  }
757
1194
  }
758
1195
  }
759
1196
 
760
- if (definedTester(scheme.max)) {
1197
+ if (definedTester(dateScheme.max)
1198
+ || definedTester(parentDateScheme?.max)) {
1199
+ const maxDateCandidate: string | Date = dateScheme.max || parentDateScheme?.max as string | Date;
1200
+
761
1201
  if (!(
762
- (stringTester(scheme.max) && (dateFormatTester(scheme.max as string) || dateTimeFormatTester(scheme.max as string)))
763
- || dateTester(scheme.max)
1202
+ (stringTester(maxDateCandidate)
1203
+ && (
1204
+ dateFormatTester(maxDateCandidate as string)
1205
+ || dateTimeFormatTester(maxDateCandidate as string)
1206
+ )
1207
+ )
1208
+ || dateTester(maxDateCandidate)
764
1209
  )) {
765
- throw new Error(ErrorMsg.MAX_DATE_SHOULD_BE_DATE_OR_STRING);
766
- }
1210
+ return new EjvError({
1211
+ type: ErrorType.INVALID_SCHEMES,
1212
+ message: createErrorMsg(ErrorMsg.MAX_DATE_SHOULD_BE_DATE_OR_STRING),
1213
+
1214
+ data: data,
767
1215
 
768
- if (definedTester(scheme.exclusiveMax) && !booleanTester(scheme.exclusiveMax)) {
769
- throw new Error(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN);
1216
+ errorScheme: dateScheme,
1217
+ isSchemeError: true
1218
+ });
770
1219
  }
771
1220
 
772
- let maxDate : Date = new Date(scheme.max as string | Date);
1221
+ const effectiveMax: Date = new Date(maxDateCandidate);
1222
+
1223
+ if (definedTester(dateScheme.exclusiveMax)) {
1224
+ if (!booleanTester(dateScheme.exclusiveMax)) {
1225
+ return new EjvError({
1226
+ type: ErrorType.INVALID_SCHEMES,
1227
+ message: createErrorMsg(ErrorMsg.EXCLUSIVE_MAX_SHOULD_BE_BOOLEAN),
773
1228
 
774
- // adjust timezone
775
- if (stringTester(scheme.max)) {
776
- // by minutes
777
- const timezoneOffset : number = maxDate.getTimezoneOffset();
1229
+ data: data,
778
1230
 
779
- maxDate = new Date(+maxDate + (timezoneOffset * 60 * 1000));
1231
+ errorScheme: dateScheme,
1232
+ isSchemeError: true
1233
+ });
1234
+ }
780
1235
  }
781
1236
 
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,
1237
+ if (dateScheme.exclusiveMax) {
1238
+ if (!exclusiveMaxDateTester(valueAsDate, effectiveMax)) {
1239
+ result = new EjvError({
1240
+ type: ErrorType.BEFORE_DATE,
1241
+ message: createErrorMsg(ErrorMsg.BEFORE_DATE, {
1242
+ placeholders: [effectiveMax.toISOString()]
1243
+ }),
1244
+
788
1245
  data,
789
- value
790
- );
1246
+ path: _options.path,
1247
+
1248
+ errorScheme: dateScheme,
1249
+ errorData: value
1250
+ });
791
1251
  break;
792
1252
  }
1253
+ }
1254
+ else {
1255
+ if (!maxDateTester(valueAsDate, effectiveMax)) {
1256
+ result = new EjvError({
1257
+ type: ErrorType.BEFORE_OR_SAME_DATE,
1258
+ message: createErrorMsg(ErrorMsg.BEFORE_OR_SAME_DATE, {
1259
+ placeholders: [effectiveMax.toISOString()]
1260
+ }),
793
1261
 
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
1262
  data,
801
- value
802
- );
1263
+ path: _options.path,
1264
+
1265
+ errorScheme: dateScheme,
1266
+ errorData: value
1267
+ });
803
1268
  break;
804
1269
  }
805
1270
  }
806
1271
  }
807
1272
  break;
1273
+ }
808
1274
 
809
- case DataType.ARRAY:
810
- const valueAsArray : any[] = value as unknown as any[];
1275
+ case DataType.ARRAY: {
1276
+ const valueAsArray: unknown[] = value as unknown as unknown[];
1277
+ const arrayScheme: ArrayScheme = scheme as ArrayScheme;
811
1278
 
812
- if (definedTester(scheme.length)) {
813
- const length : number = scheme.length as number;
1279
+ if (definedTester(arrayScheme.length)) {
1280
+ const length: number = arrayScheme.length;
814
1281
 
815
1282
  if (!(numberTester(length) && integerTester(length))) {
816
- throw new Error(ErrorMsg.LENGTH_SHOULD_BE_INTEGER);
1283
+ return new EjvError({
1284
+ type: ErrorType.INVALID_SCHEMES,
1285
+ message: createErrorMsg(ErrorMsg.LENGTH_SHOULD_BE_INTEGER),
1286
+
1287
+ data: data,
1288
+
1289
+ errorScheme: arrayScheme,
1290
+ isSchemeError: true
1291
+ });
817
1292
  }
818
1293
 
819
1294
  if (!lengthTester(valueAsArray, length)) {
820
- result = new EjvError(
821
- ErrorType.LENGTH,
822
- ErrorMsg.LENGTH.replace(ErrorMsgCursorA, '' + length),
823
- _options.path,
1295
+ result = new EjvError({
1296
+ type: ErrorType.LENGTH,
1297
+ message: createErrorMsg(ErrorMsg.LENGTH, {
1298
+ placeholders: ['' + length]
1299
+ }),
1300
+
824
1301
  data,
825
- value
826
- );
1302
+ path: _options.path,
1303
+
1304
+ errorScheme: arrayScheme,
1305
+ errorData: value
1306
+ });
827
1307
  break;
828
1308
  }
829
1309
  }
830
1310
 
831
- if (definedTester(scheme.minLength)) {
832
- const minLength : number = scheme.minLength as number;
1311
+ if (definedTester(arrayScheme.minLength)) {
1312
+ const minLength: number = arrayScheme.minLength;
833
1313
 
834
- if (!(numberTester(scheme.minLength) && integerTester(minLength))) {
835
- throw new Error(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER);
1314
+ if (!(numberTester(arrayScheme.minLength) && integerTester(minLength))) {
1315
+ return new EjvError({
1316
+ type: ErrorType.INVALID_SCHEMES,
1317
+ message: createErrorMsg(ErrorMsg.MIN_LENGTH_SHOULD_BE_INTEGER),
1318
+
1319
+ data: data,
1320
+
1321
+ errorScheme: arrayScheme,
1322
+ isSchemeError: true
1323
+ });
836
1324
  }
837
1325
 
838
1326
  if (!minLengthTester(valueAsArray, minLength)) {
839
- result = new EjvError(
840
- ErrorType.MIN_LENGTH,
841
- ErrorMsg.MIN_LENGTH.replace(ErrorMsgCursorA, '' + minLength),
842
- _options.path,
1327
+ result = new EjvError({
1328
+ type: ErrorType.MIN_LENGTH,
1329
+ message: createErrorMsg(ErrorMsg.MIN_LENGTH, {
1330
+ placeholders: ['' + minLength]
1331
+ }),
1332
+
843
1333
  data,
844
- value
845
- );
1334
+ path: _options.path,
1335
+
1336
+ errorScheme: arrayScheme,
1337
+ errorData: value
1338
+ });
846
1339
  break;
847
1340
  }
848
1341
  }
849
1342
 
850
- if (definedTester(scheme.maxLength)) {
851
- const maxLength : number = scheme.maxLength as number;
1343
+ if (definedTester(arrayScheme.maxLength)) {
1344
+ const maxLength: number = arrayScheme.maxLength;
1345
+
1346
+ if (!(numberTester(arrayScheme.maxLength) && integerTester(maxLength))) {
1347
+ return new EjvError({
1348
+ type: ErrorType.INVALID_SCHEMES,
1349
+ message: createErrorMsg(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER),
1350
+
1351
+ data: data,
852
1352
 
853
- if (!(numberTester(scheme.maxLength) && integerTester(maxLength))) {
854
- throw new Error(ErrorMsg.MAX_LENGTH_SHOULD_BE_INTEGER);
1353
+ errorScheme: arrayScheme,
1354
+ isSchemeError: true
1355
+ });
855
1356
  }
856
1357
 
857
1358
  if (!maxLengthTester(valueAsArray, maxLength)) {
858
- result = new EjvError(
859
- ErrorType.MAX_LENGTH,
860
- ErrorMsg.MAX_LENGTH.replace(ErrorMsgCursorA, '' + maxLength),
861
- _options.path,
1359
+ result = new EjvError({
1360
+ type: ErrorType.MAX_LENGTH,
1361
+ message: createErrorMsg(ErrorMsg.MAX_LENGTH, {
1362
+ placeholders: ['' + maxLength]
1363
+ }),
1364
+
862
1365
  data,
863
- value
864
- );
1366
+ path: _options.path,
1367
+
1368
+ errorScheme: arrayScheme,
1369
+ errorData: value
1370
+ });
865
1371
  break;
866
1372
  }
867
1373
  }
868
1374
 
869
- if (definedTester(scheme.unique)) {
870
- if (!booleanTester(scheme.unique)) {
871
- throw new Error(ErrorMsg.UNIQUE_SHOULD_BE_BOOLEAN);
1375
+ if (definedTester(arrayScheme.unique)) {
1376
+ if (!booleanTester(arrayScheme.unique)) {
1377
+ return new EjvError({
1378
+ type: ErrorType.INVALID_SCHEMES,
1379
+ message: createErrorMsg(ErrorMsg.UNIQUE_SHOULD_BE_BOOLEAN),
1380
+
1381
+ data: data,
1382
+
1383
+ errorScheme: arrayScheme,
1384
+ isSchemeError: true
1385
+ });
872
1386
  }
873
1387
 
874
- if (scheme.unique === true && !uniqueItemsTester(valueAsArray)) {
875
- result = new EjvError(
876
- ErrorType.UNIQUE_ITEMS,
877
- ErrorMsg.UNIQUE_ITEMS,
878
- _options.path,
1388
+ if (arrayScheme.unique && !uniqueItemsTester(valueAsArray)) {
1389
+ result = new EjvError({
1390
+ type: ErrorType.UNIQUE_ITEMS,
1391
+ message: ErrorMsg.UNIQUE_ITEMS,
1392
+
879
1393
  data,
880
- value
881
- );
1394
+ path: _options.path,
1395
+
1396
+ errorScheme: arrayScheme,
1397
+ errorData: value
1398
+ });
882
1399
  break;
883
1400
  }
884
1401
  }
885
1402
 
886
- if (definedTester(scheme.items)) {
1403
+ if (definedTester(arrayScheme.items)) {
887
1404
  // convert array to object
888
1405
  if (valueAsArray.length > 0) {
889
- const now : Date = new Date;
890
- const tempKeyArr : string[] = valueAsArray.map((value : any, i : number) => {
1406
+ const now: Date = new Date();
1407
+ const tempKeyArr: string[] = valueAsArray.map((_value: unknown, i: number): string => {
891
1408
  return '' + (+now + i);
892
1409
  });
893
1410
 
894
- if (stringTester(scheme.items) // by DataType
895
- || (arrayTester(scheme.items) && arrayTypeOfTester(scheme.items as DataType[], DataType.STRING)) // by DataType[]
1411
+ if (stringTester(arrayScheme.items) // by DataType
1412
+ || (arrayTester(arrayScheme.items) && arrayTypeOfTester(arrayScheme.items as DataType[], DataType.STRING)) // by DataType[]
896
1413
  ) {
897
- const itemTypes : DataType[] = (arrayTester(scheme.items) ? scheme.items : [scheme.items]) as DataType[];
1414
+ const itemTypes: DataType[] = (arrayTester(arrayScheme.items)
1415
+ ? arrayScheme.items
1416
+ : [arrayScheme.items]) as DataType[];
1417
+
1418
+ const itemTypeError: string | undefined = itemTypes.find((type: DataType): boolean => {
1419
+ return !definedTester(type)
1420
+ || !stringTester(type)
1421
+ || !enumTester(type, allDataType);
1422
+ });
1423
+
1424
+ if (itemTypeError) {
1425
+ return new EjvError({
1426
+ type: ErrorType.INVALID_SCHEMES,
1427
+ message: createErrorMsg(ErrorMsg.SCHEMES_HAS_INVALID_TYPE, {
1428
+ placeholders: [itemTypeError]
1429
+ }),
898
1430
 
899
- const partialData : AnyObject = {};
900
- const partialSchemes : Scheme[] = [];
1431
+ data: data,
901
1432
 
902
- tempKeyArr.forEach((tempKey : string, i : number) => {
1433
+ errorScheme: scheme,
1434
+ isSchemeError: true
1435
+ });
1436
+ }
1437
+
1438
+
1439
+ const partialData: AnyObject = {};
1440
+ const partialSchemes: Scheme[] = [];
1441
+
1442
+ tempKeyArr.forEach((tempKey: string, i: number): void => {
903
1443
  partialData[tempKey] = valueAsArray[i];
904
1444
  partialSchemes.push({
905
- key : tempKey,
906
- type : itemTypes
1445
+ key: tempKey,
1446
+ type: itemTypes
907
1447
  });
908
1448
  });
909
1449
 
1450
+ scheme.parent = arrayScheme;
1451
+
910
1452
  // call recursively
911
- const partialResult : EjvError | null = _ejv(partialData, partialSchemes, _options);
1453
+ const partialResult: EjvError | null = _ejv(partialData, partialSchemes, _options);
912
1454
 
913
1455
  // convert new EjvError
914
- if (!!partialResult) {
915
- let errorMsg : string;
1456
+ if (partialResult) {
1457
+ let errorMsg: string;
916
1458
 
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);
1459
+ if (arrayTester(arrayScheme.items)) {
1460
+ errorMsg = createErrorMsg(ErrorMsg.ITEMS_TYPE, {
1461
+ placeholders: [JSON.stringify(itemTypes)]
1462
+ });
1463
+ }
1464
+ else {
1465
+ errorMsg = createErrorMsg(ErrorMsg.ITEMS_TYPE, {
1466
+ placeholders: [arrayScheme.items as string]
1467
+ });
921
1468
  }
922
1469
 
923
- const partialKeys : string[] = partialResult.path.split('/');
924
- const partialKey : string = partialKeys[partialKeys.length - 1];
1470
+ const partialKeys: string[] = (partialResult.path || '').split('/');
1471
+ const partialKey: string = partialKeys[partialKeys.length - 1];
925
1472
 
926
- const partialScheme : Scheme = partialSchemes.find(scheme => {
927
- return scheme.key === partialKey;
1473
+ const partialScheme: Scheme = partialSchemes.find((_scheme: Scheme): boolean => {
1474
+ return _scheme.key === partialKey;
928
1475
  }) as Scheme;
929
1476
 
930
- const partialKeyIndex : number = partialSchemes.indexOf(partialScheme);
1477
+ const partialKeyIndex: number = partialSchemes.indexOf(partialScheme);
1478
+
1479
+ result = new EjvError({
1480
+ type: ErrorType.ITEMS_TYPE,
1481
+ message: errorMsg,
931
1482
 
932
- result = new EjvError(
933
- ErrorType.ITEMS_TYPE,
934
- errorMsg,
935
- [..._options.path, '' + partialKeyIndex],
936
1483
  data,
937
- partialData[partialKey]
938
- );
1484
+ path: [..._options.path, '' + partialKeyIndex],
1485
+
1486
+ errorScheme: partialScheme,
1487
+ errorData: partialData[partialKey]
1488
+ });
939
1489
  }
940
1490
  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[]
1491
+ }
1492
+ else if ((objectTester(arrayScheme.items) && arrayScheme.items !== null) // by Scheme
1493
+ || (arrayTester(arrayScheme.items) && arrayTypeOfTester(arrayScheme.items as Scheme[], DataType.OBJECT)) // by Scheme[]
943
1494
  ) {
944
- const itemsAsSchemes : Scheme[] = (arrayTester(scheme.items) ? scheme.items : [scheme.items]) as Scheme[];
1495
+ const itemsAsSchemes: Scheme[] = arrayTester(arrayScheme.items)
1496
+ ? arrayScheme.items
1497
+ : [arrayScheme.items] as Scheme[];
945
1498
 
946
- let partialError : EjvError | null | undefined = null;
1499
+ let partialError: EjvError | null | undefined = null;
947
1500
 
948
1501
  // use for() instead of forEach() to break
949
- const valueLength : number = valueAsArray.length;
1502
+ const valueLength: number = valueAsArray.length;
950
1503
 
951
1504
  for (let arrIndex = 0; arrIndex < valueLength; arrIndex++) {
952
- const oneValue : any = valueAsArray[arrIndex];
1505
+ const oneValue: unknown = valueAsArray[arrIndex];
953
1506
 
954
- const partialData : AnyObject = {};
955
- const partialSchemes : Scheme[] = [];
1507
+ const partialData: AnyObject = {};
1508
+ const partialSchemes: Scheme[] = [];
956
1509
 
957
- const tempKeyForThisValue : string = tempKeyArr[arrIndex];
1510
+ const tempKeyForThisValue: string = tempKeyArr[arrIndex];
958
1511
 
959
1512
  partialData[tempKeyForThisValue] = oneValue;
960
1513
 
961
- partialSchemes.push(...itemsAsSchemes.map((oneScheme : Scheme) => {
962
- const newScheme : Scheme = clone(oneScheme); // divide instance
1514
+ partialSchemes.push(...itemsAsSchemes.map((oneScheme: Scheme): Scheme => {
1515
+ const newScheme: Scheme = clone(oneScheme); // divide instance
963
1516
 
964
1517
  newScheme.key = tempKeyForThisValue;
965
1518
 
966
1519
  return newScheme;
967
1520
  }));
968
1521
 
969
- const partialResults : (EjvError | null)[] = partialSchemes.map((partialScheme : Scheme) => {
1522
+ const partialResults: (EjvError | null)[] = partialSchemes.map((partialScheme: Scheme): EjvError | null => {
970
1523
  // call recursively
971
- const partialResult : EjvError | null = _ejv(partialData, [partialScheme], _options);
1524
+ const partialResult: EjvError | null = _ejv(partialData, [partialScheme], _options);
972
1525
 
973
- if (!!partialResult) {
974
- partialResult.path = partialResult.path.replace(tempKeyForThisValue, '' + arrIndex);
1526
+ if (partialResult) {
1527
+ partialResult.path = (partialResult.path || '').replace(tempKeyForThisValue, '' + arrIndex);
975
1528
  }
976
1529
 
977
1530
  return partialResult;
978
1531
  });
979
1532
 
980
- if (!partialResults.some(oneResult => oneResult === null)) {
981
- partialError = partialResults.find(oneResult => {
1533
+ if (!partialResults.some((oneResult: EjvError | null): boolean => oneResult === null)) {
1534
+ partialError = partialResults.find((oneResult: EjvError | null): boolean => {
982
1535
  return !!oneResult;
983
- });
1536
+ }) as EjvError;
984
1537
  break;
985
1538
  }
986
1539
  }
987
1540
 
988
- if (!!partialError) {
989
- let errorType : ErrorType;
990
- let errorMsg : string;
1541
+ if (partialError) {
1542
+ let errorType: ErrorType;
1543
+ let errorMsg: string;
991
1544
 
992
1545
  if (!!itemsAsSchemes && itemsAsSchemes.length > 1) {
993
1546
  errorType = ErrorType.ITEMS_SCHEMES;
994
- errorMsg = ErrorMsg.ITEMS_SCHEMES.replace(ErrorMsgCursorA, JSON.stringify(itemsAsSchemes));
995
- } else {
1547
+ errorMsg = createErrorMsg(ErrorMsg.ITEMS_SCHEMES, {
1548
+ placeholders: [JSON.stringify(itemsAsSchemes)]
1549
+ });
1550
+ }
1551
+ else {
996
1552
  errorType = partialError.type;
997
1553
  errorMsg = partialError.message;
998
1554
 
999
1555
  if (errorType === ErrorType.REQUIRED) {
1000
1556
  // REQUIRED in array is TYPE_MISMATCH except with nullable === true
1001
1557
  errorType = ErrorType.TYPE_MISMATCH;
1002
- errorMsg = ErrorMsg.TYPE_MISMATCH.replace(ErrorMsgCursorA, JSON.stringify(scheme.items));
1558
+ errorMsg = createErrorMsg(ErrorMsg.TYPE_MISMATCH, {
1559
+ placeholders: [JSON.stringify(arrayScheme.items)]
1560
+ });
1003
1561
  }
1004
1562
  }
1005
1563
 
1006
- result = new EjvError(
1007
- errorType,
1008
- errorMsg,
1009
- partialError.path.split('/'),
1010
- data,
1011
- partialError.errorData
1012
- );
1564
+ result = new EjvError({
1565
+ type: errorType,
1566
+ message: errorMsg,
1567
+
1568
+ data
1569
+ });
1570
+
1571
+ if (errorType === ErrorType.INVALID_SCHEMES) {
1572
+ result.errorScheme = arrayScheme;
1573
+ result.isSchemeError = true;
1574
+ result.isDataError = false;
1575
+ }
1576
+ else {
1577
+ result.path = partialError.path;
1578
+ result.errorData = partialError.errorData;
1579
+ }
1013
1580
  break;
1014
1581
  }
1015
- } else {
1016
- throw new Error(ErrorMsg.INVALID_ITEMS_SCHEME.replace(ErrorMsgCursorA, JSON.stringify(scheme.items)));
1582
+ }
1583
+ else {
1584
+ return new EjvError({
1585
+ type: ErrorType.INVALID_SCHEMES,
1586
+ message: createErrorMsg(ErrorMsg.INVALID_ITEMS_SCHEME, {
1587
+ placeholders: [JSON.stringify(arrayScheme.items)]
1588
+ }),
1589
+
1590
+ data: data,
1591
+
1592
+ errorScheme: arrayScheme,
1593
+ isSchemeError: true
1594
+ });
1017
1595
  }
1018
1596
  }
1019
1597
  }
1020
1598
  break;
1599
+ }
1021
1600
  }
1022
1601
 
1023
- if (!!result) {
1602
+ if (result) {
1024
1603
  break;
1025
1604
  }
1026
1605
  }
1027
1606
 
1028
- if (definedTester(result) && definedTester(options.customErrorMsg)) {
1029
- const resultAsNotNull : EjvError = result as NonNullable<EjvError>;
1030
- const customErrorMsgObj : {
1031
- [key in ErrorType]? : string;
1607
+ if (result !== null && definedTester(options.customErrorMsg)) {
1608
+ const customErrorMsgObj: {
1609
+ [key in ErrorType]?: string;
1032
1610
  } = options.customErrorMsg as {
1033
- [key in ErrorType]? : string;
1611
+ [key in ErrorType]?: string;
1034
1612
  };
1035
1613
 
1036
1614
  // override error message
1037
- const customMsg : string = customErrorMsgObj[resultAsNotNull.type] as string;
1615
+ const customMsg: string | undefined = customErrorMsgObj[result.type];
1038
1616
 
1039
1617
  if (definedTester(customMsg)) {
1040
- resultAsNotNull.message = customMsg;
1618
+ result.message = customMsg;
1041
1619
  }
1042
1620
  }
1043
1621
 
1044
1622
  return result;
1045
1623
  };
1046
1624
 
1047
- export const ejv = (data : object, schemes : Scheme[], options? : Options) : null | EjvError => {
1625
+ export const ejv = (data: object, schemes: Scheme[], options?: Options): null | EjvError => {
1048
1626
  // check data itself
1049
1627
  if (!definedTester(data) || !objectTester(data) || data === null) {
1050
- return new EjvError(ErrorType.REQUIRED, ErrorMsg.NO_DATA, ['/'], data, undefined);
1051
- }
1628
+ return new EjvError({
1629
+ type: ErrorType.NO_DATA,
1630
+ message: ErrorMsg.NO_DATA,
1052
1631
 
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);
1632
+ data: data,
1633
+ errorData: data
1634
+ });
1060
1635
  }
1061
1636
 
1062
- if (!arrayTypeOfTester(schemes, DataType.OBJECT)) {
1063
- throw new Error(ErrorMsg.NO_OBJECT_ARRAY_SCHEME);
1064
- }
1637
+ const internalOption: InternalOptions = (options
1638
+ ? {
1639
+ ...options
1640
+ }
1641
+ : {}) as InternalOptions;
1065
1642
 
1066
- if (!minLengthTester(schemes, 1)) {
1067
- throw new Error(ErrorMsg.EMPTY_SCHEME);
1643
+ if (!definedTester(internalOption.path)) {
1644
+ internalOption.path = [];
1068
1645
  }
1069
1646
 
1070
- return _ejv(data, schemes, options as InternalOptions);
1647
+ return _ejv(data, schemes, internalOption);
1071
1648
  };