@jarenjs/validate 0.8.3 → 0.9.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 (48) hide show
  1. package/ARCHITECTURE.md +1067 -0
  2. package/LICENSE +21 -0
  3. package/README.md +339 -2
  4. package/dist/types/array.d.ts +2 -0
  5. package/dist/types/bigint.d.ts +1 -0
  6. package/dist/types/combine.d.ts +1 -0
  7. package/dist/types/condition.d.ts +1 -0
  8. package/dist/types/content.d.ts +3 -0
  9. package/dist/types/data.d.ts +7 -0
  10. package/dist/types/dollar-data.d.ts +20 -0
  11. package/dist/types/dynamic-ref.d.ts +44 -0
  12. package/dist/types/enum.d.ts +1 -0
  13. package/dist/types/format.d.ts +21 -0
  14. package/dist/types/index.d.ts +874 -0
  15. package/dist/types/number.d.ts +1 -0
  16. package/dist/types/object.d.ts +3 -0
  17. package/dist/types/query-keyword.d.ts +19 -0
  18. package/dist/types/query.d.ts +29 -0
  19. package/dist/types/schema.d.ts +1 -0
  20. package/dist/types/string.d.ts +1 -0
  21. package/dist/types/tools.d.ts +51 -0
  22. package/dist/types/traverse.d.ts +32 -0
  23. package/dist/types/unevaluated.d.ts +12 -0
  24. package/package.json +32 -7
  25. package/src/array.js +565 -0
  26. package/src/bigint.js +97 -0
  27. package/src/combine.js +226 -0
  28. package/src/condition.js +109 -0
  29. package/src/content.js +83 -0
  30. package/src/data.js +477 -0
  31. package/src/dollar-data.js +629 -0
  32. package/src/dynamic-ref.js +121 -0
  33. package/src/enum.js +148 -0
  34. package/src/format.js +66 -0
  35. package/src/index.js +1854 -0
  36. package/src/number.js +159 -0
  37. package/src/object.js +755 -0
  38. package/src/query-keyword.js +99 -0
  39. package/src/query.js +59 -0
  40. package/src/schema.js +645 -0
  41. package/src/string.js +152 -0
  42. package/src/tools.js +205 -0
  43. package/src/traverse.js +433 -0
  44. package/src/unevaluated.js +151 -0
  45. package/dist/index.js +0 -1802
  46. package/dist/index.js.map +0 -7
  47. package/dist/index.min.js +0 -2
  48. package/dist/index.min.js.map +0 -7
