z-schema 6.0.2 → 7.0.0-beta.2

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 (51) hide show
  1. package/README.md +154 -134
  2. package/bin/z-schema +128 -124
  3. package/cjs/ZSchema.d.ts +227 -0
  4. package/cjs/ZSchema.js +13785 -0
  5. package/dist/Errors.js +50 -0
  6. package/dist/FormatValidators.js +136 -0
  7. package/{src → dist}/JsonValidation.js +184 -213
  8. package/dist/Report.js +220 -0
  9. package/{src → dist}/SchemaCache.js +67 -82
  10. package/{src → dist}/SchemaCompilation.js +89 -129
  11. package/dist/SchemaValidation.js +631 -0
  12. package/{src → dist}/Utils.js +96 -104
  13. package/dist/ZSchema.js +365 -0
  14. package/dist/index.js +2 -0
  15. package/dist/schemas/hyper-schema.json +156 -0
  16. package/dist/schemas/schema.json +151 -0
  17. package/dist/types/Errors.d.ts +44 -0
  18. package/dist/types/FormatValidators.d.ts +12 -0
  19. package/dist/types/JsonValidation.d.ts +37 -0
  20. package/dist/types/Report.d.ts +87 -0
  21. package/dist/types/SchemaCache.d.ts +26 -0
  22. package/dist/types/SchemaCompilation.d.ts +1 -0
  23. package/dist/types/SchemaValidation.d.ts +6 -0
  24. package/dist/types/Utils.d.ts +64 -0
  25. package/dist/types/ZSchema.d.ts +97 -0
  26. package/dist/types/index.d.ts +2 -0
  27. package/package.json +59 -45
  28. package/src/Errors.ts +56 -0
  29. package/src/FormatValidators.ts +136 -0
  30. package/src/JsonValidation.ts +624 -0
  31. package/src/Report.ts +337 -0
  32. package/src/SchemaCache.ts +189 -0
  33. package/src/SchemaCompilation.ts +293 -0
  34. package/src/SchemaValidation.ts +629 -0
  35. package/src/Utils.ts +286 -0
  36. package/src/ZSchema.ts +467 -0
  37. package/src/index.ts +3 -0
  38. package/src/schemas/_ +0 -0
  39. package/umd/ZSchema.js +13791 -0
  40. package/umd/ZSchema.min.js +1 -0
  41. package/dist/ZSchema-browser-min.js +0 -2
  42. package/dist/ZSchema-browser-min.js.map +0 -1
  43. package/dist/ZSchema-browser-test.js +0 -32247
  44. package/dist/ZSchema-browser.js +0 -12745
  45. package/index.d.ts +0 -175
  46. package/src/Errors.js +0 -60
  47. package/src/FormatValidators.js +0 -129
  48. package/src/Polyfills.js +0 -16
  49. package/src/Report.js +0 -299
  50. package/src/SchemaValidation.js +0 -619
  51. package/src/ZSchema.js +0 -409
