@hyperjump/json-schema 1.13.0 → 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 (37) hide show
  1. package/README.md +14 -20
  2. package/annotations/index.js +27 -7
  3. package/bundle/index.js +39 -227
  4. package/draft-04/additionalItems.js +13 -19
  5. package/draft-04/items.js +26 -14
  6. package/draft-2020-12/dynamicRef.js +20 -8
  7. package/lib/core.js +19 -4
  8. package/lib/evaluation-plugins/annotations.js +32 -0
  9. package/lib/evaluation-plugins/basic-output.js +34 -0
  10. package/lib/evaluation-plugins/detailed-output.js +36 -0
  11. package/lib/experimental.d.ts +18 -5
  12. package/lib/experimental.js +3 -0
  13. package/lib/instance.js +12 -3
  14. package/lib/keywords/additionalProperties.js +8 -22
  15. package/lib/keywords/allOf.js +1 -29
  16. package/lib/keywords/anyOf.js +1 -27
  17. package/lib/keywords/conditional.js +1 -27
  18. package/lib/keywords/contains.js +14 -18
  19. package/lib/keywords/dependentSchemas.js +1 -21
  20. package/lib/keywords/dynamicRef.js +19 -6
  21. package/lib/keywords/else.js +2 -19
  22. package/lib/keywords/if.js +1 -9
  23. package/lib/keywords/itemPattern.js +5 -9
  24. package/lib/keywords/items.js +5 -14
  25. package/lib/keywords/oneOf.js +1 -33
  26. package/lib/keywords/patternProperties.js +7 -25
  27. package/lib/keywords/prefixItems.js +2 -5
  28. package/lib/keywords/properties.js +6 -22
  29. package/lib/keywords/propertyDependencies.js +1 -20
  30. package/lib/keywords/propertyNames.js +1 -1
  31. package/lib/keywords/ref.js +1 -3
  32. package/lib/keywords/then.js +2 -19
  33. package/lib/keywords/unevaluatedItems.js +44 -20
  34. package/lib/keywords/unevaluatedProperties.js +40 -26
  35. package/lib/keywords/validation.js +31 -129
  36. package/lib/keywords.js +30 -30
  37. package/package.json +1 -1
@@ -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 };
package/lib/keywords.js CHANGED
@@ -35,17 +35,19 @@ export const defineVocabulary = (id, keywords) => {
35
35
  };
36
36
 
37
37
  const _dialects = {};
38
- const _allowUnknownKeywords = {};
39
- const _persistentDialects = {};
40
38
 
41
- export const getKeywordId = (keyword, dialectId) => getDialect(dialectId)?.[keyword]
42
- || (_allowUnknownKeywords[dialectId] || keyword.startsWith("x-"))
43
- && `https://json-schema.org/keyword/unknown#${keyword}`;
39
+ export const getKeywordId = (keyword, dialectId) => {
40
+ const dialect = getDialect(dialectId);
41
+ return dialect.keywords[keyword]
42
+ ?? ((dialect.allowUnknownKeywords || keyword.startsWith("x-"))
43
+ ? `https://json-schema.org/keyword/unknown#${keyword}`
44
+ : undefined);
45
+ };
44
46
 
45
47
  export const getKeywordName = (dialectId, keywordId) => {
46
48
  const dialect = getDialect(dialectId);
47
- for (const keyword in dialect) {
48
- if (dialect[keyword] === keywordId) {
49
+ for (const keyword in dialect.keywords) {
50
+ if (dialect.keywords[keyword] === keywordId) {
49
51
  return keyword;
50
52
  }
51
53
  }
@@ -62,33 +64,31 @@ const getDialect = (dialectId) => {
62
64
  export const hasDialect = (dialectId) => dialectId in _dialects;
63
65
 
64
66
  export const loadDialect = (dialectId, dialect, allowUnknownKeywords = false, isPersistent = true) => {
65
- _allowUnknownKeywords[dialectId] = allowUnknownKeywords;
66
- _persistentDialects[dialectId] = _persistentDialects[dialectId] || isPersistent;
67
-
68
- _dialects[dialectId] = {};
69
- Object.entries(dialect)
70
- .forEach(([vocabularyId, isRequired]) => {
71
- if (vocabularyId in _vocabularies) {
72
- Object.entries(_vocabularies[vocabularyId])
73
- .forEach(([keyword, keywordId]) => {
74
- if (!(keywordId in _keywords) && !isRequired) {
75
- // Allow keyword to be ignored
76
- keywordId = `https://json-schema.org/keyword/unknown#${keyword}`;
77
- }
78
- _dialects[dialectId][keyword] = keywordId;
79
- });
80
- } else if (!allowUnknownKeywords || isRequired) {
81
- delete _dialects[dialectId];
82
- delete _allowUnknownKeywords[dialectId];
83
- delete _persistentDialects[dialectId];
84
- throw Error(`Unrecognized vocabulary: ${vocabularyId}. You can define this vocabulary with the 'defineVocabulary' function.`);
67
+ _dialects[dialectId] = {
68
+ keywords: {},
69
+ allowUnknownKeywords: allowUnknownKeywords,
70
+ persistentDialects: _dialects[dialectId]?.persistentDialects || isPersistent
71
+ };
72
+
73
+ for (const vocabularyId in dialect) {
74
+ if (vocabularyId in _vocabularies) {
75
+ for (const keyword in _vocabularies[vocabularyId]) {
76
+ let keywordId = _vocabularies[vocabularyId][keyword];
77
+ if (!(keywordId in _keywords) && !dialect[vocabularyId]) {
78
+ // Allow keyword to be ignored
79
+ keywordId = `https://json-schema.org/keyword/unknown#${keyword}`;
80
+ }
81
+ _dialects[dialectId].keywords[keyword] = keywordId;
85
82
  }
86
- });
83
+ } else if (!allowUnknownKeywords || dialect[vocabularyId]) {
84
+ delete _dialects[dialectId];
85
+ throw Error(`Unrecognized vocabulary: ${vocabularyId}. You can define this vocabulary with the 'defineVocabulary' function.`);
86
+ }
87
+ }
87
88
  };
88
89
 
89
90
  export const unloadDialect = (dialectId) => {
90
- if (!_persistentDialects[dialectId]) {
91
- delete _allowUnknownKeywords[dialectId];
91
+ if (!_dialects[dialectId]?.persistentDialects) {
92
92
  delete _dialects[dialectId];
93
93
  }
94
94
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperjump/json-schema",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "A JSON Schema validator with support for custom keywords, vocabularies, and dialects",
5
5
  "type": "module",
6
6
  "main": "./stable/index.js",