package/src/object.js ADDED
@@ -0,0 +1,755 @@
1
+ //@ts-check
2
+
3
+ import {
4
+ // isObjectClass, // to specific
5
+ isObjectType, // slowerc
6
+ isArrayClass,
7
+ getObjectType,
8
+ } from '@jarenjs/core';
9
+
10
+ import {
11
+ getIntishType,
12
+ } from '@jarenjs/core/number';
13
+
14
+ import {
15
+ createRegExp,
16
+ } from '@jarenjs/core/string';
17
+
18
+ import {
19
+ trueThat,
20
+ } from '@jarenjs/core/function';
21
+
22
+ import {
23
+ isBoolOrObjectClass,
24
+ getBoolOrObjectClass,
25
+ getArrayClassMinItems,
26
+ isOfSchemaType,
27
+ ValidationResult,
28
+ } from './tools.js';
29
+
30
+ import {
31
+ includesAll,
32
+ } from '@jarenjs/core/array';
33
+
34
+ //#region Primitives
35
+ function compileMinProperties(schemaObj, jsonSchema) {
36
+ const min = getIntishType(jsonSchema.minProperties) || 0;
37
+ if (min < 1) return undefined;
38
+
39
+ const addError = schemaObj.createErrorHandler(min, 'minProperties');
40
+ return function validateMinProperties(len = 0, dataPath = '') {
41
+ return len >= min || addError(len, dataPath);
42
+ };
43
+ }
44
+
45
+ function compileMaxProperties(schemaObj, jsonSchema) {
46
+ const max = getIntishType(jsonSchema.maxProperties);
47
+ if (max == null || max < 0) return undefined;
48
+ const min = getIntishType(jsonSchema.minProperties) || 0;
49
+ if (max < min) throw new Error('maxProperties must be greater then minProperties');
50
+
51
+ const addError = schemaObj.createErrorHandler(max, 'maxProperties');
52
+ return function validateMaxProperties(len = 0, dataPath = '') {
53
+ return len <= max || addError(len, dataPath);
54
+ };
55
+ }
56
+
57
+ function compileRequiredProperties(schemaObj, jsonSchema) {
58
+ const required = getArrayClassMinItems(jsonSchema.required, 1);
59
+ if (required == null) return undefined;
60
+
61
+ const rlength = required.length;
62
+ /** @type {function(string, any, string):boolean} */
63
+ // Use array key to get keyed error handler: addKeyedError(dataKey, data, ...meta)
64
+ const addError = schemaObj.createErrorHandler(required, ['required']);
65
+ return function validateRequiredProperties(data = {}, dataKeys = [], dataPath = '') {
66
+ if (!(dataKeys.length > 0))
67
+ return false;
68
+
69
+ let valid = true;
70
+ for (let i = 0; i < rlength; ++i) {
71
+ const key = required[i];
72
+ const idx = dataKeys.indexOf(key);
73
+ if (idx === -1)
74
+ valid &&= addError(key, data, dataPath);
75
+ }
76
+ return valid;
77
+ };
78
+ }
79
+ //#endregion
80
+
81
+ //#region Constraints
82
+ function compilePropertyNames(schemaObj, jsonSchema) {
83
+ const propNames = getBoolOrObjectClass(jsonSchema.propertyNames);
84
+ if (propNames == null) return undefined;
85
+
86
+ const propertyNamesValidator = schemaObj.createValidator(propNames, 'propertyNames');
87
+ return function validatePropertyNames(dataKey) {
88
+ return propertyNamesValidator(dataKey);
89
+ }
90
+ }
91
+
92
+ function buildPropertyValidators(schemaObj, jsonSchema) {
93
+ const properties = getObjectType(jsonSchema.properties);
94
+ if (properties == null) return undefined;
95
+
96
+ // Use Object.getOwnPropertyNames to handle __proto__ correctly
97
+ // Object.keys() doesn't return __proto__ when defined via { __proto__: value }
98
+ const keys = Object.getOwnPropertyNames(properties);
99
+ if (keys.length === 0) return undefined;
100
+
101
+ const validators = new Map();
102
+ for (let i = 0; i < keys.length; i++) {
103
+ const key = keys[i];
104
+ const schemas = properties[key];
105
+ const validator = schemaObj.createValidator(schemas, 'properties', key);
106
+ if (validator != null)
107
+ validators.set(key, validator);
108
+ }
109
+ if (validators.size === 0)
110
+ return undefined;
111
+
112
+ return validators;
113
+ }
114
+
115
+ function compileProperties(schemaObj, jsonSchema) {
116
+ const validators = buildPropertyValidators(schemaObj, jsonSchema);
117
+ if (validators == null) return undefined;
118
+
119
+ const root = schemaObj.root;
120
+ const track = root.usesUnevaluated;
121
+
122
+ return function validatePropertyItem(data, dataPath, dataRoot, dataKey) {
123
+ const result = new ValidationResult();
124
+ const validator = validators.get(dataKey);
125
+ if (validator == null)
126
+ return result;
127
+ else {
128
+ const valid = validator(data[dataKey], dataPath, dataRoot, dataKey);
129
+ if (track && valid === true) root.evalLog.add(data, dataKey);
130
+ return result.addMatch(valid);
131
+ }
132
+ };
133
+ }
134
+
135
+ function buildPatternValidators(schemaObj, jsonSchema) {
136
+ const entries = getObjectType(jsonSchema.patternProperties);
137
+ if (entries == null) return undefined;
138
+
139
+ // Use Object.getOwnPropertyNames to handle __proto__ correctly
140
+ const entryKeys = Object.getOwnPropertyNames(entries);
141
+ if (entryKeys.length === 0) return undefined;
142
+
143
+ const list = [];
144
+ for (let i = 0; i < entryKeys.length; ++i) {
145
+ const key = entryKeys[i];
146
+ const pattern = createRegExp(key);
147
+ if (pattern == null) continue;
148
+
149
+ const validator = schemaObj.createValidator(entries[key], 'patternProperties', key);
150
+ if (validator != null)
151
+ list.push({ pattern, validator });
152
+ }
153
+
154
+ if (list.length === 0) return undefined;
155
+
156
+ return list;
157
+ }
158
+
159
+ function compilePatternProperties(schemaObj, jsonSchema) {
160
+ const list = buildPatternValidators(schemaObj, jsonSchema);
161
+ if (list == null) return undefined;
162
+
163
+ const root = schemaObj.root;
164
+ const track = root.usesUnevaluated;
165
+
166
+ return function validatePatternPropertiesItem(data, dataPath, dataRoot, dataKey) {
167
+ const result = new ValidationResult();
168
+ for (let i = 0; i < list.length; ++i) {
169
+ const { pattern, validator } = list[i];
170
+ if (pattern.test(dataKey)) {
171
+ const valid = validator(data[dataKey], dataPath, dataRoot, dataKey);
172
+ if (track && valid === true) root.evalLog.add(data, dataKey);
173
+ result.addMatch(valid);
174
+ }
175
+ }
176
+ return result;
177
+ };
178
+ }
179
+
180
+ function compileAdditionalProperties(schemaObj, jsonSchema) {
181
+ const additional = getBoolOrObjectClass(jsonSchema.additionalProperties);
182
+ if (additional == null) return undefined;
183
+
184
+ if (additional === false) {
185
+ const addError = schemaObj.createErrorHandler(false, ['additionalProperties']);
186
+
187
+ return function validateNoAdditionalProperties(data, dataPath, dataRoot, dataKey) {
188
+ return addError(dataKey, data);
189
+ };
190
+ }
191
+
192
+ const root = schemaObj.root;
193
+ const track = root.usesUnevaluated;
194
+ const validator = schemaObj.createValidator(additional, 'additionalProperties');
195
+
196
+ return function validateAdditionalPropertyItem(data, dataPath, dataRoot, dataKey) {
197
+ const valid = validator(data[dataKey], dataPath, dataRoot, dataKey);
198
+ if (track && valid === true) root.evalLog.add(data, dataKey);
199
+ return valid;
200
+ };
201
+ }
202
+ //#endregion
203
+
204
+ //#region Dependencies
205
+ function compileDependentRequired(schemaObj, jsonSchema) {
206
+ // TODO: before we go to release remove this check, since it doesn't help anyone.
207
+ // dependentRequired only exists since draft 2019-09; a document that
208
+ // declares an older draft via $schema treats it as an unknown keyword.
209
+ if (schemaObj.declaredDraft != null && schemaObj.declaredDraft < 2019)
210
+ return undefined;
211
+
212
+ const dependentRequired = getObjectType(jsonSchema.dependentRequired);
213
+ if (dependentRequired == null)
214
+ return undefined;
215
+
216
+ if (Object.keys(dependentRequired).length === 0)
217
+ return undefined;
218
+
219
+ const addError = schemaObj.createErrorHandler(false, 'dependentRequired');
220
+
221
+ return function validateDependentRequiredItem(data, dataPath, dataRoot, dataKey) {
222
+ if (dataKey in dependentRequired) {
223
+ const required = dependentRequired[dataKey];
224
+ return includesAll(Object.keys(data), required)
225
+ || addError(data, dataKey, dataPath);
226
+ }
227
+ return true;
228
+ };
229
+ }
230
+
231
+ function compileDependentSchemas(schemaObj, jsonSchema) {
232
+ // TODO: before we go to release remove this check, since it doesn't help anyone.
233
+ // dependentSchemas only exists since draft 2019-09; a document that
234
+ // declares an older draft via $schema treats it as an unknown keyword.
235
+ if (schemaObj.declaredDraft != null && schemaObj.declaredDraft < 2019)
236
+ return undefined;
237
+
238
+ const dependentSchemas = getObjectType(jsonSchema.dependentSchemas);
239
+ if (dependentSchemas == null) return undefined;
240
+
241
+ const validators = new Map();
242
+ for (const key in dependentSchemas) {
243
+ if (Object.prototype.hasOwnProperty.call(dependentSchemas, key)) {
244
+ const schema = dependentSchemas[key];
245
+ if (!isBoolOrObjectClass(schema) && !schemaObj.options.skipErrors)
246
+ throw new Error(`Expected Schema at '${schemaObj.path}/${key}'`);
247
+
248
+ const validator = schemaObj.createValidator(schema, 'dependentSchemas', key);
249
+ if (validator != null)
250
+ validators.set(key, validator);
251
+ else
252
+ throw new Error(`Expected Validator at '${schemaObj.path}/${key}'`);
253
+ }
254
+ }
255
+
256
+ if (validators.size === 0)
257
+ return undefined;
258
+
259
+ return function validateDependentSchemasItem(data, dataPath, dataRoot, dataKey) {
260
+ if (validators.has(dataKey)) {
261
+ const validator = validators.get(dataKey);
262
+ return validator(data, dataPath, dataRoot, dataKey);
263
+ }
264
+ return true;
265
+ };
266
+ }
267
+
268
+ function compileDependencies(schemaObj, jsonSchema) {
269
+ const dependencies = getObjectType(jsonSchema.dependencies);
270
+ if (dependencies == null)
271
+ return undefined;
272
+
273
+ // Collect dependency entries into arrays for faster access
274
+ const depKeys = Object.keys(dependencies);
275
+ if (depKeys.length === 0)
276
+ return undefined;
277
+
278
+ // Separate schema dependencies from required dependencies for optimization
279
+ const schemaDeps = [];
280
+ const requiredDeps = [];
281
+
282
+ for (let i = 0; i < depKeys.length; i++) {
283
+ const key = depKeys[i];
284
+ const right = dependencies[key];
285
+ if (isBoolOrObjectClass(right)) {
286
+ const validator = schemaObj.createValidator(right, 'dependencies', key);
287
+ if (validator != null)
288
+ schemaDeps.push({ key, validator });
289
+ else if (!schemaObj.options.skipErrors)
290
+ throw new Error(`Expected Validator at '${schemaObj.path}/${key}'`);
291
+ }
292
+ else if (isArrayClass(right)) {
293
+ const addError = schemaObj.createErrorHandler(right, ['dependencies', key]);
294
+ requiredDeps.push({ key, required: right, addError });
295
+ }
296
+ else if (!schemaObj.options.skipErrors)
297
+ throw new Error(`Expected Schema or Array at '${schemaObj.path}/${key}'`);
298
+ }
299
+
300
+ if (schemaDeps.length === 0 && requiredDeps.length === 0)
301
+ return undefined;
302
+
303
+ // Single schema dependency - most common case
304
+ if (schemaDeps.length === 1 && requiredDeps.length === 0) {
305
+ const { key, validator } = schemaDeps[0];
306
+ return function validateSingleSchemaDep(data, dataPath, dataRoot, dataKey) {
307
+ if (dataKey === key) {
308
+ return validator(data, dataPath, dataRoot, dataKey);
309
+ }
310
+ return true;
311
+ };
312
+ }
313
+
314
+ // Single required dependency - common case
315
+ if (requiredDeps.length === 1 && schemaDeps.length === 0) {
316
+ const { key, required, addError } = requiredDeps[0];
317
+ const rlen = required.length;
318
+ return function validateSingleRequiredDep(data, dataPath, dataRoot, dataKey) {
319
+ if (dataKey === key) {
320
+ const dataKeys = Object.keys(data);
321
+ for (let i = 0; i < rlen; i++) {
322
+ if (!dataKeys.includes(required[i])) {
323
+ return addError(data, dataKey, dataPath);
324
+ }
325
+ }
326
+ }
327
+ return true;
328
+ };
329
+ }
330
+
331
+ // Multiple dependencies - generic case
332
+ // Create lookup maps for faster access
333
+ const schemaDepMap = new Map();
334
+ for (let i = 0; i < schemaDeps.length; i++) {
335
+ schemaDepMap.set(schemaDeps[i].key, schemaDeps[i].validator);
336
+ }
337
+ const requiredDepMap = new Map();
338
+ for (let i = 0; i < requiredDeps.length; i++) {
339
+ requiredDepMap.set(requiredDeps[i].key, requiredDeps[i]);
340
+ }
341
+
342
+ return function validateDependenciesItem(data, dataPath, dataRoot, dataKey) {
343
+ // Check schema dependencies first
344
+ const schemaValidator = schemaDepMap.get(dataKey);
345
+ if (schemaValidator != null) {
346
+ return schemaValidator(data, dataPath, dataRoot, dataKey);
347
+ }
348
+ // Check required dependencies
349
+ const reqDep = requiredDepMap.get(dataKey);
350
+ if (reqDep != null) {
351
+ const { required, addError } = reqDep;
352
+ return includesAll(Object.keys(data), required)
353
+ || addError(data, dataKey, dataPath);
354
+ }
355
+ return true;
356
+ };
357
+ }
358
+ //#endregion
359
+
360
+ //#region Main
361
+ export function compileObjectPrimitives(schemaObj, jsonSchema) {
362
+ // TODO: figure out if we need such a check for real!
363
+ // minProperties/maxProperties/required belong to the validation
364
+ // vocabulary; assert nothing when the metaschema disables it.
365
+ if (schemaObj.options.vocabValidation === false)
366
+ return undefined;
367
+
368
+ const minProperties = compileMinProperties(schemaObj, jsonSchema);
369
+ const maxProperties = compileMaxProperties(schemaObj, jsonSchema);
370
+ const requiredProperties = compileRequiredProperties(schemaObj, jsonSchema);
371
+
372
+ if ((minProperties
373
+ || maxProperties
374
+ || requiredProperties) == null)
375
+ return undefined;
376
+
377
+ // Inline the validation to reduce function call overhead
378
+ const min = getIntishType(jsonSchema.minProperties) || 0;
379
+ const max = getIntishType(jsonSchema.maxProperties);
380
+ const required = getArrayClassMinItems(jsonSchema.required, 1);
381
+
382
+ const hasMin = min > 0;
383
+ const hasMax = max != null && max >= 0;
384
+ const hasRequired = required != null && required.length > 0;
385
+
386
+ // Pre-bind error handlers outside the returned function
387
+ if (hasMin && !hasMax && !hasRequired) {
388
+ const addError = schemaObj.createErrorHandler(min, 'minProperties');
389
+ return function validateMinPropertiesOnly(data, dataPath, dataRoot, dataKeys) {
390
+ const len = dataKeys ? dataKeys.length : Object.keys(data).length;
391
+ return len >= min || addError(len, dataPath);
392
+ };
393
+ }
394
+
395
+ if (!hasMin && hasMax && !hasRequired) {
396
+ const addError = schemaObj.createErrorHandler(max, 'maxProperties');
397
+ return function validateMaxPropertiesOnly(data, dataPath, dataRoot, dataKeys) {
398
+ const len = dataKeys ? dataKeys.length : Object.keys(data).length;
399
+ return len <= max || addError(len, dataPath);
400
+ };
401
+ }
402
+
403
+ if (hasMin && hasMax && !hasRequired) {
404
+ const addMinError = schemaObj.createErrorHandler(min, 'minProperties');
405
+ const addMaxError = schemaObj.createErrorHandler(max, 'maxProperties');
406
+ return function validateMinMaxProperties(data, dataPath, dataRoot, dataKeys) {
407
+ const len = dataKeys ? dataKeys.length : Object.keys(data).length;
408
+ return (len >= min || addMinError(len, dataPath))
409
+ && (len <= max || addMaxError(len, dataPath));
410
+ };
411
+ }
412
+
413
+ // Specialized paths for required properties
414
+ if (!hasMin && !hasMax && hasRequired) {
415
+ const rlength = required.length;
416
+ const addError = schemaObj.createErrorHandler(required, ['required']);
417
+ return function validateRequiredOnly(data, dataPath, dataRoot, dataKeys) {
418
+ // Required properties only apply to objects, not arrays or other types
419
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
420
+ return true;
421
+ }
422
+ const keys = dataKeys || Object.keys(data);
423
+ let valid = true;
424
+ for (let i = 0; i < rlength; ++i) {
425
+ const key = required[i];
426
+ if (keys.indexOf(key) === -1)
427
+ valid &&= addError(key, data, dataPath);
428
+ }
429
+ return valid;
430
+ };
431
+ }
432
+
433
+ if (hasMin && !hasMax && hasRequired) {
434
+ const addMinError = schemaObj.createErrorHandler(min, 'minProperties');
435
+ const rlength = required.length;
436
+ const addReqError = schemaObj.createErrorHandler(required, ['required']);
437
+ return function validateMinAndRequired(data, dataPath, dataRoot, dataKeys) {
438
+ // Required/minProperties only apply to objects, not arrays or other types
439
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
440
+ return true;
441
+ }
442
+ const keys = dataKeys || Object.keys(data);
443
+ const len = keys.length;
444
+ if (len < min && !addMinError(len, dataPath))
445
+ return false;
446
+ let valid = true;
447
+ for (let i = 0; i < rlength; ++i) {
448
+ const key = required[i];
449
+ if (keys.indexOf(key) === -1)
450
+ valid &&= addReqError(key, data, dataPath);
451
+ }
452
+ return valid;
453
+ };
454
+ }
455
+
456
+ if (!hasMin && hasMax && hasRequired) {
457
+ const addMaxError = schemaObj.createErrorHandler(max, 'maxProperties');
458
+ const rlength = required.length;
459
+ const addReqError = schemaObj.createErrorHandler(required, ['required']);
460
+ return function validateMaxAndRequired(data, dataPath, dataRoot, dataKeys) {
461
+ // Required/maxProperties only apply to objects, not arrays or other types
462
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
463
+ return true;
464
+ }
465
+ const keys = dataKeys || Object.keys(data);
466
+ const len = keys.length;
467
+ if (len > max && !addMaxError(len, dataPath))
468
+ return false;
469
+ let valid = true;
470
+ for (let i = 0; i < rlength; ++i) {
471
+ const key = required[i];
472
+ if (keys.indexOf(key) === -1)
473
+ valid &&= addReqError(key, data, dataPath);
474
+ }
475
+ return valid;
476
+ };
477
+ }
478
+
479
+ // Generic case with all checks
480
+ const isMinProperties = minProperties || trueThat;
481
+ const isMaxProperties = maxProperties || trueThat;
482
+ const hasRequiredProperties = requiredProperties || trueThat;
483
+
484
+ return function validateObjectPrimitives(data, dataPath, dataRoot, dataKeys) {
485
+ const keys = dataKeys || Object.keys(data);
486
+ const len = keys.length;
487
+ return isMinProperties(len, dataPath)
488
+ && isMaxProperties(len, dataPath)
489
+ && hasRequiredProperties(data, keys, dataPath);
490
+ };
491
+ }
492
+
493
+ function compileObjectProperty(schemaObj, jsonSchema) {
494
+ const namesValidator = compilePropertyNames(schemaObj, jsonSchema);
495
+ const propertyValidator = compileProperties(schemaObj, jsonSchema);
496
+ const patternValidator = compilePatternProperties(schemaObj, jsonSchema);
497
+ const additionalValidator = compileAdditionalProperties(schemaObj, jsonSchema);
498
+ const depSchemasValidator = compileDependentSchemas(schemaObj, jsonSchema);
499
+ const dependencyValidator = compileDependencies(schemaObj, jsonSchema);
500
+ const depRequiredValidator = compileDependentRequired(schemaObj, jsonSchema);
501
+
502
+ if ((patternValidator
503
+ || namesValidator
504
+ || propertyValidator
505
+ || depRequiredValidator
506
+ || depSchemasValidator
507
+ || dependencyValidator
508
+ || additionalValidator) == null)
509
+ return undefined;
510
+
511
+ const validateName = namesValidator || trueThat;
512
+ const validateProperty = propertyValidator || ValidationResult.undefThat;
513
+ const validatePattern = patternValidator || ValidationResult.undefThat;
514
+
515
+ const validateDepRequired = depRequiredValidator || trueThat;
516
+ const validateDepSchemas = depSchemasValidator || trueThat;
517
+ const validateDependency = dependencyValidator || trueThat;
518
+
519
+ return function validateObjectProperty(data, dataPath, dataRoot, dataKey) {
520
+ const result = new ValidationResult();
521
+ // Build the child dataPath by appending the property key
522
+ const newPath = dataPath ? `${dataPath}/${dataKey}` : `/${dataKey}`;
523
+
524
+ result.addValid(validateName(dataKey))
525
+ .addResult(validateProperty(data, newPath, dataRoot, dataKey))
526
+ .addResult(validatePattern(data, newPath, dataRoot, dataKey))
527
+ .addValid(validateDepRequired(data, newPath, dataRoot, dataKey))
528
+ .addValid(validateDepSchemas(data, newPath, dataRoot, dataKey))
529
+ .addValid(validateDependency(data, newPath, dataRoot, dataKey));
530
+
531
+ if (additionalValidator)
532
+ return !result.match
533
+ ? result.addMatch(additionalValidator(data, newPath, dataRoot, dataKey))
534
+ : result;
535
+
536
+ return result;
537
+ };
538
+ }
539
+
540
+ /**
541
+ * Fused per-key validation loop for the default skipErrors mode.
542
+ * Avoids the per-key ValidationResult allocations and eagerly built child
543
+ * paths of the generic path; returns on the first failing property.
544
+ * Child paths are still built (lazily) because $data validators resolve
545
+ * relative JSON pointers against them at validation time.
546
+ */
547
+ function compileObjectChildrenFast(schemaObj, jsonSchema) {
548
+ const namesValidator = compilePropertyNames(schemaObj, jsonSchema);
549
+ const propsMap = buildPropertyValidators(schemaObj, jsonSchema) || null;
550
+ const patternList = buildPatternValidators(schemaObj, jsonSchema) || null;
551
+ const depSchemasValidator = compileDependentSchemas(schemaObj, jsonSchema) || null;
552
+ const dependencyValidator = compileDependencies(schemaObj, jsonSchema) || null;
553
+ const depRequiredValidator = compileDependentRequired(schemaObj, jsonSchema) || null;
554
+
555
+ const root = schemaObj.root;
556
+ const track = root.usesUnevaluated;
557
+
558
+ const additional = getBoolOrObjectClass(jsonSchema.additionalProperties);
559
+ const additionalFalse = additional === false;
560
+ const additionalValidator = (additional != null && additional !== false && additional !== true)
561
+ ? schemaObj.createValidator(additional, 'additionalProperties')
562
+ : null;
563
+ // additionalProperties: true evaluates every leftover property, which
564
+ // matters when annotations are tracked for unevaluatedProperties.
565
+ const additionalTrue = additional === true && track;
566
+ const hasAdditional = additionalFalse || additionalTrue || additionalValidator != null;
567
+
568
+ if (namesValidator == null
569
+ && propsMap == null
570
+ && patternList == null
571
+ && depSchemasValidator == null
572
+ && dependencyValidator == null
573
+ && depRequiredValidator == null
574
+ && !hasAdditional)
575
+ return undefined;
576
+
577
+ const validateName = namesValidator || null;
578
+
579
+ // Child paths are only consumed by $data relative-pointer resolution
580
+ // in skipErrors mode; skip the per-property string concat otherwise.
581
+ const extendPaths = root.usesDollarData;
582
+
583
+ return function validateObjectChildrenFast(data, dataPath, dataRoot, dataKeys) {
584
+ const len = dataKeys.length;
585
+ for (let i = 0; i < len; ++i) {
586
+ const dataKey = dataKeys[i];
587
+ if (validateName != null && validateName(dataKey) === false)
588
+ return false;
589
+
590
+ let matched = false;
591
+ let childPath = null;
592
+
593
+ if (propsMap != null) {
594
+ const propValidator = propsMap.get(dataKey);
595
+ if (propValidator != null) {
596
+ matched = true;
597
+ childPath = extendPaths ? dataPath + '/' + dataKey : dataPath;
598
+ if (propValidator(data[dataKey], childPath, dataRoot, dataKey) === false)
599
+ return false;
600
+ if (track) root.evalLog.add(data, dataKey);
601
+ }
602
+ }
603
+
604
+ if (patternList != null) {
605
+ for (let j = 0; j < patternList.length; ++j) {
606
+ const entry = patternList[j];
607
+ if (entry.pattern.test(dataKey)) {
608
+ matched = true;
609
+ if (childPath === null) childPath = extendPaths ? dataPath + '/' + dataKey : dataPath;
610
+ if (entry.validator(data[dataKey], childPath, dataRoot, dataKey) === false)
611
+ return false;
612
+ if (track) root.evalLog.add(data, dataKey);
613
+ }
614
+ }
615
+ }
616
+
617
+ if (matched === false && hasAdditional) {
618
+ if (additionalFalse)
619
+ return false;
620
+ if (additionalValidator != null) {
621
+ if (childPath === null) childPath = extendPaths ? dataPath + '/' + dataKey : dataPath;
622
+ if (additionalValidator(data[dataKey], childPath, dataRoot, dataKey) === false)
623
+ return false;
624
+ }
625
+ if (track) root.evalLog.add(data, dataKey);
626
+ }
627
+
628
+ if (depRequiredValidator != null || depSchemasValidator != null || dependencyValidator != null) {
629
+ if (childPath === null) childPath = extendPaths ? dataPath + '/' + dataKey : dataPath;
630
+ if (depRequiredValidator != null && depRequiredValidator(data, childPath, dataRoot, dataKey) === false)
631
+ return false;
632
+ if (depSchemasValidator != null && depSchemasValidator(data, childPath, dataRoot, dataKey) === false)
633
+ return false;
634
+ if (dependencyValidator != null && dependencyValidator(data, childPath, dataRoot, dataKey) === false)
635
+ return false;
636
+ }
637
+ }
638
+ return true;
639
+ };
640
+ }
641
+
642
+ export function compileObjectChildren(schemaObj, jsonSchema) {
643
+ // Fast path: when errors are skipped (default) we can bail on the first
644
+ // failure and avoid per-key result bookkeeping entirely.
645
+ // (unevaluatedProperties runs separately as a final-stage validator,
646
+ // see unevaluated.js)
647
+ if (schemaObj.options.skipErrors)
648
+ return compileObjectChildrenFast(schemaObj, jsonSchema);
649
+
650
+ const propertyValidator = compileObjectProperty(schemaObj, jsonSchema);
651
+ if (propertyValidator == null)
652
+ return undefined;
653
+
654
+ // Inline ValidationResult operations to reduce object allocations
655
+ return function validateObjectChildren(data, dataPath, dataRoot, dataKeys) {
656
+ let totalErrors = 0;
657
+ const len = dataKeys.length;
658
+ for (let i = 0; i < len; ++i) {
659
+ const result = propertyValidator(data, dataPath, dataRoot, dataKeys[i]);
660
+ if (result !== true) {
661
+ // result can be false or a ValidationResult-like object
662
+ if (result === false) {
663
+ totalErrors++;
664
+ } else {
665
+ totalErrors += result.errors || 0;
666
+ }
667
+ }
668
+ }
669
+ return totalErrors === 0;
670
+ };
671
+ }
672
+
673
+
674
+ export function compileObjectSchema(schemaObj, jsonSchema) {
675
+ if (isOfSchemaType(jsonSchema, 'map'))
676
+ return undefined;
677
+
678
+ // Fast path: properties-only schema in skipErrors mode. Iterate the
679
+ // (fixed) schema keys with direct property access instead of allocating
680
+ // Object.keys(data) and doing a map lookup per data key.
681
+ if (schemaObj.options.skipErrors
682
+ && jsonSchema.patternProperties == null
683
+ && jsonSchema.additionalProperties == null
684
+ && jsonSchema.propertyNames == null
685
+ && jsonSchema.dependencies == null
686
+ && jsonSchema.dependentSchemas == null
687
+ && jsonSchema.dependentRequired == null
688
+ && jsonSchema.minProperties == null
689
+ && jsonSchema.maxProperties == null
690
+ && jsonSchema.required == null
691
+ && getObjectType(jsonSchema.properties) != null) {
692
+ const propsMap = buildPropertyValidators(schemaObj, jsonSchema);
693
+ if (propsMap == null)
694
+ return undefined;
695
+
696
+ const propKeys = Array.from(propsMap.keys());
697
+ const propValidators = Array.from(propsMap.values());
698
+ const propCount = propKeys.length;
699
+
700
+ const root = schemaObj.root;
701
+ const track = root.usesUnevaluated;
702
+
703
+ // Child paths are only consumed by $data relative-pointer resolution
704
+ // in skipErrors mode; skip the per-property string concat otherwise.
705
+ if (root.usesDollarData || track) {
706
+ const extendPaths = root.usesDollarData;
707
+ return function validateObjectPropertiesOnlyTracked(data, dataPath, dataRoot) {
708
+ if (!isObjectType(data)) return true;
709
+ for (let i = 0; i < propCount; ++i) {
710
+ const key = propKeys[i];
711
+ // Object.hasOwn: avoid picking up inherited members like toString
712
+ if (Object.hasOwn(data, key)) {
713
+ const childPath = extendPaths ? dataPath + '/' + key : dataPath;
714
+ if (propValidators[i](data[key], childPath, dataRoot, key) === false)
715
+ return false;
716
+ if (track) root.evalLog.add(data, key);
717
+ }
718
+ }
719
+ return true;
720
+ };
721
+ }
722
+
723
+ return function validateObjectPropertiesOnly(data, dataPath, dataRoot) {
724
+ if (!isObjectType(data)) return true;
725
+ for (let i = 0; i < propCount; ++i) {
726
+ const key = propKeys[i];
727
+ // Object.hasOwn: avoid picking up inherited members like toString
728
+ if (Object.hasOwn(data, key)
729
+ && propValidators[i](data[key], dataPath, dataRoot, key) === false)
730
+ return false;
731
+ }
732
+ return true;
733
+ };
734
+ }
735
+
736
+ const objectPrimitives = compileObjectPrimitives(schemaObj, jsonSchema);
737
+ const objectChildren = compileObjectChildren(schemaObj, jsonSchema);
738
+
739
+ if ((objectPrimitives
740
+ || objectChildren) == null)
741
+ return undefined;
742
+
743
+ const validatePrimitives = objectPrimitives || trueThat;
744
+ const validateChildren = objectChildren || trueThat;
745
+
746
+ return function validateObjectSchema(data, dataPath, dataRoot) {
747
+ if (isObjectType(data)) {
748
+ const dataKeys = Object.keys(data);
749
+ return validatePrimitives(data, dataPath, dataRoot, dataKeys)
750
+ && validateChildren(data, dataPath, dataRoot, dataKeys);
751
+ }
752
+ return true;
753
+ };
754
+ }
755
+ //#endregion