@jarenjs/validate 0.8.4 → 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.
- package/ARCHITECTURE.md +1067 -0
- package/LICENSE +21 -0
- package/README.md +339 -2
- package/dist/types/array.d.ts +2 -0
- package/dist/types/bigint.d.ts +1 -0
- package/dist/types/combine.d.ts +1 -0
- package/dist/types/condition.d.ts +1 -0
- package/dist/types/content.d.ts +3 -0
- package/dist/types/data.d.ts +7 -0
- package/dist/types/dollar-data.d.ts +20 -0
- package/dist/types/dynamic-ref.d.ts +44 -0
- package/dist/types/enum.d.ts +1 -0
- package/dist/types/format.d.ts +21 -0
- package/dist/types/index.d.ts +874 -0
- package/dist/types/number.d.ts +1 -0
- package/dist/types/object.d.ts +3 -0
- package/dist/types/query-keyword.d.ts +19 -0
- package/dist/types/query.d.ts +29 -0
- package/dist/types/schema.d.ts +1 -0
- package/dist/types/string.d.ts +1 -0
- package/dist/types/tools.d.ts +51 -0
- package/dist/types/traverse.d.ts +32 -0
- package/dist/types/unevaluated.d.ts +12 -0
- package/package.json +32 -7
- package/src/array.js +565 -0
- package/src/bigint.js +97 -0
- package/src/combine.js +226 -0
- package/src/condition.js +109 -0
- package/src/content.js +83 -0
- package/src/data.js +477 -0
- package/src/dollar-data.js +629 -0
- package/src/dynamic-ref.js +121 -0
- package/src/enum.js +148 -0
- package/src/format.js +66 -0
- package/src/index.js +1854 -0
- package/src/number.js +159 -0
- package/src/object.js +755 -0
- package/src/query-keyword.js +99 -0
- package/src/query.js +59 -0
- package/src/schema.js +645 -0
- package/src/string.js +152 -0
- package/src/tools.js +205 -0
- package/src/traverse.js +433 -0
- package/src/unevaluated.js +151 -0
- package/dist/index.js +0 -1998
- package/dist/index.js.map +0 -7
- package/dist/index.min.js +0 -2
- package/dist/index.min.js.map +0 -7
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Ajv $data keyword implementation for referencing instance data.
|
|
5
|
+
*
|
|
6
|
+
* This implements the Ajv-style "$data" keyword, allowing constraint keyword values
|
|
7
|
+
* to be sourced from instance data at validation time using Relative JSON Pointers.
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
* {
|
|
11
|
+
* "type": "object",
|
|
12
|
+
* "properties": {
|
|
13
|
+
* "smaller": {
|
|
14
|
+
* "type": "number",
|
|
15
|
+
* "maximum": { "$data": "1/larger" }
|
|
16
|
+
* },
|
|
17
|
+
* "larger": {
|
|
18
|
+
* "type": "number"
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* The "$data" value is a Relative JSON Pointer that resolves from the current data location.
|
|
24
|
+
* Format: <non-negative-integer>("#" | <json-pointer>)
|
|
25
|
+
* - "0" - The current value itself
|
|
26
|
+
* - "0#" - The property name/index of the current value
|
|
27
|
+
* - "0/foo" - The "foo" property of the current value
|
|
28
|
+
* - "1" - The parent value
|
|
29
|
+
* - "1/foo" - The "foo" property of the parent value
|
|
30
|
+
* - "2/bar" - Go up 2 levels, then look for "bar"
|
|
31
|
+
*
|
|
32
|
+
* @see https://github.com/ajv-validator/ajv/tree/master/spec/extras/%24data
|
|
33
|
+
* @see https://datatracker.ietf.org/doc/html/draft-luff-relative-json-pointer-00
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import {
|
|
37
|
+
isObjectClass,
|
|
38
|
+
isStringType,
|
|
39
|
+
isNumberType,
|
|
40
|
+
isArrayClass,
|
|
41
|
+
isObjectType,
|
|
42
|
+
} from '@jarenjs/core';
|
|
43
|
+
|
|
44
|
+
import {
|
|
45
|
+
equalsDeep,
|
|
46
|
+
} from '@jarenjs/core/object';
|
|
47
|
+
|
|
48
|
+
import {
|
|
49
|
+
compileRelativeJSONPointer,
|
|
50
|
+
JSONPOINTER_NOTHING,
|
|
51
|
+
} from '@jarenjs/json';
|
|
52
|
+
|
|
53
|
+
// A ref that fails the strict compile keeps the lax keyword semantics:
|
|
54
|
+
// it resolves as not-found and the keyword asserts nothing.
|
|
55
|
+
const resolveNothing = () => JSONPOINTER_NOTHING;
|
|
56
|
+
|
|
57
|
+
function compileRefResolver(ref) {
|
|
58
|
+
try {
|
|
59
|
+
return compileRelativeJSONPointer(ref);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return resolveNothing;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if a value is a $data reference object
|
|
68
|
+
* @param {*} value - The value to check
|
|
69
|
+
* @returns {boolean} True if the value is a $data reference object
|
|
70
|
+
*/
|
|
71
|
+
function isDollarDataRef(value) {
|
|
72
|
+
return isObjectClass(value) && isStringType(value.$data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
//#region Number Validators
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Compile $data reference for minimum constraint
|
|
80
|
+
* @param {object} schemaObj - The validation object
|
|
81
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
82
|
+
* @returns {function|undefined} The compiled validator function
|
|
83
|
+
*/
|
|
84
|
+
function compileDollarDataMinimum(schemaObj, ref) {
|
|
85
|
+
const addError = schemaObj.createErrorHandler(ref, 'minimum');
|
|
86
|
+
const resolveRef = compileRefResolver(ref);
|
|
87
|
+
|
|
88
|
+
return function validateDollarDataMinimum(data, dataPath, dataRoot) {
|
|
89
|
+
if (!isNumberType(data)) return true;
|
|
90
|
+
|
|
91
|
+
const minValue = resolveRef(dataRoot, dataPath);
|
|
92
|
+
if (minValue === JSONPOINTER_NOTHING || !isNumberType(minValue)) return true;
|
|
93
|
+
|
|
94
|
+
return data >= minValue || addError(minValue, data, dataPath);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Compile $data reference for maximum constraint
|
|
100
|
+
* @param {object} schemaObj - The validation object
|
|
101
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
102
|
+
* @returns {function|undefined} The compiled validator function
|
|
103
|
+
*/
|
|
104
|
+
function compileDollarDataMaximum(schemaObj, ref) {
|
|
105
|
+
const addError = schemaObj.createErrorHandler(ref, 'maximum');
|
|
106
|
+
const resolveRef = compileRefResolver(ref);
|
|
107
|
+
|
|
108
|
+
return function validateDollarDataMaximum(data, dataPath, dataRoot) {
|
|
109
|
+
if (!isNumberType(data)) return true;
|
|
110
|
+
|
|
111
|
+
const maxValue = resolveRef(dataRoot, dataPath);
|
|
112
|
+
if (maxValue === JSONPOINTER_NOTHING || !isNumberType(maxValue)) return true;
|
|
113
|
+
|
|
114
|
+
return data <= maxValue || addError(maxValue, data, dataPath);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Compile $data reference for exclusiveMinimum constraint
|
|
120
|
+
* @param {object} schemaObj - The validation object
|
|
121
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
122
|
+
* @returns {function|undefined} The compiled validator function
|
|
123
|
+
*/
|
|
124
|
+
function compileDollarDataExclusiveMinimum(schemaObj, ref) {
|
|
125
|
+
const addError = schemaObj.createErrorHandler(ref, 'exclusiveMinimum');
|
|
126
|
+
const resolveRef = compileRefResolver(ref);
|
|
127
|
+
|
|
128
|
+
return function validateDollarDataExclusiveMinimum(data, dataPath, dataRoot) {
|
|
129
|
+
if (!isNumberType(data)) return true;
|
|
130
|
+
|
|
131
|
+
const minValue = resolveRef(dataRoot, dataPath);
|
|
132
|
+
if (minValue === JSONPOINTER_NOTHING || !isNumberType(minValue)) return true;
|
|
133
|
+
|
|
134
|
+
return data > minValue || addError(minValue, data, dataPath);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Compile $data reference for exclusiveMaximum constraint
|
|
140
|
+
* @param {object} schemaObj - The validation object
|
|
141
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
142
|
+
* @returns {function|undefined} The compiled validator function
|
|
143
|
+
*/
|
|
144
|
+
function compileDollarDataExclusiveMaximum(schemaObj, ref) {
|
|
145
|
+
const addError = schemaObj.createErrorHandler(ref, 'exclusiveMaximum');
|
|
146
|
+
const resolveRef = compileRefResolver(ref);
|
|
147
|
+
|
|
148
|
+
return function validateDollarDataExclusiveMaximum(data, dataPath, dataRoot) {
|
|
149
|
+
if (!isNumberType(data)) return true;
|
|
150
|
+
|
|
151
|
+
const maxValue = resolveRef(dataRoot, dataPath);
|
|
152
|
+
if (maxValue === JSONPOINTER_NOTHING || !isNumberType(maxValue)) return true;
|
|
153
|
+
|
|
154
|
+
return data < maxValue || addError(maxValue, data, dataPath);
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Compile $data reference for multipleOf constraint
|
|
160
|
+
* @param {object} schemaObj - The validation object
|
|
161
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
162
|
+
* @returns {function|undefined} The compiled validator function
|
|
163
|
+
*/
|
|
164
|
+
function compileDollarDataMultipleOf(schemaObj, ref) {
|
|
165
|
+
const addError = schemaObj.createErrorHandler(ref, 'multipleOf');
|
|
166
|
+
const resolveRef = compileRefResolver(ref);
|
|
167
|
+
|
|
168
|
+
return function validateDollarDataMultipleOf(data, dataPath, dataRoot) {
|
|
169
|
+
if (!isNumberType(data)) return true;
|
|
170
|
+
|
|
171
|
+
const multipleOf = resolveRef(dataRoot, dataPath);
|
|
172
|
+
if (multipleOf === JSONPOINTER_NOTHING || !isNumberType(multipleOf)) return true;
|
|
173
|
+
|
|
174
|
+
const q = data / multipleOf;
|
|
175
|
+
return Math.abs(q - Math.round(q)) < 1e-6 || addError(multipleOf, data, dataPath);
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
|
|
181
|
+
//#region String Validators
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Compile $data reference for minLength constraint
|
|
185
|
+
* @param {object} schemaObj - The validation object
|
|
186
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
187
|
+
* @returns {function|undefined} The compiled validator function
|
|
188
|
+
*/
|
|
189
|
+
function compileDollarDataMinLength(schemaObj, ref) {
|
|
190
|
+
const addError = schemaObj.createErrorHandler(ref, 'minLength');
|
|
191
|
+
const resolveRef = compileRefResolver(ref);
|
|
192
|
+
|
|
193
|
+
return function validateDollarDataMinLength(data, dataPath, dataRoot) {
|
|
194
|
+
if (typeof data !== 'string') return true;
|
|
195
|
+
|
|
196
|
+
const minLen = resolveRef(dataRoot, dataPath);
|
|
197
|
+
if (minLen === JSONPOINTER_NOTHING || !isNumberType(minLen)) return true;
|
|
198
|
+
|
|
199
|
+
return data.length >= minLen || addError(minLen, data, dataPath);
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Compile $data reference for maxLength constraint
|
|
205
|
+
* @param {object} schemaObj - The validation object
|
|
206
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
207
|
+
* @returns {function|undefined} The compiled validator function
|
|
208
|
+
*/
|
|
209
|
+
function compileDollarDataMaxLength(schemaObj, ref) {
|
|
210
|
+
const addError = schemaObj.createErrorHandler(ref, 'maxLength');
|
|
211
|
+
const resolveRef = compileRefResolver(ref);
|
|
212
|
+
|
|
213
|
+
return function validateDollarDataMaxLength(data, dataPath, dataRoot) {
|
|
214
|
+
if (typeof data !== 'string') return true;
|
|
215
|
+
|
|
216
|
+
const maxLen = resolveRef(dataRoot, dataPath);
|
|
217
|
+
if (maxLen === JSONPOINTER_NOTHING || !isNumberType(maxLen)) return true;
|
|
218
|
+
|
|
219
|
+
return data.length <= maxLen || addError(maxLen, data, dataPath);
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Compile $data reference for pattern constraint
|
|
225
|
+
* @param {object} schemaObj - The validation object
|
|
226
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
227
|
+
* @returns {function|undefined} The compiled validator function
|
|
228
|
+
*/
|
|
229
|
+
function compileDollarDataPattern(schemaObj, ref) {
|
|
230
|
+
const addError = schemaObj.createErrorHandler(ref, 'pattern');
|
|
231
|
+
const resolveRef = compileRefResolver(ref);
|
|
232
|
+
|
|
233
|
+
return function validateDollarDataPattern(data, dataPath, dataRoot) {
|
|
234
|
+
if (typeof data !== 'string') return true;
|
|
235
|
+
|
|
236
|
+
const pattern = resolveRef(dataRoot, dataPath);
|
|
237
|
+
if (pattern === JSONPOINTER_NOTHING || !isStringType(pattern)) return true;
|
|
238
|
+
|
|
239
|
+
const regex = new RegExp(pattern, 'u');
|
|
240
|
+
return regex.test(data) || addError(pattern, data, dataPath);
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Compile $data reference for format constraint
|
|
246
|
+
* @param {object} schemaObj - The validation object
|
|
247
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
248
|
+
* @returns {function|undefined} The compiled validator function
|
|
249
|
+
*/
|
|
250
|
+
function compileDollarDataFormat(schemaObj, ref) {
|
|
251
|
+
const formats = schemaObj.formats;
|
|
252
|
+
if (!formats) return undefined;
|
|
253
|
+
|
|
254
|
+
const addError = schemaObj.createErrorHandler(ref, 'format');
|
|
255
|
+
const resolveRef = compileRefResolver(ref);
|
|
256
|
+
|
|
257
|
+
return function validateDollarDataFormat(data, dataPath, dataRoot) {
|
|
258
|
+
if (typeof data !== 'string') return true;
|
|
259
|
+
|
|
260
|
+
const formatName = resolveRef(dataRoot, dataPath);
|
|
261
|
+
if (formatName === JSONPOINTER_NOTHING || !isStringType(formatName)) return true;
|
|
262
|
+
|
|
263
|
+
const formatCompiler = formats[formatName];
|
|
264
|
+
if (!formatCompiler) return true;
|
|
265
|
+
|
|
266
|
+
// The format compiler needs to be called to create the validator
|
|
267
|
+
// We pass a mock schemaObj that only has createErrorHandler
|
|
268
|
+
const mockSchemaObj = {
|
|
269
|
+
createErrorHandler: () => () => false,
|
|
270
|
+
options: { skipErrors: true }
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
// Get the validator function from the compiler
|
|
275
|
+
const validator = formatCompiler(mockSchemaObj, { format: formatName });
|
|
276
|
+
if (typeof validator !== 'function') return true;
|
|
277
|
+
|
|
278
|
+
return validator(data, dataPath) || addError(formatName, data, dataPath);
|
|
279
|
+
} catch (e) {
|
|
280
|
+
// If compilation fails, skip validation
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
//#endregion
|
|
287
|
+
|
|
288
|
+
//#region Array Validators
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Compile $data reference for minItems constraint
|
|
292
|
+
* @param {object} schemaObj - The validation object
|
|
293
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
294
|
+
* @returns {function|undefined} The compiled validator function
|
|
295
|
+
*/
|
|
296
|
+
function compileDollarDataMinItems(schemaObj, ref) {
|
|
297
|
+
const addError = schemaObj.createErrorHandler(ref, 'minItems');
|
|
298
|
+
const resolveRef = compileRefResolver(ref);
|
|
299
|
+
|
|
300
|
+
return function validateDollarDataMinItems(data, dataPath, dataRoot) {
|
|
301
|
+
if (!Array.isArray(data)) return true;
|
|
302
|
+
|
|
303
|
+
const minItems = resolveRef(dataRoot, dataPath);
|
|
304
|
+
if (minItems === JSONPOINTER_NOTHING || !isNumberType(minItems)) return true;
|
|
305
|
+
|
|
306
|
+
return data.length >= minItems || addError(minItems, data, dataPath);
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Compile $data reference for maxItems constraint
|
|
312
|
+
* @param {object} schemaObj - The validation object
|
|
313
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
314
|
+
* @returns {function|undefined} The compiled validator function
|
|
315
|
+
*/
|
|
316
|
+
function compileDollarDataMaxItems(schemaObj, ref) {
|
|
317
|
+
const addError = schemaObj.createErrorHandler(ref, 'maxItems');
|
|
318
|
+
const resolveRef = compileRefResolver(ref);
|
|
319
|
+
|
|
320
|
+
return function validateDollarDataMaxItems(data, dataPath, dataRoot) {
|
|
321
|
+
if (!Array.isArray(data)) return true;
|
|
322
|
+
|
|
323
|
+
const maxItems = resolveRef(dataRoot, dataPath);
|
|
324
|
+
if (maxItems === JSONPOINTER_NOTHING || !isNumberType(maxItems)) return true;
|
|
325
|
+
|
|
326
|
+
return data.length <= maxItems || addError(maxItems, data, dataPath);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Compile $data reference for uniqueItems constraint
|
|
332
|
+
* @param {object} schemaObj - The validation object
|
|
333
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
334
|
+
* @returns {function|undefined} The compiled validator function
|
|
335
|
+
*/
|
|
336
|
+
function compileDollarDataUniqueItems(schemaObj, ref) {
|
|
337
|
+
const addError = schemaObj.createErrorHandler(ref, 'uniqueItems');
|
|
338
|
+
const resolveRef = compileRefResolver(ref);
|
|
339
|
+
|
|
340
|
+
return function validateDollarDataUniqueItems(data, dataPath, dataRoot) {
|
|
341
|
+
if (!Array.isArray(data)) return true;
|
|
342
|
+
|
|
343
|
+
const shouldBeUnique = resolveRef(dataRoot, dataPath);
|
|
344
|
+
if (shouldBeUnique === JSONPOINTER_NOTHING || !shouldBeUnique) return true;
|
|
345
|
+
|
|
346
|
+
// Check for duplicates using deep equality
|
|
347
|
+
for (let i = 0; i < data.length; i++) {
|
|
348
|
+
for (let j = i + 1; j < data.length; j++) {
|
|
349
|
+
if (equalsDeep(data[i], data[j])) {
|
|
350
|
+
return addError(true, data, dataPath);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return true;
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
//#endregion
|
|
359
|
+
|
|
360
|
+
//#region Object Validators
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Compile $data reference for minProperties constraint
|
|
364
|
+
* @param {object} schemaObj - The validation object
|
|
365
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
366
|
+
* @returns {function|undefined} The compiled validator function
|
|
367
|
+
*/
|
|
368
|
+
function compileDollarDataMinProperties(schemaObj, ref) {
|
|
369
|
+
const addError = schemaObj.createErrorHandler(ref, 'minProperties');
|
|
370
|
+
const resolveRef = compileRefResolver(ref);
|
|
371
|
+
|
|
372
|
+
return function validateDollarDataMinProperties(data, dataPath, dataRoot) {
|
|
373
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) return true;
|
|
374
|
+
|
|
375
|
+
const minProps = resolveRef(dataRoot, dataPath);
|
|
376
|
+
if (minProps === JSONPOINTER_NOTHING || !isNumberType(minProps)) return true;
|
|
377
|
+
|
|
378
|
+
const propCount = Object.keys(data).length;
|
|
379
|
+
return propCount >= minProps || addError(minProps, data, dataPath);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Compile $data reference for maxProperties constraint
|
|
385
|
+
* @param {object} schemaObj - The validation object
|
|
386
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
387
|
+
* @returns {function|undefined} The compiled validator function
|
|
388
|
+
*/
|
|
389
|
+
function compileDollarDataMaxProperties(schemaObj, ref) {
|
|
390
|
+
const addError = schemaObj.createErrorHandler(ref, 'maxProperties');
|
|
391
|
+
const resolveRef = compileRefResolver(ref);
|
|
392
|
+
|
|
393
|
+
return function validateDollarDataMaxProperties(data, dataPath, dataRoot) {
|
|
394
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) return true;
|
|
395
|
+
|
|
396
|
+
const maxProps = resolveRef(dataRoot, dataPath);
|
|
397
|
+
if (maxProps === JSONPOINTER_NOTHING || !isNumberType(maxProps)) return true;
|
|
398
|
+
|
|
399
|
+
const propCount = Object.keys(data).length;
|
|
400
|
+
return propCount <= maxProps || addError(maxProps, data, dataPath);
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Compile $data reference for required constraint
|
|
406
|
+
* @param {object} schemaObj - The validation object
|
|
407
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
408
|
+
* @returns {function|undefined} The compiled validator function
|
|
409
|
+
*/
|
|
410
|
+
function compileDollarDataRequired(schemaObj, ref) {
|
|
411
|
+
const addError = schemaObj.createErrorHandler(ref, 'required');
|
|
412
|
+
const resolveRef = compileRefResolver(ref);
|
|
413
|
+
|
|
414
|
+
return function validateDollarDataRequired(data, dataPath, dataRoot) {
|
|
415
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) return true;
|
|
416
|
+
|
|
417
|
+
const requiredProps = resolveRef(dataRoot, dataPath);
|
|
418
|
+
if (requiredProps === JSONPOINTER_NOTHING || !Array.isArray(requiredProps)) return true;
|
|
419
|
+
|
|
420
|
+
for (const prop of requiredProps) {
|
|
421
|
+
if (!(prop in data)) {
|
|
422
|
+
return addError(prop, data, dataPath);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
//#endregion
|
|
430
|
+
|
|
431
|
+
//#region Enum/Const Validators
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Compile $data reference for enum constraint
|
|
435
|
+
* @param {object} schemaObj - The validation object
|
|
436
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
437
|
+
* @returns {function|undefined} The compiled validator function
|
|
438
|
+
*/
|
|
439
|
+
function compileDollarDataEnum(schemaObj, ref) {
|
|
440
|
+
const addError = schemaObj.createErrorHandler(ref, 'enum');
|
|
441
|
+
const resolveRef = compileRefResolver(ref);
|
|
442
|
+
|
|
443
|
+
return function validateDollarDataEnum(data, dataPath, dataRoot) {
|
|
444
|
+
if (data === undefined) return true;
|
|
445
|
+
|
|
446
|
+
const enumValues = resolveRef(dataRoot, dataPath);
|
|
447
|
+
if (enumValues === JSONPOINTER_NOTHING || !Array.isArray(enumValues)) return true;
|
|
448
|
+
|
|
449
|
+
return enumValues.includes(data) || addError(enumValues, data, dataPath);
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Compile $data reference for const constraint
|
|
455
|
+
* @param {object} schemaObj - The validation object
|
|
456
|
+
* @param {string} ref - The $data reference (relative JSON Pointer)
|
|
457
|
+
* @returns {function|undefined} The compiled validator function
|
|
458
|
+
*/
|
|
459
|
+
function compileDollarDataConst(schemaObj, ref) {
|
|
460
|
+
const addError = schemaObj.createErrorHandler(ref, 'const');
|
|
461
|
+
const resolveRef = compileRefResolver(ref);
|
|
462
|
+
|
|
463
|
+
return function validateDollarDataConst(data, dataPath, dataRoot) {
|
|
464
|
+
if (data === undefined) return true;
|
|
465
|
+
|
|
466
|
+
const constValue = resolveRef(dataRoot, dataPath);
|
|
467
|
+
if (constValue === JSONPOINTER_NOTHING) return true;
|
|
468
|
+
|
|
469
|
+
return data === constValue || addError(constValue, data, dataPath);
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
//#endregion
|
|
474
|
+
|
|
475
|
+
//#region Main Compilation
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Compile $data keyword validators for a schema object
|
|
479
|
+
* This detects when keyword values are { $data: "..." } objects and creates
|
|
480
|
+
* dynamic validators that resolve the reference at validation time.
|
|
481
|
+
*
|
|
482
|
+
* @param {object} schemaObj - The validation object
|
|
483
|
+
* @param {object} jsonSchema - The JSON schema to compile
|
|
484
|
+
* @returns {function|undefined} The compiled validator function or undefined
|
|
485
|
+
*/
|
|
486
|
+
export function compileDollarDataSchema(schemaObj, jsonSchema) {
|
|
487
|
+
const validators = [];
|
|
488
|
+
|
|
489
|
+
// Number constraints
|
|
490
|
+
if (isDollarDataRef(jsonSchema.minimum)) {
|
|
491
|
+
const validator = compileDollarDataMinimum(schemaObj, jsonSchema.minimum.$data);
|
|
492
|
+
if (validator) validators.push(validator);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (isDollarDataRef(jsonSchema.maximum)) {
|
|
496
|
+
const validator = compileDollarDataMaximum(schemaObj, jsonSchema.maximum.$data);
|
|
497
|
+
if (validator) validators.push(validator);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (isDollarDataRef(jsonSchema.exclusiveMinimum)) {
|
|
501
|
+
const validator = compileDollarDataExclusiveMinimum(schemaObj, jsonSchema.exclusiveMinimum.$data);
|
|
502
|
+
if (validator) validators.push(validator);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (isDollarDataRef(jsonSchema.exclusiveMaximum)) {
|
|
506
|
+
const validator = compileDollarDataExclusiveMaximum(schemaObj, jsonSchema.exclusiveMaximum.$data);
|
|
507
|
+
if (validator) validators.push(validator);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (isDollarDataRef(jsonSchema.multipleOf)) {
|
|
511
|
+
const validator = compileDollarDataMultipleOf(schemaObj, jsonSchema.multipleOf.$data);
|
|
512
|
+
if (validator) validators.push(validator);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// String constraints
|
|
516
|
+
if (isDollarDataRef(jsonSchema.minLength)) {
|
|
517
|
+
const validator = compileDollarDataMinLength(schemaObj, jsonSchema.minLength.$data);
|
|
518
|
+
if (validator) validators.push(validator);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (isDollarDataRef(jsonSchema.maxLength)) {
|
|
522
|
+
const validator = compileDollarDataMaxLength(schemaObj, jsonSchema.maxLength.$data);
|
|
523
|
+
if (validator) validators.push(validator);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (isDollarDataRef(jsonSchema.pattern)) {
|
|
527
|
+
const validator = compileDollarDataPattern(schemaObj, jsonSchema.pattern.$data);
|
|
528
|
+
if (validator) validators.push(validator);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (isDollarDataRef(jsonSchema.format)) {
|
|
532
|
+
const validator = compileDollarDataFormat(schemaObj, jsonSchema.format.$data);
|
|
533
|
+
if (validator) validators.push(validator);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Array constraints
|
|
537
|
+
if (isDollarDataRef(jsonSchema.minItems)) {
|
|
538
|
+
const validator = compileDollarDataMinItems(schemaObj, jsonSchema.minItems.$data);
|
|
539
|
+
if (validator) validators.push(validator);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (isDollarDataRef(jsonSchema.maxItems)) {
|
|
543
|
+
const validator = compileDollarDataMaxItems(schemaObj, jsonSchema.maxItems.$data);
|
|
544
|
+
if (validator) validators.push(validator);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (isDollarDataRef(jsonSchema.uniqueItems)) {
|
|
548
|
+
const validator = compileDollarDataUniqueItems(schemaObj, jsonSchema.uniqueItems.$data);
|
|
549
|
+
if (validator) validators.push(validator);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Object constraints
|
|
553
|
+
if (isDollarDataRef(jsonSchema.minProperties)) {
|
|
554
|
+
const validator = compileDollarDataMinProperties(schemaObj, jsonSchema.minProperties.$data);
|
|
555
|
+
if (validator) validators.push(validator);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (isDollarDataRef(jsonSchema.maxProperties)) {
|
|
559
|
+
const validator = compileDollarDataMaxProperties(schemaObj, jsonSchema.maxProperties.$data);
|
|
560
|
+
if (validator) validators.push(validator);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (isDollarDataRef(jsonSchema.required)) {
|
|
564
|
+
const validator = compileDollarDataRequired(schemaObj, jsonSchema.required.$data);
|
|
565
|
+
if (validator) validators.push(validator);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Enum/Const constraints
|
|
569
|
+
if (isDollarDataRef(jsonSchema.enum)) {
|
|
570
|
+
const validator = compileDollarDataEnum(schemaObj, jsonSchema.enum.$data);
|
|
571
|
+
if (validator) validators.push(validator);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (isDollarDataRef(jsonSchema.const)) {
|
|
575
|
+
const validator = compileDollarDataConst(schemaObj, jsonSchema.const.$data);
|
|
576
|
+
if (validator) validators.push(validator);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (validators.length === 0) {
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (validators.length === 1) {
|
|
584
|
+
return validators[0];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return function validateDollarDataSchema(data, dataPath, dataRoot) {
|
|
588
|
+
for (let i = 0; i < validators.length; i++) {
|
|
589
|
+
if (!validators[i](data, dataPath, dataRoot)) {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return true;
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Check if the schema has any $data references
|
|
599
|
+
* This is used to determine if we should use the $data-aware compilation path
|
|
600
|
+
* or the standard static compilation path.
|
|
601
|
+
*
|
|
602
|
+
* @param {object} jsonSchema - The JSON schema to check
|
|
603
|
+
* @returns {boolean} True if the schema has any $data references
|
|
604
|
+
*/
|
|
605
|
+
export function hasDollarDataReferences(jsonSchema) {
|
|
606
|
+
if (!isObjectType(jsonSchema)) return false;
|
|
607
|
+
|
|
608
|
+
// Keywords that can have $data values
|
|
609
|
+
const keywords = [
|
|
610
|
+
'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf',
|
|
611
|
+
'minLength', 'maxLength', 'pattern', 'format',
|
|
612
|
+
'minItems', 'maxItems', 'uniqueItems',
|
|
613
|
+
'minProperties', 'maxProperties', 'required',
|
|
614
|
+
'enum', 'const'
|
|
615
|
+
];
|
|
616
|
+
|
|
617
|
+
for (const keyword of keywords) {
|
|
618
|
+
if (isDollarDataRef(jsonSchema[keyword])) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
export function isDollarDataReference(jsonSchema) {
|
|
627
|
+
return (jsonSchema !== null && (isObjectType(jsonSchema) && jsonSchema.$data));
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|