@jarenjs/validate 0.9.2 → 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.
- package/ARCHITECTURE.md +81 -17
- package/README.md +462 -5
- package/dist/types/dollar-data.d.ts +0 -9
- package/dist/types/index.d.ts +167 -69
- package/dist/types/messages.d.ts +142 -0
- package/dist/types/normalize.d.ts +107 -0
- package/dist/types/tools.d.ts +58 -0
- package/docs/ERROR-MESSAGES.md +251 -0
- package/package.json +9 -4
- package/src/array.js +68 -23
- package/src/bigint.js +18 -7
- package/src/combine.js +61 -11
- package/src/condition.js +34 -14
- package/src/content.js +3 -3
- package/src/data.js +11 -387
- package/src/dollar-data.js +55 -472
- package/src/enum.js +0 -1
- package/src/format.js +46 -4
- package/src/index.js +280 -238
- package/src/messages.js +497 -0
- package/src/normalize.js +585 -0
- package/src/number.js +19 -9
- package/src/object.js +126 -33
- package/src/query.js +28 -2
- package/src/schema.js +72 -27
- package/src/string.js +18 -6
- package/src/tools.js +192 -0
- package/src/traverse.js +13 -4
- package/src/unevaluated.js +27 -5
package/src/dollar-data.js
CHANGED
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
import {
|
|
37
37
|
isObjectClass,
|
|
38
38
|
isStringType,
|
|
39
|
-
isNumberType,
|
|
40
|
-
isArrayClass,
|
|
41
39
|
isObjectType,
|
|
42
40
|
} from '@jarenjs/core';
|
|
43
41
|
|
|
@@ -46,21 +44,34 @@ import {
|
|
|
46
44
|
} from '@jarenjs/core/object';
|
|
47
45
|
|
|
48
46
|
import {
|
|
49
|
-
|
|
47
|
+
NUMERIC_CONSTRAINTS, STRING_CONSTRAINTS,
|
|
48
|
+
ARRAY_CONSTRAINTS, OBJECT_CONSTRAINTS,
|
|
49
|
+
} from '@jarenjs/core/schema';
|
|
50
|
+
|
|
51
|
+
import {
|
|
52
|
+
compileDataRef,
|
|
50
53
|
JSONPOINTER_NOTHING,
|
|
51
54
|
} from '@jarenjs/json';
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
import {
|
|
57
|
+
createDataRefCompilers,
|
|
58
|
+
} from './tools.js';
|
|
56
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a `$data` reference.
|
|
62
|
+
*
|
|
63
|
+
* Ajv's `$data` takes the same three forms the `data` keyword does — empty for
|
|
64
|
+
* the root, a Relative JSON Pointer, or an absolute JSON Pointer — and this
|
|
65
|
+
* used to compile only the relative form. An absolute `$data: '/limit'` threw,
|
|
66
|
+
* was swallowed by the catch, and became `resolveNothing`: the constraint
|
|
67
|
+
* silently never asserted, so a document that should have failed passed. A
|
|
68
|
+
* disabled constraint is worse than a rejected schema, which is why an
|
|
69
|
+
* uncompilable reference is now a compile-time error.
|
|
70
|
+
* @param {string} ref
|
|
71
|
+
* @returns {(dataRoot: any, dataPath: string) => any}
|
|
72
|
+
*/
|
|
57
73
|
function compileRefResolver(ref) {
|
|
58
|
-
|
|
59
|
-
return compileRelativeJSONPointer(ref);
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
return resolveNothing;
|
|
63
|
-
}
|
|
74
|
+
return compileDataRef(ref);
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
/**
|
|
@@ -72,260 +83,12 @@ function isDollarDataRef(value) {
|
|
|
72
83
|
return isObjectClass(value) && isStringType(value.$data);
|
|
73
84
|
}
|
|
74
85
|
|
|
86
|
+
// The fifteen keyword validators are shared with the `data` keyword;
|
|
87
|
+
// only `compileRefResolver` above differs (see tools.js). uniqueItems
|
|
88
|
+
// and required below are $data-only and stay local.
|
|
89
|
+
const KEYWORD_COMPILERS = createDataRefCompilers(compileRefResolver);
|
|
75
90
|
|
|
76
|
-
//#region
|
|
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
|
-
}
|
|
91
|
+
//#region $data-only Validators
|
|
329
92
|
|
|
330
93
|
/**
|
|
331
94
|
* Compile $data reference for uniqueItems constraint
|
|
@@ -347,7 +110,7 @@ function compileDollarDataUniqueItems(schemaObj, ref) {
|
|
|
347
110
|
for (let i = 0; i < data.length; i++) {
|
|
348
111
|
for (let j = i + 1; j < data.length; j++) {
|
|
349
112
|
if (equalsDeep(data[i], data[j])) {
|
|
350
|
-
return addError(
|
|
113
|
+
return addError(data, dataPath);
|
|
351
114
|
}
|
|
352
115
|
}
|
|
353
116
|
}
|
|
@@ -355,52 +118,6 @@ function compileDollarDataUniqueItems(schemaObj, ref) {
|
|
|
355
118
|
};
|
|
356
119
|
}
|
|
357
120
|
|
|
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
121
|
/**
|
|
405
122
|
* Compile $data reference for required constraint
|
|
406
123
|
* @param {object} schemaObj - The validation object
|
|
@@ -408,7 +125,9 @@ function compileDollarDataMaxProperties(schemaObj, ref) {
|
|
|
408
125
|
* @returns {function|undefined} The compiled validator function
|
|
409
126
|
*/
|
|
410
127
|
function compileDollarDataRequired(schemaObj, ref) {
|
|
411
|
-
|
|
128
|
+
// Keyed handler: the missing property name travels as the dataKey, so
|
|
129
|
+
// error conversion extracts params.missingProperty like static required.
|
|
130
|
+
const addError = schemaObj.createErrorHandler(ref, ['required']);
|
|
412
131
|
const resolveRef = compileRefResolver(ref);
|
|
413
132
|
|
|
414
133
|
return function validateDollarDataRequired(data, dataPath, dataRoot) {
|
|
@@ -428,52 +147,26 @@ function compileDollarDataRequired(schemaObj, ref) {
|
|
|
428
147
|
|
|
429
148
|
//#endregion
|
|
430
149
|
|
|
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
150
|
//#region Main Compilation
|
|
476
151
|
|
|
152
|
+
/** Application order of the $data-capable keywords (error order is part
|
|
153
|
+
* of the observable behavior; the shared groups' internal order IS the
|
|
154
|
+
* historical per-keyword dispatch sequence — `@jarenjs/core/schema`
|
|
155
|
+
* documents that order as contract). */
|
|
156
|
+
const DOLLAR_KEYWORD_ORDER = [
|
|
157
|
+
...NUMERIC_CONSTRAINTS,
|
|
158
|
+
...STRING_CONSTRAINTS,
|
|
159
|
+
...ARRAY_CONSTRAINTS,
|
|
160
|
+
...OBJECT_CONSTRAINTS, 'required',
|
|
161
|
+
'enum', 'const',
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const DOLLAR_ONLY_COMPILERS = {
|
|
165
|
+
__proto__: null,
|
|
166
|
+
uniqueItems: compileDollarDataUniqueItems,
|
|
167
|
+
required: compileDollarDataRequired,
|
|
168
|
+
};
|
|
169
|
+
|
|
477
170
|
/**
|
|
478
171
|
* Compile $data keyword validators for a schema object
|
|
479
172
|
* This detects when keyword values are { $data: "..." } objects and creates
|
|
@@ -486,93 +179,12 @@ function compileDollarDataConst(schemaObj, ref) {
|
|
|
486
179
|
export function compileDollarDataSchema(schemaObj, jsonSchema) {
|
|
487
180
|
const validators = [];
|
|
488
181
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
}
|
|
182
|
+
for (const keyword of DOLLAR_KEYWORD_ORDER) {
|
|
183
|
+
const value = jsonSchema[keyword];
|
|
184
|
+
if (!isDollarDataRef(value)) continue;
|
|
562
185
|
|
|
563
|
-
|
|
564
|
-
const validator =
|
|
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);
|
|
186
|
+
const compileKeyword = KEYWORD_COMPILERS[keyword] ?? DOLLAR_ONLY_COMPILERS[keyword];
|
|
187
|
+
const validator = compileKeyword(schemaObj, value.$data);
|
|
576
188
|
if (validator) validators.push(validator);
|
|
577
189
|
}
|
|
578
190
|
|
|
@@ -594,35 +206,6 @@ export function compileDollarDataSchema(schemaObj, jsonSchema) {
|
|
|
594
206
|
};
|
|
595
207
|
}
|
|
596
208
|
|
|
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
209
|
export function isDollarDataReference(jsonSchema) {
|
|
627
210
|
return (jsonSchema !== null && (isObjectType(jsonSchema) && jsonSchema.$data));
|
|
628
211
|
}
|
package/src/enum.js
CHANGED
package/src/format.js
CHANGED
|
@@ -40,6 +40,11 @@ export function registerFormatCompilers(registered, formatCompilers) {
|
|
|
40
40
|
return registered;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** Named once: both wrong-shape paths end at the same mistake. */
|
|
44
|
+
const WRONG_SHAPE_HINT = 'Register the format COMPILERS (stringFormats, dateTimeFormats, '
|
|
45
|
+
+ 'numberFormats, jsonFormats, geoFormats) rather than the raw testers (formatTesters) — '
|
|
46
|
+
+ 'both are objects full of functions, and only the compilers take (schemaObj, jsonSchema).';
|
|
47
|
+
|
|
43
48
|
export function getSchemaFormatCompiler(registered, name) {
|
|
44
49
|
if (isStringType(name))
|
|
45
50
|
return registered[name];
|
|
@@ -53,14 +58,51 @@ export function compileFormatBasic(schemaObj, jsonSchema) {
|
|
|
53
58
|
|
|
54
59
|
// From draft 2020-12 on, format is annotation-only unless assertion is
|
|
55
60
|
// enabled (formatAssertion option or format-assertion vocabulary).
|
|
61
|
+
// Nothing is lost by an unregistered name here — the keyword would not
|
|
62
|
+
// have asserted even with a compiler — so the unknownFormats check
|
|
63
|
+
// below deliberately does not run in this branch. Complaining here
|
|
64
|
+
// would be complaining about the spec.
|
|
56
65
|
if (schemaObj.options.formatAssertion === false)
|
|
57
66
|
return undefined;
|
|
58
67
|
const compiler = getSchemaFormatCompiler(
|
|
59
68
|
schemaObj.formats,
|
|
60
69
|
jsonSchema.format);
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
// An unregistered name with assertion ON is the silent failure this
|
|
72
|
+
// check exists to end: the author asked for the value to be checked,
|
|
73
|
+
// the registry has nothing to check it with, and without this the
|
|
74
|
+
// schema compiles to a validator that accepts everything. Raising it
|
|
75
|
+
// at COMPILE time keeps instance validation spec-exact — no data is
|
|
76
|
+
// ever invalidated by an unknown format, the schema's AUTHOR is told.
|
|
77
|
+
if (compiler == null) {
|
|
78
|
+
if (schemaObj.options.unknownFormats === 'ignore')
|
|
79
|
+
return undefined;
|
|
80
|
+
throw new Error(`Unknown format '${jsonSchema.format}': no compiler is registered for it, `
|
|
81
|
+
+ 'so this schema would accept every value for that keyword. Register one '
|
|
82
|
+
+ '(addFormats(dateTimeFormats) etc. from @jarenjs/formats, or addFormat(name, compiler) '
|
|
83
|
+
+ "for your own), or pass { unknownFormats: 'ignore' } to accept it as an annotation.");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// A format COMPILER is called once per schema location and returns the
|
|
87
|
+
// per-value validator. Handing it a bare tester — `formatTesters`
|
|
88
|
+
// instead of `dateTimeFormats`, an easy mistake since both are objects
|
|
89
|
+
// full of functions — either returns a boolean (which the caller
|
|
90
|
+
// silently drops, so the format checks nothing again) or throws deep
|
|
91
|
+
// inside the tester on an argument it never expected. Both become one
|
|
92
|
+
// legible complaint here.
|
|
93
|
+
let validator;
|
|
94
|
+
try {
|
|
95
|
+
validator = compiler(schemaObj, jsonSchema);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
throw new Error(`Format '${jsonSchema.format}' failed to compile: ${err?.message ?? err}. `
|
|
99
|
+
+ `${WRONG_SHAPE_HINT}`, { cause: err });
|
|
100
|
+
}
|
|
101
|
+
// Nullish stays legal: a compiler may decline a schema it cannot serve.
|
|
102
|
+
if (validator != null && !isFn(validator) && !Array.isArray(validator)) {
|
|
103
|
+
throw new Error(`Format '${jsonSchema.format}' is registered with something that is not a `
|
|
104
|
+
+ `format compiler: calling it returned ${typeof validator}, not a validator function. `
|
|
105
|
+
+ `${WRONG_SHAPE_HINT}`);
|
|
106
|
+
}
|
|
107
|
+
return validator;
|
|
66
108
|
}
|