@hyperjump/json-schema 1.6.6 → 1.6.7

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/bundle/index.js CHANGED
@@ -223,8 +223,8 @@ const loadKeywordSupport = () => {
223
223
 
224
224
  const dependencies = getKeyword("https://json-schema.org/keyword/draft-04/dependencies");
225
225
  if (dependencies) {
226
- dependencies.collectExternalIds = (dependentSchemas, externalIds, ast, dynamicAnchors) => {
227
- Object.values(dependentSchemas).forEach(([, dependency]) => {
226
+ dependencies.collectExternalIds = (dependencies, externalIds, ast, dynamicAnchors) => {
227
+ Object.values(dependencies).forEach(([, dependency]) => {
228
228
  if (typeof dependency === "string") {
229
229
  Validation.collectExternalIds(dependency, externalIds, ast, dynamicAnchors);
230
230
  }
@@ -265,4 +265,52 @@ const loadKeywordSupport = () => {
265
265
  Validation.collectExternalIds(contains, externalIds, ast, dynamicAnchors);
266
266
  };
267
267
  }
268
+
269
+ // Experimental
270
+
271
+ const propertyDependencies = getKeyword("https://json-schema.org/keyword/propertyDependencies");
272
+ if (propertyDependencies) {
273
+ propertyDependencies.collectExternalIds = (propertyDependencies, externalIds, ast, dynamicAnchors) => {
274
+ for (const key in propertyDependencies) {
275
+ for (const value in propertyDependencies[key]) {
276
+ Validation.collectExternalIds(propertyDependencies[key][value], externalIds, ast, dynamicAnchors);
277
+ }
278
+ }
279
+ };
280
+ }
281
+
282
+ const conditional = getKeyword("https://json-schema.org/keyword/conditional");
283
+ if (conditional) {
284
+ conditional.collectExternalIds = (conditional, externalIds, ast, dynamicAnchors) => {
285
+ for (const schema of conditional) {
286
+ Validation.collectExternalIds(schema, externalIds, ast, dynamicAnchors);
287
+ }
288
+ };
289
+ }
290
+
291
+ const itemPattern = getKeyword("https://json-schema.org/keyword/itemPattern");
292
+ if (itemPattern) {
293
+ itemPattern.collectExternalIds = (nfa, externalIds, ast, dynamicAnchors) => {
294
+ for (const itemSchema of collectNfaSchemas(nfa.start)) {
295
+ Validation.collectExternalIds(itemSchema, externalIds, ast, dynamicAnchors);
296
+ }
297
+ };
298
+ }
299
+
300
+ const collectNfaSchemas = function* (node, visited = new Set()) {
301
+ if (visited.has(node)) {
302
+ return;
303
+ }
304
+
305
+ visited.add(node);
306
+
307
+ for (const schema in node.transition) {
308
+ yield schema;
309
+ yield* collectNfaSchemas(node.transition[schema], visited);
310
+ }
311
+
312
+ for (const epsilon of node.epsilonTransitions) {
313
+ yield* collectNfaSchemas(epsilon, visited);
314
+ }
315
+ };
268
316
  };
package/lib/keywords.js CHANGED
@@ -15,9 +15,22 @@ export const defineVocabulary = (id, keywords) => {
15
15
 
16
16
  const _dialects = {};
17
17
  const _allowUnknownKeywords = {};
18
- export const getKeywordId = (dialectId, keyword) => _dialects[dialectId]?.[keyword]
19
- || (_allowUnknownKeywords[dialectId] || keyword[0] === "@") && `https://json-schema.org/keyword/unknown#${keyword}`;
18
+
19
+ export const getKeywordId = (dialectId, keyword) => {
20
+ if (!hasDialect(dialectId)) {
21
+ throw Error(`Encountered unknown dialect '${dialectId}'`);
22
+ }
23
+
24
+ return _dialects[dialectId]?.[keyword]
25
+ || (_allowUnknownKeywords[dialectId] || keyword.startsWith("x-"))
26
+ && `https://json-schema.org/keyword/unknown#${keyword}`;
27
+ };
28
+
20
29
  export const getKeywordName = (dialectId, keywordId) => {
30
+ if (!hasDialect(dialectId)) {
31
+ throw Error(`Encountered unknown dialect '${dialectId}'`);
32
+ }
33
+
21
34
  for (const keyword in _dialects[dialectId]) {
22
35
  if (_dialects[dialectId][keyword] === keywordId) {
23
36
  return keyword;
package/lib/schema.js CHANGED
@@ -22,14 +22,10 @@ export const add = (schema, retrievalUri = undefined, contextDialectId = undefin
22
22
  const dialectId = toAbsoluteIri(schema.$schema || contextDialectId);
23
23
  delete schema.$schema;
24
24
 
25
- if (!hasDialect(dialectId)) {
26
- throw Error(`Encountered unknown dialect '${dialectId}'`);
27
- }
28
-
29
25
  // Identifiers
30
26
  const idToken = getKeywordName(dialectId, "https://json-schema.org/keyword/id")
31
27
  || getKeywordName(dialectId, "https://json-schema.org/keyword/draft-04/id");
32
- if (retrievalUri === undefined && !(idToken in schema)) {
28
+ if (!retrievalUri && typeof schema?.[idToken] !== "string") {
33
29
  throw Error(`Unable to determine an identifier for the schema. Use the '${idToken}' keyword or pass a retrievalUri when loading the schema.`);
34
30
  }
35
31
  const internalUrl = resolveUri(schema[idToken] || retrievalUri, retrievalUri);
@@ -77,9 +73,6 @@ const processSchema = (subject, id, dialectId, pointer, anchors, dynamicAnchors)
77
73
  if (jsonTypeOf(subject, "object")) {
78
74
  // Embedded Schema
79
75
  const embeddedDialectId = typeof subject.$schema === "string" ? toAbsoluteIri(subject.$schema) : dialectId;
80
- if (!hasDialect(embeddedDialectId)) {
81
- throw Error(`Encountered unknown dialect '${embeddedDialectId}'`);
82
- }
83
76
 
84
77
  const idToken = getKeywordName(embeddedDialectId, "https://json-schema.org/keyword/id");
85
78
  if (typeof subject[idToken] === "string") {
@@ -1,5 +1,5 @@
1
1
  import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js";
2
- import { addSchema } from "../lib/core.js";
2
+ import { addSchema } from "../lib/index.js";
3
3
  import "../lib/openapi.js";
4
4
 
5
5
  import dialectSchema from "./dialect.js";
@@ -244,7 +244,7 @@ type Header = {
244
244
  description?: string;
245
245
  required?: boolean;
246
246
  deprecated?: boolean;
247
- schema?: OpenApi31;
247
+ schema?: OasSchema31;
248
248
  style?: "simple";
249
249
  explode?: boolean;
250
250
  content?: Content;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperjump/json-schema",
3
- "version": "1.6.6",
3
+ "version": "1.6.7",
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",
@@ -27,7 +27,7 @@
27
27
  "scripts": {
28
28
  "clean": "xargs -a .gitignore rm -rf",
29
29
  "lint": "eslint lib stable draft-* openapi-* bundle annotations",
30
- "test": "mocha 'lib/**/*.spec.ts' 'stable/**/*.spec.ts' 'draft-*/**/*.spec.ts' 'openapi-*/**/*.spec.ts' 'bundle/**/*.spec.ts' 'annotations/**/*.spec.ts'"
30
+ "test": "vitest --watch=false"
31
31
  },
32
32
  "repository": "github:hyperjump-io/json-schema",
33
33
  "keywords": [
@@ -51,20 +51,18 @@
51
51
  "url": "https://github.com/sponsors/jdesrosiers"
52
52
  },
53
53
  "devDependencies": {
54
- "@types/chai": "*",
55
- "@types/mocha": "*",
56
54
  "@types/node": "*",
57
55
  "@typescript-eslint/eslint-plugin": "*",
58
56
  "@typescript-eslint/parser": "*",
59
- "chai": "*",
57
+ "@vitest/coverage-v8": "^1.0.4",
60
58
  "eslint": "*",
59
+ "eslint-import-resolver-exports": "*",
61
60
  "eslint-import-resolver-node": "*",
62
61
  "eslint-import-resolver-typescript": "*",
63
62
  "eslint-plugin-import": "*",
64
63
  "json-schema-test-suite": "github:json-schema-org/JSON-Schema-Test-Suite",
65
- "mocha": "*",
66
- "ts-node": "*",
67
64
  "typescript": "*",
65
+ "vitest": "*",
68
66
  "yaml": "*"
69
67
  },
70
68
  "dependencies": {
@@ -1,375 +0,0 @@
1
- [
2
- {
3
- "title": "`properties`, `patternProperties`, and `additionalProperties`",
4
- "schema": {
5
- "properties": {
6
- "foo": { "title": "Foo" }
7
- },
8
- "patternProperties": {
9
- "^a": { "title": "Bar" }
10
- },
11
- "additionalProperties": { "title": "Baz" }
12
- },
13
- "subjects": [
14
- {
15
- "instance": {},
16
- "assertions": [
17
- {
18
- "location": "#/foo",
19
- "keyword": "title",
20
- "expected": []
21
- },
22
- {
23
- "location": "#/apple",
24
- "keyword": "title",
25
- "expected": []
26
- },
27
- {
28
- "location": "#/bar",
29
- "keyword": "title",
30
- "expected": []
31
- }
32
- ]
33
- },
34
- {
35
- "instance": {
36
- "foo": {},
37
- "apple": {},
38
- "baz": {}
39
- },
40
- "assertions": [
41
- {
42
- "location": "#/foo",
43
- "keyword": "title",
44
- "expected": ["Foo"]
45
- },
46
- {
47
- "location": "#/apple",
48
- "keyword": "title",
49
- "expected": ["Bar"]
50
- },
51
- {
52
- "location": "#/baz",
53
- "keyword": "title",
54
- "expected": ["Baz"]
55
- }
56
- ]
57
- }
58
- ]
59
- },
60
- {
61
- "title": "`propertyNames`",
62
- "schema": {
63
- "propertyNames": {
64
- "const": "foo",
65
- "title": "Foo"
66
- }
67
- },
68
- "subjects": [
69
- {
70
- "instance": { "foo": 42 },
71
- "assertions": [
72
- {
73
- "location": "#",
74
- "keyword": "propertyNames",
75
- "expected": []
76
- },
77
- {
78
- "location": "#/foo",
79
- "keyword": "title",
80
- "expected": []
81
- }
82
- ]
83
- }
84
- ]
85
- },
86
- {
87
- "title": "`prefixItems` and `items`",
88
- "schema": {
89
- "prefixItems": [{ "title": "Foo" }],
90
- "items": { "title": "Bar" }
91
- },
92
- "subjects": [
93
- {
94
- "instance": ["foo", "bar"],
95
- "assertions": [
96
- {
97
- "location": "#/0",
98
- "keyword": "title",
99
- "expected": ["Foo"]
100
- },
101
- {
102
- "location": "#/1",
103
- "keyword": "title",
104
- "expected": ["Bar"]
105
- },
106
- {
107
- "location": "#/2",
108
- "keyword": "title",
109
- "expected": []
110
- }
111
- ]
112
- }
113
- ]
114
- },
115
- {
116
- "title": "`contains`",
117
- "schema": {
118
- "contains": {
119
- "type": "number",
120
- "title": "Foo"
121
- }
122
- },
123
- "subjects": [
124
- {
125
- "instance": ["foo", 42, true],
126
- "assertions": [
127
- {
128
- "location": "#/0",
129
- "keyword": "title",
130
- "expected": []
131
- },
132
- {
133
- "location": "#/1",
134
- "keyword": "title",
135
- "expected": ["Foo"]
136
- },
137
- {
138
- "location": "#/2",
139
- "keyword": "title",
140
- "expected": []
141
- },
142
- {
143
- "location": "#/3",
144
- "keyword": "title",
145
- "expected": []
146
- }
147
- ]
148
- }
149
- ]
150
- },
151
- {
152
- "title": "`allOf`",
153
- "schema": {
154
- "allOf": [
155
- { "title": "Foo" },
156
- { "title": "Bar" }
157
- ]
158
- },
159
- "subjects": [
160
- {
161
- "instance": "foo",
162
- "assertions": [
163
- {
164
- "location": "#",
165
- "keyword": "allOf",
166
- "expected": []
167
- },
168
- {
169
- "location": "#",
170
- "keyword": "title",
171
- "expected": ["Bar", "Foo"]
172
- }
173
- ]
174
- }
175
- ]
176
- },
177
- {
178
- "title": "`anyOf`",
179
- "schema": {
180
- "anyOf": [
181
- {
182
- "type": "integer",
183
- "title": "Foo"
184
- },
185
- {
186
- "type": "number",
187
- "title": "Bar"
188
- }
189
- ]
190
- },
191
- "subjects": [
192
- {
193
- "instance": 42,
194
- "assertions": [
195
- {
196
- "location": "#",
197
- "keyword": "anyOf",
198
- "expected": []
199
- },
200
- {
201
- "location": "#",
202
- "keyword": "title",
203
- "expected": ["Bar", "Foo"]
204
- }
205
- ]
206
- },
207
- {
208
- "instance": 4.2,
209
- "assertions": [
210
- {
211
- "location": "#",
212
- "keyword": "title",
213
- "expected": ["Bar"]
214
- }
215
- ]
216
- }
217
- ]
218
- },
219
- {
220
- "title": "`oneOf`",
221
- "schema": {
222
- "oneOf": [
223
- {
224
- "type": "string",
225
- "title": "Foo"
226
- },
227
- {
228
- "type": "number",
229
- "title": "Bar"
230
- }
231
- ]
232
- },
233
- "subjects": [
234
- {
235
- "instance": "foo",
236
- "assertions": [
237
- {
238
- "location": "#",
239
- "keyword": "oneOf",
240
- "expected": []
241
- },
242
- {
243
- "location": "#",
244
- "keyword": "title",
245
- "expected": ["Foo"]
246
- }
247
- ]
248
- },
249
- {
250
- "instance": 42,
251
- "assertions": [
252
- {
253
- "location": "#",
254
- "keyword": "title",
255
- "expected": ["Bar"]
256
- }
257
- ]
258
- }
259
- ]
260
- },
261
- {
262
- "title": "`not`",
263
- "schema": {
264
- "title": "Foo",
265
- "not": {
266
- "not": { "title": "Bar" }
267
- }
268
- },
269
- "subjects": [
270
- {
271
- "instance": {},
272
- "assertions": [
273
- {
274
- "location": "#",
275
- "keyword": "not",
276
- "expected": []
277
- },
278
- {
279
- "location": "#",
280
- "keyword": "title",
281
- "expected": ["Foo"]
282
- }
283
- ]
284
- }
285
- ]
286
- },
287
- {
288
- "title": "`dependentSchemas`",
289
- "schema": {
290
- "dependentSchemas": {
291
- "foo": { "title": "Foo" }
292
- }
293
- },
294
- "subjects": [
295
- {
296
- "instance": { "foo": 42 },
297
- "assertions": [
298
- {
299
- "keyword": "dependentSchemas",
300
- "location": "#",
301
- "expected": []
302
- },
303
- {
304
- "keyword": "title",
305
- "location": "#",
306
- "expected": ["Foo"]
307
- }
308
- ]
309
- },
310
- {
311
- "instance": { "foo": 42 },
312
- "assertions": [
313
- {
314
- "keyword": "title",
315
- "location": "#/foo",
316
- "expected": []
317
- }
318
- ]
319
- }
320
- ]
321
- },
322
- {
323
- "title": "`if`, `then`, and `else`",
324
- "schema": {
325
- "if": {
326
- "title": "If",
327
- "type": "string"
328
- },
329
- "then": { "title": "Then" },
330
- "else": { "title": "Else" }
331
- },
332
- "subjects": [
333
- {
334
- "instance": "foo",
335
- "assertions": [
336
- {
337
- "location": "#",
338
- "keyword": "if",
339
- "expected": []
340
- },
341
- {
342
- "location": "#",
343
- "keyword": "then",
344
- "expected": []
345
- },
346
- {
347
- "location": "#",
348
- "keyword": "title",
349
- "expected": ["Then", "If"]
350
- }
351
- ]
352
- },
353
- {
354
- "instance": 42,
355
- "assertions": [
356
- {
357
- "location": "#",
358
- "keyword": "if",
359
- "expected": []
360
- },
361
- {
362
- "location": "#",
363
- "keyword": "else",
364
- "expected": []
365
- },
366
- {
367
- "location": "#",
368
- "keyword": "title",
369
- "expected": ["Else", "If"]
370
- }
371
- ]
372
- }
373
- ]
374
- }
375
- ]
@@ -1,57 +0,0 @@
1
- [
2
- {
3
- "title": "`contentMediaType` is an annotation",
4
- "schema": {
5
- "contentMediaType": "application/json"
6
- },
7
- "subjects": [
8
- {
9
- "instance": "{ \"foo\": \"bar\" }",
10
- "assertions": [
11
- {
12
- "location": "#",
13
- "keyword": "contentMediaType",
14
- "expected": ["application/json"]
15
- }
16
- ]
17
- }
18
- ]
19
- },
20
- {
21
- "title": "`contentEncoding` is an annotation",
22
- "schema": {
23
- "contentEncoding": "base64"
24
- },
25
- "subjects": [
26
- {
27
- "instance": "SGVsbG8gZnJvbSBKU09OIFNjaGVtYQ==",
28
- "assertions": [
29
- {
30
- "location": "#",
31
- "keyword": "contentEncoding",
32
- "expected": ["base64"]
33
- }
34
- ]
35
- }
36
- ]
37
- },
38
- {
39
- "title": "`contentSchema` is an annotation",
40
- "schema": {
41
- "$id": "https://annotations.json-schema.org/test/contentSchema-is-an-annotation",
42
- "contentSchema": { "type": "number" }
43
- },
44
- "subjects": [
45
- {
46
- "instance": "42",
47
- "assertions": [
48
- {
49
- "location": "#",
50
- "keyword": "contentSchema",
51
- "expected": ["https://annotations.json-schema.org/test/contentSchema-is-an-annotation#/contentSchema"]
52
- }
53
- ]
54
- }
55
- ]
56
- }
57
- ]
@@ -1,33 +0,0 @@
1
- [
2
- {
3
- "title": "`$ref` and `$defs`",
4
- "schema": {
5
- "$ref": "#/$defs/foo",
6
- "$defs": {
7
- "foo": { "title": "Foo" }
8
- }
9
- },
10
- "subjects": [
11
- {
12
- "instance": "foo",
13
- "assertions": [
14
- {
15
- "location": "#",
16
- "keyword": "$ref",
17
- "expected": []
18
- },
19
- {
20
- "location": "#",
21
- "keyword": "$defs",
22
- "expected": []
23
- },
24
- {
25
- "location": "#",
26
- "keyword": "title",
27
- "expected": ["Foo"]
28
- }
29
- ]
30
- }
31
- ]
32
- }
33
- ]
@@ -1,20 +0,0 @@
1
- [
2
- {
3
- "title": "`format` is an annotation",
4
- "schema": {
5
- "format": "email"
6
- },
7
- "subjects": [
8
- {
9
- "instance": "foo@bar.com",
10
- "assertions": [
11
- {
12
- "location": "#",
13
- "keyword": "format",
14
- "expected": ["email"]
15
- }
16
- ]
17
- }
18
- ]
19
- }
20
- ]