@@ -0,0 +1,624 @@
1
+ import { FormatValidators } from './FormatValidators.js';
2
+ import { Report } from './Report.js';
3
+ import * as Utils from './Utils.js';
4
+
5
+ const shouldSkipValidate = function (options, errors) {
6
+ return (
7
+ options &&
8
+ Array.isArray(options.includeErrors) &&
9
+ options.includeErrors.length > 0 &&
10
+ !errors.some(function (err) {
11
+ return options.includeErrors.includes(err);
12
+ })
13
+ );
14
+ };
15
+
16
+ export const JsonValidators = {
17
+ multipleOf: function (report, schema, json) {
18
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.1.2
19
+ if (shouldSkipValidate(this.validateOptions, ['MULTIPLE_OF'])) {
20
+ return;
21
+ }
22
+ if (typeof json !== 'number') {
23
+ return;
24
+ }
25
+
26
+ const stringMultipleOf = String(schema.multipleOf);
27
+ const scale = Math.pow(10, stringMultipleOf.length - stringMultipleOf.indexOf('.') - 1);
28
+ if (Utils.whatIs((json * scale) / (schema.multipleOf * scale)) !== 'integer') {
29
+ report.addError('MULTIPLE_OF', [json, schema.multipleOf], null, schema);
30
+ }
31
+ },
32
+ maximum: function (report, schema, json) {
33
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.2.2
34
+ if (shouldSkipValidate(this.validateOptions, ['MAXIMUM', 'MAXIMUM_EXCLUSIVE'])) {
35
+ return;
36
+ }
37
+ if (typeof json !== 'number') {
38
+ return;
39
+ }
40
+ if (schema.exclusiveMaximum !== true) {
41
+ if (json > schema.maximum) {
42
+ report.addError('MAXIMUM', [json, schema.maximum], null, schema);
43
+ }
44
+ } else {
45
+ if (json >= schema.maximum) {
46
+ report.addError('MAXIMUM_EXCLUSIVE', [json, schema.maximum], null, schema);
47
+ }
48
+ }
49
+ },
50
+ exclusiveMaximum: function () {
51
+ // covered in maximum
52
+ },
53
+ minimum: function (report, schema, json) {
54
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.3.2
55
+ if (shouldSkipValidate(this.validateOptions, ['MINIMUM', 'MINIMUM_EXCLUSIVE'])) {
56
+ return;
57
+ }
58
+ if (typeof json !== 'number') {
59
+ return;
60
+ }
61
+ if (schema.exclusiveMinimum !== true) {
62
+ if (json < schema.minimum) {
63
+ report.addError('MINIMUM', [json, schema.minimum], null, schema);
64
+ }
65
+ } else {
66
+ if (json <= schema.minimum) {
67
+ report.addError('MINIMUM_EXCLUSIVE', [json, schema.minimum], null, schema);
68
+ }
69
+ }
70
+ },
71
+ exclusiveMinimum: function () {
72
+ // covered in minimum
73
+ },
74
+ maxLength: function (report, schema, json) {
75
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1.2
76
+ if (shouldSkipValidate(this.validateOptions, ['MAX_LENGTH'])) {
77
+ return;
78
+ }
79
+ if (typeof json !== 'string') {
80
+ return;
81
+ }
82
+ if (Utils.ucs2decode(json).length > schema.maxLength) {
83
+ report.addError('MAX_LENGTH', [json.length, schema.maxLength], null, schema);
84
+ }
85
+ },
86
+ minLength: function (report, schema, json) {
87
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.2.2
88
+ if (shouldSkipValidate(this.validateOptions, ['MIN_LENGTH'])) {
89
+ return;
90
+ }
91
+ if (typeof json !== 'string') {
92
+ return;
93
+ }
94
+ if (Utils.ucs2decode(json).length < schema.minLength) {
95
+ report.addError('MIN_LENGTH', [json.length, schema.minLength], null, schema);
96
+ }
97
+ },
98
+ pattern: function (report, schema, json) {
99
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.3.2
100
+ if (shouldSkipValidate(this.validateOptions, ['PATTERN'])) {
101
+ return;
102
+ }
103
+ if (typeof json !== 'string') {
104
+ return;
105
+ }
106
+ if (RegExp(schema.pattern).test(json) === false) {
107
+ report.addError('PATTERN', [schema.pattern, json], null, schema);
108
+ }
109
+ },
110
+ additionalItems: function (report, schema, json) {
111
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.1.2
112
+ if (shouldSkipValidate(this.validateOptions, ['ARRAY_ADDITIONAL_ITEMS'])) {
113
+ return;
114
+ }
115
+ if (!Array.isArray(json)) {
116
+ return;
117
+ }
118
+ // if the value of "additionalItems" is boolean value false and the value of "items" is an array,
119
+ // the json is valid if its size is less than, or equal to, the size of "items".
120
+ if (schema.additionalItems === false && Array.isArray(schema.items)) {
121
+ if (json.length > schema.items.length) {
122
+ report.addError('ARRAY_ADDITIONAL_ITEMS', null, null, schema);
123
+ }
124
+ }
125
+ },
126
+ items: function () {
127
+ /*report, schema, json*/
128
+ // covered in additionalItems
129
+ },
130
+ maxItems: function (report, schema, json) {
131
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.2.2
132
+ if (shouldSkipValidate(this.validateOptions, ['ARRAY_LENGTH_LONG'])) {
133
+ return;
134
+ }
135
+ if (!Array.isArray(json)) {
136
+ return;
137
+ }
138
+ if (json.length > schema.maxItems) {
139
+ report.addError('ARRAY_LENGTH_LONG', [json.length, schema.maxItems], null, schema);
140
+ }
141
+ },
142
+ minItems: function (report, schema, json) {
143
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.3.2
144
+ if (shouldSkipValidate(this.validateOptions, ['ARRAY_LENGTH_SHORT'])) {
145
+ return;
146
+ }
147
+ if (!Array.isArray(json)) {
148
+ return;
149
+ }
150
+ if (json.length < schema.minItems) {
151
+ report.addError('ARRAY_LENGTH_SHORT', [json.length, schema.minItems], null, schema);
152
+ }
153
+ },
154
+ uniqueItems: function (report, schema, json) {
155
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.4.2
156
+ if (shouldSkipValidate(this.validateOptions, ['ARRAY_UNIQUE'])) {
157
+ return;
158
+ }
159
+ if (!Array.isArray(json)) {
160
+ return;
161
+ }
162
+ if (schema.uniqueItems === true) {
163
+ const matches = [];
164
+ if (Utils.isUniqueArray(json, matches) === false) {
165
+ report.addError('ARRAY_UNIQUE', matches, null, schema);
166
+ }
167
+ }
168
+ },
169
+ maxProperties: function (report, schema, json) {
170
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.1.2
171
+ if (shouldSkipValidate(this.validateOptions, ['OBJECT_PROPERTIES_MAXIMUM'])) {
172
+ return;
173
+ }
174
+ if (Utils.whatIs(json) !== 'object') {
175
+ return;
176
+ }
177
+ const keysCount = Object.keys(json).length;
178
+ if (keysCount > schema.maxProperties) {
179
+ report.addError('OBJECT_PROPERTIES_MAXIMUM', [keysCount, schema.maxProperties], null, schema);
180
+ }
181
+ },
182
+ minProperties: function (report, schema, json) {
183
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.2.2
184
+ if (shouldSkipValidate(this.validateOptions, ['OBJECT_PROPERTIES_MINIMUM'])) {
185
+ return;
186
+ }
187
+ if (Utils.whatIs(json) !== 'object') {
188
+ return;
189
+ }
190
+ const keysCount = Object.keys(json).length;
191
+ if (keysCount < schema.minProperties) {
192
+ report.addError('OBJECT_PROPERTIES_MINIMUM', [keysCount, schema.minProperties], null, schema);
193
+ }
194
+ },
195
+ required: function (report, schema, json) {
196
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.3.2
197
+ if (shouldSkipValidate(this.validateOptions, ['OBJECT_MISSING_REQUIRED_PROPERTY'])) {
198
+ return;
199
+ }
200
+ if (Utils.whatIs(json) !== 'object') {
201
+ return;
202
+ }
203
+ let idx = schema.required.length;
204
+ while (idx--) {
205
+ const requiredPropertyName = schema.required[idx];
206
+ if (json[requiredPropertyName] === undefined) {
207
+ report.addError('OBJECT_MISSING_REQUIRED_PROPERTY', [requiredPropertyName], null, schema);
208
+ }
209
+ }
210
+ },
211
+ additionalProperties: function (report, schema, json) {
212
+ // covered in properties and patternProperties
213
+ if (schema.properties === undefined && schema.patternProperties === undefined) {
214
+ return JsonValidators.properties.call(this, report, schema, json);
215
+ }
216
+ },
217
+ patternProperties: function (report, schema, json) {
218
+ // covered in properties
219
+ if (schema.properties === undefined) {
220
+ return JsonValidators.properties.call(this, report, schema, json);
221
+ }
222
+ },
223
+ properties: function (report, schema, json) {
224
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.4.2
225
+ if (shouldSkipValidate(this.validateOptions, ['OBJECT_ADDITIONAL_PROPERTIES'])) {
226
+ return;
227
+ }
228
+ if (Utils.whatIs(json) !== 'object') {
229
+ return;
230
+ }
231
+ const properties = schema.properties !== undefined ? schema.properties : {};
232
+ const patternProperties = schema.patternProperties !== undefined ? schema.patternProperties : {};
233
+ if (schema.additionalProperties === false) {
234
+ // The property set of the json to validate.
235
+ let s = Object.keys(json);
236
+ // The property set from "properties".
237
+ const p = Object.keys(properties);
238
+ // The property set from "patternProperties".
239
+ const pp = Object.keys(patternProperties);
240
+ // remove from "s" all elements of "p", if any;
241
+ s = Utils.difference(s, p);
242
+ // for each regex in "pp", remove all elements of "s" which this regex matches.
243
+ let idx = pp.length;
244
+ while (idx--) {
245
+ const regExp = RegExp(pp[idx]);
246
+ let idx2 = s.length;
247
+ while (idx2--) {
248
+ if (regExp.test(s[idx2]) === true) {
249
+ s.splice(idx2, 1);
250
+ }
251
+ }
252
+ }
253
+ // Validation of the json succeeds if, after these two steps, set "s" is empty.
254
+ if (s.length > 0) {
255
+ // assumeAdditional can be an array of allowed properties
256
+ let idx3 = this.options.assumeAdditional.length;
257
+ if (idx3) {
258
+ while (idx3--) {
259
+ const io = s.indexOf(this.options.assumeAdditional[idx3]);
260
+ if (io !== -1) {
261
+ s.splice(io, 1);
262
+ }
263
+ }
264
+ }
265
+ let idx4 = s.length;
266
+ if (idx4) {
267
+ while (idx4--) {
268
+ report.addError('OBJECT_ADDITIONAL_PROPERTIES', [s[idx4]], null, schema);
269
+ }
270
+ }
271
+ }
272
+ }
273
+ },
274
+ dependencies: function (report, schema, json) {
275
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.5.2
276
+ if (shouldSkipValidate(this.validateOptions, ['OBJECT_DEPENDENCY_KEY'])) {
277
+ return;
278
+ }
279
+ if (Utils.whatIs(json) !== 'object') {
280
+ return;
281
+ }
282
+
283
+ const keys = Object.keys(schema.dependencies);
284
+ let idx = keys.length;
285
+
286
+ while (idx--) {
287
+ // iterate all dependencies
288
+ const dependencyName = keys[idx];
289
+ if (json[dependencyName]) {
290
+ const dependencyDefinition = schema.dependencies[dependencyName];
291
+ if (Utils.whatIs(dependencyDefinition) === 'object') {
292
+ // if dependency is a schema, validate against this schema
293
+ validate.call(this, report, dependencyDefinition, json);
294
+ } else {
295
+ // Array
296
+ // if dependency is an array, object needs to have all properties in this array
297
+ let idx2 = dependencyDefinition.length;
298
+ while (idx2--) {
299
+ const requiredPropertyName = dependencyDefinition[idx2];
300
+ if (json[requiredPropertyName] === undefined) {
301
+ report.addError('OBJECT_DEPENDENCY_KEY', [requiredPropertyName, dependencyName], null, schema);
302
+ }
303
+ }
304
+ }
305
+ }
306
+ }
307
+ },
308
+ enum: function (report, schema, json) {
309
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.1.2
310
+ if (shouldSkipValidate(this.validateOptions, ['ENUM_CASE_MISMATCH', 'ENUM_MISMATCH'])) {
311
+ return;
312
+ }
313
+ let match = false,
314
+ caseInsensitiveMatch = false,
315
+ idx = schema.enum.length;
316
+ while (idx--) {
317
+ if (Utils.areEqual(json, schema.enum[idx])) {
318
+ match = true;
319
+ break;
320
+ } else if (Utils.areEqual(json, schema.enum[idx], { caseInsensitiveComparison: true })) {
321
+ caseInsensitiveMatch = true;
322
+ }
323
+ }
324
+
325
+ if (match === false) {
326
+ const error =
327
+ caseInsensitiveMatch && this.options.enumCaseInsensitiveComparison ? 'ENUM_CASE_MISMATCH' : 'ENUM_MISMATCH';
328
+ report.addError(error, [json], null, schema);
329
+ }
330
+ },
331
+ type: function (report, schema, json) {
332
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.2.2
333
+ if (shouldSkipValidate(this.validateOptions, ['INVALID_TYPE'])) {
334
+ return;
335
+ }
336
+ const jsonType = Utils.whatIs(json);
337
+ if (typeof schema.type === 'string') {
338
+ if (jsonType !== schema.type && (jsonType !== 'integer' || schema.type !== 'number')) {
339
+ report.addError('INVALID_TYPE', [schema.type, jsonType], null, schema);
340
+ }
341
+ } else {
342
+ if (schema.type.indexOf(jsonType) === -1 && (jsonType !== 'integer' || schema.type.indexOf('number') === -1)) {
343
+ report.addError('INVALID_TYPE', [schema.type, jsonType], null, schema);
344
+ }
345
+ }
346
+ },
347
+ allOf: function (report, schema, json) {
348
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.3.2
349
+ let idx = schema.allOf.length;
350
+ while (idx--) {
351
+ const validateResult = validate.call(this, report, schema.allOf[idx], json);
352
+ if (this.options.breakOnFirstError && validateResult === false) {
353
+ break;
354
+ }
355
+ }
356
+ },
357
+ anyOf: function (report, schema, json) {
358
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.4.2
359
+ const subReports = [];
360
+ let passed = false;
361
+ let idx = schema.anyOf.length;
362
+
363
+ while (idx-- && passed === false) {
364
+ const subReport = new Report(report);
365
+ subReports.push(subReport);
366
+ passed = validate.call(this, subReport, schema.anyOf[idx], json);
367
+ }
368
+
369
+ if (passed === false) {
370
+ report.addError('ANY_OF_MISSING', undefined, subReports, schema);
371
+ }
372
+ },
373
+ oneOf: function (report, schema, json) {
374
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.5.2
375
+ let passes = 0;
376
+ const subReports = [];
377
+ let idx = schema.oneOf.length;
378
+
379
+ while (idx--) {
380
+ const subReport = new Report(report, { maxErrors: 1 });
381
+ subReports.push(subReport);
382
+ if (validate.call(this, subReport, schema.oneOf[idx], json) === true) {
383
+ passes++;
384
+ }
385
+ }
386
+
387
+ if (passes === 0) {
388
+ report.addError('ONE_OF_MISSING', undefined, subReports, schema);
389
+ } else if (passes > 1) {
390
+ report.addError('ONE_OF_MULTIPLE', null, null, schema);
391
+ }
392
+ },
393
+ not: function (report, schema, json) {
394
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.6.2
395
+ const subReport = new Report(report);
396
+ if (validate.call(this, subReport, schema.not, json) === true) {
397
+ report.addError('NOT_PASSED', null, null, schema);
398
+ }
399
+ },
400
+ definitions: function () {
401
+ /*report, schema, json*/
402
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.7.2
403
+ // nothing to do here
404
+ },
405
+ format: function (report, schema, json) {
406
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.2
407
+ const formatValidatorFn = FormatValidators[schema.format];
408
+ if (typeof formatValidatorFn === 'function') {
409
+ if (shouldSkipValidate(this.validateOptions, ['INVALID_FORMAT'])) {
410
+ return;
411
+ }
412
+ if (report.hasError('INVALID_TYPE', [schema.type, Utils.whatIs(json)])) {
413
+ return;
414
+ }
415
+ if (formatValidatorFn.length === 2) {
416
+ // async - need to clone the path here, because it will change by the time async function reports back
417
+ const pathBeforeAsync = Utils.clone(report.path);
418
+ report.addAsyncTask(formatValidatorFn, [json], function (result) {
419
+ if (result !== true) {
420
+ const backup = report.path;
421
+ report.path = pathBeforeAsync;
422
+ report.addError('INVALID_FORMAT', [schema.format, json], null, schema);
423
+ report.path = backup;
424
+ }
425
+ });
426
+ } else {
427
+ // sync
428
+ if (formatValidatorFn.call(this, json) !== true) {
429
+ report.addError('INVALID_FORMAT', [schema.format, json], null, schema);
430
+ }
431
+ }
432
+ } else if (this.options.ignoreUnknownFormats !== true) {
433
+ report.addError('UNKNOWN_FORMAT', [schema.format], null, schema);
434
+ }
435
+ },
436
+ };
437
+
438
+ const recurseArray = function (report, schema, json) {
439
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.2
440
+
441
+ let idx = json.length;
442
+
443
+ // If "items" is an array, this situation, the schema depends on the index:
444
+ // if the index is less than, or equal to, the size of "items",
445
+ // the child instance must be valid against the corresponding schema in the "items" array;
446
+ // otherwise, it must be valid against the schema defined by "additionalItems".
447
+ if (Array.isArray(schema.items)) {
448
+ while (idx--) {
449
+ // equal to doesn't make sense here
450
+ if (idx < schema.items.length) {
451
+ report.path.push(idx);
452
+ validate.call(this, report, schema.items[idx], json[idx]);
453
+ report.path.pop();
454
+ } else {
455
+ // might be boolean, so check that it's an object
456
+ if (typeof schema.additionalItems === 'object') {
457
+ report.path.push(idx);
458
+ validate.call(this, report, schema.additionalItems, json[idx]);
459
+ report.path.pop();
460
+ }
461
+ }
462
+ }
463
+ } else if (typeof schema.items === 'object') {
464
+ // If items is a schema, then the child instance must be valid against this schema,
465
+ // regardless of its index, and regardless of the value of "additionalItems".
466
+ while (idx--) {
467
+ report.path.push(idx);
468
+ validate.call(this, report, schema.items, json[idx]);
469
+ report.path.pop();
470
+ }
471
+ }
472
+ };
473
+
474
+ const recurseObject = function (report, schema, json) {
475
+ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.3
476
+
477
+ // If "additionalProperties" is absent, it is considered present with an empty schema as a value.
478
+ // In addition, boolean value true is considered equivalent to an empty schema.
479
+ let additionalProperties = schema.additionalProperties;
480
+ if (additionalProperties === true || additionalProperties === undefined) {
481
+ additionalProperties = {};
482
+ }
483
+
484
+ // p - The property set from "properties".
485
+ const p = schema.properties ? Object.keys(schema.properties) : [];
486
+
487
+ // pp - The property set from "patternProperties". Elements of this set will be called regexes for convenience.
488
+ const pp = schema.patternProperties ? Object.keys(schema.patternProperties) : [];
489
+
490
+ // m - The property name of the child.
491
+ const keys = Object.keys(json);
492
+ let idx = keys.length;
493
+
494
+ while (idx--) {
495
+ const m = keys[idx],
496
+ propertyValue = json[m];
497
+
498
+ // s - The set of schemas for the child instance.
499
+ const s = [];
500
+
501
+ // 1. If set "p" contains value "m", then the corresponding schema in "properties" is added to "s".
502
+ if (p.indexOf(m) !== -1) {
503
+ s.push(schema.properties[m]);
504
+ }
505
+
506
+ // 2. For each regex in "pp", if it matches "m" successfully, the corresponding schema in "patternProperties" is added to "s".
507
+ let idx2 = pp.length;
508
+ while (idx2--) {
509
+ const regexString = pp[idx2];
510
+ if (RegExp(regexString).test(m) === true) {
511
+ s.push(schema.patternProperties[regexString]);
512
+ }
513
+ }
514
+
515
+ // 3. The schema defined by "additionalProperties" is added to "s" if and only if, at this stage, "s" is empty.
516
+ if (s.length === 0 && additionalProperties !== false) {
517
+ s.push(additionalProperties);
518
+ }
519
+
520
+ // we are passing tests even without this assert because this is covered by properties check
521
+ // if s is empty in this stage, no additionalProperties are allowed
522
+ // report.expect(s.length !== 0, 'E001', m);
523
+
524
+ // Instance property value must pass all schemas from s
525
+ idx2 = s.length;
526
+ while (idx2--) {
527
+ report.path.push(m);
528
+ validate.call(this, report, s[idx2], propertyValue);
529
+ report.path.pop();
530
+ }
531
+ }
532
+ };
533
+
534
+ /**
535
+ *
536
+ * @param {Report} report
537
+ * @param {*} schema
538
+ * @param {*} json
539
+ */
540
+ export function validate(report, schema, json) {
541
+ report.commonErrorMessage = 'JSON_OBJECT_VALIDATION_FAILED';
542
+
543
+ // check if schema is an object
544
+ const to = Utils.whatIs(schema);
545
+ if (to !== 'object') {
546
+ report.addError('SCHEMA_NOT_AN_OBJECT', [to], null, schema);
547
+ return false;
548
+ }
549
+
550
+ // check if schema is empty, everything is valid against empty schema
551
+ let keys = Object.keys(schema);
552
+ if (keys.length === 0) {
553
+ return true;
554
+ }
555
+
556
+ // this method can be called recursively, so we need to remember our root
557
+ let isRoot = false;
558
+ if (!report.rootSchema) {
559
+ report.rootSchema = schema;
560
+ isRoot = true;
561
+ }
562
+
563
+ // follow schema.$ref keys
564
+ if (schema.$ref !== undefined) {
565
+ // avoid infinite loop with maxRefs
566
+ let maxRefs = 99;
567
+ while (schema.$ref && maxRefs > 0) {
568
+ if (!schema.__$refResolved) {
569
+ report.addError('REF_UNRESOLVED', [schema.$ref], null, schema);
570
+ break;
571
+ } else if (schema.__$refResolved === schema) {
572
+ break;
573
+ } else {
574
+ schema = schema.__$refResolved;
575
+ keys = Object.keys(schema);
576
+ }
577
+ maxRefs--;
578
+ }
579
+ if (maxRefs === 0) {
580
+ throw new Error('Circular dependency by $ref references!');
581
+ }
582
+ }
583
+
584
+ // type checking first
585
+ const jsonType = Utils.whatIs(json);
586
+ if (schema.type) {
587
+ keys.splice(keys.indexOf('type'), 1);
588
+ JsonValidators.type.call(this, report, schema, json);
589
+ if (report.errors.length && this.options.breakOnFirstError) {
590
+ return false;
591
+ }
592
+ }
593
+
594
+ // now iterate all the keys in schema and execute validation methods
595
+ let idx = keys.length;
596
+ while (idx--) {
597
+ if (JsonValidators[keys[idx]]) {
598
+ JsonValidators[keys[idx]].call(this, report, schema, json);
599
+ if (report.errors.length && this.options.breakOnFirstError) {
600
+ break;
601
+ }
602
+ }
603
+ }
604
+
605
+ if (report.errors.length === 0 || this.options.breakOnFirstError === false) {
606
+ if (jsonType === 'array') {
607
+ recurseArray.call(this, report, schema, json);
608
+ } else if (jsonType === 'object') {
609
+ recurseObject.call(this, report, schema, json);
610
+ }
611
+ }
612
+
613
+ if (typeof this.options.customValidator === 'function') {
614
+ this.options.customValidator.call(this, report, schema, json);
615
+ }
616
+
617
+ // we don't need the root pointer anymore
618
+ if (isRoot) {
619
+ report.rootSchema = undefined;
620
+ }
621
+
622
+ // return valid just to be able to break at some code points
623
+ return report.errors.length === 0;
624
+ }