@hyperjump/json-schema 1.13.0 → 1.14.1

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 (41) hide show
  1. package/README.md +14 -20
  2. package/annotations/index.js +27 -7
  3. package/annotations/test-utils.d.ts +1 -0
  4. package/annotations/test-utils.js +38 -0
  5. package/bundle/index.js +39 -227
  6. package/draft-04/additionalItems.js +13 -19
  7. package/draft-04/dependencies.js +7 -7
  8. package/draft-04/items.js +26 -14
  9. package/draft-2020-12/dynamicRef.js +20 -8
  10. package/lib/core.js +19 -4
  11. package/lib/evaluation-plugins/annotations.js +32 -0
  12. package/lib/evaluation-plugins/basic-output.js +34 -0
  13. package/lib/evaluation-plugins/detailed-output.js +36 -0
  14. package/lib/experimental.d.ts +18 -5
  15. package/lib/experimental.js +3 -0
  16. package/lib/instance.js +12 -3
  17. package/lib/keywords/additionalProperties.js +8 -22
  18. package/lib/keywords/allOf.js +1 -29
  19. package/lib/keywords/anyOf.js +1 -27
  20. package/lib/keywords/conditional.js +1 -27
  21. package/lib/keywords/contains.js +14 -18
  22. package/lib/keywords/contentSchema.js +6 -3
  23. package/lib/keywords/dependentSchemas.js +1 -21
  24. package/lib/keywords/dynamicRef.js +19 -6
  25. package/lib/keywords/else.js +2 -19
  26. package/lib/keywords/if.js +1 -9
  27. package/lib/keywords/itemPattern.js +5 -9
  28. package/lib/keywords/items.js +5 -14
  29. package/lib/keywords/oneOf.js +1 -33
  30. package/lib/keywords/patternProperties.js +7 -25
  31. package/lib/keywords/prefixItems.js +2 -5
  32. package/lib/keywords/properties.js +6 -22
  33. package/lib/keywords/propertyDependencies.js +1 -20
  34. package/lib/keywords/propertyNames.js +1 -1
  35. package/lib/keywords/ref.js +1 -3
  36. package/lib/keywords/then.js +2 -19
  37. package/lib/keywords/unevaluatedItems.js +44 -20
  38. package/lib/keywords/unevaluatedProperties.js +40 -26
  39. package/lib/keywords/validation.js +31 -129
  40. package/lib/keywords.js +30 -30
  41. package/package.json +1 -1
@@ -22,36 +22,4 @@ const interpret = (oneOf, instance, context) => {
22
22
  return validCount === 1;
23
23
  };
24
24
 
