@hyperjump/json-schema 1.12.1 → 1.14.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.
Files changed (42) hide show
  1. package/README.md +15 -21
  2. package/annotations/annotated-instance.d.ts +1 -1
  3. package/annotations/annotated-instance.js +3 -32
  4. package/annotations/index.js +27 -7
  5. package/bundle/index.js +39 -227
  6. package/draft-04/additionalItems.js +13 -19
  7. package/draft-04/items.js +26 -14
  8. package/draft-2020-12/dynamicRef.js +20 -8
  9. package/lib/core.js +19 -4
  10. package/lib/evaluation-plugins/annotations.js +32 -0
  11. package/lib/evaluation-plugins/basic-output.js +34 -0
  12. package/lib/evaluation-plugins/detailed-output.js +36 -0
  13. package/lib/experimental.d.ts +20 -6
  14. package/lib/experimental.js +3 -0
  15. package/lib/instance.js +12 -3
  16. package/lib/keywords/additionalProperties.js +8 -22
  17. package/lib/keywords/allOf.js +1 -29
  18. package/lib/keywords/anyOf.js +1 -27
  19. package/lib/keywords/conditional.js +1 -27
  20. package/lib/keywords/contains.js +14 -18
  21. package/lib/keywords/contentEncoding.js +9 -1
  22. package/lib/keywords/contentMediaType.js +9 -1
  23. package/lib/keywords/contentSchema.js +17 -2
  24. package/lib/keywords/dependentSchemas.js +1 -21
  25. package/lib/keywords/dynamicRef.js +19 -6
  26. package/lib/keywords/else.js +2 -19
  27. package/lib/keywords/if.js +1 -9
  28. package/lib/keywords/itemPattern.js +5 -9
  29. package/lib/keywords/items.js +5 -14
  30. package/lib/keywords/oneOf.js +1 -33
  31. package/lib/keywords/patternProperties.js +7 -25
  32. package/lib/keywords/prefixItems.js +2 -5
  33. package/lib/keywords/properties.js +6 -22
  34. package/lib/keywords/propertyDependencies.js +1 -20
  35. package/lib/keywords/propertyNames.js +1 -1
  36. package/lib/keywords/ref.js +1 -3
  37. package/lib/keywords/then.js +2 -19
  38. package/lib/keywords/unevaluatedItems.js +44 -20
  39. package/lib/keywords/unevaluatedProperties.js +40 -26
  40. package/lib/keywords/validation.js +31 -126
  41. package/lib/keywords.js +30 -30
  42. package/package.json +1 -1
package/README.md CHANGED
@@ -248,8 +248,8 @@ The following types are used in the above definitions
248
248
 
249
249
  * **OutputFormat**: **FLAG**
250
250
 
