@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/data.js ADDED
@@ -0,0 +1,477 @@
1
+ //@ts-check
2
+
3
+ /**
4
+ * @fileoverview Data keyword implementation for referencing instance data.
5
+ *
6
+ * This implements the "data" keyword from json-everything's data-2022 meta-schema,
7
+ * allowing schema values to be sourced from the instance data at validation time.
8
+ *
9
+ * Example:
10
+ * {
11
+ * "type": "object",
12
+ * "properties": {
13
+ * "A": { "type": "number" },
14
+ * "B": {
15
+ * "type": "number",
16
+ * "data": {
17
+ * "minimum": "/A"
18
+ * }
19
+ * }
20
+ * }
21
+ * }
22
+ *
23
+ * Supports both absolute JSON Pointers (starting with /) and relative JSON Pointers.
24
+ * @see https://docs.json-everything.net/schema/examples/data-ref/
25
+ */
26
+
27
+ import {
28
+ isObjectClass,
29
+ isStringType,
30
+ isNumberType,
31
+ isArrayClass,
32
+ } from '@jarenjs/core';
33
+
34
+ import {
35
+ compileDataRef,
36
+ JSONPOINTER_NOTHING,
37
+ } from '@jarenjs/json';
38
+
39
+ // A ref that fails the strict compile keeps the lax keyword semantics:
40
+ // it resolves as not-found and the keyword asserts nothing.
41
+ const resolveNothing = () => JSONPOINTER_NOTHING;
42
+
43
+ function compileRefResolver(ref) {
44
+ try {
45
+ return compileDataRef(ref);
46
+ }
47
+ catch {
48
+ return resolveNothing;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Compile minimum constraint from data reference
54
+ * @param {object} schemaObj - The validation object
55
+ * @param {string} ref - The data reference
56
+ * @returns {function|undefined} The compiled validator function
57
+ */
58
+ function compileDataMinimum(schemaObj, ref) {
59
+ const addError = schemaObj.createErrorHandler(ref, 'minimum');
60
+ const resolveRef = compileRefResolver(ref);
61
+
62
+ return function validateDataMinimum(data, dataPath, dataRoot) {
63
+ if (!isNumberType(data)) return true;
64
+
65
+ const minValue = resolveRef(dataRoot, dataPath);
66
+ if (minValue === JSONPOINTER_NOTHING || !isNumberType(minValue)) return true;
67
+
68
+ return data >= minValue || addError(minValue, data, dataPath);
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Compile maximum constraint from data reference
74
+ * @param {object} schemaObj - The validation object
75
+ * @param {string} ref - The data reference
76
+ * @returns {function|undefined} The compiled validator function
77
+ */
78
+ function compileDataMaximum(schemaObj, ref) {
79
+ const addError = schemaObj.createErrorHandler(ref, 'maximum');
80
+ const resolveRef = compileRefResolver(ref);
81
+
82
+ return function validateDataMaximum(data, dataPath, dataRoot) {
83
+ if (!isNumberType(data)) return true;
84
+
85
+ const maxValue = resolveRef(dataRoot, dataPath);
86
+ if (maxValue === JSONPOINTER_NOTHING || !isNumberType(maxValue)) return true;
87
+
88
+ return data <= maxValue || addError(maxValue, data, dataPath);
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Compile exclusiveMinimum constraint from data reference
94
+ * @param {object} schemaObj - The validation object
95
+ * @param {string} ref - The data reference
96
+ * @returns {function|undefined} The compiled validator function
97
+ */
98
+ function compileDataExclusiveMinimum(schemaObj, ref) {
99
+ const addError = schemaObj.createErrorHandler(ref, 'exclusiveMinimum');
100
+ const resolveRef = compileRefResolver(ref);
101
+
102
+ return function validateDataExclusiveMinimum(data, dataPath, dataRoot) {
103
+ if (!isNumberType(data)) return true;
104
+
105
+ const minValue = resolveRef(dataRoot, dataPath);
106
+ if (minValue === JSONPOINTER_NOTHING || !isNumberType(minValue)) return true;
107
+
108
+ return data > minValue || addError(minValue, data, dataPath);
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Compile exclusiveMaximum constraint from data reference
114
+ * @param {object} schemaObj - The validation object
115
+ * @param {string} ref - The data reference
116
+ * @returns {function|undefined} The compiled validator function
117
+ */
118
+ function compileDataExclusiveMaximum(schemaObj, ref) {
119
+ const addError = schemaObj.createErrorHandler(ref, 'exclusiveMaximum');
120
+ const resolveRef = compileRefResolver(ref);
121
+
122
+ return function validateDataExclusiveMaximum(data, dataPath, dataRoot) {
123
+ if (!isNumberType(data)) return true;
124
+
125
+ const maxValue = resolveRef(dataRoot, dataPath);
126
+ if (maxValue === JSONPOINTER_NOTHING || !isNumberType(maxValue)) return true;
127
+
128
+ return data < maxValue || addError(maxValue, data, dataPath);
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Compile enum constraint from data reference
134
+ * @param {object} schemaObj - The validation object
135
+ * @param {string} ref - The data reference
136
+ * @returns {function|undefined} The compiled validator function
137
+ */
138
+ function compileDataEnum(schemaObj, ref) {
139
+ const addError = schemaObj.createErrorHandler(ref, 'enum');
140
+ const resolveRef = compileRefResolver(ref);
141
+
142
+ return function validateDataEnum(data, dataPath, dataRoot) {
143
+ if (data === undefined) return true;
144
+
145
+ const enumValues = resolveRef(dataRoot, dataPath);
146
+ if (enumValues === JSONPOINTER_NOTHING || !Array.isArray(enumValues)) return true;
147
+
148
+ return enumValues.includes(data) || addError(enumValues, data, dataPath);
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Compile const constraint from data reference
154
+ * @param {object} schemaObj - The validation object
155
+ * @param {string} ref - The data reference
156
+ * @returns {function|undefined} The compiled validator function
157
+ */
158
+ function compileDataConst(schemaObj, ref) {
159
+ const addError = schemaObj.createErrorHandler(ref, 'const');
160
+ const resolveRef = compileRefResolver(ref);
161
+
162
+ return function validateDataConst(data, dataPath, dataRoot) {
163
+ if (data === undefined) return true;
164
+
165
+ const constValue = resolveRef(dataRoot, dataPath);
166
+ if (constValue === JSONPOINTER_NOTHING) return true;
167
+
168
+ return data === constValue || addError(constValue, data, dataPath);
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Compile minLength constraint from data reference
174
+ * @param {object} schemaObj - The validation object
175
+ * @param {string} ref - The data reference
176
+ * @returns {function|undefined} The compiled validator function
177
+ */
178
+ function compileDataMinLength(schemaObj, ref) {
179
+ const addError = schemaObj.createErrorHandler(ref, 'minLength');
180
+ const resolveRef = compileRefResolver(ref);
181
+
182
+ return function validateDataMinLength(data, dataPath, dataRoot) {
183
+ if (typeof data !== 'string') return true;
184
+
185
+ const minLen = resolveRef(dataRoot, dataPath);
186
+ if (minLen === JSONPOINTER_NOTHING || !isNumberType(minLen)) return true;
187
+
188
+ return data.length >= minLen || addError(minLen, data, dataPath);
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Compile maxLength constraint from data reference
194
+ * @param {object} schemaObj - The validation object
195
+ * @param {string} ref - The data reference
196
+ * @returns {function|undefined} The compiled validator function
197
+ */
198
+ function compileDataMaxLength(schemaObj, ref) {
199
+ const addError = schemaObj.createErrorHandler(ref, 'maxLength');
200
+ const resolveRef = compileRefResolver(ref);
201
+
202
+ return function validateDataMaxLength(data, dataPath, dataRoot) {
203
+ if (typeof data !== 'string') return true;
204
+
205
+ const maxLen = resolveRef(dataRoot, dataPath);
206
+ if (maxLen === JSONPOINTER_NOTHING || !isNumberType(maxLen)) return true;
207
+
208
+ return data.length <= maxLen || addError(maxLen, data, dataPath);
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Compile minItems constraint from data reference
214
+ * @param {object} schemaObj - The validation object
215
+ * @param {string} ref - The data reference
216
+ * @returns {function|undefined} The compiled validator function
217
+ */
218
+ function compileDataMinItems(schemaObj, ref) {
219
+ const addError = schemaObj.createErrorHandler(ref, 'minItems');
220
+ const resolveRef = compileRefResolver(ref);
221
+
222
+ return function validateDataMinItems(data, dataPath, dataRoot) {
223
+ if (!Array.isArray(data)) return true;
224
+
225
+ const minItems = resolveRef(dataRoot, dataPath);
226
+ if (minItems === JSONPOINTER_NOTHING || !isNumberType(minItems)) return true;
227
+
228
+ return data.length >= minItems || addError(minItems, data, dataPath);
229
+ };
230
+ }
231
+
232
+ /**
233
+ * Compile maxItems constraint from data reference
234
+ * @param {object} schemaObj - The validation object
235
+ * @param {string} ref - The data reference
236
+ * @returns {function|undefined} The compiled validator function
237
+ */
238
+ function compileDataMaxItems(schemaObj, ref) {
239
+ const addError = schemaObj.createErrorHandler(ref, 'maxItems');
240
+ const resolveRef = compileRefResolver(ref);
241
+
242
+ return function validateDataMaxItems(data, dataPath, dataRoot) {
243
+ if (!Array.isArray(data)) return true;
244
+
245
+ const maxItems = resolveRef(dataRoot, dataPath);
246
+ if (maxItems === JSONPOINTER_NOTHING || !isNumberType(maxItems)) return true;
247
+
248
+ return data.length <= maxItems || addError(maxItems, data, dataPath);
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Compile minProperties constraint from data reference
254
+ * @param {object} schemaObj - The validation object
255
+ * @param {string} ref - The data reference
256
+ * @returns {function|undefined} The compiled validator function
257
+ */
258
+ function compileDataMinProperties(schemaObj, ref) {
259
+ const addError = schemaObj.createErrorHandler(ref, 'minProperties');
260
+ const resolveRef = compileRefResolver(ref);
261
+
262
+ return function validateDataMinProperties(data, dataPath, dataRoot) {
263
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) return true;
264
+
265
+ const minProps = resolveRef(dataRoot, dataPath);
266
+ if (minProps === JSONPOINTER_NOTHING || !isNumberType(minProps)) return true;
267
+
268
+ const propCount = Object.keys(data).length;
269
+ return propCount >= minProps || addError(minProps, data, dataPath);
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Compile maxProperties constraint from data reference
275
+ * @param {object} schemaObj - The validation object
276
+ * @param {string} ref - The data reference
277
+ * @returns {function|undefined} The compiled validator function
278
+ */
279
+ function compileDataMaxProperties(schemaObj, ref) {
280
+ const addError = schemaObj.createErrorHandler(ref, 'maxProperties');
281
+ const resolveRef = compileRefResolver(ref);
282
+
283
+ return function validateDataMaxProperties(data, dataPath, dataRoot) {
284
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) return true;
285
+
286
+ const maxProps = resolveRef(dataRoot, dataPath);
287
+ if (maxProps === JSONPOINTER_NOTHING || !isNumberType(maxProps)) return true;
288
+
289
+ const propCount = Object.keys(data).length;
290
+ return propCount <= maxProps || addError(maxProps, data, dataPath);
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Compile multipleOf constraint from data reference
296
+ * @param {object} schemaObj - The validation object
297
+ * @param {string} ref - The data reference
298
+ * @returns {function|undefined} The compiled validator function
299
+ */
300
+ function compileDataMultipleOf(schemaObj, ref) {
301
+ const addError = schemaObj.createErrorHandler(ref, 'multipleOf');
302
+ const resolveRef = compileRefResolver(ref);
303
+
304
+ return function validateDataMultipleOf(data, dataPath, dataRoot) {
305
+ if (!isNumberType(data)) return true;
306
+
307
+ const multipleOf = resolveRef(dataRoot, dataPath);
308
+ if (multipleOf === JSONPOINTER_NOTHING || !isNumberType(multipleOf)) return true;
309
+
310
+ const q = data / multipleOf;
311
+ return Math.abs(q - Math.round(q)) < 1e-6 || addError(multipleOf, data, dataPath);
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Compile pattern constraint from data reference
317
+ * @param {object} schemaObj - The validation object
318
+ * @param {string} ref - The data reference
319
+ * @returns {function|undefined} The compiled validator function
320
+ */
321
+ function compileDataPattern(schemaObj, ref) {
322
+ const addError = schemaObj.createErrorHandler(ref, 'pattern');
323
+ const resolveRef = compileRefResolver(ref);
324
+
325
+ return function validateDataPattern(data, dataPath, dataRoot) {
326
+ if (typeof data !== 'string') return true;
327
+
328
+ const pattern = resolveRef(dataRoot, dataPath);
329
+ if (pattern === JSONPOINTER_NOTHING || !isStringType(pattern)) return true;
330
+
331
+ const regex = new RegExp(pattern, 'u');
332
+ return regex.test(data) || addError(pattern, data, dataPath);
333
+ };
334
+ }
335
+
336
+ /**
337
+ * Compile format constraint from data reference
338
+ * @param {object} schemaObj - The validation object
339
+ * @param {string} ref - The data reference
340
+ * @returns {function|undefined} The compiled validator function
341
+ */
342
+ function compileDataFormat(schemaObj, ref) {
343
+ const formats = schemaObj.formats;
344
+ if (!formats) return undefined;
345
+
346
+ const addError = schemaObj.createErrorHandler(ref, 'format');
347
+ const resolveRef = compileRefResolver(ref);
348
+
349
+ // The registry holds format COMPILERS; compile (and cache) a validator
350
+ // per referenced format name at validation time.
351
+ const compiled = new Map();
352
+ const mockSchemaObj = {
353
+ createErrorHandler: () => () => false,
354
+ options: { skipErrors: true },
355
+ };
356
+
357
+ return function validateDataFormat(data, dataPath, dataRoot) {
358
+ if (typeof data !== 'string') return true;
359
+
360
+ const formatName = resolveRef(dataRoot, dataPath);
361
+ if (formatName === JSONPOINTER_NOTHING || !isStringType(formatName)) return true;
362
+
363
+ let validator = compiled.get(formatName);
364
+ if (validator === undefined) {
365
+ const formatCompiler = formats[formatName];
366
+ validator = null;
367
+ if (formatCompiler) {
368
+ try {
369
+ const candidate = formatCompiler(mockSchemaObj, { format: formatName });
370
+ if (typeof candidate === 'function') validator = candidate;
371
+ } catch (e) {
372
+ // An uncompilable format asserts nothing
373
+ }
374
+ }
375
+ compiled.set(formatName, validator);
376
+ }
377
+ if (validator === null) return true;
378
+
379
+ return validator(data, dataPath) || addError(formatName, data, dataPath);
380
+ };
381
+ }
382
+
383
+ /**
384
+ * Compile the data keyword schema
385
+ * @param {object} schemaObj - The validation object
386
+ * @param {object} jsonSchema - The JSON schema containing the data keyword
387
+ * @returns {function|undefined} The compiled validator function or undefined
388
+ */
389
+ export function compileDataSchema(schemaObj, jsonSchema) {
390
+ const dataSchema = jsonSchema.data;
391
+ if (!isObjectClass(dataSchema)) {
392
+ return undefined;
393
+ }
394
+
395
+ const validators = [];
396
+
397
+ // Compile each supported keyword within the data object
398
+ for (const [keyword, ref] of Object.entries(dataSchema)) {
399
+ if (!isStringType(ref)) {
400
+ continue; // Skip non-string references
401
+ }
402
+
403
+ let validator;
404
+
405
+ switch (keyword) {
406
+ case 'minimum':
407
+ validator = compileDataMinimum(schemaObj, ref);
408
+ break;
409
+ case 'maximum':
410
+ validator = compileDataMaximum(schemaObj, ref);
411
+ break;
412
+ case 'exclusiveMinimum':
413
+ validator = compileDataExclusiveMinimum(schemaObj, ref);
414
+ break;
415
+ case 'exclusiveMaximum':
416
+ validator = compileDataExclusiveMaximum(schemaObj, ref);
417
+ break;
418
+ case 'enum':
419
+ validator = compileDataEnum(schemaObj, ref);
420
+ break;
421
+ case 'const':
422
+ validator = compileDataConst(schemaObj, ref);
423
+ break;
424
+ case 'minLength':
425
+ validator = compileDataMinLength(schemaObj, ref);
426
+ break;
427
+ case 'maxLength':
428
+ validator = compileDataMaxLength(schemaObj, ref);
429
+ break;
430
+ case 'minItems':
431
+ validator = compileDataMinItems(schemaObj, ref);
432
+ break;
433
+ case 'maxItems':
434
+ validator = compileDataMaxItems(schemaObj, ref);
435
+ break;
436
+ case 'minProperties':
437
+ validator = compileDataMinProperties(schemaObj, ref);
438
+ break;
439
+ case 'maxProperties':
440
+ validator = compileDataMaxProperties(schemaObj, ref);
441
+ break;
442
+ case 'multipleOf':
443
+ validator = compileDataMultipleOf(schemaObj, ref);
444
+ break;
445
+ case 'pattern':
446
+ validator = compileDataPattern(schemaObj, ref);
447
+ break;
448
+ case 'format':
449
+ validator = compileDataFormat(schemaObj, ref);
450
+ break;
451
+ default:
452
+ // Unknown keyword in data, skip
453
+ break;
454
+ }
455
+
456
+ if (validator) {
457
+ validators.push(validator);
458
+ }
459
+ }
460
+
461
+ if (validators.length === 0) {
462
+ return undefined;
463
+ }
464
+
465
+ if (validators.length === 1) {
466
+ return validators[0];
467
+ }
468
+
469
+ return function validateDataSchema(data, dataPath, dataRoot) {
470
+ for (let i = 0; i < validators.length; i++) {
471
+ if (!validators[i](data, dataPath, dataRoot)) {
472
+ return false;
473
+ }
474
+ }
475
+ return true;
476
+ };
477
+ }