json-schema-library 11.2.0 → 11.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## Changelog
2
2
 
3
+ ### v11.3.0
4
+
5
+ - added option 'draft' as fallback for a missing `$schema` id
6
+ - added properties to merge when resolvinf a $ref to `settings.PROPERTIES_TO_MERGE`
7
+
3
8
  ### v11.2.0
4
9
 
5
10
  - introduced `throwOnInvalidRef` to abort validation when a $ref cannot be resolved
package/bowtie/BOWTIE.md CHANGED
@@ -27,14 +27,14 @@ echo '{"cmd":"stop"}' | docker run -i jlib
27
27
  bowtie smoke -i localhost/jlib
28
28
  bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/main/tests/draft7/type.json | bowtie summary --show failures
29
29
 
30
- bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft7 > draft7.json
31
- bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2019-09 > draft2019-09.json
32
- bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2020-12 > draft2020-12.json
33
-
30
+ bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft4 | bowtie summary --show failures
31
+ bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft6 | bowtie summary --show failures
34
32
  bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft7 | bowtie summary --show failures
35
33
  bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2019-09 | bowtie summary --show failures
36
34
  bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2020-12 | bowtie summary --show failures
37
35
 
36
+ bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft7 > draft7.json
37
+
38
38
  **Fails**
39
39
  bowtie suite $(bowtie filter-implementations | sed 's/^/-i /') https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2020-12 >draft2020-12.json
40
40
 
package/bowtie/Dockerfile CHANGED
@@ -3,4 +3,4 @@ COPY . /usr/app
3
3
  WORKDIR /usr/app
4
4
  RUN npm install --omit=dev
5
5
  RUN npm -g install tsm