25
- const collectEvaluatedProperties = (oneOf, instance, context) => {
26
- let evaluatedProperties = false;
27
- for (const schemaUrl of oneOf) {
28
- const propertyNames = Validation.collectEvaluatedProperties(schemaUrl, instance, context);
29
- if (propertyNames) {
30
- if (evaluatedProperties) {
31
- return false;
32
- }
33
-
34
- evaluatedProperties = propertyNames;
35
- }
36
- }
37
-
38
- return evaluatedProperties;
39
- };
40
-
41
- const collectEvaluatedItems = (oneOf, instance, context) => {
42
- let evaluatedItemIndexes = false;
43
- for (const schemaUrl of oneOf) {
44
- const itemIndexes = Validation.collectEvaluatedItems(schemaUrl, instance, context);
45
- if (itemIndexes) {
46
- if (evaluatedItemIndexes) {
47
- return false;
48
- }
49
-
50
- evaluatedItemIndexes = itemIndexes;
51
- }
52
- }
53
-
54
- return evaluatedItemIndexes;
55
- };
56
-
57
- export default { id, compile, interpret, collectEvaluatedProperties, collectEvaluatedItems };
25
+ export default { id, compile, interpret };
@@ -23,38 +23,20 @@ const interpret = (patternProperties, instance, context) => {
23
23
  let isValid = true;
24
24
  for (const [pattern, schemaUri] of patternProperties) {
25
25
  for (const [propertyNameNode, propertyValue] of Instance.entries(instance)) {
26
- const propertyName = Instance.value(propertyNameNode);
27
- if (pattern.test(propertyName) && !Validation.interpret(schemaUri, propertyValue, context)) {
28
- isValid = false;
29
- }
30
- }
31
- }
32
-
33
- return isValid;
34
- };
35
-
36
- const simpleApplicator = true;
37
-
38
- const collectEvaluatedProperties = (patternProperties, instance, context) => {
39
- if (Instance.typeOf(instance) !== "object") {
40
- return false;
41
- }
42
-
43
- const evaluatedPropertyNames = new Set();
44
- for (const [pattern, propertySchema] of patternProperties) {
45
- for (const [propertyNameNode, property] of Instance.entries(instance)) {
46
26
  const propertyName = Instance.value(propertyNameNode);
47
27
  if (pattern.test(propertyName)) {
48
- if (!Validation.interpret(propertySchema, property, context)) {
49
- return false;
28
+ if (!Validation.interpret(schemaUri, propertyValue, context)) {
29
+ isValid = false;
50
30
  }
51
31
 
52
- evaluatedPropertyNames.add(propertyName);
32
+ context.evaluatedProperties?.add(propertyName);
53
33
  }
54
34
  }
55
35
  }
56
36
 
57
- return evaluatedPropertyNames;
37
+ return isValid;
58
38
  };
59
39
 
60
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties };
40
+ const simpleApplicator = true;
41
+
42
+ export default { id, compile, interpret, simpleApplicator };
@@ -29,6 +29,7 @@ const interpret = (prefixItems, instance, context) => {
29
29
  isValid = false;
30
30
  }
31
31
 
32
+ context.evaluatedItems?.add(index);
32
33
  index++;
33
34
  }
34
35
 
@@ -37,8 +38,4 @@ const interpret = (prefixItems, instance, context) => {
37
38
 
38
39
  const simpleApplicator = true;
39
40
 
40
- const collectEvaluatedItems = (items, instance, context) => {
41
- return interpret(items, instance, context) && new Set(items.map((_item, ndx) => ndx));
42
- };
43
-
44
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedItems };
41
+ export default { id, compile, interpret, simpleApplicator };
@@ -18,36 +18,20 @@ const interpret = (properties, instance, context) => {
18
18
  }
19
19
 
20
20
  let isValid = true;
21
- for (const [propertyNameNode, property] of Instance.entries(instance)) {
22
- const propertyName = Instance.value(propertyNameNode);
23
- if (propertyName in properties && !Validation.interpret(properties[propertyName], property, context)) {
24
- isValid = false;
25
- }
26
- }
27
-
28
- return isValid;
29
- };
30
-
31
- const simpleApplicator = true;
32
-
33
- const collectEvaluatedProperties = (properties, instance, context) => {
34
- if (Instance.typeOf(instance) !== "object") {
35
- return false;
36
- }
37
-
38
- const evaluatedPropertyNames = new Set();
39
21
  for (const [propertyNameNode, property] of Instance.entries(instance)) {
40
22
  const propertyName = Instance.value(propertyNameNode);
41
23
  if (propertyName in properties) {
42
24
  if (!Validation.interpret(properties[propertyName], property, context)) {
43
- return false;
25
+ isValid = false;
44
26
  }
45
27
 
46
- evaluatedPropertyNames.add(propertyName);
28
+ context.evaluatedProperties?.add(propertyName);
47
29
  }
48
30
  }
49
31
 
50
- return evaluatedPropertyNames;
32
+ return isValid;
51
33
  };
52
34
 
