@hyperjump/json-schema 1.6.4 → 1.6.6
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/README.md +134 -0
- package/bundle/file.json +57 -0
- package/lib/instance.js +1 -2
- package/lib/keywords/enum.js +8 -1
- package/lib/schema.js +9 -1
- package/package.json +1 -1
package/bundle/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Bundler Tests
|
|
2
|
+
|
|
3
|
+
The testing strategy used by this Test Suite is based on the concept that it
|
|
4
|
+
should be indistinguishable whether the user evaluated the schema using the
|
|
5
|
+
bundle or using individually loaded schemas. Test Runners should validate each
|
|
6
|
+
Test twice, once with all schemas loaded individually and again using just the
|
|
7
|
+
bundle. The Test passes if both evaluations produce identical output.
|
|
8
|
+
|
|
9
|
+
The accuracy of this testing approach is dependent on how detailed the output
|
|
10
|
+
is. It's recommended that Test Runner use a validator implementation that
|
|
11
|
+
supports the
|
|
12
|
+
["verbose"](https://json-schema.org/draft/2020-12/json-schema-core#name-verbose)
|
|
13
|
+
official output format or something similarly detailed that can be used for
|
|
14
|
+
comparison.
|
|
15
|
+
|
|
16
|
+
## Supported Dialects
|
|
17
|
+
|
|
18
|
+
Although the topic of bundling didn't appear in the spec until 2020-12, the
|
|
19
|
+
process described is applicable since `id` (now `$id`) was introduced in
|
|
20
|
+
draft-03. Test Cases in this Test Suite are designed to be compatible with as
|
|
21
|
+
many releases of JSON Schema as possible. They do not include a `$schema`
|
|
22
|
+
keyword so that implementations can run the same Test Suite for each dialect
|
|
23
|
+
they support. The Test Runner should define what dialect it's using to run the
|
|
24
|
+
Test Suite.
|
|
25
|
+
|
|
26
|
+
Since this Test Suite can be used for a variety of dialects, there are a couple
|
|
27
|
+
of options that can be used by Test Runners to filter out Test Cases that don't
|
|
28
|
+
apply to the dialect under test.
|
|
29
|
+
|
|
30
|
+
## Test Case Components
|
|
31
|
+
|
|
32
|
+
### description
|
|
33
|
+
|
|
34
|
+
A short description of what behavior the Test Case is covering.
|
|
35
|
+
|
|
36
|
+
### compatibility
|
|
37
|
+
|
|
38
|
+
The `compatibility` option allows you to set which dialects the Test Case is
|
|
39
|
+
compatible with. Test Runners can use this value to filter out Test Cases that
|
|
40
|
+
don't apply the to dialect currently under test. Dialects are indicated by the
|
|
41
|
+
number corresponding to their release. Date-based releases use just the year.
|
|
42
|
+
|
|
43
|
+
If this option isn't present, it means the Test Case is compatible with draft-03
|
|
44
|
+
and above.
|
|
45
|
+
|
|
46
|
+
If this option is present with a number, the number indicates the minimum
|
|
47
|
+
release the Test Case is compatible with. This example indicates that the Test
|
|
48
|
+
Case is compatible with draft-07 and up.
|
|
49
|
+
|
|
50
|
+
**Example**: `"compatibility": "7"`
|
|
51
|
+
|
|
52
|
+
You can use a `<=` operator to indicate that the Test Case is compatible with
|
|
53
|
+
releases less then or equal to the given release. This example indicates that
|
|
54
|
+
the Test Case is compatible with 2019-09 and under.
|
|
55
|
+
|
|
56
|
+
**Example**: `"compatibility": "<=2019"`
|
|
57
|
+
|
|
58
|
+
You can use comma-separated values to indicate multiple constraints if needed.
|
|
59
|
+
This example indicates that the Test Case is compatible with releases between
|
|
60
|
+
draft-06 and 2019-09.
|
|
61
|
+
|
|
62
|
+
**Example**: `"compatibility": "6,<=2019"`
|
|
63
|
+
|
|
64
|
+
For convenience, you can use the `=` operator to indicate a Test Case is only
|
|
65
|
+
compatible with a single release. This example indicates that the Test Case is
|
|
66
|
+
compatible only with 2020-12.
|
|
67
|
+
|
|
68
|
+
**Example**: `"compatibility": "=2020"`
|
|
69
|
+
|
|
70
|
+
### requiredDialects
|
|
71
|
+
|
|
72
|
+
Since 2020-12, it's been allowed to have bundles that include schemas using
|
|
73
|
+
dialects different from the parent schema. In order to test this, it's necessary
|
|
74
|
+
to have an external schema that declares a dialect with `$schema`. The
|
|
75
|
+
`requiredDialects` option can be included to tell the Test Runner which dialects
|
|
76
|
+
are required to run the Test Case other than the dialect under test. This option
|
|
77
|
+
is an array of numbers corresponding to release numbers. Date-based releases use
|
|
78
|
+
just the year.
|
|
79
|
+
|
|
80
|
+
**Example**: `"requiredDialects": [2019]`
|
|
81
|
+
|
|
82
|
+
### schema
|
|
83
|
+
|
|
84
|
+
The schema that will serve as the entry point of the bundle. This schema
|
|
85
|
+
shouldn't include `$schema` or `id`/`$id` because Test Cases should be designed
|
|
86
|
+
to work with as many releases as possible.
|
|
87
|
+
|
|
88
|
+
### externalSchemas
|
|
89
|
+
|
|
90
|
+
`externalSchemas` is where you define the schemas that the schema in `schema`
|
|
91
|
+
references and you expect to get embedded into the bundled schema. The value is
|
|
92
|
+
an object where the keys are retrieval URIs and values are schemas. Most
|
|
93
|
+
external schemas aren't self identifying (using `id`/`$id`) and rely solely on
|
|
94
|
+
the retrieval URI for identification. This is done to increase the number of
|
|
95
|
+
dialects that the test is compatible with. Because `id` changed to `$id` in
|
|
96
|
+
draft-06, if you use `$id`, the test becomes incompatible with draft-03/4 and in
|
|
97
|
+
most cases, that's not necessary.
|
|
98
|
+
|
|
99
|
+
### tests
|
|
100
|
+
|
|
101
|
+
`tests` are a collection of Tests to run to verify the Test Case. Tests don't
|
|
102
|
+
include an expected pass/fail for the instance because we don't care if the
|
|
103
|
+
instance is valid or not, we care only whether the bundle produces the same
|
|
104
|
+
results as its unbundled equivalent.
|
|
105
|
+
|
|
106
|
+
Tests should be designed to expose potential differences between the bundled and
|
|
107
|
+
unbundled evaluations. You should have at least one Test that's expected to pass
|
|
108
|
+
to ensure that annotations are equivalent and at least one Test that's expected
|
|
109
|
+
to fail to ensure errors are equivalent.
|
|
110
|
+
|
|
111
|
+
## Fixtures
|
|
112
|
+
|
|
113
|
+
Often we don't care what's in the externally referenced schemas and end up using
|
|
114
|
+
the same simple and generic schemas in a lot of tests. In order to reduce
|
|
115
|
+
duplication, there are a couple of generic schemas defined that test runners
|
|
116
|
+
should load in addition to `externalSchemas` for each Test Case.
|
|
117
|
+
|
|
118
|
+
Fixtures are expected to be simple, compatible with all dialects, and not
|
|
119
|
+
critical to understanding what is being tested. Anything that doesn't meet those
|
|
120
|
+
criteria is better off declared in `externalSchemas`.
|
|
121
|
+
|
|
122
|
+
## Directory Structure
|
|
123
|
+
|
|
124
|
+
* tests - A directory containing the full Test Suite.
|
|
125
|
+
* tests.json - A Test Suite covering functionality in the latest spec release.
|
|
126
|
+
* legacy-tests.json - A Test Suite covering functionality that was removed in
|
|
127
|
+
a previous release. This is mostly adaptations of Test Cases that use `$id`
|
|
128
|
+
or `$defs` to cover the same functionality for older releases that use `id`
|
|
129
|
+
or `definitions`. Test Cases involving other removed keywords such as
|
|
130
|
+
`dependencies` and `recursiveRef` can be found here as well.
|
|
131
|
+
* fixtures - A collection of simple and reusable schemas
|
|
132
|
+
* string.schema.json
|
|
133
|
+
* number.schema.json
|
|
134
|
+
* ... etc
|
package/bundle/file.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"description": "Use relative paths for files",
|
|
4
|
+
"schema": "main.schema.json",
|
|
5
|
+
"externalSchemas": [],
|
|
6
|
+
"bundledSchema": {
|
|
7
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
8
|
+
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"foo": { "$ref": "string.schema.json" }
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"$defs": {
|
|
15
|
+
"string.schema.json": {
|
|
16
|
+
"$id": "string.schema.json",
|
|
17
|
+
|
|
18
|
+
"type": "string"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Allow mix of http and file if main is file",
|
|
25
|
+
"schema": "mixed-schemes.schema.json",
|
|
26
|
+
"externalSchemas": [
|
|
27
|
+
{
|
|
28
|
+
"$id": "https://bundler.hyperjump.io/number",
|
|
29
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
30
|
+
|
|
31
|
+
"type": "number"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"bundledSchema": {
|
|
35
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
36
|
+
|
|
37
|
+
"type": "object",
|
|
38
|
+
"properties": {
|
|
39
|
+
"foo": { "$ref": "string.schema.json" },
|
|
40
|
+
"bar": { "$ref": "https://bundler.hyperjump.io/number" }
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
"$defs": {
|
|
44
|
+
"string.schema.json": {
|
|
45
|
+
"$id": "string.schema.json",
|
|
46
|
+
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"https://bundler.hyperjump.io/number": {
|
|
50
|
+
"$id": "https://bundler.hyperjump.io/number",
|
|
51
|
+
|
|
52
|
+
"type": "number"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
package/lib/instance.js
CHANGED
|
@@ -2,7 +2,6 @@ import { append as pointerAppend, get as pointerGet } from "@hyperjump/json-poin
|
|
|
2
2
|
import { toAbsoluteIri } from "@hyperjump/uri";
|
|
3
3
|
import curry from "just-curry-it";
|
|
4
4
|
import { jsonTypeOf } from "./common.js";
|
|
5
|
-
import * as Reference from "./reference.js";
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
export const nil = { id: undefined, pointer: "", instance: undefined, value: undefined };
|
|
@@ -27,7 +26,7 @@ export const get = (url, instance = nil) => {
|
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
export const uri = (doc) => `${doc.id || ""}#${encodeURI(doc.pointer)}`;
|
|
30
|
-
export const value = (doc) =>
|
|
29
|
+
export const value = (doc) => doc.value;
|
|
31
30
|
export const has = (key, doc) => key in value(doc);
|
|
32
31
|
export const typeOf = curry((doc, type) => jsonTypeOf(value(doc), type));
|
|
33
32
|
|
package/lib/keywords/enum.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact";
|
|
1
2
|
import jsonStringify from "fastest-stable-stringify";
|
|
2
3
|
import * as Schema from "../schema.js";
|
|
3
4
|
import * as Instance from "../instance.js";
|
|
@@ -5,7 +6,13 @@ import * as Instance from "../instance.js";
|
|
|
5
6
|
|
|
6
7
|
const id = "https://json-schema.org/keyword/enum";
|
|
7
8
|
|
|
8
|
-
const compile = (schema) =>
|
|
9
|
+
const compile = (schema) => pipe(
|
|
10
|
+
Schema.iter(schema),
|
|
11
|
+
asyncMap(Schema.value),
|
|
12
|
+
asyncMap(jsonStringify),
|
|
13
|
+
asyncCollectArray
|
|
14
|
+
);
|
|
15
|
+
|
|
9
16
|
const interpret = (enum_, instance) => enum_.some((enumValue) => jsonStringify(Instance.value(instance)) === enumValue);
|
|
10
17
|
|
|
11
18
|
export default { id, compile, interpret };
|
package/lib/schema.js
CHANGED
|
@@ -259,6 +259,7 @@ export const toSchema = (schemaDoc, options = {}) => {
|
|
|
259
259
|
const idToken = getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/id")
|
|
260
260
|
|| getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/draft-04/id");
|
|
261
261
|
const anchorToken = getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/anchor");
|
|
262
|
+
const legacyAnchorToken = getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/draft-04/id");
|
|
262
263
|
const dynamicAnchorToken = getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/dynamicAnchor");
|
|
263
264
|
const legacyDynamicAnchorToken = getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/draft-2020-12/dynamicAnchor");
|
|
264
265
|
const recursiveAnchorToken = getKeywordName(schemaDoc.dialectId, "https://json-schema.org/keyword/recursiveAnchor");
|
|
@@ -284,7 +285,14 @@ export const toSchema = (schemaDoc, options = {}) => {
|
|
|
284
285
|
}
|
|
285
286
|
} else {
|
|
286
287
|
if (pointer in anchors) {
|
|
287
|
-
|
|
288
|
+
if (anchorToken) {
|
|
289
|
+
value = { [anchorToken]: anchors[pointer], ...value };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Legacy anchor
|
|
293
|
+
if (legacyAnchorToken) {
|
|
294
|
+
value = { [legacyAnchorToken]: `#${anchors[pointer]}`, ...value };
|
|
295
|
+
}
|
|
288
296
|
}
|
|
289
297
|
if (pointer in dynamicAnchors) {
|
|
290
298
|
if (dynamicAnchorToken) {
|