6
- CMD ["tsm", "bowtie.ts"]
6
+ CMD ["tsm", "bowtie-jlib.ts"]
@@ -19,6 +19,7 @@ import {
19
19
  let state: "started" | "dialect" | "testing" | "stopped" = "stopped";
20
20
  /** current JSON Schema draft version to test */
21
21
  let dialect: Dialect;
22
+ /** all meta-schemata added as remtoes */
22
23
  let remote: SchemaNode;
23
24
 
24
25
  const cmds: CommandMap = {
@@ -26,6 +27,7 @@ const cmds: CommandMap = {
26
27
  console.assert(args.version === 1, { args });
27
28
  console.assert(state === "stopped");
28
29
  state = "started";
30
+
29
31
  return {
30
32
  version: 1,
31
33
  implementation: {
@@ -53,32 +55,23 @@ const cmds: CommandMap = {
53
55
  dialect: async (args) => {
54
56
  console.assert(state === "started");
55
57
  state = "dialect";
56
- dialect = args.dialect;
57
-
58
- const node = compileSchema({ $schema: dialect });
59
- remotes.forEach((schema) => {
60
- node.addRemoteSchema(schema.$id ?? schema.id, schema);
61
- });
62
- remote = node;
63
58
 
59
+ dialect = args.dialect;
60
+ remote = compileSchema({ $schema: dialect });
61
+ remotes.forEach((schema) => remote.addRemoteSchema(schema.$id ?? schema.id, schema));
64
62
  return { ok: true };
65
63
  },
66
64
 
67
65
  run: async (args) => {
68
66
  console.assert(state === "dialect" || state === "testing");
69
67
  state = "testing";
70
- const { schema, tests, registry } = args.case;
71
- if (schema != null && typeof schema === "object") {
72
- schema.$schema = dialect; // set draft version to non-boolean schema
73
- }
74
- // compile schema
75
- const node = compileSchema(schema, { remote, formatAssertion: false });
76
- // add remote schemata
77
- for (const id in registry) {
78
- node.addRemoteSchema(id, registry[id]);
68
+
69
+ const node = compileSchema(args.case.schema, { remote, draft: dialect, formatAssertion: false });
70
+ for (const id in args.case.registry) {
71
+ node.addRemoteSchema(id, args.case.registry[id]);
79
72
  }
80
73
  // run test cases and collect results to be sent back to bowtie
81
- const results: RunCmdResponse["results"] = tests.map((test) => {
74
+ const results: RunCmdResponse["results"] = args.case.tests.map((test) => {
82
75
  try {
83
76
  return { valid: node.validate(test.instance).valid };
84
77
  } catch (e) {
@@ -92,6 +85,7 @@ const cmds: CommandMap = {
92
85
  stop: async (_, stdio) => {
93
86
  console.assert(state === "testing");
94
87
  state = "stopped";
88
+
95
89
  if (process.env.JLIB_TEST_RUN !== "true") {
96
90
  stdio?.close();
97
91
  process.exit(0);
@@ -2,7 +2,7 @@ import { strict as assert } from "node:assert";
2
2
  import { compileSchema } from "../src/compileSchema";
3
3
  import { remotes } from "json-schema-library/remotes";
4
4
  import { JsonSchema } from "../src/types";
5
- import { runCommand } from "./bowtie";
5
+ import { runCommand } from "./bowtie-jlib";
6
6
  import { ErrorResponse, RunCmdResponse } from "./bowtie-api";
7
7
 
8
8
  const isRunCmdResponse = (value: unknown): value is RunCmdResponse =>
@@ -54,23 +54,214 @@ describe("bowtie (draft7)", async () => {
54
54
  });
55
55
  });
56
56
 
57
- describe("bowtie (2020-12)", () => {
58
- describe("validate definition against metaschema", () => {
59
- const node = compileSchema(
60
- {
61
- $schema: "https://json-schema.org/draft/2020-12/schema",
62
- $ref: "https://json-schema.org/draft/2020-12/schema"
63
- },
64
- { remote }
65
- );
57
+ describe("bowtie (2019-09)", () => {
58
+ const testCase = {
59
+ description: "schema that uses custom metaschema with with no validation vocabulary",
60
+ schema: {
61
+ $id: "https://schema/using/no/validation",
62
+ $schema: "http://localhost:1234/draft2019-09/metaschema-no-validation.json",
63
+ properties: { badProperty: false, numberProperty: { minimum: 10 } }
64
+ },
65
+ registry: {
66
+ "http://localhost:1234/draft2019-09/integer.json": {
67
+ $schema: "https://json-schema.org/draft/2019-09/schema",
68
+ type: "integer"
69
+ },
70
+ "http://localhost:1234/draft2019-09/dependentRequired.json": {
71
+ $id: "http://localhost:1234/draft2019-09/dependentRequired.json",
72
+ $schema: "https://json-schema.org/draft/2019-09/schema",
73
+ dependentRequired: { foo: ["bar"] }
74
+ },
75
+ "http://localhost:1234/draft2019-09/detached-ref.json": {
76
+ $id: "http://localhost:1234/draft2019-09/detached-ref.json",
77
+ $schema: "https://json-schema.org/draft/2019-09/schema",
78
+ $defs: { foo: { $ref: "#detached" }, detached: { $anchor: "detached", type: "integer" } }
79
+ },
80
+ "http://localhost:1234/integer.json": { type: "integer" },
81
+ "http://localhost:1234/draft2019-09/nested/foo-ref-string.json": {
82
+ $schema: "https://json-schema.org/draft/2019-09/schema",
83
+ type: "object",
84
+ properties: { foo: { $ref: "string.json" } }
85
+ },
86
+ "http://localhost:1234/draft2019-09/urn-ref-string.json": {
87
+ $id: "urn:uuid:feebdaed-ffff-0000-2019-0900deadbeef",
88
+ $defs: { bar: { type: "string" } },
89
+ $ref: "#/$defs/bar"
90
+ },
91
+ "http://localhost:1234/draft2019-09/locationIndependentIdentifier.json": {
92
+ $schema: "https://json-schema.org/draft/2019-09/schema",
93
+ $defs: { refToInteger: { $ref: "#foo" }, A: { $anchor: "foo", type: "integer" } }
94
+ },
95
+ "http://localhost:1234/baseUriChangeFolder/folderInteger.json": { type: "integer" },
96
+ "http://localhost:1234/draft2019-09/baseUriChange/folderInteger.json": {
97
+ $schema: "https://json-schema.org/draft/2019-09/schema",
98
+ type: "integer"
99
+ },
100
+ "http://localhost:1234/draft2019-09/ref-and-defs.json": {
101
+ $schema: "https://json-schema.org/draft/2019-09/schema",
102
+ $id: "http://localhost:1234/draft2019-09/ref-and-defs.json",
103
+ $defs: { inner: { properties: { bar: { type: "string" } } } },
104
+ $ref: "#/$defs/inner"
105
+ },
106
+ "http://localhost:1234/baseUriChangeFolderInSubschema/folderInteger.json": { type: "integer" },
107
+ "http://localhost:1234/draft2019-09/baseUriChangeFolder/folderInteger.json": {
108
+ $schema: "https://json-schema.org/draft/2019-09/schema",
109
+ type: "integer"
110
+ },
111
+ "http://localhost:1234/nested/foo-ref-string.json": {
112
+ type: "object",
113
+ properties: { foo: { $ref: "string.json" } }
114
+ },
115
+ "http://localhost:1234/nested/string.json": { type: "string" },
116
+ "http://localhost:1234/draft2019-09/baseUriChangeFolderInSubschema/folderInteger.json": {
117
+ $schema: "https://json-schema.org/draft/2019-09/schema",
118
+ type: "integer"
119
+ },
120
+ "http://localhost:1234/draft2019-09/name-defs.json": {
121
+ $schema: "https://json-schema.org/draft/2019-09/schema",
122
+ $defs: { orNull: { anyOf: [{ type: "null" }, { $ref: "#" }] } },
123
+ type: "string"
124
+ },
125
+ "http://localhost:1234/v1/different-id-ref-string.json": {
126
+ $id: "http://localhost:1234/v1/real-id-ref-string.json",
127
+ $defs: { bar: { type: "string" } },
128
+ $ref: "#/$defs/bar"
129
+ },
130
+ "http://localhost:1234/v1/nested-absolute-ref-to-string.json": {
131
+ $defs: { bar: { $id: "http://localhost:1234/v1/the-nested-id.json", type: "string" } },
132
+ $ref: "http://localhost:1234/v1/the-nested-id.json"
133
+ },
134
+ "http://localhost:1234/v1/urn-ref-string.json": {
135
+ $id: "urn:uuid:feebdaed-ffff-0000-ff01-0000deadbeef",
136
+ $defs: { bar: { type: "string" } },
137
+ $ref: "#/$defs/bar"
138
+ },
139
+ "http://localhost:1234/draft2019-09/ignore-prefixItems.json": {
140
+ $id: "http://localhost:1234/draft2019-09/ignore-prefixItems.json",
141
+ $schema: "https://json-schema.org/draft/2019-09/schema",
142
+ prefixItems: [{ type: "string" }]
143
+ },
144
+ "http://localhost:1234/draft2019-09/subSchemas.json": {
145
+ $schema: "https://json-schema.org/draft/2019-09/schema",
146
+ $defs: { integer: { type: "integer" }, refToInteger: { $ref: "#/$defs/integer" } }
147
+ },
148
+ "http://localhost:1234/draft2019-09/nested-absolute-ref-to-string.json": {
149
+ $defs: { bar: { $id: "http://localhost:1234/draft2019-09/the-nested-id.json", type: "string" } },
150
+ $ref: "http://localhost:1234/draft2019-09/the-nested-id.json"
151
+ },
152
+ "http://localhost:1234/draft2019-09/nested/string.json": {
153
+ $schema: "https://json-schema.org/draft/2019-09/schema",
154
+ type: "string"
155
+ },
156
+ "http://localhost:1234/draft2019-09/metaschema-optional-vocabulary.json": {
157
+ $schema: "https://json-schema.org/draft/2019-09/schema",
158
+ $id: "http://localhost:1234/draft2019-09/metaschema-optional-vocabulary.json",
159
+ $vocabulary: {
160
+ "https://json-schema.org/draft/2019-09/vocab/validation": true,
161
+ "https://json-schema.org/draft/2019-09/vocab/core": true,
162
+ "http://localhost:1234/draft/2019-09/vocab/custom": false
163
+ },
164
+ $recursiveAnchor: true,
165
+ allOf: [
166
+ { $ref: "https://json-schema.org/draft/2019-09/meta/validation" },
167
+ { $ref: "https://json-schema.org/draft/2019-09/meta/core" }
168
+ ]
169
+ },
170
+ "http://localhost:1234/draft2019-09/different-id-ref-string.json": {
171
+ $id: "http://localhost:1234/draft2019-09/real-id-ref-string.json",
172
+ $defs: { bar: { type: "string" } },
173
+ $ref: "#/$defs/bar"
174
+ },
175
+ "http://localhost:1234/draft2019-09/extendible-dynamic-ref.json": {
176
+ description: "extendible array",
177
+ $schema: "https://json-schema.org/draft/2019-09/schema",
178
+ $id: "http://localhost:1234/draft2019-09/extendible-dynamic-ref.json",
179
+ type: "object",
180
+ properties: { elements: { type: "array", items: { $dynamicRef: "#elements" } } },
181
+ required: ["elements"],
182
+ additionalProperties: false,
183
+ $defs: { elements: { $dynamicAnchor: "elements" } }
184
+ },
185
+ "http://localhost:1234/draft2019-09/metaschema-no-validation.json": {
186
+ $schema: "https://json-schema.org/draft/2019-09/schema",
187
+ $id: "http://localhost:1234/draft2019-09/metaschema-no-validation.json",
188
+ $vocabulary: {
189
+ "https://json-schema.org/draft/2019-09/vocab/applicator": true,
190
+ "https://json-schema.org/draft/2019-09/vocab/core": true
191
+ },
192
+ $recursiveAnchor: true,
193
+ allOf: [
194
+ { $ref: "https://json-schema.org/draft/2019-09/meta/applicator" },
195
+ { $ref: "https://json-schema.org/draft/2019-09/meta/core" }
196
+ ]
197
+ },
198
+ "http://localhost:1234/baseUriChange/folderInteger.json": { type: "integer" }
199
+ }
200
+ };
66
201
 
67
- it("valid definition schema", () => {
68
- const first = node.validate({ $defs: { foo: { type: "integer" } } });
69
- assert.deepEqual(first.valid, true);
202
+ before(async () => {
203
+ await runCommand({ cmd: "start", version: 1 });
204
+ await runCommand({ cmd: "dialect", dialect: "https://json-schema.org/draft/2019-09/schema" });
205
+ });
206
+ after(async () => runCommand({ cmd: "stop" }));
207
+
208
+ it(`${testCase.description} - applicator vocabulary still works`, async () => {
209
+ const response = await runCommand({
210
+ cmd: "run",
211
+ seq: 1,
212
+ case: {
213
+ ...testCase,
214
+ tests: [
215
+ {
216
+ description: "applicator vocabulary still works",
217
+ instance: { badProperty: "this property should not exist" },
218
+ valid: false
219
+ }
220
+ ]
221
+ }
222
+ });
223
+ assert(isRunCmdResponse(response));
224
+ assert(!isErrorResponse(response.results[0]));
225
+ assert.equal(response.results[0].valid, false);
226
+ });
227
+
228
+ it(`${testCase.description} - no validation: valid number`, async () => {
229
+ const response = await runCommand({
230
+ cmd: "run",
231
+ seq: 1,
232
+ case: {
233
+ ...testCase,
234
+ tests: [
235
+ {
236
+ description: "no validation: valid number",
237
+ instance: { numberProperty: 20 },
238
+ valid: true
239
+ }
240
+ ]
241
+ }
70
242
  });
71
- it("invalid definition schema", () => {
72
- const second = node.validate({ $defs: { foo: { type: 1 } } });
73
- assert.deepEqual(second.valid, false);
243
+ assert(isRunCmdResponse(response));
244
+ assert(!isErrorResponse(response.results[0]));
245
+ assert.equal(response.results[0].valid, true);
246
+ });
247
+
248
+ it(`${testCase.description} - no validation: invalid number, but it still validates`, async () => {
249
+ const response = await runCommand({
250
+ cmd: "run",
251
+ seq: 1,
252
+ case: {
253
+ ...testCase,
254
+ tests: [
255
+ {
256
+ description: "no validation: invalid number, but it still validates",
257
+ instance: { numberProperty: 1 },
258
+ valid: true
259
+ }
260
+ ]
261
+ }
74
262
  });
263
+ assert(isRunCmdResponse(response));
264
+ assert(!isErrorResponse(response.results[0]));
265
+ assert.equal(response.results[0].valid, true);
75
266
  });
76
267
  });
@@ -5,7 +5,7 @@
5
5
  "format": "prettier -w ."
6
6
  },
7
7
  "dependencies": {
8
- "json-schema-library": "*",
8
+ "json-schema-library": ">=11.2.0",
9
9
  "prettier": "^3.8.1"
10
10
  }
11
11
  }