z-schema 12.2.0 → 12.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/z-schema +1 -1
- package/cjs/{index.js → index.cjs} +696 -687
- package/cjs/{index.d.ts → index.d.cts} +47 -26
- package/dist/{errors.d.mts → errors.d.ts} +2 -2
- package/dist/{errors.mjs → errors.js} +1 -2
- package/dist/{format-validators.mjs → format-validators.js} +43 -36
- package/dist/{index.d.mts → index.d.ts} +9 -9
- package/dist/{index.mjs → index.js} +3 -3
- package/dist/{json-schema-versions.d.mts → json-schema-versions.d.ts} +34 -3
- package/dist/{json-schema.d.mts → json-schema.d.ts} +7 -7
- package/dist/{json-schema.mjs → json-schema.js} +7 -12
- package/dist/{json-validation.mjs → json-validation.js} +143 -127
- package/dist/{report.d.mts → report.d.ts} +7 -8
- package/dist/{report.mjs → report.js} +28 -31
- package/dist/{schema-cache.d.mts → schema-cache.d.ts} +4 -4
- package/dist/{schema-cache.mjs → schema-cache.js} +10 -11
- package/dist/{schema-compiler.d.mts → schema-compiler.d.ts} +4 -4
- package/dist/{schema-compiler.mjs → schema-compiler.js} +95 -77
- package/dist/{schema-validator.d.mts → schema-validator.d.ts} +5 -5
- package/dist/{schema-validator.mjs → schema-validator.js} +138 -166
- package/dist/utils/{array.mjs → array.js} +4 -3
- package/dist/utils/{base64.mjs → base64.js} +3 -2
- package/dist/utils/{clone.mjs → clone.js} +18 -20
- package/dist/utils/{hostname.mjs → hostname.js} +19 -22
- package/dist/utils/{json.mjs → json.js} +11 -7
- package/dist/utils/{schema-regex.mjs → schema-regex.js} +5 -5
- package/dist/utils/{time.mjs → time.js} +5 -5
- package/dist/utils/unicode.js +22 -0
- package/dist/utils/{what-is.mjs → what-is.js} +1 -2
- package/dist/validation/{array.mjs → array.js} +18 -20
- package/dist/validation/{combinators.mjs → combinators.js} +16 -16
- package/dist/validation/{numeric.mjs → numeric.js} +11 -11
- package/dist/validation/{object.mjs → object.js} +35 -34
- package/dist/validation/{ref.mjs → ref.js} +4 -4
- package/dist/validation/{shared.mjs → shared.js} +12 -11
- package/dist/validation/{string.mjs → string.js} +32 -32
- package/dist/validation/type.js +34 -0
- package/dist/{z-schema-base.d.mts → z-schema-base.d.ts} +11 -12
- package/dist/{z-schema-base.mjs → z-schema-base.js} +45 -40
- package/dist/{z-schema-options.d.mts → z-schema-options.d.ts} +3 -3
- package/dist/{z-schema-options.mjs → z-schema-options.js} +4 -4
- package/dist/{z-schema-reader.d.mts → z-schema-reader.d.ts} +1 -1
- package/dist/{z-schema-versions.mjs → z-schema-versions.js} +21 -21
- package/dist/{z-schema.d.mts → z-schema.d.ts} +5 -13
- package/dist/{z-schema.mjs → z-schema.js} +37 -47
- package/package.json +22 -23
- package/src/errors.ts +1 -2
- package/src/format-validators.ts +139 -59
- package/src/json-schema-versions.ts +56 -2
- package/src/json-schema.ts +10 -9
- package/src/json-validation.ts +189 -146
- package/src/report.ts +37 -49
- package/src/schema-cache.ts +13 -13
- package/src/schema-compiler.ts +170 -117
- package/src/schema-validator.ts +239 -238
- package/src/utils/array.ts +9 -6
- package/src/utils/base64.ts +13 -2
- package/src/utils/clone.ts +28 -30
- package/src/utils/date.ts +6 -3
- package/src/utils/hostname.ts +27 -27
- package/src/utils/json.ts +16 -9
- package/src/utils/properties.ts +2 -2
- package/src/utils/schema-regex.ts +4 -4
- package/src/utils/time.ts +5 -5
- package/src/utils/unicode.ts +12 -5
- package/src/utils/what-is.ts +1 -5
- package/src/validation/array.ts +24 -22
- package/src/validation/combinators.ts +14 -14
- package/src/validation/numeric.ts +14 -28
- package/src/validation/object.ts +32 -36
- package/src/validation/ref.ts +5 -6
- package/src/validation/shared.ts +22 -21
- package/src/validation/string.ts +29 -39
- package/src/validation/type.ts +17 -17
- package/src/z-schema-base.ts +49 -38
- package/src/z-schema-options.ts +4 -3
- package/src/z-schema.ts +35 -45
- package/umd/ZSchema.js +711 -695
- package/umd/ZSchema.min.js +2 -2
- package/umd/package.json +3 -0
- package/dist/utils/unicode.mjs +0 -12
- package/dist/validation/type.mjs +0 -32
- /package/dist/{format-validators.d.mts → format-validators.d.ts} +0 -0
- /package/dist/{json-schema-versions.mjs → json-schema-versions.js} +0 -0
- /package/dist/schemas/{draft-04-schema.mjs → draft-04-schema.js} +0 -0
- /package/dist/schemas/{draft-06-schema.mjs → draft-06-schema.js} +0 -0
- /package/dist/schemas/{draft-07-schema.mjs → draft-07-schema.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-applicator.mjs → draft-2019-09-meta-applicator.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-content.mjs → draft-2019-09-meta-content.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-core.mjs → draft-2019-09-meta-core.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-format.mjs → draft-2019-09-meta-format.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-meta-data.mjs → draft-2019-09-meta-meta-data.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-validation.mjs → draft-2019-09-meta-validation.js} +0 -0
- /package/dist/schemas/{draft-2019-09-schema.mjs → draft-2019-09-schema.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-applicator.mjs → draft-2020-12-meta-applicator.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-content.mjs → draft-2020-12-meta-content.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-core.mjs → draft-2020-12-meta-core.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-format-annotation.mjs → draft-2020-12-meta-format-annotation.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-format-assertion.mjs → draft-2020-12-meta-format-assertion.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-meta-data.mjs → draft-2020-12-meta-meta-data.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-unevaluated.mjs → draft-2020-12-meta-unevaluated.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-validation.mjs → draft-2020-12-meta-validation.js} +0 -0
- /package/dist/schemas/{draft-2020-12-schema.mjs → draft-2020-12-schema.js} +0 -0
- /package/dist/utils/{constants.mjs → constants.js} +0 -0
- /package/dist/utils/{date.mjs → date.js} +0 -0
- /package/dist/utils/{properties.mjs → properties.js} +0 -0
- /package/dist/utils/{symbols.mjs → symbols.js} +0 -0
- /package/dist/utils/{uri.mjs → uri.js} +0 -0
- /package/dist/{z-schema-reader.mjs → z-schema-reader.js} +0 -0
package/src/json-validation.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JsonSchemaAll, JsonSchemaInternal } from './json-schema-versions.js';
|
|
2
|
+
import type { JsonValidatorFn } from './validation/shared.js';
|
|
2
3
|
import type { ZSchemaBase } from './z-schema-base.js';
|
|
3
4
|
|
|
4
5
|
import { getId } from './json-schema.js';
|
|
@@ -48,7 +49,6 @@ import { resolveDynamicRef, resolveRecursiveRef } from './validation/ref.js';
|
|
|
48
49
|
import {
|
|
49
50
|
getCachedValidationResult,
|
|
50
51
|
isValidationVocabularyEnabled,
|
|
51
|
-
type JsonValidatorFn,
|
|
52
52
|
VALIDATION_VOCAB_KEYWORDS,
|
|
53
53
|
} from './validation/shared.js';
|
|
54
54
|
import {
|
|
@@ -65,34 +65,34 @@ import { constValidator, enumValidator, typeValidator } from './validation/type.
|
|
|
65
65
|
// collectEvaluated — unified traversal for unevaluatedItems / unevaluatedProperties
|
|
66
66
|
// ---------------------------------------------------------------------------
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
interface CollectEvaluatedItemsArgs {
|
|
69
69
|
report: Report;
|
|
70
70
|
currentSchema: JsonSchemaInternal | boolean | undefined;
|
|
71
71
|
json: unknown;
|
|
72
72
|
mode: 'items';
|
|
73
73
|
jsonArr: unknown[];
|
|
74
74
|
depth: number;
|
|
75
|
-
}
|
|
75
|
+
}
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
interface CollectEvaluatedPropertiesArgs {
|
|
78
78
|
report: Report;
|
|
79
79
|
currentSchema: JsonSchemaInternal | boolean | undefined;
|
|
80
80
|
json: unknown;
|
|
81
81
|
mode: 'properties';
|
|
82
82
|
jsonData: Record<string, unknown>;
|
|
83
83
|
depth: number;
|
|
84
|
-
}
|
|
84
|
+
}
|
|
85
85
|
|
|
86
86
|
type CollectEvaluatedArgs = CollectEvaluatedItemsArgs | CollectEvaluatedPropertiesArgs;
|
|
87
87
|
|
|
88
|
-
function collectEvaluated(
|
|
88
|
+
function collectEvaluated(ctx: ZSchemaBase, args: CollectEvaluatedArgs): Set<number | string> | 'all' {
|
|
89
89
|
const { report, currentSchema, json, mode, depth } = args;
|
|
90
90
|
|
|
91
91
|
if (!currentSchema || typeof currentSchema === 'boolean') {
|
|
92
92
|
return new Set();
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
if (depth > (
|
|
95
|
+
if (depth > (ctx.options.maxRecursionDepth ?? 100)) {
|
|
96
96
|
report.addError('COLLECT_EVALUATED_DEPTH_EXCEEDED', [depth]);
|
|
97
97
|
return new Set();
|
|
98
98
|
}
|
|
@@ -100,7 +100,9 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
100
100
|
const evaluated = new Set<number | string>();
|
|
101
101
|
|
|
102
102
|
const merge = (other: Set<number | string> | 'all') => {
|
|
103
|
-
if (other === 'all')
|
|
103
|
+
if (other === 'all') {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
104
106
|
for (const v of other) {
|
|
105
107
|
evaluated.add(v);
|
|
106
108
|
}
|
|
@@ -109,28 +111,28 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
109
111
|
|
|
110
112
|
const recurse = (subSchema: JsonSchemaInternal | boolean | undefined): Set<number | string> | 'all' => {
|
|
111
113
|
if (mode === 'items') {
|
|
112
|
-
return collectEvaluated
|
|
114
|
+
return collectEvaluated(ctx, {
|
|
113
115
|
report,
|
|
114
116
|
currentSchema: subSchema,
|
|
115
117
|
json,
|
|
116
118
|
mode: 'items',
|
|
117
|
-
jsonArr:
|
|
119
|
+
jsonArr: args.jsonArr,
|
|
118
120
|
depth: depth + 1,
|
|
119
121
|
});
|
|
120
122
|
}
|
|
121
|
-
return collectEvaluated
|
|
123
|
+
return collectEvaluated(ctx, {
|
|
122
124
|
report,
|
|
123
125
|
currentSchema: subSchema,
|
|
124
126
|
json,
|
|
125
127
|
mode: 'properties',
|
|
126
|
-
jsonData:
|
|
128
|
+
jsonData: args.jsonData,
|
|
127
129
|
depth: depth + 1,
|
|
128
130
|
});
|
|
129
131
|
};
|
|
130
132
|
|
|
131
133
|
// --- Mode-specific leaf collection ---
|
|
132
134
|
if (mode === 'items') {
|
|
133
|
-
const jsonArr =
|
|
135
|
+
const { jsonArr } = args;
|
|
134
136
|
|
|
135
137
|
// prefixItems (2020-12 tuple)
|
|
136
138
|
if (Array.isArray(currentSchema.prefixItems)) {
|
|
@@ -167,7 +169,7 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
167
169
|
let passed = getCachedValidationResult(report, currentSchema.contains, jsonArr[i]);
|
|
168
170
|
if (passed === undefined) {
|
|
169
171
|
const subReport = new Report(report);
|
|
170
|
-
validate
|
|
172
|
+
validate(ctx, subReport, currentSchema.contains as JsonSchemaInternal | boolean, jsonArr[i]);
|
|
171
173
|
passed = subReport.errors.length === 0;
|
|
172
174
|
}
|
|
173
175
|
if (passed) {
|
|
@@ -182,11 +184,13 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
182
184
|
}
|
|
183
185
|
} else {
|
|
184
186
|
// mode === 'properties'
|
|
185
|
-
const jsonData =
|
|
187
|
+
const { jsonData } = args;
|
|
186
188
|
|
|
187
189
|
// properties
|
|
188
190
|
if (isObject(currentSchema.properties)) {
|
|
189
|
-
|
|
191
|
+
const propKeysCE = Object.keys(currentSchema.properties);
|
|
192
|
+
for (let i = 0; i < propKeysCE.length; i++) {
|
|
193
|
+
const key = propKeysCE[i];
|
|
190
194
|
if (Object.hasOwn(jsonData, key)) {
|
|
191
195
|
evaluated.add(key);
|
|
192
196
|
}
|
|
@@ -198,9 +202,10 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
198
202
|
for (const pattern of Object.keys(currentSchema.patternProperties)) {
|
|
199
203
|
const result = compileSchemaRegex(pattern);
|
|
200
204
|
if (result.ok) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
const jdKeys = Object.keys(jsonData);
|
|
206
|
+
for (let i = 0; i < jdKeys.length; i++) {
|
|
207
|
+
if (result.value.test(jdKeys[i])) {
|
|
208
|
+
evaluated.add(jdKeys[i]);
|
|
204
209
|
}
|
|
205
210
|
}
|
|
206
211
|
}
|
|
@@ -209,7 +214,7 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
209
214
|
|
|
210
215
|
// additionalProperties - evaluates all non-properties/non-patternProperties keys
|
|
211
216
|
if (currentSchema.additionalProperties !== undefined) {
|
|
212
|
-
const
|
|
217
|
+
const propKeySet = new Set(isObject(currentSchema.properties) ? Object.keys(currentSchema.properties) : []);
|
|
213
218
|
const patternRegexes: RegExp[] = [];
|
|
214
219
|
if (isObject(currentSchema.patternProperties)) {
|
|
215
220
|
for (const pattern of Object.keys(currentSchema.patternProperties)) {
|
|
@@ -219,9 +224,22 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
219
224
|
}
|
|
220
225
|
}
|
|
221
226
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
227
|
+
const apKeys = Object.keys(jsonData);
|
|
228
|
+
for (let i = 0; i < apKeys.length; i++) {
|
|
229
|
+
const key = apKeys[i];
|
|
230
|
+
if (propKeySet.has(key)) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
let matchedPattern = false;
|
|
234
|
+
for (let pi = 0; pi < patternRegexes.length; pi++) {
|
|
235
|
+
if (patternRegexes[pi].test(key)) {
|
|
236
|
+
matchedPattern = true;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (matchedPattern) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
225
243
|
evaluated.add(key);
|
|
226
244
|
}
|
|
227
245
|
}
|
|
@@ -229,10 +247,8 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
229
247
|
// dependentSchemas - only applies when the dependency key is present in the data
|
|
230
248
|
if (isObject(currentSchema.dependentSchemas)) {
|
|
231
249
|
for (const [depKey, depSchema] of Object.entries(currentSchema.dependentSchemas as Record<string, unknown>)) {
|
|
232
|
-
if (Object.hasOwn(jsonData, depKey)) {
|
|
233
|
-
|
|
234
|
-
return 'all';
|
|
235
|
-
}
|
|
250
|
+
if (Object.hasOwn(jsonData, depKey) && merge(recurse(depSchema as JsonSchemaInternal | boolean))) {
|
|
251
|
+
return 'all';
|
|
236
252
|
}
|
|
237
253
|
}
|
|
238
254
|
}
|
|
@@ -247,8 +263,8 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
247
263
|
|
|
248
264
|
// allOf
|
|
249
265
|
if (Array.isArray(currentSchema.allOf)) {
|
|
250
|
-
for (
|
|
251
|
-
if (merge(recurse(
|
|
266
|
+
for (let i = 0; i < currentSchema.allOf.length; i++) {
|
|
267
|
+
if (merge(recurse(currentSchema.allOf[i] as JsonSchemaInternal | boolean))) {
|
|
252
268
|
return 'all';
|
|
253
269
|
}
|
|
254
270
|
}
|
|
@@ -256,34 +272,32 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
256
272
|
|
|
257
273
|
// anyOf - only matching branches contribute
|
|
258
274
|
if (Array.isArray(currentSchema.anyOf)) {
|
|
259
|
-
for (
|
|
275
|
+
for (let i = 0; i < currentSchema.anyOf.length; i++) {
|
|
276
|
+
const subSchema = currentSchema.anyOf[i];
|
|
260
277
|
let passed = getCachedValidationResult(report, subSchema, json);
|
|
261
278
|
if (passed === undefined) {
|
|
262
279
|
const subReport = new Report(report);
|
|
263
|
-
validate
|
|
280
|
+
validate(ctx, subReport, subSchema as JsonSchemaInternal | boolean, json);
|
|
264
281
|
passed = subReport.errors.length === 0;
|
|
265
282
|
}
|
|
266
|
-
if (passed) {
|
|
267
|
-
|
|
268
|
-
return 'all';
|
|
269
|
-
}
|
|
283
|
+
if (passed && merge(recurse(subSchema as JsonSchemaInternal | boolean))) {
|
|
284
|
+
return 'all';
|
|
270
285
|
}
|
|
271
286
|
}
|
|
272
287
|
}
|
|
273
288
|
|
|
274
289
|
// oneOf - only matching branches contribute
|
|
275
290
|
if (Array.isArray(currentSchema.oneOf)) {
|
|
276
|
-
for (
|
|
291
|
+
for (let i = 0; i < currentSchema.oneOf.length; i++) {
|
|
292
|
+
const subSchema = currentSchema.oneOf[i];
|
|
277
293
|
let passed = getCachedValidationResult(report, subSchema, json);
|
|
278
294
|
if (passed === undefined) {
|
|
279
295
|
const subReport = new Report(report);
|
|
280
|
-
validate
|
|
296
|
+
validate(ctx, subReport, subSchema as JsonSchemaInternal | boolean, json);
|
|
281
297
|
passed = subReport.errors.length === 0;
|
|
282
298
|
}
|
|
283
|
-
if (passed) {
|
|
284
|
-
|
|
285
|
-
return 'all';
|
|
286
|
-
}
|
|
299
|
+
if (passed && merge(recurse(subSchema as JsonSchemaInternal | boolean))) {
|
|
300
|
+
return 'all';
|
|
287
301
|
}
|
|
288
302
|
}
|
|
289
303
|
}
|
|
@@ -293,48 +307,40 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
293
307
|
let condPassed = getCachedValidationResult(report, currentSchema.if, json);
|
|
294
308
|
if (condPassed === undefined) {
|
|
295
309
|
const condReport = new Report(report);
|
|
296
|
-
validate
|
|
310
|
+
validate(ctx, condReport, currentSchema.if, json);
|
|
297
311
|
condPassed = condReport.errors.length === 0;
|
|
298
312
|
}
|
|
299
313
|
if (condPassed) {
|
|
300
|
-
if (merge(recurse(currentSchema.if
|
|
314
|
+
if (merge(recurse(currentSchema.if))) {
|
|
301
315
|
return 'all';
|
|
302
316
|
}
|
|
303
|
-
if (currentSchema.then !== undefined) {
|
|
304
|
-
|
|
305
|
-
return 'all';
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
} else {
|
|
309
|
-
if (currentSchema.else !== undefined) {
|
|
310
|
-
if (merge(recurse(currentSchema.else as JsonSchemaInternal | boolean))) {
|
|
311
|
-
return 'all';
|
|
312
|
-
}
|
|
317
|
+
if (currentSchema.then !== undefined && merge(recurse(currentSchema.then))) {
|
|
318
|
+
return 'all';
|
|
313
319
|
}
|
|
320
|
+
} else if (currentSchema.else !== undefined && merge(recurse(currentSchema.else))) {
|
|
321
|
+
return 'all';
|
|
314
322
|
}
|
|
315
323
|
}
|
|
316
324
|
|
|
317
325
|
// $ref resolved
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
326
|
+
if (
|
|
327
|
+
currentSchema.__$refResolved &&
|
|
328
|
+
currentSchema.__$refResolved !== currentSchema &&
|
|
329
|
+
merge(recurse(currentSchema.__$refResolved))
|
|
330
|
+
) {
|
|
331
|
+
return 'all';
|
|
322
332
|
}
|
|
323
333
|
|
|
324
334
|
// $recursiveRef
|
|
325
335
|
const recursiveTarget = resolveRecursiveRef(currentSchema, report.__$recursiveAnchorStack);
|
|
326
|
-
if (recursiveTarget && recursiveTarget !== currentSchema) {
|
|
327
|
-
|
|
328
|
-
return 'all';
|
|
329
|
-
}
|
|
336
|
+
if (recursiveTarget && recursiveTarget !== currentSchema && merge(recurse(recursiveTarget))) {
|
|
337
|
+
return 'all';
|
|
330
338
|
}
|
|
331
339
|
|
|
332
340
|
// $dynamicRef
|
|
333
341
|
const dynamicTarget = resolveDynamicRef(currentSchema, report.__$dynamicScopeStack);
|
|
334
|
-
if (dynamicTarget && dynamicTarget !== currentSchema) {
|
|
335
|
-
|
|
336
|
-
return 'all';
|
|
337
|
-
}
|
|
342
|
+
if (dynamicTarget && dynamicTarget !== currentSchema && merge(recurse(dynamicTarget as JsonSchemaInternal))) {
|
|
343
|
+
return 'all';
|
|
338
344
|
}
|
|
339
345
|
|
|
340
346
|
return evaluated;
|
|
@@ -344,7 +350,7 @@ function collectEvaluated(this: ZSchemaBase, args: CollectEvaluatedArgs): Set<nu
|
|
|
344
350
|
// unevaluatedItems
|
|
345
351
|
// ---------------------------------------------------------------------------
|
|
346
352
|
|
|
347
|
-
function unevaluatedItemsValidator(
|
|
353
|
+
function unevaluatedItemsValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
348
354
|
if (!Array.isArray(json)) {
|
|
349
355
|
return;
|
|
350
356
|
}
|
|
@@ -363,7 +369,7 @@ function unevaluatedItemsValidator(this: ZSchemaBase, report: Report, schema: Js
|
|
|
363
369
|
return;
|
|
364
370
|
}
|
|
365
371
|
|
|
366
|
-
const evaluatedItems = collectEvaluated
|
|
372
|
+
const evaluatedItems = collectEvaluated(ctx, {
|
|
367
373
|
report,
|
|
368
374
|
currentSchema: schema,
|
|
369
375
|
json,
|
|
@@ -391,9 +397,10 @@ function unevaluatedItemsValidator(this: ZSchemaBase, report: Report, schema: Js
|
|
|
391
397
|
report.addError('ARRAY_UNEVALUATED_ITEMS', undefined, undefined, schema, 'unevaluatedItems');
|
|
392
398
|
} else {
|
|
393
399
|
// unevaluatedItems as a schema — validate each unevaluated item against it
|
|
394
|
-
for (
|
|
400
|
+
for (let i = 0; i < unevaluatedIndices.length; i++) {
|
|
401
|
+
const idx = unevaluatedIndices[i];
|
|
395
402
|
const subReport = new Report(report);
|
|
396
|
-
validate
|
|
403
|
+
validate(ctx, subReport, unevalSchema, json[idx]);
|
|
397
404
|
if (subReport.errors.length > 0) {
|
|
398
405
|
report.addError('ARRAY_UNEVALUATED_ITEMS', undefined, undefined, schema, 'unevaluatedItems');
|
|
399
406
|
break;
|
|
@@ -406,7 +413,7 @@ function unevaluatedItemsValidator(this: ZSchemaBase, report: Report, schema: Js
|
|
|
406
413
|
// unevaluatedProperties
|
|
407
414
|
// ---------------------------------------------------------------------------
|
|
408
415
|
|
|
409
|
-
function unevaluatedPropertiesValidator(
|
|
416
|
+
function unevaluatedPropertiesValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
410
417
|
if (!isObject(json)) {
|
|
411
418
|
return;
|
|
412
419
|
}
|
|
@@ -427,7 +434,7 @@ function unevaluatedPropertiesValidator(this: ZSchemaBase, report: Report, schem
|
|
|
427
434
|
return;
|
|
428
435
|
}
|
|
429
436
|
|
|
430
|
-
const evaluatedProperties = collectEvaluated
|
|
437
|
+
const evaluatedProperties = collectEvaluated(ctx, {
|
|
431
438
|
report,
|
|
432
439
|
currentSchema: schema,
|
|
433
440
|
json,
|
|
@@ -440,7 +447,12 @@ function unevaluatedPropertiesValidator(this: ZSchemaBase, report: Report, schem
|
|
|
440
447
|
return;
|
|
441
448
|
}
|
|
442
449
|
|
|
443
|
-
const unevaluatedKeys =
|
|
450
|
+
const unevaluatedKeys: string[] = [];
|
|
451
|
+
for (let i = 0; i < allKeys.length; i++) {
|
|
452
|
+
if (!evaluatedProperties.has(allKeys[i])) {
|
|
453
|
+
unevaluatedKeys.push(allKeys[i]);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
444
456
|
|
|
445
457
|
if (unevaluatedKeys.length === 0) {
|
|
446
458
|
return;
|
|
@@ -456,9 +468,10 @@ function unevaluatedPropertiesValidator(this: ZSchemaBase, report: Report, schem
|
|
|
456
468
|
);
|
|
457
469
|
} else {
|
|
458
470
|
// unevaluatedProperties as a schema — validate each unevaluated key against it
|
|
459
|
-
for (
|
|
471
|
+
for (let i = 0; i < unevaluatedKeys.length; i++) {
|
|
472
|
+
const key = unevaluatedKeys[i];
|
|
460
473
|
const subReport = new Report(report);
|
|
461
|
-
validate
|
|
474
|
+
validate(ctx, subReport, unevalSchema, (json as Record<string, unknown>)[key]);
|
|
462
475
|
if (subReport.errors.length > 0) {
|
|
463
476
|
report.addError('OBJECT_UNEVALUATED_PROPERTIES', [key], undefined, schema, 'unevaluatedProperties');
|
|
464
477
|
}
|
|
@@ -480,23 +493,27 @@ function definitionsValidator() {
|
|
|
480
493
|
// JsonValidators — keyword dispatch table
|
|
481
494
|
// ---------------------------------------------------------------------------
|
|
482
495
|
|
|
496
|
+
const noopValidator: JsonValidatorFn = () => {
|
|
497
|
+
// intentional no-op: metadata keyword or handled elsewhere in the pipeline
|
|
498
|
+
};
|
|
499
|
+
|
|
483
500
|
export const JsonValidators: Record<keyof JsonSchemaAll, JsonValidatorFn> = {
|
|
484
501
|
// no-op validators (metadata / handled elsewhere)
|
|
485
|
-
id:
|
|
486
|
-
$id:
|
|
487
|
-
$ref:
|
|
488
|
-
$schema:
|
|
489
|
-
$dynamicAnchor:
|
|
490
|
-
$dynamicRef:
|
|
491
|
-
$anchor:
|
|
492
|
-
$defs:
|
|
493
|
-
$vocabulary:
|
|
494
|
-
$recursiveAnchor:
|
|
495
|
-
$recursiveRef:
|
|
496
|
-
examples:
|
|
497
|
-
title:
|
|
498
|
-
description:
|
|
499
|
-
default:
|
|
502
|
+
id: noopValidator,
|
|
503
|
+
$id: noopValidator,
|
|
504
|
+
$ref: noopValidator,
|
|
505
|
+
$schema: noopValidator,
|
|
506
|
+
$dynamicAnchor: noopValidator,
|
|
507
|
+
$dynamicRef: noopValidator,
|
|
508
|
+
$anchor: noopValidator,
|
|
509
|
+
$defs: noopValidator,
|
|
510
|
+
$vocabulary: noopValidator,
|
|
511
|
+
$recursiveAnchor: noopValidator,
|
|
512
|
+
$recursiveRef: noopValidator,
|
|
513
|
+
examples: noopValidator,
|
|
514
|
+
title: noopValidator,
|
|
515
|
+
description: noopValidator,
|
|
516
|
+
default: noopValidator,
|
|
500
517
|
|
|
501
518
|
// type validators
|
|
502
519
|
type: typeValidator,
|
|
@@ -560,25 +577,25 @@ export const JsonValidators: Record<keyof JsonSchemaAll, JsonValidatorFn> = {
|
|
|
560
577
|
// recurseArray
|
|
561
578
|
// ---------------------------------------------------------------------------
|
|
562
579
|
|
|
563
|
-
|
|
580
|
+
function recurseArray(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown[]) {
|
|
564
581
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.2
|
|
565
582
|
|
|
566
583
|
const schemaUri = typeof schema.$schema === 'string' ? schema.$schema : undefined;
|
|
567
584
|
const isDraft202012Schema =
|
|
568
585
|
schemaUri === 'https://json-schema.org/draft/2020-12/schema' ||
|
|
569
|
-
(!schemaUri &&
|
|
586
|
+
(!schemaUri && ctx.options.version === 'draft2020-12');
|
|
570
587
|
const prefixItems = isDraft202012Schema && Array.isArray(schema.prefixItems) ? schema.prefixItems : undefined;
|
|
571
588
|
|
|
572
589
|
if (prefixItems) {
|
|
573
590
|
for (let idx = 0; idx < json.length; idx++) {
|
|
574
591
|
if (idx < prefixItems.length) {
|
|
575
592
|
report.path.push(idx);
|
|
576
|
-
validate
|
|
593
|
+
validate(ctx, report, prefixItems[idx], json[idx]);
|
|
577
594
|
report.path.pop();
|
|
578
595
|
} else if (schema.items !== undefined && !Array.isArray(schema.items)) {
|
|
579
596
|
report.path.push(idx);
|
|
580
597
|
report.schemaPath.push('items');
|
|
581
|
-
validate
|
|
598
|
+
validate(ctx, report, schema.items, json[idx]);
|
|
582
599
|
report.schemaPath.pop();
|
|
583
600
|
report.path.pop();
|
|
584
601
|
}
|
|
@@ -595,15 +612,13 @@ const recurseArray = function (this: ZSchemaBase, report: Report, schema: JsonSc
|
|
|
595
612
|
// equal to doesn't make sense here
|
|
596
613
|
if (idx < schema.items.length) {
|
|
597
614
|
report.path.push(idx);
|
|
598
|
-
validate
|
|
615
|
+
validate(ctx, report, schema.items[idx], json[idx]);
|
|
599
616
|
report.path.pop();
|
|
600
|
-
} else {
|
|
617
|
+
} else if (typeof schema.additionalItems === 'object') {
|
|
601
618
|
// might be boolean, so check that it's an object
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
report.path.pop();
|
|
606
|
-
}
|
|
619
|
+
report.path.push(idx);
|
|
620
|
+
validate(ctx, report, schema.additionalItems, json[idx]);
|
|
621
|
+
report.path.pop();
|
|
607
622
|
}
|
|
608
623
|
}
|
|
609
624
|
} else if (typeof schema.items === 'object' || typeof schema.items === 'boolean') {
|
|
@@ -613,23 +628,23 @@ const recurseArray = function (this: ZSchemaBase, report: Report, schema: JsonSc
|
|
|
613
628
|
report.path.push(idx);
|
|
614
629
|
// Track schema path for array items validation
|
|
615
630
|
report.schemaPath.push('items');
|
|
616
|
-
validate
|
|
631
|
+
validate(ctx, report, schema.items, json[idx]);
|
|
617
632
|
report.schemaPath.pop();
|
|
618
633
|
report.path.pop();
|
|
619
634
|
}
|
|
620
635
|
}
|
|
621
|
-
}
|
|
636
|
+
}
|
|
622
637
|
|
|
623
638
|
// ---------------------------------------------------------------------------
|
|
624
639
|
// recurseObject
|
|
625
640
|
// ---------------------------------------------------------------------------
|
|
626
641
|
|
|
627
|
-
|
|
642
|
+
function recurseObject(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: Record<string, unknown>) {
|
|
628
643
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.3
|
|
629
644
|
|
|
630
645
|
// If "additionalProperties" is absent, it is considered present with an empty schema as a value.
|
|
631
646
|
// In addition, boolean value true is considered equivalent to an empty schema.
|
|
632
|
-
let additionalProperties = schema
|
|
647
|
+
let { additionalProperties } = schema;
|
|
633
648
|
if (additionalProperties === true || additionalProperties === undefined) {
|
|
634
649
|
additionalProperties = {};
|
|
635
650
|
}
|
|
@@ -643,22 +658,33 @@ const recurseObject = function (this: ZSchemaBase, report: Report, schema: JsonS
|
|
|
643
658
|
// m - The property name of the child.
|
|
644
659
|
const keys = Object.keys(json);
|
|
645
660
|
|
|
661
|
+
// Precompile patternProperties regexes once before the per-key loop
|
|
662
|
+
const ppCompiled: Array<{ key: string; re: RegExp }> = [];
|
|
663
|
+
for (let i = 0; i < pp.length; i++) {
|
|
664
|
+
const r = compileSchemaRegex(pp[i]);
|
|
665
|
+
if (r.ok) {
|
|
666
|
+
ppCompiled.push({ key: pp[i], re: r.value });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
646
670
|
for (const m of keys) {
|
|
647
671
|
const propertyValue = json[m];
|
|
648
672
|
|
|
673
|
+
// Hoist membership check: compute once per key
|
|
674
|
+
const isProp = p.includes(m);
|
|
675
|
+
|
|
649
676
|
// s - The set of schemas for the child instance.
|
|
650
677
|
const s = [];
|
|
651
678
|
|
|
652
679
|
// 1. If set "p" contains value "m", then the corresponding schema in "properties" is added to "s".
|
|
653
|
-
if (
|
|
680
|
+
if (isProp) {
|
|
654
681
|
s.push(schema.properties![m]);
|
|
655
682
|
}
|
|
656
683
|
|
|
657
684
|
// 2. For each regex in "pp", if it matches "m" successfully, the corresponding schema in "patternProperties" is added to "s".
|
|
658
|
-
for (
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
s.push(schema.patternProperties![regexString]);
|
|
685
|
+
for (let i = 0; i < ppCompiled.length; i++) {
|
|
686
|
+
if (ppCompiled[i].re.test(m)) {
|
|
687
|
+
s.push(schema.patternProperties![ppCompiled[i].key]);
|
|
662
688
|
}
|
|
663
689
|
}
|
|
664
690
|
|
|
@@ -675,7 +701,7 @@ const recurseObject = function (this: ZSchemaBase, report: Report, schema: JsonS
|
|
|
675
701
|
for (const schema_s of s) {
|
|
676
702
|
report.path.push(m);
|
|
677
703
|
// Track schema path for properties validation
|
|
678
|
-
if (
|
|
704
|
+
if (isProp) {
|
|
679
705
|
// This is a defined property
|
|
680
706
|
report.schemaPath.push('properties');
|
|
681
707
|
report.schemaPath.push(m);
|
|
@@ -683,22 +709,22 @@ const recurseObject = function (this: ZSchemaBase, report: Report, schema: JsonS
|
|
|
683
709
|
// This is additionalProperties or patternProperties
|
|
684
710
|
report.schemaPath.push('additionalProperties');
|
|
685
711
|
}
|
|
686
|
-
validate
|
|
712
|
+
validate(ctx, report, schema_s, propertyValue);
|
|
687
713
|
report.path.pop();
|
|
688
714
|
report.schemaPath.pop();
|
|
689
|
-
if (
|
|
715
|
+
if (isProp) {
|
|
690
716
|
report.schemaPath.pop(); // pop the property name for defined properties
|
|
691
717
|
}
|
|
692
718
|
}
|
|
693
719
|
}
|
|
694
|
-
}
|
|
720
|
+
}
|
|
695
721
|
|
|
696
722
|
// ---------------------------------------------------------------------------
|
|
697
723
|
// validate — main entry point
|
|
698
724
|
// ---------------------------------------------------------------------------
|
|
699
725
|
|
|
700
726
|
export function validate(
|
|
701
|
-
|
|
727
|
+
ctx: ZSchemaBase,
|
|
702
728
|
report: Report,
|
|
703
729
|
schema: boolean | JsonSchemaInternal,
|
|
704
730
|
json: unknown
|
|
@@ -738,9 +764,9 @@ export function validate(
|
|
|
738
764
|
let pushedRecursiveAnchor = false;
|
|
739
765
|
let pushedDynamicScope = false;
|
|
740
766
|
const schemaId = getId(schema);
|
|
741
|
-
const schemaResourceRoot =
|
|
767
|
+
const schemaResourceRoot = schema.__$resourceRoot;
|
|
742
768
|
const dynamicScopeEntry = schemaResourceRoot || (isRoot || typeof schemaId === 'string' ? schema : undefined);
|
|
743
|
-
if (dynamicScopeEntry && dynamicScopeStack
|
|
769
|
+
if (dynamicScopeEntry && dynamicScopeStack.at(-1) !== dynamicScopeEntry) {
|
|
744
770
|
dynamicScopeStack.push(dynamicScopeEntry);
|
|
745
771
|
pushedDynamicScope = true;
|
|
746
772
|
}
|
|
@@ -752,15 +778,18 @@ export function validate(
|
|
|
752
778
|
// follow schema.$ref keys
|
|
753
779
|
if (schema.$ref !== undefined) {
|
|
754
780
|
const applySiblingKeywordsWithRef =
|
|
755
|
-
|
|
781
|
+
ctx.options.version === 'draft2019-09' || ctx.options.version === 'draft2020-12';
|
|
756
782
|
|
|
757
783
|
if (applySiblingKeywordsWithRef) {
|
|
758
|
-
if (
|
|
759
|
-
|
|
784
|
+
if (schema.__$refResolved) {
|
|
785
|
+
validate(ctx, report, schema.__$refResolved, json);
|
|
760
786
|
} else {
|
|
761
|
-
|
|
787
|
+
report.addError('REF_UNRESOLVED', [schema.$ref], undefined, schema);
|
|
788
|
+
}
|
|
789
|
+
const refIdx = keys.indexOf('$ref');
|
|
790
|
+
if (refIdx !== -1) {
|
|
791
|
+
keys.splice(refIdx, 1);
|
|
762
792
|
}
|
|
763
|
-
keys = keys.filter((key) => key !== '$ref');
|
|
764
793
|
} else {
|
|
765
794
|
// avoid infinite loop with maxRefs
|
|
766
795
|
let maxRefs = 99;
|
|
@@ -787,48 +816,60 @@ export function validate(
|
|
|
787
816
|
// follow schema.$recursiveRef keys
|
|
788
817
|
if (schema.$recursiveRef !== undefined) {
|
|
789
818
|
const applySiblingKeywordsWithRecursiveRef =
|
|
790
|
-
|
|
819
|
+
ctx.options.version === 'draft2019-09' || ctx.options.version === 'draft2020-12';
|
|
791
820
|
|
|
792
821
|
if (applySiblingKeywordsWithRecursiveRef) {
|
|
793
822
|
const recursiveRefTarget = resolveRecursiveRef(schema, recursiveAnchorStack);
|
|
794
823
|
|
|
795
|
-
if (
|
|
796
|
-
|
|
824
|
+
if (recursiveRefTarget) {
|
|
825
|
+
validate(ctx, report, recursiveRefTarget, json);
|
|
797
826
|
} else {
|
|
798
|
-
|
|
827
|
+
report.addError('REF_UNRESOLVED', [schema.$recursiveRef], undefined, schema);
|
|
828
|
+
}
|
|
829
|
+
const recursiveRefIdx = keys.indexOf('$recursiveRef');
|
|
830
|
+
if (recursiveRefIdx !== -1) {
|
|
831
|
+
keys.splice(recursiveRefIdx, 1);
|
|
799
832
|
}
|
|
800
|
-
keys = keys.filter((key) => key !== '$recursiveRef');
|
|
801
833
|
}
|
|
802
834
|
}
|
|
803
835
|
|
|
804
836
|
// follow schema.$dynamicRef keys
|
|
805
837
|
if (schema.$dynamicRef !== undefined) {
|
|
806
|
-
const applySiblingKeywordsWithDynamicRef =
|
|
838
|
+
const applySiblingKeywordsWithDynamicRef = ctx.options.version === 'draft2020-12';
|
|
807
839
|
|
|
808
840
|
if (applySiblingKeywordsWithDynamicRef) {
|
|
809
841
|
const dynamicRefTarget = resolveDynamicRef(schema, dynamicScopeStack);
|
|
810
842
|
|
|
811
|
-
if (
|
|
843
|
+
if (dynamicRefTarget === undefined) {
|
|
812
844
|
report.addError('REF_UNRESOLVED', [schema.$dynamicRef], undefined, schema);
|
|
813
845
|
} else {
|
|
814
|
-
validate
|
|
846
|
+
validate(ctx, report, dynamicRefTarget, json);
|
|
847
|
+
}
|
|
848
|
+
const dynamicRefIdx = keys.indexOf('$dynamicRef');
|
|
849
|
+
if (dynamicRefIdx !== -1) {
|
|
850
|
+
keys.splice(dynamicRefIdx, 1);
|
|
815
851
|
}
|
|
816
|
-
keys = keys.filter((key) => key !== '$dynamicRef');
|
|
817
852
|
}
|
|
818
853
|
}
|
|
819
854
|
|
|
820
|
-
const validationVocabularyEnabled = isValidationVocabularyEnabled(schema, report,
|
|
855
|
+
const validationVocabularyEnabled = isValidationVocabularyEnabled(schema, report, ctx.options.version);
|
|
821
856
|
if (!validationVocabularyEnabled) {
|
|
822
|
-
|
|
857
|
+
let wi = 0;
|
|
858
|
+
for (let ri = 0; ri < keys.length; ri++) {
|
|
859
|
+
if (!VALIDATION_VOCAB_KEYWORDS.has(keys[ri])) {
|
|
860
|
+
keys[wi++] = keys[ri];
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
keys.length = wi;
|
|
823
864
|
}
|
|
824
865
|
|
|
825
866
|
// type checking first
|
|
826
867
|
if (validationVocabularyEnabled && schema.type) {
|
|
827
868
|
keys.splice(keys.indexOf('type'), 1);
|
|
828
869
|
report.schemaPath.push('type');
|
|
829
|
-
JsonValidators.type
|
|
870
|
+
JsonValidators.type(ctx, report, schema, json);
|
|
830
871
|
report.schemaPath.pop();
|
|
831
|
-
if (report.errors.length &&
|
|
872
|
+
if (report.errors.length && ctx.options.breakOnFirstError) {
|
|
832
873
|
if (pushedRecursiveAnchor) {
|
|
833
874
|
recursiveAnchorStack.pop();
|
|
834
875
|
}
|
|
@@ -843,43 +884,45 @@ export function validate(
|
|
|
843
884
|
// Defer unevaluatedItems/unevaluatedProperties to run after other validators,
|
|
844
885
|
// so combinator validation results are cached and available for annotation collection
|
|
845
886
|
const deferredUnevaluatedKeys: Array<keyof JsonSchemaAll> = [];
|
|
846
|
-
for (
|
|
887
|
+
for (let i = 0; i < keys.length; i++) {
|
|
888
|
+
const key = keys[i];
|
|
847
889
|
if (key === 'unevaluatedItems' || key === 'unevaluatedProperties') {
|
|
848
890
|
deferredUnevaluatedKeys.push(key);
|
|
849
891
|
continue;
|
|
850
892
|
}
|
|
851
893
|
const validator = JsonValidators[key];
|
|
852
894
|
if (validator) {
|
|
853
|
-
validator
|
|
854
|
-
if (report.errors.length &&
|
|
895
|
+
validator(ctx, report, schema, json);
|
|
896
|
+
if (report.errors.length && ctx.options.breakOnFirstError) {
|
|
855
897
|
break;
|
|
856
898
|
}
|
|
857
899
|
}
|
|
858
900
|
}
|
|
859
901
|
|
|
860
902
|
// Run unevaluated* validators after all others have cached their combinator results
|
|
861
|
-
if (deferredUnevaluatedKeys.length > 0 && !(report.errors.length > 0 &&
|
|
862
|
-
for (
|
|
903
|
+
if (deferredUnevaluatedKeys.length > 0 && !(report.errors.length > 0 && ctx.options.breakOnFirstError)) {
|
|
904
|
+
for (let i = 0; i < deferredUnevaluatedKeys.length; i++) {
|
|
905
|
+
const key = deferredUnevaluatedKeys[i];
|
|
863
906
|
const validator = JsonValidators[key];
|
|
864
907
|
if (validator) {
|
|
865
|
-
validator
|
|
866
|
-
if (report.errors.length &&
|
|
908
|
+
validator(ctx, report, schema, json);
|
|
909
|
+
if (report.errors.length && ctx.options.breakOnFirstError) {
|
|
867
910
|
break;
|
|
868
911
|
}
|
|
869
912
|
}
|
|
870
913
|
}
|
|
871
914
|
}
|
|
872
915
|
|
|
873
|
-
if (report.errors.length === 0 ||
|
|
916
|
+
if (report.errors.length === 0 || ctx.options.breakOnFirstError === false) {
|
|
874
917
|
if (Array.isArray(json)) {
|
|
875
|
-
recurseArray
|
|
918
|
+
recurseArray(ctx, report, schema, json);
|
|
876
919
|
} else if (isObject(json)) {
|
|
877
|
-
recurseObject
|
|
920
|
+
recurseObject(ctx, report, schema, json);
|
|
878
921
|
}
|
|
879
922
|
}
|
|
880
923
|
|
|
881
|
-
if (typeof
|
|
882
|
-
|
|
924
|
+
if (typeof ctx.options.customValidator === 'function') {
|
|
925
|
+
ctx.options.customValidator.call(ctx, report, schema, json);
|
|
883
926
|
}
|
|
884
927
|
|
|
885
928
|
if (pushedRecursiveAnchor) {
|