53
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties };
35
+ const simpleApplicator = true;
36
+
37
+ export default { id, compile, interpret, simpleApplicator };
@@ -43,23 +43,4 @@ const interpret = (propertyDependencies, instance, context) => {
43
43
 
44
44
  const simpleApplicator = true;
45
45
 
46
- const collectEvaluatedProperties = (propertyDependencies, instance, context) => {
47
- const evaluatedPropertyNames = new Set();
48
- for (const propertyName in propertyDependencies) {
49
- const propertyValue = Instance.value(instance)[propertyName];
50
-
51
- const valueMappings = propertyDependencies[propertyName];
52
- if (Instance.has(propertyName, instance) && propertyValue in valueMappings) {
53
- const propertyNames = Validation.collectEvaluatedProperties(valueMappings[propertyValue], instance, context);
54
- if (!propertyNames) {
55
- return false;
56
- }
57
-
58
- propertyNames.forEach(evaluatedPropertyNames.add, evaluatedPropertyNames);
59
- }
60
- }
61
-
62
- return evaluatedPropertyNames;
63
- };
64
-
65
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties };
46
+ export default { id, compile, interpret, simpleApplicator };
@@ -13,7 +13,7 @@ const interpret = (propertyNames, instance, context) => {
13
13
 
14
14
  let isValid = true;
15
15
  for (const key of Instance.keys(instance)) {
16
- if (!Validation.interpret(propertyNames, key, { ...context, annotations: [] })) {
16
+ if (!Validation.interpret(propertyNames, key, context)) {
17
17
  isValid = false;
18
18
  }
19
19
  }
@@ -5,9 +5,7 @@ const id = "https://json-schema.org/keyword/ref";
5
5
 
6
6
  const compile = (...args) => Validation.compile(...args);
7
7
  const interpret = (...args) => Validation.interpret(...args);
8
- const collectEvaluatedProperties = (...args) => Validation.collectEvaluatedProperties(...args);
9
- const collectEvaluatedItems = (...args) => Validation.collectEvaluatedItems(...args);
10
8
 
11
9
  const simpleApplicator = true;
12
10
 
13
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties, collectEvaluatedItems };
11
+ export default { id, compile, interpret, simpleApplicator };
@@ -1,5 +1,4 @@
1
1
  import * as Browser from "@hyperjump/browser";
2
- import { FLAG } from "../index.js";
3
2
  import { getKeywordName, Validation } from "../experimental.js";
4
3
 
5
4
 
@@ -17,26 +16,10 @@ const compile = async (schema, ast, parentSchema) => {
17
16
 
18
17
  const interpret = ([ifSchema, thenSchema], instance, context) => {
19
18
  return ifSchema === undefined
20
- || !Validation.interpret(ifSchema, instance, { ...context, errors: [], annotations: [], outputFormat: FLAG })
19
+ || !Validation.interpret(ifSchema, instance, { ...context, plugins: context.ast.plugins })
21
20
  || Validation.interpret(thenSchema, instance, context);
22
21
  };
23
22
 
24
23
  const simpleApplicator = true;
25
24
 
26
- const collectEvaluatedProperties = ([ifSchema, thenSchema], instance, context) => {
27
- if (ifSchema === undefined || !Validation.interpret(ifSchema, instance, context)) {
28
- return new Set();
29
- }
30
-
31
- return Validation.collectEvaluatedProperties(thenSchema, instance, context);
32
- };
33
-
34
- const collectEvaluatedItems = ([ifSchema, thenSchema], instance, context) => {
35
- if (ifSchema === undefined || !Validation.interpret(ifSchema, instance, context)) {
36
- return new Set();
37
- }
38
-
39
- return Validation.collectEvaluatedItems(thenSchema, instance, context);
40
- };
41
-
42
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties, collectEvaluatedItems };
25
+ export default { id, compile, interpret, simpleApplicator };
@@ -1,5 +1,3 @@
1
- import { zip, range } from "@hyperjump/pact";
2
- import { FLAG } from "../index.js";
3
1
  import { canonicalUri, Validation } from "../experimental.js";
4
2
  import * as Instance from "../instance.js";
5
3
 
@@ -15,17 +13,33 @@ const interpret = ([schemaUrl, unevaluatedItems], instance, context) => {
15
13
  return true;
16
14
  }
17
15
 
18
- const keywordContext = { ...context, errors: [], annotations: [], outputFormat: FLAG };
19
- const itemIndexes = Validation.collectEvaluatedItems(schemaUrl, instance, keywordContext, true);
20
- if (itemIndexes === false) {
16
+ if (context.rootSchema === schemaUrl) {
17
+ return true;
18
+ }
19
+
20
+ // Because order matters, we re-evaluate this schema skipping this keyword
21
+ // just to collect all the evalauted items.
22
+ const keywordContext = {
23
+ ...context,
24
+ plugins: [...context.ast.plugins, unevaluatedPlugin],
25
+ rootSchema: schemaUrl
26
+ };
27
+ if (!Validation.interpret(schemaUrl, instance, keywordContext)) {
21
28
  return true;
22
29
  }
23
30
 
24
31
  let isValid = true;
25
- for (const [item, index] of zip(Instance.iter(instance), range(0))) {
26
- if (!itemIndexes.has(index) && !Validation.interpret(unevaluatedItems, item, context)) {
27
- isValid = false;
32
+ let index = 0;
33
+ for (const item of Instance.iter(instance)) {
34
+ if (!keywordContext.evaluatedItems.has(index)) {
35
+ if (!Validation.interpret(unevaluatedItems, item, context)) {
36
+ isValid = false;
37
+ }
38
+
39
+ context.evaluatedItems?.add(index);
28
40
  }
41
+
42
+ index++;
29
43
  }
30
44
 
31
45
  return isValid;
@@ -33,19 +47,29 @@ const interpret = ([schemaUrl, unevaluatedItems], instance, context) => {
33
47
 
34
48
  const simpleApplicator = true;
35
49
 
36
- const collectEvaluatedItems = (keywordValue, instance, context) => {
37
- const itemIndexes = Validation.collectEvaluatedItems(keywordValue[0], instance, context, true);
38
- if (!itemIndexes) {
39
- return false;
40
- }
41
-
42
- const evaluatedIndexes = new Set();
43
- for (let ndx = 0; ndx < Instance.length(instance); ndx++) {
44
- if (!itemIndexes.has(ndx)) {
45
- evaluatedIndexes.add(ndx);
50
+ const unevaluatedPlugin = {
51
+ beforeSchema(_url, _instance, context) {
52
+ context.evaluatedItems ??= new Set();
53
+ context.schemaEvaluatedItems ??= new Set();
54
+ },
55
+ beforeKeyword(_node, _instance, context, schemaContext) {
56
+ context.rootSchema = schemaContext.rootSchema;
57
+ context.evaluatedItems = new Set();
58
+ },
59
+ afterKeyword(_node, _instance, context, valid, schemaContext) {
60
+ if (valid) {
61
+ for (const index of context.evaluatedItems) {
62
+ schemaContext.schemaEvaluatedItems.add(index);
63
+ }
64
+ }
65
+ },
66
+ afterSchema(_url, _instance, context, valid) {
67
+ if (valid) {
68
+ for (const index of context.schemaEvaluatedItems) {
69
+ context.evaluatedItems.add(index);
70
+ }
46
71
  }
47
72
  }
48
- return evaluatedIndexes;
49
73
  };
50
74
 
51
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedItems };
75
+ export default { id, compile, interpret, simpleApplicator };
@@ -1,4 +1,3 @@
1
- import { FLAG } from "../index.js";
2
1
  import { Validation, canonicalUri } from "../experimental.js";
3
2
  import * as Instance from "../instance.js";
4
3
 
@@ -14,18 +13,33 @@ const interpret = ([schemaUrl, unevaluatedProperties], instance, context) => {
14
13
  return true;
15
14
  }
16
15
 
17
- const keywordContext = { ...context, errors: [], annotations: [], outputFormat: FLAG };
18
- const evaluatedPropertyNames = Validation.collectEvaluatedProperties(schemaUrl, instance, keywordContext, true);
19
- if (evaluatedPropertyNames === false) {
16
+ if (context.rootSchema === schemaUrl) {
17
+ return true;
18
+ }
19
+
20
+ // Because order matters, we re-evaluate this schema skipping this keyword
21
+ // just to collect all the evalauted properties.
22
+ const keywordContext = {
23
+ ...context,
24
+ plugins: [...context.ast.plugins, unevaluatedPlugin],
25
+ rootSchema: schemaUrl
26
+ };
27
+ if (!Validation.interpret(schemaUrl, instance, keywordContext)) {
20
28
  return true;
21
29
  }
22
30
 
23
31
  let isValid = true;
24
32
  for (const [propertyNameNode, property] of Instance.entries(instance)) {
25
33
  const propertyName = Instance.value(propertyNameNode);
26
- if (!evaluatedPropertyNames.has(propertyName) && !Validation.interpret(unevaluatedProperties, property, context)) {
34
+ if (keywordContext.evaluatedProperties.has(propertyName)) {
35
+ continue;
36
+ }
37
+
38
+ if (!Validation.interpret(unevaluatedProperties, property, context)) {
27
39
  isValid = false;
28
40
  }
41
+
42
+ context.evaluatedProperties?.add(propertyName);
29
43
  }
30
44
 
31
45
  return isValid;
@@ -33,29 +47,29 @@ const interpret = ([schemaUrl, unevaluatedProperties], instance, context) => {
33
47
 
34
48
  const simpleApplicator = true;
35
49
 
36
- const collectEvaluatedProperties = ([schemaUrl, unevaluatedProperties], instance, context) => {
37
- if (Instance.typeOf(instance) !== "object") {
38
- return true;
39
- }
40
-
41
- const evaluatedPropertyNames = Validation.collectEvaluatedProperties(schemaUrl, instance, context, true);
42
-
43
- if (!evaluatedPropertyNames) {
44
- return false;
45
- }
46
-
47
- for (const [propertyNameNode, property] of Instance.entries(instance)) {
48
- const propertyName = Instance.value(propertyNameNode);
49
- if (!evaluatedPropertyNames.has(propertyName)) {
50
- if (!Validation.interpret(unevaluatedProperties, property, context)) {
51
- return false;
50
+ const unevaluatedPlugin = {
51
+ beforeSchema(_url, _instance, context) {
52
+ context.evaluatedProperties ??= new Set();
53
+ context.schemaEvaluatedProperties ??= new Set();
54
+ },
55
+ beforeKeyword(_node, _instance, context, schemaContext) {
56
+ context.rootSchema = schemaContext.rootSchema;
57
+ context.evaluatedProperties = new Set();
58
+ },
59
+ afterKeyword(_node, _instance, context, valid, schemaContext) {
60
+ if (valid) {
61
+ for (const property of context.evaluatedProperties) {
62
+ schemaContext.schemaEvaluatedProperties.add(property);
63
+ }
64
+ }
65
+ },
66
+ afterSchema(_url, _instance, context, valid) {
67
+ if (valid) {
68
+ for (const property of context.schemaEvaluatedProperties) {
69
+ context.evaluatedProperties.add(property);
52
70
  }
53
-
54
- evaluatedPropertyNames.add(propertyName);
55
71
  }
56
72
  }
57
-
58
- return evaluatedPropertyNames;
59
73
  };
60
74
 
61
- export default { id, compile, interpret, simpleApplicator, collectEvaluatedProperties };
75
+ export default { id, compile, interpret, simpleApplicator };
@@ -2,10 +2,7 @@ import { value, entries } from "@hyperjump/browser";
2
2
  import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact";
3
3
  import { append as pointerAppend } from "@hyperjump/json-pointer";
4
4
  import { publishAsync } from "../pubsub.js";
5
- import * as Instance from "../instance.js";
6
- import { toAbsoluteUri } from "../common.js";
7
- import { canonicalUri, getKeyword, getKeywordByName, BASIC, DETAILED } from "../experimental.js";
8
- import { FLAG } from "../index.js";
5
+ import { canonicalUri, getKeyword, getKeywordByName } from "../experimental.js";
9
6
 
10
7
 
11
8
  const id = "https://json-schema.org/evaluation/validate";
@@ -35,6 +32,9 @@ const compile = async (schema, ast) => {
35
32
  entries(schema),
36
33
  asyncMap(async ([keyword, keywordSchema]) => {
37
34
  const keywordHandler = getKeywordByName(keyword, schema.document.dialectId);
35
+ if (keywordHandler.plugin) {
36
+ ast.plugins.add(keywordHandler.plugin);
37
+ }
38
38
  const keywordAst = await keywordHandler.compile(keywordSchema, ast, schema);
39
39
  return [keywordHandler.id, pointerAppend(keyword, canonicalUri(schema)), keywordAst];
40
40
  }),
@@ -45,140 +45,42 @@ const compile = async (schema, ast) => {
45
45
  return url;
46
46
  };
47
47
 
48
- const interpret = (url, instance, { ast, dynamicAnchors, errors, annotations, outputFormat }) => {
49
- dynamicAnchors = { ...ast.metaData[toAbsoluteUri(url)].dynamicAnchors, ...dynamicAnchors };
48
+ const interpret = (url, instance, context) => {
49
+ let valid = true;
50
50
 
51
- if (typeof ast[url] === "boolean") {
52
- if (ast[url]) {
53
- return true;
54
- } else {
55
- errors.push({
56
- keyword: id,
57
- absoluteKeywordLocation: url,
58
- instanceLocation: Instance.uri(instance)
59
- });
60
- return false;
61
- }
62
- } else {
63
- const schemaAnnotations = [];
51
+ for (const plugin of context.plugins) {
52
+ plugin.beforeSchema(url, instance, context);
53
+ }
64
54
 
65
- let isSchemaValid = true;
66
- for (const [keywordId, schemaUri, keywordValue] of ast[url]) {
67
- const context = { ast, dynamicAnchors, errors: [], annotations: [], outputFormat };
68
- const keywordHandler = getKeyword(keywordId);
69
- const isKeywordValid = keywordHandler.interpret(keywordValue, instance, context);
55
+ if (typeof context.ast[url] === "boolean") {
56
+ valid = context.ast[url];
57
+ } else {
58
+ for (const node of context.ast[url]) {
59
+ const [keywordId, , keywordValue] = node;
60
+ const keyword = getKeyword(keywordId);
61
+
62
+ const keywordContext = {
63
+ ast: context.ast,
64
+ plugins: context.plugins
65
+ };
66
+ for (const plugin of context.plugins) {
67
+ plugin.beforeKeyword(node, instance, keywordContext, context, keyword);
68
+ }
69
+ const isKeywordValid = keyword.interpret(keywordValue, instance, keywordContext);
70
70
  if (!isKeywordValid) {
71
- isSchemaValid = false;
71
+ valid = false;
72
72
  }
73
73
 
74
- switch (outputFormat) {
75
- case FLAG:
76
- break;
77
- case BASIC:
78
- if (!isKeywordValid) {
79
- if (!keywordHandler.simpleApplicator) {
80
- errors.push({
81
- keyword: keywordId,
82
- absoluteKeywordLocation: schemaUri,
83
- instanceLocation: Instance.uri(instance)
84
- });
85
- }
86
- errors.push(...context.errors);
87
- } else {
88
- const annotation = keywordHandler.annotation?.(keywordValue, instance);
89
- if (annotation !== undefined) {
90
- schemaAnnotations.unshift({
91
- keyword: keywordId,
92
- absoluteKeywordLocation: schemaUri,
93
- instanceLocation: Instance.uri(instance),
94
- annotation: annotation
95
- });
96
- }
97
- for (const contextAnnotation of context.annotations) {
98
- schemaAnnotations.unshift(contextAnnotation);
99
- }
100
- }
101
- break;
102
- case DETAILED: {
103
- if (!isKeywordValid) {
104
- const outputUnit = {
105
- keyword: keywordId,
106
- absoluteKeywordLocation: schemaUri,
107
- instanceLocation: Instance.uri(instance)
108
- };
109
-
110
- errors.push(outputUnit);
111
- if (context.errors.length > 0) {
112
- outputUnit.errors = context.errors;
113
- }
114
- }
115
- break;
116
- }
117
- default:
118
- throw Error(`Unsupported output format '${outputFormat}'`);
74
+ for (const plugin of context.plugins) {
75
+ plugin.afterKeyword(node, instance, keywordContext, isKeywordValid, context, keyword);
119
76
  }
120
77
  }
121
-
122
- if (outputFormat === BASIC && isSchemaValid) {
123
- annotations.push(...schemaAnnotations);
124
- }
125
-
126
- return isSchemaValid;
127
- }
128
- };
129
-
130
- const emptyPropertyNames = new Set();
131
- const collectEvaluatedProperties = (url, instance, context, isTop = false) => {
132
- if (typeof context.ast[url] === "boolean") {
133
- return context.ast[url] ? emptyPropertyNames : false;
134
- }
135
-
136
- const accumulatedPropertyNames = new Set();
137
- for (const [keywordId, , keywordValue] of context.ast[url]) {
138
- if (isTop && keywordId === "https://json-schema.org/keyword/unevaluatedProperties") {
139
- continue;
140
- }
141
-
142
- const keywordHandler = getKeyword(keywordId);
143
- const propertyNames = "collectEvaluatedProperties" in keywordHandler
144
- ? keywordHandler.collectEvaluatedProperties(keywordValue, instance, context, isTop)
145
- : keywordHandler.interpret(keywordValue, instance, context) && emptyPropertyNames;
146
-
147
- if (propertyNames === false) {
148
- return false;
149
- }
150
-
151
- propertyNames.forEach(accumulatedPropertyNames.add, accumulatedPropertyNames);
152
78
  }
153
79
 
154
- return accumulatedPropertyNames;
155
- };
156
-
157
- const emptyItemIndexes = new Set();
158
- const collectEvaluatedItems = (url, instance, context, isTop = false) => {
159
- if (typeof context.ast[url] === "boolean") {
160
- return context.ast[url] ? emptyItemIndexes : false;
80
+ for (const plugin of context.plugins) {
81
+ plugin.afterSchema(url, instance, context, valid);
161
82
  }
162
-
163
- const accumulatedItemIndexes = new Set();
164
- for (const [keywordId, , keywordValue] of context.ast[url]) {
165
- if (isTop && keywordId === "https://json-schema.org/keyword/unevaluatedItems") {
166
- continue;
167
- }
168
-
169
- const keywordHandler = getKeyword(keywordId);
170
- const itemIndexes = "collectEvaluatedItems" in keywordHandler
171
- ? keywordHandler.collectEvaluatedItems(keywordValue, instance, context, isTop)
172
- : keywordHandler.interpret(keywordValue, instance, context) && emptyItemIndexes;
173
-
174
- if (itemIndexes === false) {
175
- return false;
176
- }
177
-
178
- itemIndexes.forEach(accumulatedItemIndexes.add, accumulatedItemIndexes);
179
- }
180
-
181
- return accumulatedItemIndexes;
83
+ return valid;
182
84
  };
183
85
 
184
- export default { id, compile, interpret, collectEvaluatedProperties, collectEvaluatedItems };
86
+ export default { id, compile, interpret };