@hyperjump/json-schema 1.13.0 → 1.14.1
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/README.md +14 -20
- package/annotations/index.js +27 -7
- package/annotations/test-utils.d.ts +1 -0
- package/annotations/test-utils.js +38 -0
- package/bundle/index.js +39 -227
- package/draft-04/additionalItems.js +13 -19
- package/draft-04/dependencies.js +7 -7
- package/draft-04/items.js +26 -14
- package/draft-2020-12/dynamicRef.js +20 -8
- package/lib/core.js +19 -4
- package/lib/evaluation-plugins/annotations.js +32 -0
- package/lib/evaluation-plugins/basic-output.js +34 -0
- package/lib/evaluation-plugins/detailed-output.js +36 -0
- package/lib/experimental.d.ts +18 -5
- package/lib/experimental.js +3 -0
- package/lib/instance.js +12 -3
- package/lib/keywords/additionalProperties.js +8 -22
- package/lib/keywords/allOf.js +1 -29
- package/lib/keywords/anyOf.js +1 -27
- package/lib/keywords/conditional.js +1 -27
- package/lib/keywords/contains.js +14 -18
- package/lib/keywords/contentSchema.js +6 -3
- package/lib/keywords/dependentSchemas.js +1 -21
- package/lib/keywords/dynamicRef.js +19 -6
- package/lib/keywords/else.js +2 -19
- package/lib/keywords/if.js +1 -9
- package/lib/keywords/itemPattern.js +5 -9
- package/lib/keywords/items.js +5 -14
- package/lib/keywords/oneOf.js +1 -33
- package/lib/keywords/patternProperties.js +7 -25
- package/lib/keywords/prefixItems.js +2 -5
- package/lib/keywords/properties.js +6 -22
- package/lib/keywords/propertyDependencies.js +1 -20
- package/lib/keywords/propertyNames.js +1 -1
- package/lib/keywords/ref.js +1 -3
- package/lib/keywords/then.js +2 -19
- package/lib/keywords/unevaluatedItems.js +44 -20
- package/lib/keywords/unevaluatedProperties.js +40 -26
- package/lib/keywords/validation.js +31 -129
- package/lib/keywords.js +30 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -248,8 +248,8 @@ The following types are used in the above definitions
|
|
|
248
248
|
|
|
249
249
|
* **OutputFormat**: **FLAG**
|
|
250
250
|
|
|
251
|
-
Only the `FLAG` output format is part of the Stable API. Additional output
|
|
252
|
-
formats are included as part of the Experimental API.
|
|
251
|
+
Only the `FLAG` output format is part of the Stable API. Additional [output
|
|
252
|
+
formats](#output-formats) are included as part of the Experimental API.
|
|
253
253
|
* **OutputUnit**: { valid: boolean }
|
|
254
254
|
|
|
255
255
|
Output is an experimental feature of the JSON Schema specification. There
|
|
@@ -336,7 +336,8 @@ These are available from the `@hyperjump/json-schema/bundle` export.
|
|
|
336
336
|
The `FLAG` output format isn't very informative. You can change the output
|
|
337
337
|
format used for validation to get more information about failures. The official
|
|
338
338
|
output format is still evolving, so these may change or be replaced in the
|
|
339
|
-
future.
|
|
339
|
+
future. This implementation currently supports the BASIC and DETAILED output
|
|
340
|
+
formats.
|
|
340
341
|
|
|
341
342
|
```javascript
|
|
342
343
|
import { BASIC } from "@hyperjump/json-schema/experimental";
|
|
@@ -536,28 +537,21 @@ These are available from the `@hyperjump/json-schema/experimental` export.
|
|
|
536
537
|
validation of its own. In these cases, it isn't helpful to include
|
|
537
538
|
them in BASIC output. This flag is used to trim those nodes from the
|
|
538
539
|
output.
|
|
539
|
-
*
|
|
540
|
-
|
|
541
|
-
If the keyword is an applicator, it will need to implement this
|
|
542
|
-
function for `unevaluatedProperties` to work as expected.
|
|
543
|
-
* collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonNode, context: ValidationContext, isTop?: boolean) => Set\<number> | false
|
|
544
|
-
|
|
545
|
-
If the keyword is an applicator, it will need to implement this
|
|
546
|
-
function for `unevaluatedItems` to work as expected.
|
|
547
|
-
* collectExternalIds?: (visited: Set\<string>, parentSchema: Browser, schema: Browser) => Set\<string>
|
|
548
|
-
If the keyword is an applicator, it will need to implement this
|
|
549
|
-
function to work properly with the [bundle](#bundling) feature.
|
|
550
|
-
* annotation?: (compiledKeywordValue: any) => any
|
|
540
|
+
* annotation?: (compiledKeywordValue: any) => any | undefined
|
|
551
541
|
|
|
552
542
|
If the keyword is an annotation, it will need to implement this
|
|
553
543
|
function to return the annotation.
|
|
544
|
+
* plugin?: EvaluationPlugin
|
|
554
545
|
|
|
555
546
|
* **ValidationContext**: object
|
|
556
547
|
* ast: AST
|
|
557
|
-
*
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
*
|
|
548
|
+
* plugins: EvaluationPlugins[]
|
|
549
|
+
|
|
550
|
+
* **EvaluationPlugin**: object
|
|
551
|
+
* beforeSchema(url: string, instance: JsonNode, context: Context): void
|
|
552
|
+
* beforeKeyword(keywordNode: Node<any>, instance: JsonNode, context: Context, schemaContext: Context, keyword: Keyword): void
|
|
553
|
+
* afterKeyword(keywordNode: Node<any>, instance: JsonNode, context: Context, valid: boolean, schemaContext: Context, keyword: Keyword): void
|
|
554
|
+
* afterSchema(url: string, instance: JsonNode, context: Context, valid: boolean): void
|
|
561
555
|
* **defineVocabulary**: (id: string, keywords: { [keyword: string]: string }) => void
|
|
562
556
|
|
|
563
557
|
Define a vocabulary that maps keyword name to keyword URIs defined using
|
|
@@ -596,7 +590,7 @@ These are available from the `@hyperjump/json-schema/experimental` export.
|
|
|
596
590
|
* **getSchema**: (uri: string, browser?: Browser) => Promise\<Browser>
|
|
597
591
|
|
|
598
592
|
Get a schema by it's URI taking the local schema registry into account.
|
|
599
|
-
* buildSchemaDocument
|
|
593
|
+
* **buildSchemaDocument**: (schema: SchemaObject | boolean, retrievalUri?: string, contextDialectId?: string) => SchemaDocument
|
|
600
594
|
|
|
601
595
|
Build a SchemaDocument from a JSON-compatible value. You might use this if
|
|
602
596
|
you're creating a custom media type plugin, such as supporting JSON Schemas
|
package/annotations/index.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { FLAG } from "../lib/index.js";
|
|
2
2
|
import { ValidationError } from "./validation-error.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getSchema,
|
|
5
|
+
compile,
|
|
6
|
+
BASIC,
|
|
7
|
+
DETAILED,
|
|
8
|
+
annotationsPlugin,
|
|
9
|
+
basicOutputPlugin,
|
|
10
|
+
detailedOutputPlugin
|
|
11
|
+
} from "../lib/experimental.js";
|
|
4
12
|
import Validation from "../lib/keywords/validation.js";
|
|
5
13
|
import * as Instance from "../lib/instance.js";
|
|
6
14
|
|
|
@@ -14,23 +22,35 @@ export const annotate = async (schemaUri, json = undefined, outputFormat = undef
|
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
export const interpret = ({ ast, schemaUri }, instance, outputFormat = BASIC) => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
const context = { ast, plugins: [annotationsPlugin, ...ast.plugins] };
|
|
26
|
+
|
|
27
|
+
switch (outputFormat) {
|
|
28
|
+
case FLAG:
|
|
29
|
+
break;
|
|
30
|
+
case BASIC:
|
|
31
|
+
context.plugins.push(basicOutputPlugin);
|
|
32
|
+
break;
|
|
33
|
+
case DETAILED:
|
|
34
|
+
context.plugins.push(detailedOutputPlugin);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
throw Error(`Unsupported output format '${outputFormat}'`);
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
const valid = Validation.interpret(schemaUri, instance, context);
|
|
21
41
|
|
|
22
42
|
if (!valid) {
|
|
23
|
-
const result =
|
|
43
|
+
const result = !valid && "errors" in context ? { valid, errors: context.errors } : { valid };
|
|
24
44
|
throw new ValidationError(result);
|
|
25
45
|
}
|
|
26
46
|
|
|
27
|
-
for (const annotation of annotations) {
|
|
47
|
+
for (const annotation of context.annotations) {
|
|
28
48
|
const node = Instance.get(annotation.instanceLocation, instance);
|
|
29
49
|
const keyword = annotation.keyword;
|
|
30
50
|
if (!node.annotations[keyword]) {
|
|
31
51
|
node.annotations[keyword] = [];
|
|
32
52
|
}
|
|
33
|
-
node.annotations[keyword].
|
|
53
|
+
node.annotations[keyword].unshift(annotation.annotation);
|
|
34
54
|
}
|
|
35
55
|
|
|
36
56
|
return instance;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const isCompatible: (compatibility: string | undefined, versionUnderTest: number) => boolean;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const isCompatible = (compatibility, versionUnderTest) => {
|
|
2
|
+
if (compatibility === undefined) {
|
|
3
|
+
return true;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const constraints = compatibility.split(",");
|
|
7
|
+
for (const constraint of constraints) {
|
|
8
|
+
const matches = /(?<operator><=|>=|=)?(?<version>\d+)/.exec(constraint);
|
|
9
|
+
if (!matches) {
|
|
10
|
+
throw Error(`Invalid compatibility string: ${compatibility}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const operator = matches[1] ?? ">=";
|
|
14
|
+
const version = parseInt(matches[2], 10);
|
|
15
|
+
|
|
16
|
+
switch (operator) {
|
|
17
|
+
case ">=":
|
|
18
|
+
if (versionUnderTest < version) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
break;
|
|
22
|
+
case "<=":
|
|
23
|
+
if (versionUnderTest > version) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
case "=":
|
|
28
|
+
if (versionUnderTest !== version) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
throw Error(`Unsupported contraint operator: ${operator}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return true;
|
|
38
|
+
};
|
package/bundle/index.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import curry from "just-curry-it";
|
|
2
1
|
import { v4 as uuid } from "uuid";
|
|
3
|
-
import
|
|
2
|
+
import { jrefTypeOf } from "@hyperjump/browser/jref";
|
|
4
3
|
import * as JsonPointer from "@hyperjump/json-pointer";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
Validation,
|
|
8
|
-
canonicalUri, getSchema, toSchema,
|
|
9
|
-
getKeywordName, getKeyword, getKeywordByName
|
|
10
|
-
} from "../lib/experimental.js";
|
|
4
|
+
import { resolveIri, toAbsoluteIri } from "@hyperjump/uri";
|
|
5
|
+
import { getSchema, toSchema, getKeywordName } from "../lib/experimental.js";
|
|
11
6
|
|
|
12
7
|
|
|
13
8
|
export const URI = "uri", UUID = "uuid";
|
|
@@ -19,32 +14,36 @@ const defaultOptions = {
|
|
|
19
14
|
};
|
|
20
15
|
|
|
21
16
|
export const bundle = async (url, options = {}) => {
|
|
22
|
-
loadKeywordSupport();
|
|
23
17
|
const fullOptions = { ...defaultOptions, ...options };
|
|
24
18
|
|
|
25
19
|
const mainSchema = await getSchema(url);
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
fullOptions.contextUri = mainSchema.document.baseUri;
|
|
21
|
+
fullOptions.contextDialectId = mainSchema.document.dialectId;
|
|
22
|
+
|
|
28
23
|
const bundled = toSchema(mainSchema);
|
|
24
|
+
fullOptions.bundlingLocation = "/" + getKeywordName(fullOptions.contextDialectId, "https://json-schema.org/keyword/definitions");
|
|
25
|
+
if (JsonPointer.get(fullOptions.bundlingLocation, bundled) === undefined) {
|
|
26
|
+
JsonPointer.assign(fullOptions.bundlingLocation, bundled, {});
|
|
27
|
+
}
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
return await doBundling(mainSchema.uri, bundled, fullOptions);
|
|
30
|
+
};
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (JsonPointer.get(bundlingLocation, bundled) === undefined && externalIds.size > 0) {
|
|
35
|
-
JsonPointer.assign(bundlingLocation, bundled, {});
|
|
36
|
-
}
|
|
32
|
+
const doBundling = async (schemaUri, bundled, fullOptions, contextSchema, visited = new Set()) => {
|
|
33
|
+
visited.add(schemaUri);
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
const schema = await getSchema(schemaUri, contextSchema);
|
|
36
|
+
for (const reference of allReferences(schema.document.root)) {
|
|
37
|
+
const uri = toAbsoluteIri(resolveIri(reference.href, schema.document.baseUri));
|
|
38
|
+
if (visited.has(uri) || fullOptions.externalSchemas.includes(uri) || (uri in schema.document.embedded && !(uri in schema._cache))) {
|
|
40
39
|
continue;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
const externalSchema = await getSchema(uri);
|
|
42
|
+
const externalSchema = await getSchema(uri, contextSchema);
|
|
44
43
|
const embeddedSchema = toSchema(externalSchema, {
|
|
45
|
-
contextUri: externalSchema.document.baseUri.startsWith("file:") ? contextUri : undefined,
|
|
44
|
+
contextUri: externalSchema.document.baseUri.startsWith("file:") ? fullOptions.contextUri : undefined,
|
|
46
45
|
includeDialect: fullOptions.alwaysIncludeDialect ? "always" : "auto",
|
|
47
|
-
contextDialectId: contextDialectId
|
|
46
|
+
contextDialectId: fullOptions.contextDialectId
|
|
48
47
|
});
|
|
49
48
|
let id;
|
|
50
49
|
if (fullOptions.definitionNamingStrategy === URI) {
|
|
@@ -56,216 +55,29 @@ export const bundle = async (url, options = {}) => {
|
|
|
56
55
|
} else {
|
|
57
56
|
throw Error(`Unknown definition naming stragety: ${fullOptions.definitionNamingStrategy}`);
|
|
58
57
|
}
|
|
59
|
-
const pointer = JsonPointer.append(id, bundlingLocation);
|
|
58
|
+
const pointer = JsonPointer.append(id, fullOptions.bundlingLocation);
|
|
60
59
|
JsonPointer.assign(pointer, bundled, embeddedSchema);
|
|
60
|
+
|
|
61
|
+
bundled = await doBundling(uri, bundled, fullOptions, schema, visited);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
return bundled;
|
|
64
65
|
};
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
: new Set();
|
|
82
|
-
}),
|
|
83
|
-
asyncFlatten,
|
|
84
|
-
asyncCollectSet
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
if (parentSchema.document.baseUri !== schema.document.baseUri
|
|
88
|
-
&& (!(schema.document.baseUri in parentSchema.document.embedded) || schema.document.baseUri in parentSchema._cache)
|
|
89
|
-
) {
|
|
90
|
-
externalIds.add(schema.document.baseUri);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return externalIds;
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const collectExternalIdsFromArrayOfSchemas = (visited, parentSchema, schema) => pipe(
|
|
97
|
-
Browser.iter(schema),
|
|
98
|
-
asyncMap(Validation.collectExternalIds(visited, parentSchema)),
|
|
99
|
-
asyncFlatten,
|
|
100
|
-
asyncCollectSet
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const collectExternalIdsFromObjectOfSchemas = async (visited, parentSchema, schema) => pipe(
|
|
104
|
-
Browser.values(schema),
|
|
105
|
-
asyncMap(Validation.collectExternalIds(visited, parentSchema)),
|
|
106
|
-
asyncFlatten,
|
|
107
|
-
asyncCollectSet
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const loadKeywordSupport = () => {
|
|
111
|
-
// Stable
|
|
112
|
-
|
|
113
|
-
const additionalProperties = getKeyword("https://json-schema.org/keyword/additionalProperties");
|
|
114
|
-
if (additionalProperties) {
|
|
115
|
-
additionalProperties.collectExternalIds = Validation.collectExternalIds;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const allOf = getKeyword("https://json-schema.org/keyword/allOf");
|
|
119
|
-
if (allOf) {
|
|
120
|
-
allOf.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const anyOf = getKeyword("https://json-schema.org/keyword/anyOf");
|
|
124
|
-
if (anyOf) {
|
|
125
|
-
anyOf.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const contains = getKeyword("https://json-schema.org/keyword/contains");
|
|
129
|
-
if (contains) {
|
|
130
|
-
contains.collectExternalIds = Validation.collectExternalIds;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const dependentSchemas = getKeyword("https://json-schema.org/keyword/dependentSchemas");
|
|
134
|
-
if (dependentSchemas) {
|
|
135
|
-
dependentSchemas.collectExternalIds = collectExternalIdsFromObjectOfSchemas;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const if_ = getKeyword("https://json-schema.org/keyword/if");
|
|
139
|
-
if (if_) {
|
|
140
|
-
if_.collectExternalIds = Validation.collectExternalIds;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const then = getKeyword("https://json-schema.org/keyword/then");
|
|
144
|
-
if (then) {
|
|
145
|
-
then.collectExternalIds = Validation.collectExternalIds;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const else_ = getKeyword("https://json-schema.org/keyword/else");
|
|
149
|
-
if (else_) {
|
|
150
|
-
else_.collectExternalIds = Validation.collectExternalIds;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const items = getKeyword("https://json-schema.org/keyword/items");
|
|
154
|
-
if (items) {
|
|
155
|
-
items.collectExternalIds = Validation.collectExternalIds;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const not = getKeyword("https://json-schema.org/keyword/not");
|
|
159
|
-
if (not) {
|
|
160
|
-
not.collectExternalIds = Validation.collectExternalIds;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const oneOf = getKeyword("https://json-schema.org/keyword/oneOf");
|
|
164
|
-
if (oneOf) {
|
|
165
|
-
oneOf.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const patternProperties = getKeyword("https://json-schema.org/keyword/patternProperties");
|
|
169
|
-
if (patternProperties) {
|
|
170
|
-
patternProperties.collectExternalIds = collectExternalIdsFromObjectOfSchemas;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const prefixItems = getKeyword("https://json-schema.org/keyword/prefixItems");
|
|
174
|
-
if (prefixItems) {
|
|
175
|
-
prefixItems.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const properties = getKeyword("https://json-schema.org/keyword/properties");
|
|
179
|
-
if (properties) {
|
|
180
|
-
properties.collectExternalIds = collectExternalIdsFromObjectOfSchemas;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const propertyNames = getKeyword("https://json-schema.org/keyword/propertyNames");
|
|
184
|
-
if (propertyNames) {
|
|
185
|
-
propertyNames.collectExternalIds = Validation.collectExternalIds;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const ref = getKeyword("https://json-schema.org/keyword/ref");
|
|
189
|
-
if (ref) {
|
|
190
|
-
ref.collectExternalIds = Validation.collectExternalIds;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const unevaluatedItems = getKeyword("https://json-schema.org/keyword/unevaluatedItems");
|
|
194
|
-
if (unevaluatedItems) {
|
|
195
|
-
unevaluatedItems.collectExternalIds = Validation.collectExternalIds;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const unevaluatedProperties = getKeyword("https://json-schema.org/keyword/unevaluatedProperties");
|
|
199
|
-
if (unevaluatedProperties) {
|
|
200
|
-
unevaluatedProperties.collectExternalIds = Validation.collectExternalIds;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Draft-04
|
|
204
|
-
|
|
205
|
-
const additionalItems4 = getKeyword("https://json-schema.org/keyword/draft-04/additionalItems");
|
|
206
|
-
if (additionalItems4) {
|
|
207
|
-
additionalItems4.collectExternalIds = Validation.collectExternalIds;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const dependencies = getKeyword("https://json-schema.org/keyword/draft-04/dependencies");
|
|
211
|
-
if (dependencies) {
|
|
212
|
-
dependencies.collectExternalIds = (visited, parentSchema, schema) => pipe(
|
|
213
|
-
Browser.values(schema),
|
|
214
|
-
asyncFilter((subSchema) => Browser.typeOf(subSchema) === "object"),
|
|
215
|
-
asyncMap(Validation.collectExternalIds(visited, parentSchema)),
|
|
216
|
-
asyncFlatten,
|
|
217
|
-
asyncCollectSet
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const items4 = getKeyword("https://json-schema.org/keyword/draft-04/items");
|
|
222
|
-
if (items4) {
|
|
223
|
-
items4.collectExternalIds = (visited, parentSchema, schema) => Browser.typeOf(schema) === "array"
|
|
224
|
-
? collectExternalIdsFromArrayOfSchemas(visited, parentSchema, schema)
|
|
225
|
-
: Validation.collectExternalIds(visited, parentSchema, schema);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Draft-06
|
|
229
|
-
|
|
230
|
-
const contains6 = getKeyword("https://json-schema.org/keyword/draft-06/contains");
|
|
231
|
-
if (contains6) {
|
|
232
|
-
contains6.collectExternalIds = Validation.collectExternalIds;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Draft-2019-09
|
|
236
|
-
|
|
237
|
-
const contains19 = getKeyword("https://json-schema.org/keyword/draft-2019-09/contains");
|
|
238
|
-
if (contains19) {
|
|
239
|
-
contains19.collectExternalIds = Validation.collectExternalIds;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Extensions
|
|
243
|
-
|
|
244
|
-
const propertyDependencies = getKeyword("https://json-schema.org/keyword/propertyDependencies");
|
|
245
|
-
if (propertyDependencies) {
|
|
246
|
-
propertyDependencies.collectExternalIds = (visited, parentSchema, schema) => pipe(
|
|
247
|
-
Browser.values(schema),
|
|
248
|
-
asyncMap((mapping) => Browser.values(mapping)),
|
|
249
|
-
asyncFlatten,
|
|
250
|
-
asyncMap(Validation.collectExternalIds(visited, parentSchema)),
|
|
251
|
-
asyncFlatten,
|
|
252
|
-
asyncCollectSet
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const conditional = getKeyword("https://json-schema.org/keyword/conditional");
|
|
257
|
-
if (conditional) {
|
|
258
|
-
conditional.collectExternalIds = collectExternalIdsFromArrayOfSchemas;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const itemPattern = getKeyword("https://json-schema.org/keyword/itemPattern");
|
|
262
|
-
if (itemPattern) {
|
|
263
|
-
itemPattern.collectExternalIds = (visited, parentSchema, schema) => pipe(
|
|
264
|
-
Browser.iter(schema),
|
|
265
|
-
asyncFilter((item) => Browser.typeOf(item) === "object"),
|
|
266
|
-
asyncMap(Validation.collectExternalIds(visited, parentSchema)),
|
|
267
|
-
asyncFlatten,
|
|
268
|
-
asyncCollectSet
|
|
269
|
-
);
|
|
67
|
+
const allReferences = function* (node) {
|
|
68
|
+
switch (jrefTypeOf(node)) {
|
|
69
|
+
case "object":
|
|
70
|
+
for (const property in node) {
|
|
71
|
+
yield* allReferences(node[property]);
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
case "array":
|
|
75
|
+
for (const item of node) {
|
|
76
|
+
yield* allReferences(item);
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case "reference":
|
|
80
|
+
yield node;
|
|
81
|
+
break;
|
|
270
82
|
}
|
|
271
83
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { drop } from "@hyperjump/pact";
|
|
2
2
|
import * as Browser from "@hyperjump/browser";
|
|
3
3
|
import * as Instance from "../lib/instance.js";
|
|
4
4
|
import { getKeywordName, Validation } from "../lib/experimental.js";
|
|
@@ -19,26 +19,20 @@ const interpret = ([numberOfItems, additionalItems], instance, context) => {
|
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const simpleApplicator = true;
|
|
22
|
+
let isValid = true;
|
|
23
|
+
let index = numberOfItems;
|
|
24
|
+
for (const item of drop(numberOfItems, Instance.iter(instance))) {
|
|
25
|
+
if (!Validation.interpret(additionalItems, item, context)) {
|
|
26
|
+
isValid = false;
|
|
27
|
+
}
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return false;
|
|
29
|
+
context.evaluatedItems?.add(index);
|
|
30
|
+
index++;
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
for (let ndx = keywordValue[0]; ndx < Instance.length(instance); ndx++) {
|
|
38
|
-
evaluatedIndexes.add(ndx);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return evaluatedIndexes;
|
|
33
|
+
return isValid;
|
|
42
34
|
};
|
|
43
35
|
|
|
44
|
-
|
|
36
|
+
const simpleApplicator = true;
|
|
37
|
+
|
|
38
|
+
export default { id, compile, interpret, simpleApplicator };
|
package/draft-04/dependencies.js
CHANGED
|
@@ -16,21 +16,21 @@ const compile = (schema, ast) => pipe(
|
|
|
16
16
|
);
|
|
17
17
|
|
|
18
18
|
const interpret = (dependencies, instance, context) => {
|
|
19
|
-
|
|
19
|
+
if (Instance.typeOf(instance) !== "object") {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
20
22
|
|
|
21
|
-
return
|
|
22
|
-
if (!(propertyName
|
|
23
|
+
return dependencies.every(([propertyName, dependency]) => {
|
|
24
|
+
if (!Instance.has(propertyName, instance)) {
|
|
23
25
|
return true;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
if (Array.isArray(dependency)) {
|
|
27
|
-
return dependency.every((key) => key
|
|
29
|
+
return dependency.every((key) => Instance.has(key, instance));
|
|
28
30
|
} else {
|
|
29
31
|
return Validation.interpret(dependency, instance, context);
|
|
30
32
|
}
|
|
31
33
|
});
|
|
32
34
|
};
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export default { id, compile, interpret, simpleApplicator };
|
|
36
|
+
export default { id, compile, interpret };
|
package/draft-04/items.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pipe, asyncMap, asyncCollectArray,
|
|
1
|
+
import { pipe, asyncMap, asyncCollectArray, zip } from "@hyperjump/pact";
|
|
2
2
|
import * as Browser from "@hyperjump/browser";
|
|
3
3
|
import * as Instance from "../lib/instance.js";
|
|
4
4
|
import { Validation } from "../lib/experimental.js";
|
|
@@ -23,23 +23,35 @@ const interpret = (items, instance, context) => {
|
|
|
23
23
|
return true;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
let isValid = true;
|
|
27
|
+
let index = 0;
|
|
28
|
+
|
|
26
29
|
if (typeof items === "string") {
|
|
27
|
-
|
|
30
|
+
for (const item of Instance.iter(instance)) {
|
|
31
|
+
if (!Validation.interpret(items, item, context)) {
|
|
32
|
+
isValid = false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
context.evaluatedItems?.add(index++);
|
|
36
|
+
}
|
|
28
37
|
} else {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
for (const [tupleItem, tupleInstance] of zip(items, Instance.iter(instance))) {
|
|
39
|
+
if (!tupleInstance) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!Validation.interpret(tupleItem, tupleInstance, context)) {
|
|
44
|
+
isValid = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
context.evaluatedItems?.add(index);
|
|
48
|
+
index++;
|
|
49
|
+
}
|
|
34
50
|
}
|
|
51
|
+
|
|
52
|
+
return isValid;
|
|
35
53
|
};
|
|
36
54
|
|
|
37
55
|
const simpleApplicator = true;
|
|
38
56
|
|
|
39
|
-
|
|
40
|
-
return interpret(items, instance, context) && (typeof items === "string"
|
|
41
|
-
? collectSet(range(0, Instance.length(instance)))
|
|
42
|
-
: collectSet(range(0, items.length)));
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export default { id, compile, interpret, simpleApplicator, collectEvaluatedItems };
|
|
57
|
+
export default { id, compile, interpret, simpleApplicator };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Browser from "@hyperjump/browser";
|
|
2
2
|
import { Validation, canonicalUri } from "../lib/experimental.js";
|
|
3
|
-
import { uriFragment } from "../lib/common.js";
|
|
3
|
+
import { toAbsoluteUri, uriFragment } from "../lib/common.js";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
const id = "https://json-schema.org/keyword/draft-2020-12/dynamicRef";
|
|
@@ -12,19 +12,31 @@ const compile = async (dynamicRef, ast) => {
|
|
|
12
12
|
return [referencedSchema.document.baseUri, fragment, canonicalUri(referencedSchema)];
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const interpret = ([id, fragment, ref], instance, context) => {
|
|
16
16
|
if (fragment in context.ast.metaData[id].dynamicAnchors) {
|
|
17
17
|
context.dynamicAnchors = { ...context.ast.metaData[id].dynamicAnchors, ...context.dynamicAnchors };
|
|
18
|
-
return
|
|
18
|
+
return Validation.interpret(context.dynamicAnchors[fragment], instance, context);
|
|
19
19
|
} else {
|
|
20
|
-
return
|
|
20
|
+
return Validation.interpret(ref, instance, context);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const simpleApplicator = true;
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const plugin = {
|
|
27
|
+
beforeSchema(url, _instance, context) {
|
|
28
|
+
context.dynamicAnchors = {
|
|
29
|
+
...context.ast.metaData[toAbsoluteUri(url)].dynamicAnchors,
|
|
30
|
+
...context.dynamicAnchors
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
beforeKeyword(_url, _instance, context, schemaContext) {
|
|
34
|
+
context.dynamicAnchors = schemaContext.dynamicAnchors;
|
|
35
|
+
},
|
|
36
|
+
afterKeyword() {
|
|
37
|
+
},
|
|
38
|
+
afterSchema() {
|
|
39
|
+
}
|
|
40
|
+
};
|
|
29
41
|
|
|
30
|
-
export default { id, compile, interpret, simpleApplicator,
|
|
42
|
+
export default { id, compile, interpret, simpleApplicator, plugin };
|