@jarenjs/validate 0.8.4 → 0.34.0

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