json-schema-library 11.0.5 → 11.2.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/.mocharc.js +1 -0
- package/CHANGELOG.md +17 -0
- package/README.md +120 -0
- package/bowtie/.editorconfig +21 -0
- package/bowtie/.prettierrc +6 -0
- package/bowtie/BOWTIE.md +54 -0
- package/bowtie/Dockerfile +6 -0
- package/bowtie/bowtie-api.ts +101 -0
- package/bowtie/bowtie.test.ts +76 -0
- package/bowtie/bowtie.ts +156 -0
- package/bowtie/package.json +11 -0
- package/bowtie/tsconfig.json +12 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +39 -470
- package/dist/index.d.mts +39 -470
- package/dist/index.mjs +1 -1
- package/dist/jlib.js +2 -13
- package/dist/remotes/index.cjs +1 -0
- package/dist/remotes/index.d.cts +7 -0
- package/dist/remotes/index.d.mts +7 -0
- package/dist/remotes/index.mjs +1 -0
- package/dist/types-B2wwNWyo.d.cts +513 -0
- package/dist/types-BhTU1l2h.d.mts +513 -0
- package/index.ts +10 -4
- package/package.json +14 -8
- package/src/Keyword.ts +37 -12
- package/src/SchemaNode.ts +84 -16
- package/src/compileSchema.ts +56 -4
- package/src/draft04/keywords/$ref.ts +22 -14
- package/src/draft04/keywords/exclusiveMaximum.ts +14 -0
- package/src/draft04/keywords/exclusiveMinimum.ts +14 -0
- package/src/draft04/validateSchema.test.ts +20 -0
- package/src/draft06/keywords/$ref.ts +15 -5
- package/src/draft2019-09/keywords/$ref.test.ts +3 -1
- package/src/draft2019-09/keywords/$ref.ts +40 -16
- package/src/draft2019-09/keywords/additionalItems.ts +33 -10
- package/src/draft2019-09/keywords/items.ts +32 -10
- package/src/draft2019-09/keywords/unevaluatedItems.ts +19 -6
- package/src/draft2019-09/methods/getData.ts +1 -1
- package/src/draft2019-09/validateSchema.test.ts +28 -0
- package/src/errors/errors.ts +8 -1
- package/src/formats/formats.ts +35 -28
- package/src/formats/hyperjump.d.ts +172 -0
- package/src/keywords/$defs.ts +34 -8
- package/src/keywords/$ref.ts +59 -13
- package/src/keywords/additionalProperties.ts +19 -8
- package/src/keywords/allOf.ts +44 -18
- package/src/keywords/anyOf.ts +38 -19
- package/src/keywords/contains.ts +21 -9
- package/src/keywords/dependencies.ts +37 -17
- package/src/keywords/dependentRequired.ts +56 -38
- package/src/keywords/dependentSchemas.ts +37 -13
- package/src/keywords/deprecated.ts +32 -8
- package/src/keywords/enum.ts +30 -8
- package/src/keywords/exclusiveMaximum.ts +21 -2
- package/src/keywords/exclusiveMinimum.ts +22 -3
- package/src/keywords/format.ts +21 -2
- package/src/keywords/ifthenelse.ts +49 -5
- package/src/keywords/items.ts +27 -13
- package/src/keywords/maxItems.ts +22 -2
- package/src/keywords/maxLength.ts +30 -9
- package/src/keywords/maxProperties.ts +30 -9
- package/src/keywords/maximum.ts +28 -8
- package/src/keywords/minItems.ts +30 -9
- package/src/keywords/minLength.ts +30 -9
- package/src/keywords/minProperties.ts +26 -5
- package/src/keywords/minimum.ts +32 -13
- package/src/keywords/multipleOf.ts +33 -12
- package/src/keywords/not.ts +23 -10
- package/src/keywords/oneOf.ts +29 -9
- package/src/keywords/pattern.ts +35 -9
- package/src/keywords/properties.ts +34 -11
- package/src/keywords/propertyDependencies.test.ts +180 -0
- package/src/keywords/propertyDependencies.ts +173 -0
- package/src/keywords/propertyNames.ts +26 -14
- package/src/keywords/required.ts +31 -8
- package/src/keywords/type.ts +53 -16
- package/src/keywords/unevaluatedItems.ts +24 -8
- package/src/keywords/unevaluatedProperties.ts +24 -7
- package/src/keywords/uniqueItems.ts +23 -4
- package/src/mergeNode.ts +9 -4
- package/src/methods/getData.ts +1 -1
- package/src/settings.ts +2 -1
- package/src/types.ts +1 -1
- package/src/utils/isListOfStrings.ts +3 -0
- package/src/validate.test.ts +0 -2
- package/src/validateNode.ts +6 -3
- package/src/validateSchema.test.ts +312 -0
- package/tsconfig.json +11 -4
- package/tsconfig.test.json +9 -2
- package/tsdown.config.ts +1 -1
- package/Dockerfile +0 -6
- package/bowtie_jlib.js +0 -140
- package/remotes/draft04.json +0 -150
- package/remotes/draft06.json +0 -155
- package/remotes/draft07.json +0 -155
- package/remotes/draft2019-09.json +0 -42
- package/remotes/draft2019-09_meta_applicator.json +0 -53
- package/remotes/draft2019-09_meta_content.json +0 -14
- package/remotes/draft2019-09_meta_core.json +0 -54
- package/remotes/draft2019-09_meta_format.json +0 -11
- package/remotes/draft2019-09_meta_meta-data.json +0 -34
- package/remotes/draft2019-09_meta_validation.json +0 -95
- package/remotes/draft2020-12.json +0 -55
- package/remotes/draft2020-12_meta_applicator.json +0 -45
- package/remotes/draft2020-12_meta_content.json +0 -14
- package/remotes/draft2020-12_meta_core.json +0 -48
- package/remotes/draft2020-12_meta_format_annotation.json +0 -11
- package/remotes/draft2020-12_meta_format_assertion.json +0 -11
- package/remotes/draft2020-12_meta_meta_data.json +0 -34
- package/remotes/draft2020-12_meta_unevaluated.json +0 -12
- package/remotes/draft2020-12_meta_validation.json +0 -87
- package/remotes/index.ts +0 -48
package/.mocharc.js
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### v11.2.0
|
|
4
|
+
|
|
5
|
+
- introduced `throwOnInvalidRef` to abort validation when a $ref cannot be resolved
|
|
6
|
+
- added export of meta-schema for all drafts using `import "json-schema-library/remotes"`
|
|
7
|
+
- added support for format validations: `idn-hostname`, `iri`, `iri-reference`
|
|
8
|
+
|
|
9
|
+
### v11.1.0
|
|
10
|
+
|
|
11
|
+
- introduced upcoming keyword `propertyDependenciesKeyword` to package export
|
|
12
|
+
- added support for keyword `deprecatedMessage`
|
|
13
|
+
- introduced schema validation
|
|
14
|
+
|
|
15
|
+
### v11.0.4, v11.0.5
|
|
16
|
+
|
|
17
|
+
- removed enforcing packageManager as this failed build-jobs
|
|
18
|
+
|
|
3
19
|
### v11.0.3
|
|
4
20
|
|
|
5
21
|
- fixed type of main input-schema to support boolean
|
|
22
|
+
- enforced yarn as a package manager
|
|
6
23
|
|
|
7
24
|
### v11.0.2
|
|
8
25
|
|
package/README.md
CHANGED
|
@@ -58,6 +58,12 @@ type CompileOptions = {
|
|
|
58
58
|
remote: SchemaNode;
|
|
59
59
|
// if format-validations should create errors. Defaults to true
|
|
60
60
|
formatAssertion: boolean | "meta-schema";
|
|
61
|
+
/** set to true to throw an Error on errors in input schema. Defaults to false */
|
|
62
|
+
throwOnInvalidSchema?: boolean;
|
|
63
|
+
/** set to true to collect unknown keywords of input schema in `node.schemaAnnotations`. Defaults to false */
|
|
64
|
+
withSchemaAnnotations?: boolean;
|
|
65
|
+
/** set to true to throw an Error when encountering an unresolvable ref */
|
|
66
|
+
throwOnInvalidRef?: boolean;
|
|
61
67
|
// default options for all calls to node.getData()
|
|
62
68
|
getDataDefaultOptions?: {
|
|
63
69
|
// Add all properties (required and optional) to the generated data
|
|
@@ -94,6 +100,81 @@ compileSchema(mySchema, { getDataDefaultOptions: { addOptionalProps: true } });
|
|
|
94
100
|
Details on _drafts_ are documented in [draft customization](#draft-customization).
|
|
95
101
|
Details on `getDataDefaultOptions` are documented in [getData](#getData).
|
|
96
102
|
|
|
103
|
+
### validate input schema
|
|
104
|
+
|
|
105
|
+
All JSON Schema passed to `compileSchema` are validated automatically. To retrieve any schema errors you can access the property `schemaErrors` of the main node:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const root = compileSchema(mySchema);
|
|
109
|
+
const { schemaErrors } = root; // JsonError[]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Use the option `throwOnInvalidSchema:true` of `compileSchema` to throw an Error for a input schema containing errors:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const root = compileSchema({ properties: 123 }, { throwOnInvalidSchema: true });
|
|
116
|
+
// throws Error
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
<details><summary>Example for schema validation errors</summary>
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const { schemaErrors } = compileSchema({ $defs: 999 });
|
|
125
|
+
console.log(schemaErrors[0]);
|
|
126
|
+
{
|
|
127
|
+
type: 'error',
|
|
128
|
+
code: 'schema-error',
|
|
129
|
+
message: 'Invalid schema found at #: $defs must be an object - received: number',
|
|
130
|
+
data: {
|
|
131
|
+
pointer: '#',
|
|
132
|
+
schema: { '$defs': 999 },
|
|
133
|
+
value: 999,
|
|
134
|
+
message: '$defs must be an object - received: number'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
</details>
|
|
142
|
+
|
|
143
|
+
To collect JSON Schema annotations for unused keywords you can opt in with option `withSchemaAnnotations`:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const root = compileSchema(mySchema, { withSchemaAnnotations: true });
|
|
147
|
+
const { schemaAnnotations } = root; // JsonAnnotation[]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
This collects all JSON Schema keywords not part of the used draft and any custom keywords. Custom keywords starting with `x-` are allowed and thus will not create an annotation.
|
|
151
|
+
|
|
152
|
+
<details><summary>Example for validation annotations</summary>
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const { schemaAnnotations } = compileSchema({ unknown: true }, { withSchemaAnnotations: true });
|
|
158
|
+
console.log(schemaAnnotations[0]);
|
|
159
|
+
{
|
|
160
|
+
type: "annotation",
|
|
161
|
+
code: "unknown-keyword-warning",
|
|
162
|
+
message: "Keyword 'unknown' is not a valid keyword to draft 'draft-2020-12'",
|
|
163
|
+
data: {
|
|
164
|
+
pointer: "#/unknown",
|
|
165
|
+
schema: {
|
|
166
|
+
unknown: true
|
|
167
|
+
},
|
|
168
|
+
value: "unknown",
|
|
169
|
+
draft: "draft-2020-12"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
</details>
|
|
177
|
+
|
|
97
178
|
### SchemaNode
|
|
98
179
|
|
|
99
180
|
`compileSchema` builds a tree where each sub-schema becomes its own SchemaNode. Every node in the tree offers the same set of methods.
|
|
@@ -1379,6 +1460,45 @@ const dependentSchemasKeyword = draft2020.keywords.find((f) => f.keyword === "de
|
|
|
1379
1460
|
|
|
1380
1461
|
## Keyword extensions
|
|
1381
1462
|
|
|
1463
|
+
### propertyDependencies
|
|
1464
|
+
|
|
1465
|
+
Resolves an object-schema by `propertyName:propertyValue`
|
|
1466
|
+
|
|
1467
|
+
```ts
|
|
1468
|
+
{
|
|
1469
|
+
type: "object",
|
|
1470
|
+
propertyDependencies: {
|
|
1471
|
+
propertyName: {
|
|
1472
|
+
propertyValue: {
|
|
1473
|
+
properties: {
|
|
1474
|
+
id: { type: "string" }
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// matches and returns error for id
|
|
1481
|
+
{
|
|
1482
|
+
"propertyName": "propertyValue",
|
|
1483
|
+
"id": 123
|
|
1484
|
+
}
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
Note that this keyword is not added by default as it is not part yet of the JSONSchema spec. Add this keyword with:
|
|
1488
|
+
|
|
1489
|
+
```ts
|
|
1490
|
+
import { compileSchema, draft2020, extendDraft, propertyDependenciesKeyword } from "json-schema-library";
|
|
1491
|
+
|
|
1492
|
+
const draft = extendDraft(draft2020, {
|
|
1493
|
+
keywords: [propertyDependenciesKeyword]
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
const node = compileSchema({ propertyDependencies: {} }, { drafts: [draft] });
|
|
1497
|
+
```
|
|
1498
|
+
|
|
1499
|
+
- Note: this keyword may replace `OneOfProperty`
|
|
1500
|
+
- Reference: https://docs.google.com/presentation/d/1ajXlCQcsjjiMLsluFIILR7sN5aDRBnfqQ9DLbcFbqjI/mobilepresent?slide=id.g3ae4fb2e16d_0_15
|
|
1501
|
+
|
|
1382
1502
|
### oneOfProperty
|
|
1383
1503
|
|
|
1384
1504
|
For `oneOf` resolution, JSON Schema states that data is valid if it validates against exactly one of those sub-schemas. In some scenarios this is unwanted behaviour, as the actual `oneOf` schema is known and only validation errors of this exact sub-schema should be returned.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# EditorConfig helps developers define and maintain consistent
|
|
2
|
+
# coding styles between different editors and IDEs
|
|
3
|
+
# editorconfig.org
|
|
4
|
+
|
|
5
|
+
root = true
|
|
6
|
+
|
|
7
|
+
[*]
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 4
|
|
10
|
+
end_of_line = lf
|
|
11
|
+
charset = utf-8
|
|
12
|
+
trim_trailing_whitespace = true
|
|
13
|
+
insert_final_newline = true
|
|
14
|
+
|
|
15
|
+
[*.md]
|
|
16
|
+
trim_trailing_whitespace = false
|
|
17
|
+
indent_size = 2
|
|
18
|
+
|
|
19
|
+
[package.json]
|
|
20
|
+
indent_style = space
|
|
21
|
+
indent_size = 2
|
package/bowtie/BOWTIE.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# install
|
|
2
|
+
|
|
3
|
+
brew install bowtie-json-schema/tap/bowtie
|
|
4
|
+
https://docs.bowtie.report/en/stable/implementers/
|
|
5
|
+
|
|
6
|
+
> docker build -t localhost/jlib .
|
|
7
|
+
> bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2020-12 | bowtie summary --show failures
|
|
8
|
+
> bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/main/tests/draft7 --fail-fast
|
|
9
|
+
|
|
10
|
+
# build
|
|
11
|
+
|
|
12
|
+
docker build -t localhost/jlib .
|
|
13
|
+
|
|
14
|
+
# test without bowtie
|
|
15
|
+
|
|
16
|
+
docker run --rm localhost/jlib
|
|
17
|
+
echo '{"cmd":"start","version":1}' | docker run -i jlib
|
|
18
|
+
echo '{"cmd":"run","seq":1,"case":{"schema":{"type": "string"},"tests":["valid",999]}}' | docker run -i jlib
|
|
19
|
+
echo '{"cmd":"dialect","dialect":"http://json-schema.org/draft-07/schema#"}' | docker run -i jlib
|
|
20
|
+
echo '{"cmd":"stop"}' | docker run -i jlib
|
|
21
|
+
|
|
22
|
+
# test with bowtie
|
|
23
|
+
|
|
24
|
+
> docker build -t localhost/jlib .
|
|
25
|
+
> then:
|
|
26
|
+
|
|
27
|
+
bowtie smoke -i localhost/jlib
|
|
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
|
+
|
|
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
|
+
|
|
34
|
+
bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft7 | bowtie summary --show failures
|
|
35
|
+
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
|
+
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
|
+
|
|
38
|
+
**Fails**
|
|
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
|
+
|
|
41
|
+
bowtie suite $(bowtie filter-implementations | sed 's/^jlib/-i /') https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft2020-12 >draft2020-12.json
|
|
42
|
+
|
|
43
|
+
bowtie suite -i localhost/jlib draft7 | bowtie summary
|
|
44
|
+
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
|
|
45
|
+
bowtie suite -i localhost/jlib https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/main/tests/draft7 | bowtie summary --show failures
|
|
46
|
+
bowtie suite -i localhost/jlib draft7 | bowtie summary --show failures
|
|
47
|
+
bowtie validate -i localhost/jlib draft7 | bowtie summary --show failures
|
|
48
|
+
|
|
49
|
+
**Does not finish**
|
|
50
|
+
bowtie run --dialect 7 -i localhost/jlib
|
|
51
|
+
bowtie validate -i localhost/jlib <(printf '{"type": "integer"}') <(printf 37) <(printf '"foo"')
|
|
52
|
+
bowtie run -i localhost/jlib draft7 | bowtie summary --show failures
|
|
53
|
+
bowtie run -i localhost/jlib -V '{"description": "test case 1", "schema": {}, "tests": [{"description": "a test", "instance": {}}]}'
|
|
54
|
+
bowtie run -i localhost/jlib -V --fail-fast
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import readline from "readline/promises";
|
|
2
|
+
import { JsonSchema } from "src/types";
|
|
3
|
+
|
|
4
|
+
export type Dialect =
|
|
5
|
+
| "https://json-schema.org/draft/2020-12/schema"
|
|
6
|
+
| "https://json-schema.org/draft/2019-09/schema"
|
|
7
|
+
| "http://json-schema.org/draft-07/schema#"
|
|
8
|
+
| "http://json-schema.org/draft-06/schema#"
|
|
9
|
+
| "http://json-schema.org/draft-04/schema#"
|
|
10
|
+
| "http://json-schema.org/draft-03/schema#";
|
|
11
|
+
|
|
12
|
+
type StartCmd = {
|
|
13
|
+
cmd: "start";
|
|
14
|
+
version: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type StartCmdResponse = {
|
|
18
|
+
version: number;
|
|
19
|
+
implementation: {
|
|
20
|
+
/** library language */
|
|
21
|
+
language: "typescript" | "typescript";
|
|
22
|
+
/** library name */
|
|
23
|
+
name: string;
|
|
24
|
+
/** library version */
|
|
25
|
+
version: string;
|
|
26
|
+
homepage: string;
|
|
27
|
+
issues: string;
|
|
28
|
+
source: string;
|
|
29
|
+
dialects: Dialect[];
|
|
30
|
+
/** operating system platform */
|
|
31
|
+
os: string;
|
|
32
|
+
/** operating system version */
|
|
33
|
+
os_version: string;
|
|
34
|
+
/** Node.js version */
|
|
35
|
+
language_version: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type DialectCommand = {
|
|
40
|
+
cmd: "dialect";
|
|
41
|
+
dialect: Dialect;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type DialectCmdResponse = { ok: true };
|
|
45
|
+
|
|
46
|
+
type RunCmd = {
|
|
47
|
+
cmd: "run";
|
|
48
|
+
seq: number;
|
|
49
|
+
// dialect: Dialect;
|
|
50
|
+
case: {
|
|
51
|
+
schema: JsonSchema;
|
|
52
|
+
tests: {
|
|
53
|
+
description: string;
|
|
54
|
+
instance: unknown;
|
|
55
|
+
}[];
|
|
56
|
+
registry: Record<string, JsonSchema>;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type RunCmdResponse = {
|
|
61
|
+
seq: number;
|
|
62
|
+
results: ({ valid: boolean } | ErrorResponse)[];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type ErrorResponse = {
|
|
66
|
+
errored: true;
|
|
67
|
+
context: {
|
|
68
|
+
traceback?: string | undefined;
|
|
69
|
+
message: string | undefined;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type StopCommand = {
|
|
74
|
+
cmd: "stop";
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type Command = StartCmd | RunCmd | DialectCommand | StopCommand;
|
|
78
|
+
export type CommandResponse = RunCmdResponse | StartCmdResponse | DialectCmdResponse | ErrorResponse | undefined;
|
|
79
|
+
|
|
80
|
+
export type CommandMap = {
|
|
81
|
+
/** Start nxet test run, informing bowtie of suppored draft versions and general metadata */
|
|
82
|
+
start: (args: StartCmd) => Promise<StartCmdResponse>;
|
|
83
|
+
/** Set JSON Schema draft-version of following tests */
|
|
84
|
+
dialect: (args: DialectCommand) => Promise<DialectCmdResponse>;
|
|
85
|
+
/** Run test cases for a specfic schema */
|
|
86
|
+
run: (args: RunCmd) => Promise<RunCmdResponse>;
|
|
87
|
+
/** Finalize test run and exit container */
|
|
88
|
+
stop: (args: StopCommand, stdio?: readline.Interface) => Promise<void>;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const createBowtieError = (message: string, stack?: string): ErrorResponse => ({
|
|
92
|
+
errored: true,
|
|
93
|
+
context: {
|
|
94
|
+
message: message,
|
|
95
|
+
traceback: stack
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export function sendToBowtie(data?: Record<string, unknown>) {
|
|
100
|
+
console.log(JSON.stringify(data ?? createBowtieError("missing response")));
|
|
101
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { strict as assert } from "node:assert";
|
|
2
|
+
import { compileSchema } from "../src/compileSchema";
|
|
3
|
+
import { remotes } from "json-schema-library/remotes";
|
|
4
|
+
import { JsonSchema } from "../src/types";
|
|
5
|
+
import { runCommand } from "./bowtie";
|
|
6
|
+
import { ErrorResponse, RunCmdResponse } from "./bowtie-api";
|
|
7
|
+
|
|
8
|
+
const isRunCmdResponse = (value: unknown): value is RunCmdResponse =>
|
|
9
|
+
value != null && typeof value === "object" && "results" in value && Array.isArray(value.results);
|
|
10
|
+
|
|
11
|
+
const isErrorResponse = (value: unknown): value is ErrorResponse =>
|
|
12
|
+
value != null && typeof value === "object" && "errored" in value && value.errored === true;
|
|
13
|
+
|
|
14
|
+
const remote = compileSchema({ $id: "draft2020-12" });
|
|
15
|
+
remotes.map((schema: JsonSchema) => remote.addRemoteSchema(schema.$id ?? schema.id, schema));
|
|
16
|
+
|
|
17
|
+
describe("bowtie (draft7)", async () => {
|
|
18
|
+
before(async () => {
|
|
19
|
+
await runCommand({ cmd: "start", version: 1 });
|
|
20
|
+
await runCommand({ cmd: "dialect", dialect: "http://json-schema.org/draft-07/schema#" });
|
|
21
|
+
});
|
|
22
|
+
after(async () => runCommand({ cmd: "stop" }));
|
|
23
|
+
|
|
24
|
+
it("additionalItems as schema - additional items match schema", async () => {
|
|
25
|
+
const response = await runCommand({
|
|
26
|
+
cmd: "run",
|
|
27
|
+
seq: 1,
|
|
28
|
+
case: {
|
|
29
|
+
description: "additionalItems as schema",
|
|
30
|
+
schema: { items: [{}], additionalItems: { type: "integer" } },
|
|
31
|
+
tests: [{ description: "additional items match schema", instance: [null, 2, 3, 4], valid: true }]
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
assert(isRunCmdResponse(response));
|
|
35
|
+
assert(!isErrorResponse(response.results[0]));
|
|
36
|
+
assert.equal(response.results[0].valid, true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("additionalItems as schema - additional items do not match schema", async () => {
|
|
40
|
+
const response = await runCommand({
|
|
41
|
+
cmd: "run",
|
|
42
|
+
seq: 1,
|
|
43
|
+
case: {
|
|
44
|
+
description: "additionalItems as schema",
|
|
45
|
+
schema: { items: [{}], additionalItems: { type: "integer" } },
|
|
46
|
+
tests: [
|
|
47
|
+
{ description: "additional items do not match schema", instance: [null, 2, 3, "foo"], valid: false }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
assert(isRunCmdResponse(response));
|
|
52
|
+
assert(!isErrorResponse(response.results[0]));
|
|
53
|
+
assert.equal(response.results[0].valid, false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
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
|
+
);
|
|
66
|
+
|
|
67
|
+
it("valid definition schema", () => {
|
|
68
|
+
const first = node.validate({ $defs: { foo: { type: "integer" } } });
|
|
69
|
+
assert.deepEqual(first.valid, true);
|
|
70
|
+
});
|
|
71
|
+
it("invalid definition schema", () => {
|
|
72
|
+
const second = node.validate({ $defs: { foo: { type: 1 } } });
|
|
73
|
+
assert.deepEqual(second.valid, false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
package/bowtie/bowtie.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/* json-schema-library bowtie integration (test harness) */
|
|
2
|
+
import readline from "readline/promises";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import packageJson from "json-schema-library/package.json";
|
|
6
|
+
import { compileSchema, type SchemaNode } from "json-schema-library";
|
|
7
|
+
import { remotes } from "json-schema-library/remotes";
|
|
8
|
+
import {
|
|
9
|
+
createBowtieError,
|
|
10
|
+
type Command,
|
|
11
|
+
type CommandMap,
|
|
12
|
+
type CommandResponse,
|
|
13
|
+
type Dialect,
|
|
14
|
+
RunCmdResponse,
|
|
15
|
+
sendToBowtie
|
|
16
|
+
} from "./bowtie-api";
|
|
17
|
+
|
|
18
|
+
/** track command sequence and abort if something is off */
|
|
19
|
+
let state: "started" | "dialect" | "testing" | "stopped" = "stopped";
|
|
20
|
+
/** current JSON Schema draft version to test */
|
|
21
|
+
let dialect: Dialect;
|
|
22
|
+
let remote: SchemaNode;
|
|
23
|
+
|
|
24
|
+
const cmds: CommandMap = {
|
|
25
|
+
start: async (args) => {
|
|
26
|
+
console.assert(args.version === 1, { args });
|
|
27
|
+
console.assert(state === "stopped");
|
|
28
|
+
state = "started";
|
|
29
|
+
return {
|
|
30
|
+
version: 1,
|
|
31
|
+
implementation: {
|
|
32
|
+
language: "typescript",
|
|
33
|
+
name: "json-schema-library",
|
|
34
|
+
version: packageJson.version,
|
|
35
|
+
homepage: "https://github.com/sagold/json-schema-library",
|
|
36
|
+
issues: "https://github.com/sagold/json-schema-library/issues",
|
|
37
|
+
source: "https://github.com/sagold/json-schema-library",
|
|
38
|
+
|
|
39
|
+
dialects: [
|
|
40
|
+
"https://json-schema.org/draft/2020-12/schema",
|
|
41
|
+
"https://json-schema.org/draft/2019-09/schema",
|
|
42
|
+
"http://json-schema.org/draft-07/schema#",
|
|
43
|
+
"http://json-schema.org/draft-06/schema#",
|
|
44
|
+
"http://json-schema.org/draft-04/schema#"
|
|
45
|
+
],
|
|
46
|
+
os: os.platform(),
|
|
47
|
+
os_version: os.release(),
|
|
48
|
+
language_version: process.version
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
dialect: async (args) => {
|
|
54
|
+
console.assert(state === "started");
|
|
55
|
+
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
|
+
|
|
64
|
+
return { ok: true };
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
run: async (args) => {
|
|
68
|
+
console.assert(state === "dialect" || state === "testing");
|
|
69
|
+
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]);
|
|
79
|
+
}
|
|
80
|
+
// run test cases and collect results to be sent back to bowtie
|
|
81
|
+
const results: RunCmdResponse["results"] = tests.map((test) => {
|
|
82
|
+
try {
|
|
83
|
+
return { valid: node.validate(test.instance).valid };
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return createBowtieError(e.message ?? e, e.stack);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// response to bowtie for run command
|
|
89
|
+
return { seq: args.seq, results: results };
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
stop: async (_, stdio) => {
|
|
93
|
+
console.assert(state === "testing");
|
|
94
|
+
state = "stopped";
|
|
95
|
+
if (process.env.JLIB_TEST_RUN !== "true") {
|
|
96
|
+
stdio?.close();
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} as const;
|
|
101
|
+
|
|
102
|
+
export async function runCommand(request: Command, stdio?: readline.Interface) {
|
|
103
|
+
let response: CommandResponse = undefined;
|
|
104
|
+
switch (request.cmd) {
|
|
105
|
+
case "start":
|
|
106
|
+
response = await cmds.start(request);
|
|
107
|
+
break;
|
|
108
|
+
case "dialect":
|
|
109
|
+
response = await cmds.dialect(request);
|
|
110
|
+
break;
|
|
111
|
+
case "run":
|
|
112
|
+
try {
|
|
113
|
+
response = await cmds.run(request);
|
|
114
|
+
return response;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
response = {
|
|
117
|
+
seq: request.seq,
|
|
118
|
+
...createBowtieError((error as Error).message ?? error, (error as Error).stack)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
case "stop":
|
|
123
|
+
await cmds.stop(request, stdio);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
return response;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isCommand(value: unknown): value is Command {
|
|
130
|
+
return value != null && typeof value === "object" && "cmd" in value && cmds[value.cmd as keyof typeof cmds] != null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** listen for commands to be sent to container */
|
|
134
|
+
async function waitForBowtieCommands() {
|
|
135
|
+
const stdio = readline.createInterface({
|
|
136
|
+
input: process.stdin,
|
|
137
|
+
output: process.stdout,
|
|
138
|
+
terminal: false
|
|
139
|
+
});
|
|
140
|
+
for await (const line of stdio) {
|
|
141
|
+
let response;
|
|
142
|
+
try {
|
|
143
|
+
const request = JSON.parse(line);
|
|
144
|
+
response = isCommand(request)
|
|
145
|
+
? await runCommand(request, stdio)
|
|
146
|
+
: createBowtieError(`Unsupported command received: '${line}'`);
|
|
147
|
+
} catch (e) {
|
|
148
|
+
response = createBowtieError(`Invalid json received: '${line}': ${(e as Error)?.message}`);
|
|
149
|
+
}
|
|
150
|
+
sendToBowtie(response);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (process.env.JLIB_TEST_RUN !== "true") {
|
|
155
|
+
waitForBowtieCommands();
|
|
156
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.test.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "..",
|
|
5
|
+
"rootDir": "..",
|
|
6
|
+
"paths": {
|
|
7
|
+
"json-schema-library": ["./index.ts"],
|
|
8
|
+
"json-schema-library/remotes": ["./remotes/index.ts"],
|
|
9
|
+
"json-schema-library/package.json": ["./package.json"]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|