@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 CHANGED
@@ -6,14 +6,16 @@
6
6
  ![](https://badgen.net/bundlephobia/tree-shaking/@cfworker/json-schema)
7
7
  ![](https://badgen.net/npm/types/@cfworker/json-schema?icon=typescript)
8
8
 
9
- A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, and 2019-09.
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 use this library.
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
 
@@ -1,3 +1,4 @@
1
+ /// <reference types="@cloudflare/workers-types" />
1
2
  import { Schema } from './types.js';
2
3
  export declare const schemaKeyword: Record<string, boolean>;
3
4
  export declare const schemaArrayKeyword: Record<string, boolean>;
@@ -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
- ? new URL(self.location.origin + self.location.pathname + location.search)
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
  }
@@ -1,5 +1,3 @@
1
1
  import { Schema, SchemaDraft, ValidationResult } from './types.js';
2
- 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?: {
3
- properties?: Record<string, boolean>;
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 result = validate(instance, subSchema, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);
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 result = validate(instance, subSchema, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);
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 result = validate(instance, subSchema, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);
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.properties[key] = thisEvaluated[key] = true;
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.properties[key] = thisEvaluated[key] = true;
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.properties[key] = true;
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.properties[key]) {
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.properties[key] = true;
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 i = 0; i < length; i++) {
594
- const result = validate(instance[i], $contains, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
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.8.3",
5
- "description": "A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, and 2019-09.",
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.8.3",
43
- "@types/chai": "^4.2.16",
44
- "@types/mocha": "^8.2.2",
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#ac63eb7",
47
- "mocha": "^8.3.2",
48
- "typescript": "^4.2.3"
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
  }
@@ -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
- ? new URL(self.location.origin + self.location.pathname + location.search)
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 = /^(?=.{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;
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 = /^(?:[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
+ 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 = /^(?:(?:[^\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;
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_ = /^(?:(?: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;
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 = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
17
- const RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
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 = /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i;
23
- const FASTDATETIME = /^\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;
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 = /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i;
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 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
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 = /^((([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;
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 = /^(?:[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;
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?: { properties?: Record<string, boolean>; items?: number }
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 ($ref !== undefined) {
124
- const uri = __absolute_ref__ || $ref;
125
- const refSchema = lookup[uri];
126
- if (refSchema === undefined) {
127
- let message = `Unresolved $ref "${$ref}".`;
128
- if (__absolute_ref__ && __absolute_ref__ !== $ref) {
129
- message += ` Absolute URI "${__absolute_ref__}".`;
130
- }
131
- message += `\nKnown schemas:\n- ${Object.keys(lookup).join('\n- ')}`;
132
- throw new Error(message);
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
- refSchema,
137
+ recursiveAnchor === null ? schema : recursiveAnchor,
138
138
  draft,
139
139
  lookup,
140
140
  shortCircuit,
141
- recursiveAnchor,
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: '$ref',
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 ($recursiveAnchor === true && recursiveAnchor === null) {
163
- recursiveAnchor = schema;
164
- }
165
-
166
- if ($recursiveRef === '#') {
167
- const keywordLocation = `${schemaLocation}/$recursiveRef`;
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
- recursiveAnchor === null ? schema : recursiveAnchor,
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: '$recursiveRef',
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
- evaluated
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
- evaluated
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
- evaluated
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.properties[key] = thisEvaluated[key] = true;
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.properties[key] = thisEvaluated[key] = true;
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.properties[key] = true;
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.properties[key]) {
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.properties[key] = true;
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 i = 0; i < length; i++) {
940
+ for (let j = 0; j < length; j++) {
920
941
  const result = validate(
921
- instance[i],
942
+ instance[j],
922
943
  $contains,
923
944
  draft,
924
945
  lookup,
925
946
  shortCircuit,
926
947
  recursiveAnchor,
927
- `${instanceLocation}/${i}`,
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": "esnext",
5
- "lib": ["esnext", "webworker"],
6
- "types": ["chai", "mocha"],
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.