251
- Only the `FLAG` output format is part of the Stable API. Additional output
252
- formats are included as part of the Experimental API.
251
+ Only the `FLAG` output format is part of the Stable API. Additional [output
252
+ formats](#output-formats) are included as part of the Experimental API.
253
253
  * **OutputUnit**: { valid: boolean }
254
254
 
255
255
  Output is an experimental feature of the JSON Schema specification. There
@@ -336,7 +336,8 @@ These are available from the `@hyperjump/json-schema/bundle` export.
336
336
  The `FLAG` output format isn't very informative. You can change the output
337
337
  format used for validation to get more information about failures. The official
338
338
  output format is still evolving, so these may change or be replaced in the
339
- future.
339
+ future. This implementation currently supports the BASIC and DETAILED output
340
+ formats.
340
341
 
341
342
  ```javascript
342
343
  import { BASIC } from "@hyperjump/json-schema/experimental";
@@ -536,28 +537,21 @@ These are available from the `@hyperjump/json-schema/experimental` export.
536
537
  validation of its own. In these cases, it isn't helpful to include
537
538
  them in BASIC output. This flag is used to trim those nodes from the
538
539
  output.
539
- * collectEvaluatedProperties?: (compiledKeywordValue: any, instance: JsonNode, context: ValidationContext, isTop?: boolean) => Set\<string> | false
540
-
541
- If the keyword is an applicator, it will need to implement this
542
- function for `unevaluatedProperties` to work as expected.
543
- * collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonNode, context: ValidationContext, isTop?: boolean) => Set\<number> | false
544
-
545
- If the keyword is an applicator, it will need to implement this
546
- function for `unevaluatedItems` to work as expected.
547
- * collectExternalIds?: (visited: Set\<string>, parentSchema: Browser, schema: Browser) => Set\<string>
548
- If the keyword is an applicator, it will need to implement this
549
- function to work properly with the [bundle](#bundling) feature.
550
- * annotation?: (compiledKeywordValue: any) => any
540
+ * annotation?: (compiledKeywordValue: any) => any | undefined
551
541
 
552
542
  If the keyword is an annotation, it will need to implement this
553
543
  function to return the annotation.
544
+ * plugin?: EvaluationPlugin
554
545
 
555
546
  * **ValidationContext**: object
556
547
  * ast: AST
557
- * dynamicAnchors: object
558
- * schemaUrl: string
559
- * errors: OutputUnit[]
560
- * annotations: OutputUnit[]
548
+ * plugins: EvaluationPlugins[]
549
+
550
+ * **EvaluationPlugin**: object
551
+ * beforeSchema(url: string, instance: JsonNode, context: Context): void
552
+ * beforeKeyword(keywordNode: Node<any>, instance: JsonNode, context: Context, schemaContext: Context, keyword: Keyword): void
553
+ * afterKeyword(keywordNode: Node<any>, instance: JsonNode, context: Context, valid: boolean, schemaContext: Context, keyword: Keyword): void
554
+ * afterSchema(url: string, instance: JsonNode, context: Context, valid: boolean): void
561
555
  * **defineVocabulary**: (id: string, keywords: { [keyword: string]: string }) => void
562
556
 
563
557
  Define a vocabulary that maps keyword name to keyword URIs defined using
@@ -596,7 +590,7 @@ These are available from the `@hyperjump/json-schema/experimental` export.
596
590
  * **getSchema**: (uri: string, browser?: Browser) => Promise\<Browser>
597
591
 
598
592
  Get a schema by it's URI taking the local schema registry into account.
599
- * buildSchemaDocument: (schema: SchemaObject | boolean, retrievalUri?: string, contextDialectId?: string) => SchemaDocument
593
+ * **buildSchemaDocument**: (schema: SchemaObject | boolean, retrievalUri?: string, contextDialectId?: string) => SchemaDocument
600
594
 
601
595
  Build a SchemaDocument from a JSON-compatible value. You might use this if
602
596
  you're creating a custom media type plugin, such as supporting JSON Schemas
@@ -800,7 +794,7 @@ following functions are available in addition to the functions available in the
800
794
  * **annotation**: (instance: JsonNode, keyword: string, dialect?: string): any[];
801
795
 
802
796
  Get the annotations for a keyword for the value represented by the JsonNode.
803
- * **annotatedWith**: (instance: JsonNode, keyword: string, dialect?: string): JsonNode[];
797
+ * **annotatedWith**: (instance: JsonNode, keyword: string, dialect?: string): Generator<JsonNode>;
804
798
 
805
799
  Get all JsonNodes that are annotated with the given keyword.
806
800
  * **setAnnotation**: (instance: JsonNode, keywordId: string, value: any) => JsonNode
@@ -1,4 +1,4 @@
1
1
  export const annotation: <A>(instance: JsonNode, keyword: string, dialectUri?: string) => A[];
2
- export const annotatedWith: <A extends JsonNode>(instance: A, keyword: string, dialectUri?: string) => A[];
2
+ export const annotatedWith: <A extends JsonNode>(instance: A, keyword: string, dialectUri?: string) => Generator<A>;
3
3
 
4
4
  export * from "../lib/instance.js";
@@ -1,4 +1,3 @@
1
- import * as JsonPointer from "@hyperjump/json-pointer";
2
1
  import * as Instance from "../lib/instance.js";
3
2
  import { getKeywordId } from "../lib/keywords.js";
4
3
 
@@ -7,43 +6,15 @@ const defaultDialectId = "https://json-schema.org/validation";
7
6
 
8
7
  export const annotation = (node, keyword, dialect = defaultDialectId) => {
9
8
  const keywordUri = getKeywordId(keyword, dialect);
10
-
11
- let currentNode = node.root;
12
- const errors = [...invalidSchemas(currentNode)];
13
- for (let segment of JsonPointer.pointerSegments(node.pointer)) {
14
- segment = segment === "-" && Instance.typeOf(currentNode) === "array" ? Instance.length(currentNode) : segment;
15
- currentNode = Instance.step(segment, currentNode);
16
- errors.push(...invalidSchemas(currentNode));
17
- }
18
-
19
- const annotations = [];
20
- for (const schemaLocation in node.annotations[keywordUri]) {
21
- if (!errors.some((error) => schemaLocation.startsWith(error))) {
22
- annotations.unshift(node.annotations[keywordUri][schemaLocation]);
23
- }
24
- }
25
-
26
- return annotations;
9
+ return node.annotations[keywordUri] ?? [];
27
10
  };
28
11
 
29
- const invalidSchemas = function* (node) {
30
- for (const error in node.errors) {
31
- if (node.errors[error] === "https://json-schema.org/evaluation/validate") {
32
- yield error;
33
- }
34
- }
35
- };
36
-
37
- export const annotatedWith = (instance, keyword, dialectId = defaultDialectId) => {
38
- const nodes = [];
39
-
12
+ export const annotatedWith = function* (instance, keyword, dialectId = defaultDialectId) {
40
13
  for (const node of Instance.allNodes(instance)) {
41
14
  if (annotation(node, keyword, dialectId).length > 0) {
42
- nodes.push(node);
15
+ yield node;
43
16
  }
44
17
  }
45
-
46
- return nodes;
47
18
  };
48
19
 
49
20
  export * from "../lib/instance.js";
@@ -1,6 +1,14 @@
1
1
  import { FLAG } from "../lib/index.js";
2
2
  import { ValidationError } from "./validation-error.js";
3
- import { getSchema, compile, BASIC } from "../lib/experimental.js";
3
+ import {
4
+ getSchema,
5
+ compile,
6
+ BASIC,
7
+ DETAILED,
8
+ annotationsPlugin,
9
+ basicOutputPlugin,
10
+ detailedOutputPlugin
11
+ } from "../lib/experimental.js";
4
12
  import Validation from "../lib/keywords/validation.js";
5
13
  import * as Instance from "../lib/instance.js";
6
14
 
@@ -14,23 +22,35 @@ export const annotate = async (schemaUri, json = undefined, outputFormat = undef
14
22
  };
15
23
 
16
24
  export const interpret = ({ ast, schemaUri }, instance, outputFormat = BASIC) => {
17
- const errors = [];
18
- const annotations = [];
19
- const context = { ast, dynamicAnchors: {}, errors, annotations, outputFormat };
25
+ const context = { ast, plugins: [annotationsPlugin, ...ast.plugins] };
26
+
27
+ switch (outputFormat) {
28
+ case FLAG:
29
+ break;
30
+ case BASIC:
31
+ context.plugins.push(basicOutputPlugin);
32
+ break;
33
+ case DETAILED:
34
+ context.plugins.push(detailedOutputPlugin);
35
+ break;
36
+ default:
37
+ throw Error(`Unsupported output format '${outputFormat}'`);
38
+ }
39
+
20
40
  const valid = Validation.interpret(schemaUri, instance, context);
21
41
 
22
42
  if (!valid) {
23
- const result = outputFormat === FLAG || valid ? { valid } : { valid, errors };
43
+ const result = !valid && "errors" in context ? { valid, errors: context.errors } : { valid };
24
44
  throw new ValidationError(result);
25
45
  }
26
46
 
27
- for (const annotation of annotations) {
47
+ for (const annotation of context.annotations) {
28
48
  const node = Instance.get(annotation.instanceLocation, instance);
29
49
  const keyword = annotation.keyword;
30
50
  if (!node.annotations[keyword]) {
31
51
  node.annotations[keyword] = [];
32
52
  }
33
- node.annotations[keyword].push(annotation.annotation);
53
+ node.annotations[keyword].unshift(annotation.annotation);
34
54
  }
35
55
 
36
56
  return instance;
package/bundle/index.js CHANGED
@@ -1,13 +1,8 @@
1
- import curry from "just-curry-it";
2
1
  import { v4 as uuid } from "uuid";
3
- import * as Browser from "@hyperjump/browser";
2
+ import { jrefTypeOf } from "@hyperjump/browser/jref";
4
3
  import * as JsonPointer from "@hyperjump/json-pointer";
5
- import { asyncCollectSet, asyncFilter, asyncFlatten, asyncMap, pipe } from "@hyperjump/pact";
6
- import {
7
- Validation,
8
- canonicalUri, getSchema, toSchema,
9
- getKeywordName, getKeyword, getKeywordByName
10
- } from "../lib/experimental.js";
4
+ import { resolveIri, toAbsoluteIri } from "@hyperjump/uri";
5
+ import { getSchema, toSchema, getKeywordName } from "../lib/experimental.js";
11
6
 
12
7
 
13
8
  export const URI = "uri", UUID = "uuid";
@@ -19,32 +14,36 @@ const defaultOptions = {
19
14
  };
20
15
 
21
16
  export const bundle = async (url, options = {}) => {
22
- loadKeywordSupport();
23
17
  const fullOptions = { ...defaultOptions, ...options };
24
18
 
25
19
  const mainSchema = await getSchema(url);
26
- const contextUri = mainSchema.document.baseUri;
27
- const contextDialectId = mainSchema.document.dialectId;
20
+ fullOptions.contextUri = mainSchema.document.baseUri;
21
+ fullOptions.contextDialectId = mainSchema.document.dialectId;
22
+
28
23
  const bundled = toSchema(mainSchema);
24
+ fullOptions.bundlingLocation = "/" + getKeywordName(fullOptions.contextDialectId, "https://json-schema.org/keyword/definitions");
25
+ if (JsonPointer.get(fullOptions.bundlingLocation, bundled) === undefined) {
26
+ JsonPointer.assign(fullOptions.bundlingLocation, bundled, {});
27
+ }
29
28
 
30
- const externalIds = await Validation.collectExternalIds(new Set(), mainSchema, mainSchema);
29
+ return await doBundling(mainSchema.uri, bundled, fullOptions);
30
+ };
31
31
 
32
- // Bundle
33
- const bundlingLocation = "/" + getKeywordName(contextDialectId, "https://json-schema.org/keyword/definitions");
34
- if (JsonPointer.get(bundlingLocation, bundled) === undefined && externalIds.size > 0) {
35
- JsonPointer.assign(bundlingLocation, bundled, {});
36
- }
32
+ const doBundling = async (schemaUri, bundled, fullOptions, contextSchema, visited = new Set()) => {
33
+ visited.add(schemaUri);
37
34
 
38
- for (const uri of externalIds) {
39
- if (fullOptions.externalSchemas.includes(uri)) {
35
+ const schema = await getSchema(schemaUri, contextSchema);
36
+ for (const reference of allReferences(schema.document.root)) {
37
+ const uri = toAbsoluteIri(resolveIri(reference.href, schema.document.baseUri));
38
+ if (visited.has(uri) || fullOptions.externalSchemas.includes(uri) || (uri in schema.document.embedded && !(uri in schema._cache))) {
40
39
  continue;
41
40
  }
42
41
 
43
- const externalSchema = await getSchema(uri);
42
+ const externalSchema = await getSchema(uri, contextSchema);
44
43
  const embeddedSchema = toSchema(externalSchema, {
45
- contextUri: externalSchema.document.baseUri.startsWith("file:") ? contextUri : undefined,
44
+ contextUri: externalSchema.document.baseUri.startsWith("file:") ? fullOptions.contextUri : undefined,
46
45
  includeDialect: fullOptions.alwaysIncludeDialect ? "always" : "auto",
47
- contextDialectId: contextDialectId
46
+ contextDialectId: fullOptions.contextDialectId
48
47
  });
49
48
  let id;
50
49
  if (fullOptions.definitionNamingStrategy === URI) {
@@ -56,216 +55,29 @@ export const bundle = async (url, options = {}) => {
56
55
  } else {
57
56
  throw Error(`Unknown definition naming stragety: ${fullOptions.definitionNamingStrategy}`);
58
57
  }
59
- const pointer = JsonPointer.append(id, bundlingLocation);
58
+ const pointer = JsonPointer.append(id, fullOptions.bundlingLocation);
60
59
  JsonPointer.assign(pointer, bundled, embeddedSchema);
60
+
61
+ bundled = await doBundling(uri, bundled, fullOptions, schema, visited);
61
62
  }
62
63
 
63
64
  return bundled;
64
65
  };
65
66
 
66
- Validation.collectExternalIds = curry(async (visited, parentSchema, schema) => {
67
- const uri = canonicalUri(schema);
68
- if (visited.has(uri) || Browser.typeOf(schema) === "boolean") {
69
- return new Set();
70
- }
71
-
72
- visited.add(uri);
73
-
74
- const externalIds = await pipe(
75
- Browser.entries(schema),
76
- asyncMap(async ([keyword, keywordSchema]) => {
77
- const keywordHandler = getKeywordByName(keyword, schema.document.dialectId);
78
-
79
- return "collectExternalIds" in keywordHandler
80
- ? await keywordHandler.collectExternalIds(visited, schema, keywordSchema)
81
- : new Set();
82
- }),
83
- asyncFlatten,
84
- asyncCollectSet
85
- );
86
-
87
- if (parentSchema.document.baseUri !== schema.document.baseUri
88
- && (!(schema.document.baseUri in parentSchema.document.embedded) || schema.document.baseUri in parentSchema._cache)
89
- ) {
90
- externalIds.add(schema.document.baseUri);
91
- }
92
-
93
- return externalIds;
94
- });
95
-
96
- const collectExternalIdsFromArrayOfSchemas = (visited, parentSchema, schema) => pipe(
97
- Browser.iter(schema),
98
- asyncMap(Validation.collectExternalIds(visited, parentSchema)),
99
- asyncFlatten,
100
- asyncCollectSet
101
- );
102
-
103
- const collectExternalIdsFromObjectOfSchemas = async (visited, parentSchema, schema) => pipe(
104
- Browser.values(schema),
105
- asyncMap(Validation.collectExternalIds(visited, parentSchema)),
106
- asyncFlatten,
107
- asyncCollectSet
108
- );
109
-
110
- const loadKeywordSupport = () => {
111
- // Stable
112
-
113
- const additionalProperties = getKeyword("https://json-schema.org/keyword/additionalProperties");
114
- if (additionalProperties) {
115
- additionalProperties.collectExternalIds = Validation.collectExternalIds;
116
- }
117
-
118
- const allOf = getKeyword("https://json-schema.org/keyword/allOf");
119
- if (allOf) {
120
- allOf.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
121
- }
122
-
123
- const anyOf = getKeyword("https://json-schema.org/keyword/anyOf");
124
- if (anyOf) {
125
- anyOf.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
126
- }
127
-
128
- const contains = getKeyword("https://json-schema.org/keyword/contains");
129
- if (contains) {
130
- contains.collectExternalIds = Validation.collectExternalIds;
131
- }
132
-
133
- const dependentSchemas = getKeyword("https://json-schema.org/keyword/dependentSchemas");
134
- if (dependentSchemas) {
135
- dependentSchemas.collectExternalIds = collectExternalIdsFromObjectOfSchemas;
136
- }
137
-
138
- const if_ = getKeyword("https://json-schema.org/keyword/if");
139
- if (if_) {
140
- if_.collectExternalIds = Validation.collectExternalIds;
141
- }
142
-
143
- const then = getKeyword("https://json-schema.org/keyword/then");
144
- if (then) {
145
- then.collectExternalIds = Validation.collectExternalIds;
146
- }
147
-
148
- const else_ = getKeyword("https://json-schema.org/keyword/else");
149
- if (else_) {
150
- else_.collectExternalIds = Validation.collectExternalIds;
151
- }
152
-
153
- const items = getKeyword("https://json-schema.org/keyword/items");
154
- if (items) {
155
- items.collectExternalIds = Validation.collectExternalIds;
156
- }
157
-
158
- const not = getKeyword("https://json-schema.org/keyword/not");
159
- if (not) {
160
- not.collectExternalIds = Validation.collectExternalIds;
161
- }
162
-
163
- const oneOf = getKeyword("https://json-schema.org/keyword/oneOf");
164
- if (oneOf) {
165
- oneOf.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
166
- }
167
-
168
- const patternProperties = getKeyword("https://json-schema.org/keyword/patternProperties");
169
- if (patternProperties) {
170
- patternProperties.collectExternalIds = collectExternalIdsFromObjectOfSchemas;
171
- }
172
-
173
- const prefixItems = getKeyword("https://json-schema.org/keyword/prefixItems");
174
- if (prefixItems) {
175
- prefixItems.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
176
- }
177
-
178
- const properties = getKeyword("https://json-schema.org/keyword/properties");
179
- if (properties) {
180
- properties.collectExternalIds = collectExternalIdsFromObjectOfSchemas;
181
- }
182
-
183
- const propertyNames = getKeyword("https://json-schema.org/keyword/propertyNames");
184
- if (propertyNames) {
185
- propertyNames.collectExternalIds = Validation.collectExternalIds;
186
- }
187
-
188
- const ref = getKeyword("https://json-schema.org/keyword/ref");
189
- if (ref) {
190
- ref.collectExternalIds = Validation.collectExternalIds;
191
- }
192
-
193
- const unevaluatedItems = getKeyword("https://json-schema.org/keyword/unevaluatedItems");
194
- if (unevaluatedItems) {
195
- unevaluatedItems.collectExternalIds = Validation.collectExternalIds;
196
- }
197
-
198
- const unevaluatedProperties = getKeyword("https://json-schema.org/keyword/unevaluatedProperties");
199
- if (unevaluatedProperties) {
200
- unevaluatedProperties.collectExternalIds = Validation.collectExternalIds;
201
- }
202
-
203
- // Draft-04
204
-
205
- const additionalItems4 = getKeyword("https://json-schema.org/keyword/draft-04/additionalItems");
206
- if (additionalItems4) {
207
- additionalItems4.collectExternalIds = Validation.collectExternalIds;
208
- }
209
-
210
- const dependencies = getKeyword("https://json-schema.org/keyword/draft-04/dependencies");
211
- if (dependencies) {
212
- dependencies.collectExternalIds = (visited, parentSchema, schema) => pipe(
213
- Browser.values(schema),
214
- asyncFilter((subSchema) => Browser.typeOf(subSchema) === "object"),
215
- asyncMap(Validation.collectExternalIds(visited, parentSchema)),
216
- asyncFlatten,
217
- asyncCollectSet
218
- );
219
- }
220
-
221
- const items4 = getKeyword("https://json-schema.org/keyword/draft-04/items");
222
- if (items4) {
223
- items4.collectExternalIds = (visited, parentSchema, schema) => Browser.typeOf(schema) === "array"
224
- ? collectExternalIdsFromArrayOfSchemas(visited, parentSchema, schema)
225
- : Validation.collectExternalIds(visited, parentSchema, schema);
226
- }
227
-
228
- // Draft-06
229
-
230
- const contains6 = getKeyword("https://json-schema.org/keyword/draft-06/contains");
231
- if (contains6) {
232
- contains6.collectExternalIds = Validation.collectExternalIds;
233
- }
234
-
235
- // Draft-2019-09
236
-
237
- const contains19 = getKeyword("https://json-schema.org/keyword/draft-2019-09/contains");
238
- if (contains19) {
239
- contains19.collectExternalIds = Validation.collectExternalIds;
240
- }
241
-
242
- // Extensions
243
-
244
- const propertyDependencies = getKeyword("https://json-schema.org/keyword/propertyDependencies");
245
- if (propertyDependencies) {
246
- propertyDependencies.collectExternalIds = (visited, parentSchema, schema) => pipe(
247
- Browser.values(schema),
248
- asyncMap((mapping) => Browser.values(mapping)),
249
- asyncFlatten,
250
- asyncMap(Validation.collectExternalIds(visited, parentSchema)),
251
- asyncFlatten,
252
- asyncCollectSet
253
- );
254
- }
255
-
256
- const conditional = getKeyword("https://json-schema.org/keyword/conditional");
257
- if (conditional) {
258
- conditional.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
259
- }
260
-
261
- const itemPattern = getKeyword("https://json-schema.org/keyword/itemPattern");
262
- if (itemPattern) {
263
- itemPattern.collectExternalIds = (visited, parentSchema, schema) => pipe(
264
- Browser.iter(schema),
265
- asyncFilter((item) => Browser.typeOf(item) === "object"),
266
- asyncMap(Validation.collectExternalIds(visited, parentSchema)),
267
- asyncFlatten,
268
- asyncCollectSet
269
- );
67
+ const allReferences = function* (node) {
68
+ switch (jrefTypeOf(node)) {
69
+ case "object":
70
+ for (const property in node) {
71
+ yield* allReferences(node[property]);
72
+ }
73
+ break;
74
+ case "array":
75
+ for (const item of node) {
76
+ yield* allReferences(item);
77
+ }
78
+ break;
79
+ case "reference":
80
+ yield node;
81
+ break;
270
82
  }
271
83
  };
@@ -1,4 +1,4 @@
1
- import { pipe, drop, every } from "@hyperjump/pact";
1
+ import { drop } from "@hyperjump/pact";
2
2
  import * as Browser from "@hyperjump/browser";
3
3
  import * as Instance from "../lib/instance.js";
4
4
  import { getKeywordName, Validation } from "../lib/experimental.js";
@@ -19,26 +19,20 @@ const interpret = ([numberOfItems, additionalItems], instance, context) => {
19
19
  return true;
20
20
  }
21
21
 
22
- return pipe(
23
- Instance.iter(instance),
24
- drop(numberOfItems),
25
- every((item) => Validation.interpret(additionalItems, item, context))
26
- );
27
- };
28
-
29
- const simpleApplicator = true;
22
+ let isValid = true;
23
+ let index = numberOfItems;
24
+ for (const item of drop(numberOfItems, Instance.iter(instance))) {
25
+ if (!Validation.interpret(additionalItems, item, context)) {
26
+ isValid = false;
27
+ }
30
28
 
31
- const collectEvaluatedItems = (keywordValue, instance, context) => {
32
- if (!interpret(keywordValue, instance, context)) {
33
- return false;
29
+ context.evaluatedItems?.add(index);
30
+ index++;
34
31
  }
35
32
 
36
- const evaluatedIndexes = new Set();
37
- for (let ndx = keywordValue[0]; ndx < Instance.length(instance); ndx++) {
38
- evaluatedIndexes.add(ndx);
39
- }
40
-
41
- return evaluatedIndexes;
33
+ return isValid;
42
34
  };
43
35
 
44
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedItems };
36
+ const simpleApplicator = true;
37
+
38
+ export default { id, compile, interpret, simpleApplicator };
package/draft-04/items.js CHANGED
@@ -1,4 +1,4 @@
1
- import { pipe, asyncMap, asyncCollectArray, every, zip, take, range, collectSet } from "@hyperjump/pact";
1
+ import { pipe, asyncMap, asyncCollectArray, zip } from "@hyperjump/pact";
2
2
  import * as Browser from "@hyperjump/browser";
3
3
  import * as Instance from "../lib/instance.js";
4
4
  import { Validation } from "../lib/experimental.js";
@@ -23,23 +23,35 @@ const interpret = (items, instance, context) => {
23
23
  return true;
24
24
  }
25
25
 
26
+ let isValid = true;
27
+ let index = 0;
28
+
26
29
  if (typeof items === "string") {
27
- return every((itemValue) => Validation.interpret(items, itemValue, context), Instance.iter(instance));
30
+ for (const item of Instance.iter(instance)) {
31
+ if (!Validation.interpret(items, item, context)) {
32
+ isValid = false;
33
+ }
34
+
35
+ context.evaluatedItems?.add(index++);
36
+ }
28
37
  } else {
29
- return pipe(
30
- zip(items, Instance.iter(instance)),
31
- take(Instance.length(instance)),
32
- every(([prefixItem, item]) => Validation.interpret(prefixItem, item, context))
33
- );
38
+ for (const [tupleItem, tupleInstance] of zip(items, Instance.iter(instance))) {
39
+ if (!tupleInstance) {
40
+ break;
41
+ }
42
+
43
+ if (!Validation.interpret(tupleItem, tupleInstance, context)) {
44
+ isValid = false;
45
+ }
46
+
47
+ context.evaluatedItems?.add(index);
48
+ index++;
49
+ }
34
50
  }
51
+
52
+ return isValid;
35
53
  };
36
54
 
37
55
  const simpleApplicator = true;
38
56
 
39
- const collectEvaluatedItems = (items, instance, context) => {
40
- return interpret(items, instance, context) && (typeof items === "string"
41
- ? collectSet(range(0, Instance.length(instance)))
42
- : collectSet(range(0, items.length)));
43
- };
44
-
45
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedItems };
57
+ export default { id, compile, interpret, simpleApplicator };
@@ -1,6 +1,6 @@
1
1
  import * as Browser from "@hyperjump/browser";
2
2
  import { Validation, canonicalUri } from "../lib/experimental.js";
3
- import { uriFragment } from "../lib/common.js";
3
+ import { toAbsoluteUri, uriFragment } from "../lib/common.js";
4
4
 
5
5
 
6
6
  const id = "https://json-schema.org/keyword/draft-2020-12/dynamicRef";
@@ -12,19 +12,31 @@ const compile = async (dynamicRef, ast) => {
12
12
  return [referencedSchema.document.baseUri, fragment, canonicalUri(referencedSchema)];
13
13
  };
14
14
 
15
- const evaluate = (strategy, [id, fragment, ref], instance, context) => {
15
+ const interpret = ([id, fragment, ref], instance, context) => {
16
16
  if (fragment in context.ast.metaData[id].dynamicAnchors) {
17
17
  context.dynamicAnchors = { ...context.ast.metaData[id].dynamicAnchors, ...context.dynamicAnchors };
18
- return strategy(context.dynamicAnchors[fragment], instance, context);
18
+ return Validation.interpret(context.dynamicAnchors[fragment], instance, context);
19
19
  } else {
20
- return strategy(ref, instance, context);
20
+ return Validation.interpret(ref, instance, context);
21
21
  }
22
22
  };
23
23
 
24
24
  const simpleApplicator = true;
25
25
 
26
- const interpret = (...args) => evaluate(Validation.interpret, ...args);
27
- const collectEvaluatedProperties = (...args) => evaluate(Validation.collectEvaluatedProperties, ...args);
28
- const collectEvaluatedItems = (...args) => evaluate(Validation.collectEvaluatedItems, ...args);
26
+ const plugin = {
27
+ beforeSchema(url, _instance, context) {
28
+ context.dynamicAnchors = {
29
+ ...context.ast.metaData[toAbsoluteUri(url)].dynamicAnchors,
30
+ ...context.dynamicAnchors
31
+ };
32
+ },
33
+ beforeKeyword(_url, _instance, context, schemaContext) {
34
+ context.dynamicAnchors = schemaContext.dynamicAnchors;
35
+ },
36
+ afterKeyword() {
37
+ },
38
+ afterSchema() {
39
+ }
40
+ };
29
41
 
30
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties, collectEvaluatedItems };
42
+ export default { id, compile, interpret, simpleApplicator, plugin };