@cfworker/json-schema 1.8.3 → 1.11.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/README.md +4 -2
- package/dist/dereference.d.ts +1 -0
- package/dist/dereference.js +14 -4
- package/dist/types.d.ts +3 -1
- package/dist/validate.d.ts +2 -4
- package/dist/validate.js +87 -55
- package/package.json +9 -10
- package/src/dereference.ts +17 -4
- package/src/format.ts +24 -12
- package/src/types.ts +3 -1
- package/src/validate.ts +137 -87
- package/tsconfig.json +8 -3
- package/LICENSE.md +0 -21
package/README.md
CHANGED
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, and
|
|
9
|
+
A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, 2019-09, and 2020-12.
|
|
10
|
+
|
|
11
|
+
This library is validated against the [json-schema-test-suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite), a series of approximately 4,500 assertions maintained along with the json-schema specification. A small set of test cases are intentionally not supported due to performance constraints or lack of feature use. These list of unsupported features are maintained in [test/unsupported.ts](./test/unsupported.ts). While this library is not the fastest due to lack of code generation, it's consistently among the [most spec compliant](https://json-schema.org/implementations.html#benchmarks).
|
|
10
12
|
|
|
11
13
|
## background
|
|
12
14
|
|
|
13
15
|
_Why another JSON schema validator?_
|
|
14
16
|
|
|
15
17
|
Cloudflare workers do not have APIs required by [Ajv](https://ajv.js.org/) schema compilation (`eval` or `new Function(code)`).
|
|
16
|
-
If possible use Ajv in a build step to precompile your schema. Otherwise
|
|
18
|
+
If possible use Ajv in a build step to precompile your schema. Otherwise this library could work for you.
|
|
17
19
|
|
|
18
20
|
## basic usage
|
|
19
21
|
|
package/dist/dereference.d.ts
CHANGED
package/dist/dereference.js
CHANGED
|
@@ -13,6 +13,7 @@ export const schemaKeyword = {
|
|
|
13
13
|
else: true
|
|
14
14
|
};
|
|
15
15
|
export const schemaArrayKeyword = {
|
|
16
|
+
prefixItems: true,
|
|
16
17
|
items: true,
|
|
17
18
|
allOf: true,
|
|
18
19
|
anyOf: true,
|
|
@@ -54,13 +55,14 @@ export const ignoredKeyword = {
|
|
|
54
55
|
minProperties: true
|
|
55
56
|
};
|
|
56
57
|
export let initialBaseURI = typeof self !== 'undefined' && self.location
|
|
57
|
-
?
|
|
58
|
+
?
|
|
59
|
+
new URL(self.location.origin + self.location.pathname + location.search)
|
|
58
60
|
: new URL('https://github.com/cfworker');
|
|
59
61
|
export function dereference(schema, lookup = Object.create(null), baseURI = initialBaseURI, basePointer = '') {
|
|
60
62
|
if (schema && typeof schema === 'object' && !Array.isArray(schema)) {
|
|
61
63
|
const id = schema.$id || schema.id;
|
|
62
64
|
if (id) {
|
|
63
|
-
const url = new URL(id, baseURI);
|
|
65
|
+
const url = new URL(id, baseURI.href);
|
|
64
66
|
if (url.hash.length > 1) {
|
|
65
67
|
lookup[url.href] = schema;
|
|
66
68
|
}
|
|
@@ -93,15 +95,23 @@ export function dereference(schema, lookup = Object.create(null), baseURI = init
|
|
|
93
95
|
});
|
|
94
96
|
}
|
|
95
97
|
if (schema.$ref && schema.__absolute_ref__ === undefined) {
|
|
96
|
-
const url = new URL(schema.$ref, baseURI);
|
|
98
|
+
const url = new URL(schema.$ref, baseURI.href);
|
|
97
99
|
url.hash = url.hash;
|
|
98
100
|
Object.defineProperty(schema, '__absolute_ref__', {
|
|
99
101
|
enumerable: false,
|
|
100
102
|
value: url.href
|
|
101
103
|
});
|
|
102
104
|
}
|
|
105
|
+
if (schema.$recursiveRef && schema.__absolute_recursive_ref__ === undefined) {
|
|
106
|
+
const url = new URL(schema.$recursiveRef, baseURI.href);
|
|
107
|
+
url.hash = url.hash;
|
|
108
|
+
Object.defineProperty(schema, '__absolute_recursive_ref__', {
|
|
109
|
+
enumerable: false,
|
|
110
|
+
value: url.href
|
|
111
|
+
});
|
|
112
|
+
}
|
|
103
113
|
if (schema.$anchor) {
|
|
104
|
-
const url = new URL('#' + schema.$anchor, baseURI);
|
|
114
|
+
const url = new URL('#' + schema.$anchor, baseURI.href);
|
|
105
115
|
lookup[url.href] = schema;
|
|
106
116
|
}
|
|
107
117
|
for (let key in schema) {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare type SchemaDraft = '4' | '7' | '2019-09';
|
|
1
|
+
export declare type SchemaDraft = '4' | '7' | '2019-09' | '2020-12';
|
|
2
2
|
export declare const enum OutputFormat {
|
|
3
3
|
Flag = 1,
|
|
4
4
|
Basic = 2,
|
|
@@ -37,6 +37,7 @@ export interface Schema {
|
|
|
37
37
|
dependentRequired?: Record<string, string[]>;
|
|
38
38
|
dependentSchemas?: Record<string, Schema>;
|
|
39
39
|
dependencies?: Record<string, Schema | string[]>;
|
|
40
|
+
prefixItems?: Array<Schema | boolean>[];
|
|
40
41
|
items?: Schema | boolean | Array<Schema | boolean>;
|
|
41
42
|
additionalItems?: Schema | boolean;
|
|
42
43
|
unevaluatedItems?: Schema | boolean;
|
|
@@ -55,6 +56,7 @@ export interface Schema {
|
|
|
55
56
|
maxLength?: number;
|
|
56
57
|
pattern?: string;
|
|
57
58
|
__absolute_ref__?: string;
|
|
59
|
+
__absolute_recursive_ref__?: string;
|
|
58
60
|
__absolute_uri__?: string;
|
|
59
61
|
[key: string]: any;
|
|
60
62
|
}
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import { Schema, SchemaDraft, ValidationResult } from './types.js';
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
items?: number;
|
|
5
|
-
}): ValidationResult;
|
|
2
|
+
export declare type Evaluated = Record<string | number, boolean>;
|
|
3
|
+
export declare function validate(instance: any, schema: Schema | boolean, draft?: SchemaDraft, lookup?: Record<string, boolean | Schema>, shortCircuit?: boolean, recursiveAnchor?: Schema | null, instanceLocation?: string, schemaLocation?: string, evaluated?: Evaluated): ValidationResult;
|
package/dist/validate.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dereference } from './dereference.js';
|
|
|
3
3
|
import { fastFormat } from './format.js';
|
|
4
4
|
import { encodePointer } from './pointer.js';
|
|
5
5
|
import { ucs2length } from './ucs2-length.js';
|
|
6
|
-
export function validate(instance, schema, draft = '2019-09', lookup = dereference(schema), shortCircuit = true, recursiveAnchor = null, instanceLocation = '#', schemaLocation = '#', evaluated) {
|
|
6
|
+
export function validate(instance, schema, draft = '2019-09', lookup = dereference(schema), shortCircuit = true, recursiveAnchor = null, instanceLocation = '#', schemaLocation = '#', evaluated = Object.create(null)) {
|
|
7
7
|
if (schema === true) {
|
|
8
8
|
return { valid: true, errors: [] };
|
|
9
9
|
}
|
|
@@ -34,18 +34,34 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
34
34
|
}
|
|
35
35
|
else if (Array.isArray(instance)) {
|
|
36
36
|
instanceType = 'array';
|
|
37
|
-
evaluated = evaluated || { items: -1 };
|
|
38
37
|
}
|
|
39
38
|
else {
|
|
40
39
|
instanceType = 'object';
|
|
41
|
-
evaluated = evaluated || { properties: Object.create(null) };
|
|
42
40
|
}
|
|
43
41
|
break;
|
|
44
42
|
default:
|
|
45
43
|
throw new Error(`Instances of "${rawInstanceType}" type are not supported.`);
|
|
46
44
|
}
|
|
47
|
-
const { $ref, $recursiveRef, $recursiveAnchor, type: $type, const: $const, enum: $enum, required: $required, not: $not, anyOf: $anyOf, allOf: $allOf, oneOf: $oneOf, if: $if, then: $then, else: $else, format: $format, properties: $properties, patternProperties: $patternProperties, additionalProperties: $additionalProperties, unevaluatedProperties: $unevaluatedProperties, minProperties: $minProperties, maxProperties: $maxProperties, propertyNames: $propertyNames, dependentRequired: $dependentRequired, dependentSchemas: $dependentSchemas, dependencies: $dependencies, items: $items, additionalItems: $additionalItems, unevaluatedItems: $unevaluatedItems, contains: $contains, minContains: $minContains, maxContains: $maxContains, minItems: $minItems, maxItems: $maxItems, uniqueItems: $uniqueItems, minimum: $minimum, maximum: $maximum, exclusiveMinimum: $exclusiveMinimum, exclusiveMaximum: $exclusiveMaximum, multipleOf: $multipleOf, minLength: $minLength, maxLength: $maxLength, pattern: $pattern, __absolute_ref__ } = schema;
|
|
45
|
+
const { $ref, $recursiveRef, $recursiveAnchor, type: $type, const: $const, enum: $enum, required: $required, not: $not, anyOf: $anyOf, allOf: $allOf, oneOf: $oneOf, if: $if, then: $then, else: $else, format: $format, properties: $properties, patternProperties: $patternProperties, additionalProperties: $additionalProperties, unevaluatedProperties: $unevaluatedProperties, minProperties: $minProperties, maxProperties: $maxProperties, propertyNames: $propertyNames, dependentRequired: $dependentRequired, dependentSchemas: $dependentSchemas, dependencies: $dependencies, prefixItems: $prefixItems, items: $items, additionalItems: $additionalItems, unevaluatedItems: $unevaluatedItems, contains: $contains, minContains: $minContains, maxContains: $maxContains, minItems: $minItems, maxItems: $maxItems, uniqueItems: $uniqueItems, minimum: $minimum, maximum: $maximum, exclusiveMinimum: $exclusiveMinimum, exclusiveMaximum: $exclusiveMaximum, multipleOf: $multipleOf, minLength: $minLength, maxLength: $maxLength, pattern: $pattern, __absolute_ref__, __absolute_recursive_ref__ } = schema;
|
|
48
46
|
const errors = [];
|
|
47
|
+
if ($recursiveAnchor === true && recursiveAnchor === null) {
|
|
48
|
+
recursiveAnchor = schema;
|
|
49
|
+
}
|
|
50
|
+
if ($recursiveRef === '#') {
|
|
51
|
+
const refSchema = recursiveAnchor === null
|
|
52
|
+
? lookup[__absolute_recursive_ref__]
|
|
53
|
+
: recursiveAnchor;
|
|
54
|
+
const keywordLocation = `${schemaLocation}/$recursiveRef`;
|
|
55
|
+
const result = validate(instance, recursiveAnchor === null ? schema : recursiveAnchor, draft, lookup, shortCircuit, refSchema, instanceLocation, keywordLocation, evaluated);
|
|
56
|
+
if (!result.valid) {
|
|
57
|
+
errors.push({
|
|
58
|
+
instanceLocation,
|
|
59
|
+
keyword: '$recursiveRef',
|
|
60
|
+
keywordLocation,
|
|
61
|
+
error: 'A subschema had errors.'
|
|
62
|
+
}, ...result.errors);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
49
65
|
if ($ref !== undefined) {
|
|
50
66
|
const uri = __absolute_ref__ || $ref;
|
|
51
67
|
const refSchema = lookup[uri];
|
|
@@ -71,21 +87,6 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
71
87
|
return { valid: errors.length === 0, errors };
|
|
72
88
|
}
|
|
73
89
|
}
|
|
74
|
-
if ($recursiveAnchor === true && recursiveAnchor === null) {
|
|
75
|
-
recursiveAnchor = schema;
|
|
76
|
-
}
|
|
77
|
-
if ($recursiveRef === '#') {
|
|
78
|
-
const keywordLocation = `${schemaLocation}/$recursiveRef`;
|
|
79
|
-
const result = validate(instance, recursiveAnchor === null ? schema : recursiveAnchor, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, keywordLocation, evaluated);
|
|
80
|
-
if (!result.valid) {
|
|
81
|
-
errors.push({
|
|
82
|
-
instanceLocation,
|
|
83
|
-
keyword: '$recursiveRef',
|
|
84
|
-
keywordLocation,
|
|
85
|
-
error: 'A subschema had errors.'
|
|
86
|
-
}, ...result.errors);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
90
|
if (Array.isArray($type)) {
|
|
90
91
|
let length = $type.length;
|
|
91
92
|
let valid = false;
|
|
@@ -178,15 +179,20 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
178
179
|
});
|
|
179
180
|
}
|
|
180
181
|
}
|
|
182
|
+
let subEvaluateds = [];
|
|
181
183
|
if ($anyOf !== undefined) {
|
|
182
184
|
const keywordLocation = `${schemaLocation}/anyOf`;
|
|
183
185
|
const errorsLength = errors.length;
|
|
184
186
|
let anyValid = false;
|
|
185
187
|
for (let i = 0; i < $anyOf.length; i++) {
|
|
186
188
|
const subSchema = $anyOf[i];
|
|
187
|
-
const
|
|
189
|
+
const subEvaluated = Object.create(evaluated);
|
|
190
|
+
const result = validate(instance, subSchema, draft, lookup, shortCircuit, $recursiveAnchor === true ? recursiveAnchor : null, instanceLocation, `${keywordLocation}/${i}`, subEvaluated);
|
|
188
191
|
errors.push(...result.errors);
|
|
189
192
|
anyValid = anyValid || result.valid;
|
|
193
|
+
if (result.valid) {
|
|
194
|
+
subEvaluateds.push(subEvaluated);
|
|
195
|
+
}
|
|
190
196
|
}
|
|
191
197
|
if (anyValid) {
|
|
192
198
|
errors.length = errorsLength;
|
|
@@ -206,9 +212,13 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
206
212
|
let allValid = true;
|
|
207
213
|
for (let i = 0; i < $allOf.length; i++) {
|
|
208
214
|
const subSchema = $allOf[i];
|
|
209
|
-
const
|
|
215
|
+
const subEvaluated = Object.create(evaluated);
|
|
216
|
+
const result = validate(instance, subSchema, draft, lookup, shortCircuit, $recursiveAnchor === true ? recursiveAnchor : null, instanceLocation, `${keywordLocation}/${i}`, subEvaluated);
|
|
210
217
|
errors.push(...result.errors);
|
|
211
218
|
allValid = allValid && result.valid;
|
|
219
|
+
if (result.valid) {
|
|
220
|
+
subEvaluateds.push(subEvaluated);
|
|
221
|
+
}
|
|
212
222
|
}
|
|
213
223
|
if (allValid) {
|
|
214
224
|
errors.length = errorsLength;
|
|
@@ -226,8 +236,12 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
226
236
|
const keywordLocation = `${schemaLocation}/oneOf`;
|
|
227
237
|
const errorsLength = errors.length;
|
|
228
238
|
const matches = $oneOf.filter((subSchema, i) => {
|
|
229
|
-
const
|
|
239
|
+
const subEvaluated = Object.create(evaluated);
|
|
240
|
+
const result = validate(instance, subSchema, draft, lookup, shortCircuit, $recursiveAnchor === true ? recursiveAnchor : null, instanceLocation, `${keywordLocation}/${i}`, subEvaluated);
|
|
230
241
|
errors.push(...result.errors);
|
|
242
|
+
if (result.valid) {
|
|
243
|
+
subEvaluateds.push(subEvaluated);
|
|
244
|
+
}
|
|
231
245
|
return result.valid;
|
|
232
246
|
}).length;
|
|
233
247
|
if (matches === 1) {
|
|
@@ -242,6 +256,9 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
242
256
|
});
|
|
243
257
|
}
|
|
244
258
|
}
|
|
259
|
+
if (instanceType === 'object' || instanceType === 'array') {
|
|
260
|
+
Object.assign(evaluated, ...subEvaluateds);
|
|
261
|
+
}
|
|
245
262
|
if ($if !== undefined) {
|
|
246
263
|
const keywordLocation = `${schemaLocation}/if`;
|
|
247
264
|
const conditionResult = validate(instance, $if, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, keywordLocation, evaluated).valid;
|
|
@@ -381,9 +398,6 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
381
398
|
}
|
|
382
399
|
}
|
|
383
400
|
const thisEvaluated = Object.create(null);
|
|
384
|
-
if (!evaluated || !evaluated.properties) {
|
|
385
|
-
throw new Error('evaluated.properties should be an object');
|
|
386
|
-
}
|
|
387
401
|
let stop = false;
|
|
388
402
|
if ($properties !== undefined) {
|
|
389
403
|
const keywordLocation = `${schemaLocation}/properties`;
|
|
@@ -394,7 +408,7 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
394
408
|
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
|
|
395
409
|
const result = validate(instance[key], $properties[key], draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, `${keywordLocation}/${encodePointer(key)}`);
|
|
396
410
|
if (result.valid) {
|
|
397
|
-
evaluated
|
|
411
|
+
evaluated[key] = thisEvaluated[key] = true;
|
|
398
412
|
}
|
|
399
413
|
else {
|
|
400
414
|
stop = shortCircuit;
|
|
@@ -421,7 +435,7 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
421
435
|
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
|
|
422
436
|
const result = validate(instance[key], subSchema, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, `${keywordLocation}/${encodePointer(pattern)}`);
|
|
423
437
|
if (result.valid) {
|
|
424
|
-
evaluated
|
|
438
|
+
evaluated[key] = thisEvaluated[key] = true;
|
|
425
439
|
}
|
|
426
440
|
else {
|
|
427
441
|
stop = shortCircuit;
|
|
@@ -444,7 +458,7 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
444
458
|
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
|
|
445
459
|
const result = validate(instance[key], $additionalProperties, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, keywordLocation);
|
|
446
460
|
if (result.valid) {
|
|
447
|
-
evaluated
|
|
461
|
+
evaluated[key] = true;
|
|
448
462
|
}
|
|
449
463
|
else {
|
|
450
464
|
stop = shortCircuit;
|
|
@@ -460,11 +474,11 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
460
474
|
else if (!stop && $unevaluatedProperties !== undefined) {
|
|
461
475
|
const keywordLocation = `${schemaLocation}/unevaluatedProperties`;
|
|
462
476
|
for (const key in instance) {
|
|
463
|
-
if (!evaluated
|
|
477
|
+
if (!evaluated[key]) {
|
|
464
478
|
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
|
|
465
479
|
const result = validate(instance[key], $unevaluatedProperties, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, keywordLocation);
|
|
466
480
|
if (result.valid) {
|
|
467
|
-
evaluated
|
|
481
|
+
evaluated[key] = true;
|
|
468
482
|
}
|
|
469
483
|
else {
|
|
470
484
|
errors.push({
|
|
@@ -495,18 +509,35 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
495
509
|
error: `Array has too few items (${instance.length} < ${$minItems}).`
|
|
496
510
|
});
|
|
497
511
|
}
|
|
498
|
-
if (!evaluated || evaluated.items === undefined) {
|
|
499
|
-
throw new Error('evaluated.items should be a number');
|
|
500
|
-
}
|
|
501
512
|
const length = instance.length;
|
|
502
513
|
let i = 0;
|
|
503
514
|
let stop = false;
|
|
515
|
+
if ($prefixItems !== undefined) {
|
|
516
|
+
const keywordLocation = `${schemaLocation}/prefixItems`;
|
|
517
|
+
const length2 = Math.min($prefixItems.length, length);
|
|
518
|
+
for (; i < length2; i++) {
|
|
519
|
+
const result = validate(instance[i], $prefixItems[i], draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, `${keywordLocation}/${i}`);
|
|
520
|
+
evaluated[i] = true;
|
|
521
|
+
if (!result.valid) {
|
|
522
|
+
stop = shortCircuit;
|
|
523
|
+
errors.push({
|
|
524
|
+
instanceLocation,
|
|
525
|
+
keyword: 'prefixItems',
|
|
526
|
+
keywordLocation,
|
|
527
|
+
error: `Items did not match schema.`
|
|
528
|
+
}, ...result.errors);
|
|
529
|
+
if (stop)
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
504
534
|
if ($items !== undefined) {
|
|
505
535
|
const keywordLocation = `${schemaLocation}/items`;
|
|
506
536
|
if (Array.isArray($items)) {
|
|
507
537
|
const length2 = Math.min($items.length, length);
|
|
508
538
|
for (; i < length2; i++) {
|
|
509
539
|
const result = validate(instance[i], $items[i], draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, `${keywordLocation}/${i}`);
|
|
540
|
+
evaluated[i] = true;
|
|
510
541
|
if (!result.valid) {
|
|
511
542
|
stop = shortCircuit;
|
|
512
543
|
errors.push({
|
|
@@ -523,6 +554,7 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
523
554
|
else {
|
|
524
555
|
for (; i < length; i++) {
|
|
525
556
|
const result = validate(instance[i], $items, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
|
|
557
|
+
evaluated[i] = true;
|
|
526
558
|
if (!result.valid) {
|
|
527
559
|
stop = shortCircuit;
|
|
528
560
|
errors.push({
|
|
@@ -536,11 +568,11 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
536
568
|
}
|
|
537
569
|
}
|
|
538
570
|
}
|
|
539
|
-
evaluated.items = Math.max(i, evaluated.items);
|
|
540
571
|
if (!stop && $additionalItems !== undefined) {
|
|
541
572
|
const keywordLocation = `${schemaLocation}/additionalItems`;
|
|
542
573
|
for (; i < length; i++) {
|
|
543
574
|
const result = validate(instance[i], $additionalItems, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
|
|
575
|
+
evaluated[i] = true;
|
|
544
576
|
if (!result.valid) {
|
|
545
577
|
stop = shortCircuit;
|
|
546
578
|
errors.push({
|
|
@@ -551,24 +583,8 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
551
583
|
}, ...result.errors);
|
|
552
584
|
}
|
|
553
585
|
}
|
|
554
|
-
evaluated.items = Math.max(i, evaluated.items);
|
|
555
586
|
}
|
|
556
587
|
}
|
|
557
|
-
if (!stop && $unevaluatedItems !== undefined) {
|
|
558
|
-
const keywordLocation = `${schemaLocation}/unevaluatedItems`;
|
|
559
|
-
for (i = Math.max(evaluated.items, 0); i < length; i++) {
|
|
560
|
-
const result = validate(instance[i], $unevaluatedItems, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
|
|
561
|
-
if (!result.valid) {
|
|
562
|
-
errors.push({
|
|
563
|
-
instanceLocation,
|
|
564
|
-
keyword: 'unevaluatedItems',
|
|
565
|
-
keywordLocation,
|
|
566
|
-
error: `Items did not match unevaluated items schema.`
|
|
567
|
-
}, ...result.errors);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
evaluated.items = Math.max(i, evaluated.items);
|
|
571
|
-
}
|
|
572
588
|
if ($contains !== undefined) {
|
|
573
589
|
if (length === 0 && $minContains === undefined) {
|
|
574
590
|
errors.push({
|
|
@@ -590,13 +606,11 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
590
606
|
const keywordLocation = `${schemaLocation}/contains`;
|
|
591
607
|
const errorsLength = errors.length;
|
|
592
608
|
let contained = 0;
|
|
593
|
-
for (let
|
|
594
|
-
const result = validate(instance[
|
|
609
|
+
for (let j = 0; j < length; j++) {
|
|
610
|
+
const result = validate(instance[j], $contains, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${j}`, keywordLocation);
|
|
595
611
|
if (result.valid) {
|
|
612
|
+
evaluated[j] = true;
|
|
596
613
|
contained++;
|
|
597
|
-
if ($minContains === undefined && $maxContains === undefined) {
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
614
|
}
|
|
601
615
|
else {
|
|
602
616
|
errors.push(...result.errors);
|
|
@@ -633,6 +647,24 @@ export function validate(instance, schema, draft = '2019-09', lookup = dereferen
|
|
|
633
647
|
}
|
|
634
648
|
}
|
|
635
649
|
}
|
|
650
|
+
if (!stop && $unevaluatedItems !== undefined) {
|
|
651
|
+
const keywordLocation = `${schemaLocation}/unevaluatedItems`;
|
|
652
|
+
for (i; i < length; i++) {
|
|
653
|
+
if (evaluated[i]) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
const result = validate(instance[i], $unevaluatedItems, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
|
|
657
|
+
evaluated[i] = true;
|
|
658
|
+
if (!result.valid) {
|
|
659
|
+
errors.push({
|
|
660
|
+
instanceLocation,
|
|
661
|
+
keyword: 'unevaluatedItems',
|
|
662
|
+
keywordLocation,
|
|
663
|
+
error: `Items did not match unevaluated items schema.`
|
|
664
|
+
}, ...result.errors);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
636
668
|
if ($uniqueItems) {
|
|
637
669
|
for (let j = 0; j < length; j++) {
|
|
638
670
|
const a = instance[j];
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cfworker/json-schema",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, and
|
|
4
|
+
"version": "1.11.0",
|
|
5
|
+
"description": "A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, 2019-09, and 2020-12.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"json-schema",
|
|
8
8
|
"jsonschema",
|
|
@@ -39,13 +39,12 @@
|
|
|
39
39
|
"test": "cfworker test test/**/*.spec.ts --nocheck"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@cfworker/dev": "^1.
|
|
43
|
-
"@types/chai": "^4.2.
|
|
44
|
-
"@types/mocha": "^
|
|
42
|
+
"@cfworker/dev": "^1.11.0",
|
|
43
|
+
"@types/chai": "^4.2.22",
|
|
44
|
+
"@types/mocha": "^9.0.0",
|
|
45
45
|
"chai": "^4.3.4",
|
|
46
|
-
"json-schema-test-suite": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite#
|
|
47
|
-
"mocha": "^
|
|
48
|
-
"typescript": "^4.2
|
|
49
|
-
}
|
|
50
|
-
"gitHead": "674fb1649bd1686f9918ac90436da709381cffb9"
|
|
46
|
+
"json-schema-test-suite": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite#76b529f",
|
|
47
|
+
"mocha": "^9.1.3",
|
|
48
|
+
"typescript": "^4.5.2"
|
|
49
|
+
}
|
|
51
50
|
}
|
package/src/dereference.ts
CHANGED
|
@@ -16,6 +16,7 @@ export const schemaKeyword: Record<string, boolean> = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
export const schemaArrayKeyword: Record<string, boolean> = {
|
|
19
|
+
prefixItems: true,
|
|
19
20
|
items: true,
|
|
20
21
|
allOf: true,
|
|
21
22
|
anyOf: true,
|
|
@@ -65,8 +66,10 @@ export const ignoredKeyword: Record<string, boolean> = {
|
|
|
65
66
|
* https://tools.ietf.org/html/rfc3986#section-5.1
|
|
66
67
|
*/
|
|
67
68
|
export let initialBaseURI =
|
|
69
|
+
// @ts-ignore
|
|
68
70
|
typeof self !== 'undefined' && self.location
|
|
69
|
-
?
|
|
71
|
+
? //@ts-ignore
|
|
72
|
+
new URL(self.location.origin + self.location.pathname + location.search)
|
|
70
73
|
: new URL('https://github.com/cfworker');
|
|
71
74
|
|
|
72
75
|
export function dereference(
|
|
@@ -78,7 +81,7 @@ export function dereference(
|
|
|
78
81
|
if (schema && typeof schema === 'object' && !Array.isArray(schema)) {
|
|
79
82
|
const id: string = schema.$id || schema.id;
|
|
80
83
|
if (id) {
|
|
81
|
-
const url = new URL(id, baseURI);
|
|
84
|
+
const url = new URL(id, baseURI.href);
|
|
82
85
|
if (url.hash.length > 1) {
|
|
83
86
|
lookup[url.href] = schema;
|
|
84
87
|
} else {
|
|
@@ -116,7 +119,7 @@ export function dereference(
|
|
|
116
119
|
|
|
117
120
|
// if a $ref is found, resolve it's absolute URI.
|
|
118
121
|
if (schema.$ref && schema.__absolute_ref__ === undefined) {
|
|
119
|
-
const url = new URL(schema.$ref, baseURI);
|
|
122
|
+
const url = new URL(schema.$ref, baseURI.href);
|
|
120
123
|
url.hash = url.hash; // normalize hash https://url.spec.whatwg.org/#dom-url-hash
|
|
121
124
|
Object.defineProperty(schema, '__absolute_ref__', {
|
|
122
125
|
enumerable: false,
|
|
@@ -124,9 +127,19 @@ export function dereference(
|
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
129
|
|
|
130
|
+
// if a $recursiveRef is found, resolve it's absolute URI.
|
|
131
|
+
if (schema.$recursiveRef && schema.__absolute_recursive_ref__ === undefined) {
|
|
132
|
+
const url = new URL(schema.$recursiveRef, baseURI.href);
|
|
133
|
+
url.hash = url.hash; // normalize hash https://url.spec.whatwg.org/#dom-url-hash
|
|
134
|
+
Object.defineProperty(schema, '__absolute_recursive_ref__', {
|
|
135
|
+
enumerable: false,
|
|
136
|
+
value: url.href
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
127
140
|
// if an $anchor is found, compute it's URI and add it to the mapping.
|
|
128
141
|
if (schema.$anchor) {
|
|
129
|
-
const url = new URL('#' + schema.$anchor, baseURI);
|
|
142
|
+
const url = new URL('#' + schema.$anchor, baseURI.href);
|
|
130
143
|
lookup[url.href] = schema;
|
|
131
144
|
}
|
|
132
145
|
|
package/src/format.ts
CHANGED
|
@@ -3,27 +3,36 @@
|
|
|
3
3
|
const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
4
4
|
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
5
5
|
const TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
|
|
6
|
-
const HOSTNAME =
|
|
6
|
+
const HOSTNAME =
|
|
7
|
+
/^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i;
|
|
7
8
|
// const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
8
|
-
const URIREF =
|
|
9
|
+
const URIREF =
|
|
10
|
+
/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
9
11
|
// uri-template: https://tools.ietf.org/html/rfc6570
|
|
10
|
-
const URITEMPLATE =
|
|
12
|
+
const URITEMPLATE =
|
|
13
|
+
/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
|
|
11
14
|
// For the source: https://gist.github.com/dperini/729294
|
|
12
15
|
// For test cases: https://mathiasbynens.be/demo/url-regex
|
|
13
|
-
const URL_ =
|
|
16
|
+
const URL_ =
|
|
17
|
+
/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu;
|
|
14
18
|
const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
|
|
15
19
|
const JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
|
|
16
|
-
const JSON_POINTER_URI_FRAGMENT =
|
|
17
|
-
|
|
20
|
+
const JSON_POINTER_URI_FRAGMENT =
|
|
21
|
+
/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
|
|
22
|
+
const RELATIVE_JSON_POINTER =
|
|
23
|
+
/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
|
|
18
24
|
|
|
19
25
|
// date: http://tools.ietf.org/html/rfc3339#section-5.6
|
|
20
26
|
const FASTDATE = /^\d\d\d\d-[0-1]\d-[0-3]\d$/;
|
|
21
27
|
// date-time: http://tools.ietf.org/html/rfc3339#section-5.6
|
|
22
|
-
const FASTTIME =
|
|
23
|
-
|
|
28
|
+
const FASTTIME =
|
|
29
|
+
/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i;
|
|
30
|
+
const FASTDATETIME =
|
|
31
|
+
/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i;
|
|
24
32
|
// uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js
|
|
25
33
|
// const FASTURI = /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i;
|
|
26
|
-
const FASTURIREFERENCE =
|
|
34
|
+
const FASTURIREFERENCE =
|
|
35
|
+
/^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i;
|
|
27
36
|
|
|
28
37
|
// https://github.com/ExodusMovement/schemasafe/blob/master/src/formats.js
|
|
29
38
|
const EMAIL = (input: string) => {
|
|
@@ -50,9 +59,11 @@ const EMAIL = (input: string) => {
|
|
|
50
59
|
};
|
|
51
60
|
|
|
52
61
|
// optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html
|
|
53
|
-
const IPV4 =
|
|
62
|
+
const IPV4 =
|
|
63
|
+
/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
|
|
54
64
|
// optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
|
|
55
|
-
const IPV6 =
|
|
65
|
+
const IPV6 =
|
|
66
|
+
/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i;
|
|
56
67
|
|
|
57
68
|
// https://github.com/ExodusMovement/schemasafe/blob/master/src/formats.js
|
|
58
69
|
const DURATION = (input: string) =>
|
|
@@ -141,7 +152,8 @@ function date_time(str: string) {
|
|
|
141
152
|
}
|
|
142
153
|
|
|
143
154
|
const NOT_URI_FRAGMENT = /\/|:/;
|
|
144
|
-
const URI_PATTERN =
|
|
155
|
+
const URI_PATTERN =
|
|
156
|
+
/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
145
157
|
|
|
146
158
|
function uri(str: string): boolean {
|
|
147
159
|
// http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "."
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SchemaDraft = '4' | '7' | '2019-09';
|
|
1
|
+
export type SchemaDraft = '4' | '7' | '2019-09' | '2020-12';
|
|
2
2
|
|
|
3
3
|
export const enum OutputFormat {
|
|
4
4
|
Flag = 1 << 0,
|
|
@@ -51,6 +51,7 @@ export interface Schema {
|
|
|
51
51
|
dependentSchemas?: Record<string, Schema>;
|
|
52
52
|
dependencies?: Record<string, Schema | string[]>;
|
|
53
53
|
|
|
54
|
+
prefixItems?: Array<Schema | boolean>[];
|
|
54
55
|
items?: Schema | boolean | Array<Schema | boolean>;
|
|
55
56
|
additionalItems?: Schema | boolean;
|
|
56
57
|
unevaluatedItems?: Schema | boolean;
|
|
@@ -72,6 +73,7 @@ export interface Schema {
|
|
|
72
73
|
pattern?: string;
|
|
73
74
|
|
|
74
75
|
__absolute_ref__?: string;
|
|
76
|
+
__absolute_recursive_ref__?: string;
|
|
75
77
|
__absolute_uri__?: string;
|
|
76
78
|
|
|
77
79
|
[key: string]: any;
|
package/src/validate.ts
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
} from './types.js';
|
|
12
12
|
import { ucs2length } from './ucs2-length.js';
|
|
13
13
|
|
|
14
|
+
export type Evaluated = Record<string | number, boolean>;
|
|
15
|
+
|
|
14
16
|
export function validate(
|
|
15
17
|
instance: any,
|
|
16
18
|
schema: Schema | boolean,
|
|
@@ -20,7 +22,7 @@ export function validate(
|
|
|
20
22
|
recursiveAnchor: Schema | null = null,
|
|
21
23
|
instanceLocation = '#',
|
|
22
24
|
schemaLocation = '#',
|
|
23
|
-
evaluated
|
|
25
|
+
evaluated: Evaluated = Object.create(null)
|
|
24
26
|
): ValidationResult {
|
|
25
27
|
if (schema === true) {
|
|
26
28
|
return { valid: true, errors: [] };
|
|
@@ -53,10 +55,8 @@ export function validate(
|
|
|
53
55
|
instanceType = 'null';
|
|
54
56
|
} else if (Array.isArray(instance)) {
|
|
55
57
|
instanceType = 'array';
|
|
56
|
-
evaluated = evaluated || { items: -1 };
|
|
57
58
|
} else {
|
|
58
59
|
instanceType = 'object';
|
|
59
|
-
evaluated = evaluated || { properties: Object.create(null) };
|
|
60
60
|
}
|
|
61
61
|
break;
|
|
62
62
|
default:
|
|
@@ -95,6 +95,7 @@ export function validate(
|
|
|
95
95
|
dependentSchemas: $dependentSchemas,
|
|
96
96
|
dependencies: $dependencies,
|
|
97
97
|
|
|
98
|
+
prefixItems: $prefixItems,
|
|
98
99
|
items: $items,
|
|
99
100
|
additionalItems: $additionalItems,
|
|
100
101
|
unevaluatedItems: $unevaluatedItems,
|
|
@@ -115,30 +116,29 @@ export function validate(
|
|
|
115
116
|
maxLength: $maxLength,
|
|
116
117
|
pattern: $pattern,
|
|
117
118
|
|
|
118
|
-
__absolute_ref__
|
|
119
|
+
__absolute_ref__,
|
|
120
|
+
__absolute_recursive_ref__
|
|
119
121
|
} = schema;
|
|
120
122
|
|
|
121
123
|
const errors: OutputUnit[] = [];
|
|
122
124
|
|
|
123
|
-
if ($
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
const keywordLocation = `${schemaLocation}/$ref`;
|
|
125
|
+
if ($recursiveAnchor === true && recursiveAnchor === null) {
|
|
126
|
+
recursiveAnchor = schema;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if ($recursiveRef === '#') {
|
|
130
|
+
const refSchema =
|
|
131
|
+
recursiveAnchor === null
|
|
132
|
+
? (lookup[__absolute_recursive_ref__!] as Schema)
|
|
133
|
+
: recursiveAnchor;
|
|
134
|
+
const keywordLocation = `${schemaLocation}/$recursiveRef`;
|
|
135
135
|
const result = validate(
|
|
136
136
|
instance,
|
|
137
|
-
|
|
137
|
+
recursiveAnchor === null ? schema : recursiveAnchor,
|
|
138
138
|
draft,
|
|
139
139
|
lookup,
|
|
140
140
|
shortCircuit,
|
|
141
|
-
|
|
141
|
+
refSchema,
|
|
142
142
|
instanceLocation,
|
|
143
143
|
keywordLocation,
|
|
144
144
|
evaluated
|
|
@@ -147,27 +147,30 @@ export function validate(
|
|
|
147
147
|
errors.push(
|
|
148
148
|
{
|
|
149
149
|
instanceLocation,
|
|
150
|
-
keyword: '$
|
|
150
|
+
keyword: '$recursiveRef',
|
|
151
151
|
keywordLocation,
|
|
152
152
|
error: 'A subschema had errors.'
|
|
153
153
|
},
|
|
154
154
|
...result.errors
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
|
-
if (draft === '4' || draft === '7') {
|
|
158
|
-
return { valid: errors.length === 0, errors };
|
|
159
|
-
}
|
|
160
157
|
}
|
|
161
158
|
|
|
162
|
-
if ($
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
159
|
+
if ($ref !== undefined) {
|
|
160
|
+
const uri = __absolute_ref__ || $ref;
|
|
161
|
+
const refSchema = lookup[uri];
|
|
162
|
+
if (refSchema === undefined) {
|
|
163
|
+
let message = `Unresolved $ref "${$ref}".`;
|
|
164
|
+
if (__absolute_ref__ && __absolute_ref__ !== $ref) {
|
|
165
|
+
message += ` Absolute URI "${__absolute_ref__}".`;
|
|
166
|
+
}
|
|
167
|
+
message += `\nKnown schemas:\n- ${Object.keys(lookup).join('\n- ')}`;
|
|
168
|
+
throw new Error(message);
|
|
169
|
+
}
|
|
170
|
+
const keywordLocation = `${schemaLocation}/$ref`;
|
|
168
171
|
const result = validate(
|
|
169
172
|
instance,
|
|
170
|
-
|
|
173
|
+
refSchema,
|
|
171
174
|
draft,
|
|
172
175
|
lookup,
|
|
173
176
|
shortCircuit,
|
|
@@ -180,13 +183,16 @@ export function validate(
|
|
|
180
183
|
errors.push(
|
|
181
184
|
{
|
|
182
185
|
instanceLocation,
|
|
183
|
-
keyword: '$
|
|
186
|
+
keyword: '$ref',
|
|
184
187
|
keywordLocation,
|
|
185
188
|
error: 'A subschema had errors.'
|
|
186
189
|
},
|
|
187
190
|
...result.errors
|
|
188
191
|
);
|
|
189
192
|
}
|
|
193
|
+
if (draft === '4' || draft === '7') {
|
|
194
|
+
return { valid: errors.length === 0, errors };
|
|
195
|
+
}
|
|
190
196
|
}
|
|
191
197
|
|
|
192
198
|
if (Array.isArray($type)) {
|
|
@@ -295,25 +301,31 @@ export function validate(
|
|
|
295
301
|
}
|
|
296
302
|
}
|
|
297
303
|
|
|
304
|
+
let subEvaluateds: Array<Evaluated> = [];
|
|
305
|
+
|
|
298
306
|
if ($anyOf !== undefined) {
|
|
299
307
|
const keywordLocation = `${schemaLocation}/anyOf`;
|
|
300
308
|
const errorsLength = errors.length;
|
|
301
309
|
let anyValid = false;
|
|
302
310
|
for (let i = 0; i < $anyOf.length; i++) {
|
|
303
311
|
const subSchema = $anyOf[i];
|
|
312
|
+
const subEvaluated: Evaluated = Object.create(evaluated);
|
|
304
313
|
const result = validate(
|
|
305
314
|
instance,
|
|
306
315
|
subSchema,
|
|
307
316
|
draft,
|
|
308
317
|
lookup,
|
|
309
318
|
shortCircuit,
|
|
310
|
-
recursiveAnchor,
|
|
319
|
+
$recursiveAnchor === true ? recursiveAnchor : null,
|
|
311
320
|
instanceLocation,
|
|
312
321
|
`${keywordLocation}/${i}`,
|
|
313
|
-
|
|
322
|
+
subEvaluated
|
|
314
323
|
);
|
|
315
324
|
errors.push(...result.errors);
|
|
316
325
|
anyValid = anyValid || result.valid;
|
|
326
|
+
if (result.valid) {
|
|
327
|
+
subEvaluateds.push(subEvaluated);
|
|
328
|
+
}
|
|
317
329
|
}
|
|
318
330
|
if (anyValid) {
|
|
319
331
|
errors.length = errorsLength;
|
|
@@ -333,19 +345,23 @@ export function validate(
|
|
|
333
345
|
let allValid = true;
|
|
334
346
|
for (let i = 0; i < $allOf.length; i++) {
|
|
335
347
|
const subSchema = $allOf[i];
|
|
348
|
+
const subEvaluated: Evaluated = Object.create(evaluated);
|
|
336
349
|
const result = validate(
|
|
337
350
|
instance,
|
|
338
351
|
subSchema,
|
|
339
352
|
draft,
|
|
340
353
|
lookup,
|
|
341
354
|
shortCircuit,
|
|
342
|
-
recursiveAnchor,
|
|
355
|
+
$recursiveAnchor === true ? recursiveAnchor : null,
|
|
343
356
|
instanceLocation,
|
|
344
357
|
`${keywordLocation}/${i}`,
|
|
345
|
-
|
|
358
|
+
subEvaluated
|
|
346
359
|
);
|
|
347
360
|
errors.push(...result.errors);
|
|
348
361
|
allValid = allValid && result.valid;
|
|
362
|
+
if (result.valid) {
|
|
363
|
+
subEvaluateds.push(subEvaluated);
|
|
364
|
+
}
|
|
349
365
|
}
|
|
350
366
|
if (allValid) {
|
|
351
367
|
errors.length = errorsLength;
|
|
@@ -363,18 +379,22 @@ export function validate(
|
|
|
363
379
|
const keywordLocation = `${schemaLocation}/oneOf`;
|
|
364
380
|
const errorsLength = errors.length;
|
|
365
381
|
const matches = $oneOf.filter((subSchema, i) => {
|
|
382
|
+
const subEvaluated: Evaluated = Object.create(evaluated);
|
|
366
383
|
const result = validate(
|
|
367
384
|
instance,
|
|
368
385
|
subSchema,
|
|
369
386
|
draft,
|
|
370
387
|
lookup,
|
|
371
388
|
shortCircuit,
|
|
372
|
-
recursiveAnchor,
|
|
389
|
+
$recursiveAnchor === true ? recursiveAnchor : null,
|
|
373
390
|
instanceLocation,
|
|
374
391
|
`${keywordLocation}/${i}`,
|
|
375
|
-
|
|
392
|
+
subEvaluated
|
|
376
393
|
);
|
|
377
394
|
errors.push(...result.errors);
|
|
395
|
+
if (result.valid) {
|
|
396
|
+
subEvaluateds.push(subEvaluated);
|
|
397
|
+
}
|
|
378
398
|
return result.valid;
|
|
379
399
|
}).length;
|
|
380
400
|
if (matches === 1) {
|
|
@@ -389,6 +409,10 @@ export function validate(
|
|
|
389
409
|
}
|
|
390
410
|
}
|
|
391
411
|
|
|
412
|
+
if (instanceType === 'object' || instanceType === 'array') {
|
|
413
|
+
Object.assign(evaluated, ...subEvaluateds);
|
|
414
|
+
}
|
|
415
|
+
|
|
392
416
|
if ($if !== undefined) {
|
|
393
417
|
const keywordLocation = `${schemaLocation}/if`;
|
|
394
418
|
const conditionResult = validate(
|
|
@@ -519,7 +543,7 @@ export function validate(
|
|
|
519
543
|
const keywordLocation = `${schemaLocation}/dependantRequired`;
|
|
520
544
|
for (const key in $dependentRequired) {
|
|
521
545
|
if (key in instance) {
|
|
522
|
-
const required = $dependentRequired[key];
|
|
546
|
+
const required = $dependentRequired[key] as string[];
|
|
523
547
|
for (const dependantKey of required) {
|
|
524
548
|
if (!(dependantKey in instance)) {
|
|
525
549
|
errors.push({
|
|
@@ -568,7 +592,7 @@ export function validate(
|
|
|
568
592
|
const keywordLocation = `${schemaLocation}/dependencies`;
|
|
569
593
|
for (const key in $dependencies) {
|
|
570
594
|
if (key in instance) {
|
|
571
|
-
const propsOrSchema = $dependencies[key];
|
|
595
|
+
const propsOrSchema = $dependencies[key] as Schema | string[];
|
|
572
596
|
if (Array.isArray(propsOrSchema)) {
|
|
573
597
|
for (const dependantKey of propsOrSchema) {
|
|
574
598
|
if (!(dependantKey in instance)) {
|
|
@@ -608,9 +632,6 @@ export function validate(
|
|
|
608
632
|
}
|
|
609
633
|
|
|
610
634
|
const thisEvaluated = Object.create(null);
|
|
611
|
-
if (!evaluated || !evaluated.properties) {
|
|
612
|
-
throw new Error('evaluated.properties should be an object');
|
|
613
|
-
}
|
|
614
635
|
|
|
615
636
|
let stop = false;
|
|
616
637
|
|
|
@@ -632,7 +653,7 @@ export function validate(
|
|
|
632
653
|
`${keywordLocation}/${encodePointer(key)}`
|
|
633
654
|
);
|
|
634
655
|
if (result.valid) {
|
|
635
|
-
evaluated
|
|
656
|
+
evaluated[key] = thisEvaluated[key] = true;
|
|
636
657
|
} else {
|
|
637
658
|
stop = shortCircuit;
|
|
638
659
|
errors.push(
|
|
@@ -672,7 +693,7 @@ export function validate(
|
|
|
672
693
|
`${keywordLocation}/${encodePointer(pattern)}`
|
|
673
694
|
);
|
|
674
695
|
if (result.valid) {
|
|
675
|
-
evaluated
|
|
696
|
+
evaluated[key] = thisEvaluated[key] = true;
|
|
676
697
|
} else {
|
|
677
698
|
stop = shortCircuit;
|
|
678
699
|
errors.push(
|
|
@@ -707,7 +728,7 @@ export function validate(
|
|
|
707
728
|
keywordLocation
|
|
708
729
|
);
|
|
709
730
|
if (result.valid) {
|
|
710
|
-
evaluated
|
|
731
|
+
evaluated[key] = true;
|
|
711
732
|
} else {
|
|
712
733
|
stop = shortCircuit;
|
|
713
734
|
errors.push(
|
|
@@ -724,7 +745,7 @@ export function validate(
|
|
|
724
745
|
} else if (!stop && $unevaluatedProperties !== undefined) {
|
|
725
746
|
const keywordLocation = `${schemaLocation}/unevaluatedProperties`;
|
|
726
747
|
for (const key in instance) {
|
|
727
|
-
if (!evaluated
|
|
748
|
+
if (!evaluated[key]) {
|
|
728
749
|
const subInstancePointer = `${instanceLocation}/${encodePointer(
|
|
729
750
|
key
|
|
730
751
|
)}`;
|
|
@@ -739,7 +760,7 @@ export function validate(
|
|
|
739
760
|
keywordLocation
|
|
740
761
|
);
|
|
741
762
|
if (result.valid) {
|
|
742
|
-
evaluated
|
|
763
|
+
evaluated[key] = true;
|
|
743
764
|
} else {
|
|
744
765
|
errors.push(
|
|
745
766
|
{
|
|
@@ -773,13 +794,41 @@ export function validate(
|
|
|
773
794
|
});
|
|
774
795
|
}
|
|
775
796
|
|
|
776
|
-
if (!evaluated || evaluated.items === undefined) {
|
|
777
|
-
throw new Error('evaluated.items should be a number');
|
|
778
|
-
}
|
|
779
|
-
|
|
780
797
|
const length: number = instance.length;
|
|
781
798
|
let i = 0;
|
|
782
799
|
let stop = false;
|
|
800
|
+
|
|
801
|
+
if ($prefixItems !== undefined) {
|
|
802
|
+
const keywordLocation = `${schemaLocation}/prefixItems`;
|
|
803
|
+
const length2 = Math.min($prefixItems.length, length);
|
|
804
|
+
for (; i < length2; i++) {
|
|
805
|
+
const result = validate(
|
|
806
|
+
instance[i],
|
|
807
|
+
$prefixItems[i],
|
|
808
|
+
draft,
|
|
809
|
+
lookup,
|
|
810
|
+
shortCircuit,
|
|
811
|
+
recursiveAnchor,
|
|
812
|
+
`${instanceLocation}/${i}`,
|
|
813
|
+
`${keywordLocation}/${i}`
|
|
814
|
+
);
|
|
815
|
+
evaluated[i] = true;
|
|
816
|
+
if (!result.valid) {
|
|
817
|
+
stop = shortCircuit;
|
|
818
|
+
errors.push(
|
|
819
|
+
{
|
|
820
|
+
instanceLocation,
|
|
821
|
+
keyword: 'prefixItems',
|
|
822
|
+
keywordLocation,
|
|
823
|
+
error: `Items did not match schema.`
|
|
824
|
+
},
|
|
825
|
+
...result.errors
|
|
826
|
+
);
|
|
827
|
+
if (stop) break;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
783
832
|
if ($items !== undefined) {
|
|
784
833
|
const keywordLocation = `${schemaLocation}/items`;
|
|
785
834
|
if (Array.isArray($items)) {
|
|
@@ -795,6 +844,7 @@ export function validate(
|
|
|
795
844
|
`${instanceLocation}/${i}`,
|
|
796
845
|
`${keywordLocation}/${i}`
|
|
797
846
|
);
|
|
847
|
+
evaluated[i] = true;
|
|
798
848
|
if (!result.valid) {
|
|
799
849
|
stop = shortCircuit;
|
|
800
850
|
errors.push(
|
|
@@ -821,6 +871,7 @@ export function validate(
|
|
|
821
871
|
`${instanceLocation}/${i}`,
|
|
822
872
|
keywordLocation
|
|
823
873
|
);
|
|
874
|
+
evaluated[i] = true;
|
|
824
875
|
if (!result.valid) {
|
|
825
876
|
stop = shortCircuit;
|
|
826
877
|
errors.push(
|
|
@@ -837,8 +888,6 @@ export function validate(
|
|
|
837
888
|
}
|
|
838
889
|
}
|
|
839
890
|
|
|
840
|
-
evaluated.items = Math.max(i, evaluated.items);
|
|
841
|
-
|
|
842
891
|
if (!stop && $additionalItems !== undefined) {
|
|
843
892
|
const keywordLocation = `${schemaLocation}/additionalItems`;
|
|
844
893
|
for (; i < length; i++) {
|
|
@@ -852,6 +901,7 @@ export function validate(
|
|
|
852
901
|
`${instanceLocation}/${i}`,
|
|
853
902
|
keywordLocation
|
|
854
903
|
);
|
|
904
|
+
evaluated[i] = true;
|
|
855
905
|
if (!result.valid) {
|
|
856
906
|
stop = shortCircuit;
|
|
857
907
|
errors.push(
|
|
@@ -865,36 +915,7 @@ export function validate(
|
|
|
865
915
|
);
|
|
866
916
|
}
|
|
867
917
|
}
|
|
868
|
-
evaluated.items = Math.max(i, evaluated.items);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
if (!stop && $unevaluatedItems !== undefined) {
|
|
873
|
-
const keywordLocation = `${schemaLocation}/unevaluatedItems`;
|
|
874
|
-
for (i = Math.max(evaluated.items, 0); i < length; i++) {
|
|
875
|
-
const result = validate(
|
|
876
|
-
instance[i],
|
|
877
|
-
$unevaluatedItems,
|
|
878
|
-
draft,
|
|
879
|
-
lookup,
|
|
880
|
-
shortCircuit,
|
|
881
|
-
recursiveAnchor,
|
|
882
|
-
`${instanceLocation}/${i}`,
|
|
883
|
-
keywordLocation
|
|
884
|
-
);
|
|
885
|
-
if (!result.valid) {
|
|
886
|
-
errors.push(
|
|
887
|
-
{
|
|
888
|
-
instanceLocation,
|
|
889
|
-
keyword: 'unevaluatedItems',
|
|
890
|
-
keywordLocation,
|
|
891
|
-
error: `Items did not match unevaluated items schema.`
|
|
892
|
-
},
|
|
893
|
-
...result.errors
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
918
|
}
|
|
897
|
-
evaluated.items = Math.max(i, evaluated.items);
|
|
898
919
|
}
|
|
899
920
|
|
|
900
921
|
if ($contains !== undefined) {
|
|
@@ -916,22 +937,20 @@ export function validate(
|
|
|
916
937
|
const keywordLocation = `${schemaLocation}/contains`;
|
|
917
938
|
const errorsLength = errors.length;
|
|
918
939
|
let contained = 0;
|
|
919
|
-
for (let
|
|
940
|
+
for (let j = 0; j < length; j++) {
|
|
920
941
|
const result = validate(
|
|
921
|
-
instance[
|
|
942
|
+
instance[j],
|
|
922
943
|
$contains,
|
|
923
944
|
draft,
|
|
924
945
|
lookup,
|
|
925
946
|
shortCircuit,
|
|
926
947
|
recursiveAnchor,
|
|
927
|
-
`${instanceLocation}/${
|
|
948
|
+
`${instanceLocation}/${j}`,
|
|
928
949
|
keywordLocation
|
|
929
950
|
);
|
|
930
951
|
if (result.valid) {
|
|
952
|
+
evaluated[j] = true;
|
|
931
953
|
contained++;
|
|
932
|
-
if ($minContains === undefined && $maxContains === undefined) {
|
|
933
|
-
break;
|
|
934
|
-
}
|
|
935
954
|
} else {
|
|
936
955
|
errors.push(...result.errors);
|
|
937
956
|
}
|
|
@@ -970,6 +989,37 @@ export function validate(
|
|
|
970
989
|
}
|
|
971
990
|
}
|
|
972
991
|
|
|
992
|
+
if (!stop && $unevaluatedItems !== undefined) {
|
|
993
|
+
const keywordLocation = `${schemaLocation}/unevaluatedItems`;
|
|
994
|
+
for (i; i < length; i++) {
|
|
995
|
+
if (evaluated[i]) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
const result = validate(
|
|
999
|
+
instance[i],
|
|
1000
|
+
$unevaluatedItems,
|
|
1001
|
+
draft,
|
|
1002
|
+
lookup,
|
|
1003
|
+
shortCircuit,
|
|
1004
|
+
recursiveAnchor,
|
|
1005
|
+
`${instanceLocation}/${i}`,
|
|
1006
|
+
keywordLocation
|
|
1007
|
+
);
|
|
1008
|
+
evaluated[i] = true;
|
|
1009
|
+
if (!result.valid) {
|
|
1010
|
+
errors.push(
|
|
1011
|
+
{
|
|
1012
|
+
instanceLocation,
|
|
1013
|
+
keyword: 'unevaluatedItems',
|
|
1014
|
+
keywordLocation,
|
|
1015
|
+
error: `Items did not match unevaluated items schema.`
|
|
1016
|
+
},
|
|
1017
|
+
...result.errors
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
973
1023
|
if ($uniqueItems) {
|
|
974
1024
|
for (let j = 0; j < length; j++) {
|
|
975
1025
|
const a = instance[j];
|
package/tsconfig.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": "../../tsconfig-base",
|
|
3
3
|
"compilerOptions": {
|
|
4
|
-
"target": "
|
|
5
|
-
"lib": ["esnext"
|
|
6
|
-
"types": [
|
|
4
|
+
"target": "ES2021",
|
|
5
|
+
"lib": ["esnext"],
|
|
6
|
+
"types": [
|
|
7
|
+
"chai",
|
|
8
|
+
"mocha",
|
|
9
|
+
"@cfworker/worker-types",
|
|
10
|
+
"@cloudflare/workers-types"
|
|
11
|
+
],
|
|
7
12
|
"outDir": "dist",
|
|
8
13
|
"rootDir": "./src"
|
|
9
14
|
},
|
package/LICENSE.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020 Jeremy Danyow
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|