@hyperjump/json-schema 1.1.0 → 1.1.2
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/draft-04/maximum.js +0 -1
- package/draft-2019-09/recursiveRef.js +2 -1
- package/draft-2020-12/dynamicRef.js +3 -11
- package/lib/common.js +10 -6
- package/lib/configuration.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/instance.js +6 -6
- package/lib/keywords/anyOf.js +2 -2
- package/lib/keywords/contains.js +6 -6
- package/lib/keywords/else.js +3 -4
- package/lib/keywords/exclusiveMaximum.js +1 -1
- package/lib/keywords/exclusiveMinimum.js +1 -1
- package/lib/keywords/maximum.js +1 -1
- package/lib/keywords/minimum.js +1 -1
- package/lib/keywords/propertyDependencies.js +1 -1
- package/lib/keywords/requireAllExcept.js +1 -1
- package/lib/keywords/required.js +1 -1
- package/lib/keywords/then.js +3 -4
- package/lib/keywords/unevaluatedProperties.js +1 -1
- package/lib/keywords/validation.js +15 -16
- package/lib/keywords.js +6 -2
- package/lib/pubsub.js +0 -2
- package/lib/reference.js +1 -1
- package/lib/schema.d.ts +1 -1
- package/lib/schema.js +27 -27
- package/package.json +4 -3
package/draft-04/maximum.js
CHANGED
|
@@ -7,7 +7,6 @@ const id = "https://json-schema.org/keyword/draft-04/maximum";
|
|
|
7
7
|
|
|
8
8
|
const compile = async (schema, ast, parentSchema) => {
|
|
9
9
|
const exclusiveMaximumKeyword = getKeywordName(schema.dialectId, "https://json-schema.org/keyword/draft-04/exclusiveMaximum");
|
|
10
|
-
console.log(exclusiveMaximumKeyword);
|
|
11
10
|
const exclusiveMaximum = await Schema.step(exclusiveMaximumKeyword, parentSchema);
|
|
12
11
|
const isExclusive = Schema.value(exclusiveMaximum);
|
|
13
12
|
|
|
@@ -3,10 +3,11 @@ import Validation from "../lib/keywords/validation.js";
|
|
|
3
3
|
|
|
4
4
|
const id = "https://json-schema.org/keyword/draft-2019-09/recursiveRef";
|
|
5
5
|
|
|
6
|
-
const compile =
|
|
6
|
+
const compile = (schema) => schema.id;
|
|
7
7
|
|
|
8
8
|
const interpret = (id, instance, ast, dynamicAnchors) => {
|
|
9
9
|
if ("" in ast.metaData[id].dynamicAnchors) {
|
|
10
|
+
dynamicAnchors = { ...ast.metaData[id].dynamicAnchors, ...dynamicAnchors };
|
|
10
11
|
return Validation.interpret(dynamicAnchors[""], instance, ast, dynamicAnchors);
|
|
11
12
|
} else {
|
|
12
13
|
return Validation.interpret(`${id}#`, instance, ast, dynamicAnchors);
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import Validation from "../lib/keywords/validation.js";
|
|
2
2
|
import * as Schema from "../lib/schema.js";
|
|
3
|
+
import { uriFragment } from "../lib/common.js";
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
const id = "https://json-schema.org/keyword/draft-2020-12/dynamicRef";
|
|
6
7
|
|
|
7
8
|
const compile = async (dynamicRef, ast) => {
|
|
8
|
-
const
|
|
9
|
+
const fragment = uriFragment(Schema.value(dynamicRef));
|
|
9
10
|
const referencedSchema = await Schema.get(Schema.value(dynamicRef), dynamicRef);
|
|
10
11
|
await Validation.compile(referencedSchema, ast);
|
|
11
12
|
return [referencedSchema.id, fragment, Schema.uri(referencedSchema)];
|
|
12
|
-
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const interpret = ([id, fragment, ref], instance, ast, dynamicAnchors) => {
|
|
16
16
|
if (fragment in ast.metaData[id].dynamicAnchors) {
|
|
17
|
+
dynamicAnchors = { ...ast.metaData[id].dynamicAnchors, ...dynamicAnchors };
|
|
17
18
|
return Validation.interpret(dynamicAnchors[fragment], instance, ast, dynamicAnchors);
|
|
18
19
|
} else {
|
|
19
20
|
return Validation.interpret(ref, instance, ast, dynamicAnchors);
|
|
@@ -23,13 +24,4 @@ const interpret = ([id, fragment, ref], instance, ast, dynamicAnchors) => {
|
|
|
23
24
|
const collectEvaluatedProperties = Validation.collectEvaluatedProperties;
|
|
24
25
|
const collectEvaluatedItems = Validation.collectEvaluatedItems;
|
|
25
26
|
|
|
26
|
-
const splitUrl = (url) => {
|
|
27
|
-
const indexOfHash = url.indexOf("#");
|
|
28
|
-
const ndx = indexOfHash === -1 ? url.length : indexOfHash;
|
|
29
|
-
const urlReference = url.slice(0, ndx);
|
|
30
|
-
const urlFragment = url.slice(ndx + 1);
|
|
31
|
-
|
|
32
|
-
return [decodeURI(urlReference), decodeURI(urlFragment)];
|
|
33
|
-
};
|
|
34
|
-
|
|
35
27
|
export default { id, compile, interpret, collectEvaluatedProperties, collectEvaluatedItems };
|
package/lib/common.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { resolveIri, toAbsoluteIri, parseIriReference } from "@hyperjump/uri";
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
const isObject = (value) => typeof value === "object" && !Array.isArray(value) && value !== null;
|
|
2
5
|
const isType = {
|
|
3
6
|
null: (value) => value === null,
|
|
@@ -10,15 +13,16 @@ const isType = {
|
|
|
10
13
|
};
|
|
11
14
|
export const jsonTypeOf = (value, type) => isType[type](value);
|
|
12
15
|
|
|
13
|
-
export const
|
|
14
|
-
const resolved =
|
|
15
|
-
if (
|
|
16
|
-
throw Error(`Can't access file '${resolved
|
|
16
|
+
export const resolveUri = (uri, baseUri) => {
|
|
17
|
+
const resolved = resolveIri(uri, baseUri);
|
|
18
|
+
if (resolved.startsWith("file:") && baseUri && !baseUri.startsWith("file:")) {
|
|
19
|
+
throw Error(`Can't access file '${resolved}' resource from network context '${baseUri}'`);
|
|
17
20
|
}
|
|
18
|
-
return resolved
|
|
21
|
+
return resolved;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
|
-
export const
|
|
24
|
+
export const toAbsoluteUri = (uri) => toAbsoluteIri(uri);
|
|
25
|
+
export const uriFragment = (uri) => decodeURIComponent(parseIriReference(uri).fragment || "");
|
|
22
26
|
|
|
23
27
|
const CHAR_BACKWARD_SLASH = 47;
|
|
24
28
|
|
package/lib/configuration.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
let metaSchemaOutputFormat;
|
|
2
2
|
let shouldValidateSchema = true;
|
|
3
|
-
|
|
3
|
+
const enabledExperimentalKeywords = {};
|
|
4
4
|
|
|
5
5
|
export const getMetaSchemaOutputFormat = () => metaSchemaOutputFormat;
|
|
6
6
|
export const setMetaSchemaOutputFormat = (format) => {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { addSchema, validate, FLAG } from "./core.js";
|
|
2
|
-
export type { Validator, OutputFormat, CompiledSchema,
|
|
2
|
+
export type { Validator, OutputFormat, CompiledSchema, OutputUnit } from "./core.js";
|
|
3
3
|
export {
|
|
4
4
|
getMetaSchemaOutputFormat,
|
|
5
5
|
setMetaSchemaOutputFormat,
|
package/lib/instance.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as JsonPointer from "@hyperjump/json-pointer";
|
|
2
2
|
import curry from "just-curry-it";
|
|
3
|
-
import {
|
|
3
|
+
import { toAbsoluteUri, jsonTypeOf } from "./common.js";
|
|
4
4
|
import * as Reference from "./reference.js";
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
export const nil =
|
|
8
|
-
export const cons = (instance, id = undefined) =>
|
|
7
|
+
export const nil = { id: undefined, pointer: "", instance: undefined, value: undefined };
|
|
8
|
+
export const cons = (instance, id = undefined) => ({
|
|
9
9
|
...nil,
|
|
10
|
-
id: id ?
|
|
10
|
+
id: id ? toAbsoluteUri(id) : "",
|
|
11
11
|
instance,
|
|
12
12
|
value: instance
|
|
13
13
|
});
|
|
@@ -17,7 +17,7 @@ export const get = (url, instance = nil) => {
|
|
|
17
17
|
throw Error(`No JSON document found at '${url.split("#")[0]}'`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
return
|
|
20
|
+
return { ...instance, pointer: url.substr(1) };
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export const uri = (doc) => `${doc.id || ""}#${encodeURI(doc.pointer)}`;
|
|
@@ -25,7 +25,7 @@ export const value = (doc) => Reference.isReference(doc.value) ? Reference.value
|
|
|
25
25
|
export const has = (key, doc) => key in value(doc);
|
|
26
26
|
export const typeOf = curry((doc, type) => jsonTypeOf(value(doc), type));
|
|
27
27
|
|
|
28
|
-
export const step = (key, doc) =>
|
|
28
|
+
export const step = (key, doc) => ({
|
|
29
29
|
...doc,
|
|
30
30
|
pointer: JsonPointer.append(key, doc.pointer),
|
|
31
31
|
value: value(doc)[key]
|
package/lib/keywords/anyOf.js
CHANGED
|
@@ -18,14 +18,14 @@ const interpret = (anyOf, instance, ast, dynamicAnchors) => {
|
|
|
18
18
|
const collectEvaluatedProperties = (anyOf, instance, ast, dynamicAnchors) => {
|
|
19
19
|
return anyOf.reduce((acc, schemaUrl) => {
|
|
20
20
|
const propertyNames = Validation.collectEvaluatedProperties(schemaUrl, instance, ast, dynamicAnchors);
|
|
21
|
-
return propertyNames
|
|
21
|
+
return propertyNames === false ? acc : [...acc || [], ...propertyNames];
|
|
22
22
|
}, false);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const collectEvaluatedItems = (anyOf, instance, ast, dynamicAnchors) => {
|
|
26
26
|
return anyOf.reduce((acc, schemaUrl) => {
|
|
27
27
|
const itemIndexes = Validation.collectEvaluatedItems(schemaUrl, instance, ast, dynamicAnchors);
|
|
28
|
-
return itemIndexes
|
|
28
|
+
return itemIndexes === false ? acc : new Set([...acc || [], ...itemIndexes]);
|
|
29
29
|
}, false);
|
|
30
30
|
};
|
|
31
31
|
|
package/lib/keywords/contains.js
CHANGED
|
@@ -32,17 +32,17 @@ const interpret = ({ contains, minContains, maxContains }, instance, ast, dynami
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const collectEvaluatedItems = (keywordValue, instance, ast, dynamicAnchors) => {
|
|
35
|
-
return interpret(keywordValue, instance, ast, dynamicAnchors)
|
|
36
|
-
Instance.typeOf(instance, "array")
|
|
37
|
-
Instance.reduce((matchedIndexes, item, itemIndex) => {
|
|
35
|
+
return interpret(keywordValue, instance, ast, dynamicAnchors)
|
|
36
|
+
&& Instance.typeOf(instance, "array")
|
|
37
|
+
&& Instance.reduce((matchedIndexes, item, itemIndex) => {
|
|
38
38
|
return Validation.interpret(keywordValue.contains, item, ast, dynamicAnchors) ? matchedIndexes.add(itemIndex) : matchedIndexes;
|
|
39
39
|
}, new Set(), instance);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
const collectEvaluatedProperties = (keywordValue, instance, ast, dynamicAnchors) => {
|
|
43
|
-
return interpret(keywordValue, instance, ast, dynamicAnchors)
|
|
44
|
-
Instance.typeOf(instance, "object")
|
|
45
|
-
Instance.entries(instance).reduce((matchedPropertyNames, [propertyName, item]) => {
|
|
43
|
+
return interpret(keywordValue, instance, ast, dynamicAnchors)
|
|
44
|
+
&& Instance.typeOf(instance, "object")
|
|
45
|
+
&& Instance.entries(instance).reduce((matchedPropertyNames, [propertyName, item]) => {
|
|
46
46
|
if (Validation.interpret(keywordValue.contains, item, ast, dynamicAnchors)) {
|
|
47
47
|
matchedPropertyNames.push(propertyName);
|
|
48
48
|
}
|
package/lib/keywords/else.js
CHANGED
|
@@ -23,10 +23,9 @@ const interpret = ([guard, block], instance, ast, dynamicAnchors) => {
|
|
|
23
23
|
const quietInterpretSchema = (url, instance, ast, dynamicAnchors) => {
|
|
24
24
|
const nodes = ast[url][2];
|
|
25
25
|
|
|
26
|
-
return typeof nodes === "boolean" ? nodes : nodes
|
|
27
|
-
.
|
|
28
|
-
|
|
29
|
-
});
|
|
26
|
+
return typeof nodes === "boolean" ? nodes : nodes.every(([keywordId, , keywordValue]) => {
|
|
27
|
+
return getKeyword(keywordId).interpret(keywordValue, instance, ast, dynamicAnchors);
|
|
28
|
+
});
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
const collectEvaluatedProperties = ([guard, block], instance, ast, dynamicAnchors) => {
|
|
@@ -4,7 +4,7 @@ import * as Instance from "../instance.js";
|
|
|
4
4
|
|
|
5
5
|
const id = "https://json-schema.org/keyword/exclusiveMaximum";
|
|
6
6
|
|
|
7
|
-
const compile =
|
|
7
|
+
const compile = (schema) => Schema.value(schema);
|
|
8
8
|
const interpret = (exclusiveMaximum, instance) => !Instance.typeOf(instance, "number") || Instance.value(instance) < exclusiveMaximum;
|
|
9
9
|
|
|
10
10
|
export default { id, compile, interpret };
|
|
@@ -4,7 +4,7 @@ import * as Instance from "../instance.js";
|
|
|
4
4
|
|
|
5
5
|
const id = "https://json-schema.org/keyword/exclusiveMinimum";
|
|
6
6
|
|
|
7
|
-
const compile =
|
|
7
|
+
const compile = (schema) => Schema.value(schema);
|
|
8
8
|
const interpret = (exclusiveMinimum, instance) => !Instance.typeOf(instance, "number") || Instance.value(instance) > exclusiveMinimum;
|
|
9
9
|
|
|
10
10
|
export default { id, compile, interpret };
|
package/lib/keywords/maximum.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as Instance from "../instance.js";
|
|
|
4
4
|
|
|
5
5
|
const id = "https://json-schema.org/keyword/maximum";
|
|
6
6
|
|
|
7
|
-
const compile =
|
|
7
|
+
const compile = (schema) => Schema.value(schema);
|
|
8
8
|
const interpret = (maximum, instance) => !Instance.typeOf(instance, "number") || Instance.value(instance) <= maximum;
|
|
9
9
|
|
|
10
10
|
export default { id, compile, interpret };
|
package/lib/keywords/minimum.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as Instance from "../instance.js";
|
|
|
4
4
|
|
|
5
5
|
const id = "https://json-schema.org/keyword/minimum";
|
|
6
6
|
|
|
7
|
-
const compile =
|
|
7
|
+
const compile = (schema) => Schema.value(schema);
|
|
8
8
|
const interpret = (minimum, instance) => !Instance.typeOf(instance, "number") || Instance.value(instance) >= minimum;
|
|
9
9
|
|
|
10
10
|
export default { id, compile, interpret };
|
|
@@ -7,7 +7,7 @@ import Validation from "./validation.js";
|
|
|
7
7
|
const id = "https://json-schema.org/keyword/propertyDependencies";
|
|
8
8
|
const experimental = true;
|
|
9
9
|
|
|
10
|
-
const compile =
|
|
10
|
+
const compile = (schema, ast) => {
|
|
11
11
|
return Pact.pipeline([
|
|
12
12
|
Schema.entries,
|
|
13
13
|
Pact.reduce(async (propertyDependencies, [propertyName, valueMappings]) => {
|
|
@@ -18,7 +18,7 @@ const compile = async (schema, ast, parentSchema) => {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const interpret = (required, instance) => {
|
|
21
|
-
return !Instance.typeOf(instance, "object") || required.every((propertyName) => Instance.value(instance)
|
|
21
|
+
return !Instance.typeOf(instance, "object") || required.every((propertyName) => Object.hasOwn(Instance.value(instance), propertyName));
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export default { id, experimental, compile, interpret };
|
package/lib/keywords/required.js
CHANGED
|
@@ -7,7 +7,7 @@ const id = "https://json-schema.org/keyword/required";
|
|
|
7
7
|
const compile = (schema) => Schema.value(schema);
|
|
8
8
|
|
|
9
9
|
const interpret = (required, instance) => {
|
|
10
|
-
return !Instance.typeOf(instance, "object") || required.every((propertyName) => Object.
|
|
10
|
+
return !Instance.typeOf(instance, "object") || required.every((propertyName) => Object.hasOwn(Instance.value(instance), propertyName));
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export default { id, compile, interpret };
|
package/lib/keywords/then.js
CHANGED
|
@@ -23,10 +23,9 @@ const interpret = ([guard, block], instance, ast, dynamicAnchors) => {
|
|
|
23
23
|
const quietInterpretSchema = (url, instance, ast, dynamicAnchors) => {
|
|
24
24
|
const nodes = ast[url][2];
|
|
25
25
|
|
|
26
|
-
return typeof nodes === "boolean" ? nodes : nodes
|
|
27
|
-
.
|
|
28
|
-
|
|
29
|
-
});
|
|
26
|
+
return typeof nodes === "boolean" ? nodes : nodes.every(([keywordId, , keywordValue]) => {
|
|
27
|
+
return getKeyword(keywordId).interpret(keywordValue, instance, ast, dynamicAnchors);
|
|
28
|
+
});
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
const collectEvaluatedProperties = ([guard, block], instance, ast, dynamicAnchors) => {
|
|
@@ -21,7 +21,7 @@ const interpret = ([schemaUrl, unevaluatedProperties], instance, ast, dynamicAnc
|
|
|
21
21
|
.every(([, property]) => Validation.interpret(unevaluatedProperties, property, ast, dynamicAnchors));
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const collectEvaluatedProperties = (keywordValue, instance, ast, dynamicAnchors) =>{
|
|
24
|
+
const collectEvaluatedProperties = (keywordValue, instance, ast, dynamicAnchors) => {
|
|
25
25
|
return interpret(keywordValue, instance, ast, dynamicAnchors) && [new RegExp("")];
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -3,7 +3,7 @@ import { publishAsync, publish } from "../pubsub.js";
|
|
|
3
3
|
import { isExperimentalKeywordEnabled } from "../configuration.js";
|
|
4
4
|
import * as Instance from "../instance.js";
|
|
5
5
|
import { getKeywordId, getKeyword } from "../keywords.js";
|
|
6
|
-
import {
|
|
6
|
+
import { toAbsoluteUri } from "../common.js";
|
|
7
7
|
import * as Schema from "../schema.js";
|
|
8
8
|
|
|
9
9
|
|
|
@@ -60,24 +60,23 @@ const compile = async (schema, ast) => {
|
|
|
60
60
|
const interpret = (url, instance, ast, dynamicAnchors) => {
|
|
61
61
|
const [keywordId, schemaUrl, nodes] = ast[url];
|
|
62
62
|
|
|
63
|
-
dynamicAnchors = { ...ast.metaData[
|
|
63
|
+
dynamicAnchors = { ...ast.metaData[toAbsoluteUri(url)].dynamicAnchors, ...dynamicAnchors };
|
|
64
64
|
|
|
65
65
|
publish("result.start");
|
|
66
|
-
const isValid = typeof nodes === "boolean" ? nodes : nodes
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
ast: keywordValue
|
|
77
|
-
});
|
|
78
|
-
publish("result.end");
|
|
79
|
-
return isValid;
|
|
66
|
+
const isValid = typeof nodes === "boolean" ? nodes : nodes.every(([keywordId, schemaUrl, keywordValue]) => {
|
|
67
|
+
publish("result.start");
|
|
68
|
+
const isValid = getKeyword(keywordId).interpret(keywordValue, instance, ast, dynamicAnchors);
|
|
69
|
+
|
|
70
|
+
publish("result", {
|
|
71
|
+
keyword: keywordId,
|
|
72
|
+
absoluteKeywordLocation: schemaUrl,
|
|
73
|
+
instanceLocation: Instance.uri(instance),
|
|
74
|
+
valid: isValid,
|
|
75
|
+
ast: keywordValue
|
|
80
76
|
});
|
|
77
|
+
publish("result.end");
|
|
78
|
+
return isValid;
|
|
79
|
+
});
|
|
81
80
|
|
|
82
81
|
publish("result", {
|
|
83
82
|
keyword: keywordId,
|
package/lib/keywords.js
CHANGED
|
@@ -6,8 +6,12 @@ export const getKeyword = (id) => _keywords[id] || id.startsWith("https://json-s
|
|
|
6
6
|
|
|
7
7
|
export const addKeyword = (keywordHandler) => {
|
|
8
8
|
_keywords[keywordHandler.id] = {
|
|
9
|
-
collectEvaluatedItems: (keywordValue, instance, ast, dynamicAnchors, isTop) =>
|
|
10
|
-
|
|
9
|
+
collectEvaluatedItems: (keywordValue, instance, ast, dynamicAnchors, isTop) => {
|
|
10
|
+
return keywordHandler.interpret(keywordValue, instance, ast, dynamicAnchors, isTop) && new Set();
|
|
11
|
+
},
|
|
12
|
+
collectEvaluatedProperties: (keywordValue, instance, ast, dynamicAnchors, isTop) => {
|
|
13
|
+
return keywordHandler.interpret(keywordValue, instance, ast, dynamicAnchors, isTop) && [];
|
|
14
|
+
},
|
|
11
15
|
...keywordHandler
|
|
12
16
|
};
|
|
13
17
|
};
|
package/lib/pubsub.js
CHANGED
package/lib/reference.js
CHANGED
package/lib/schema.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { JsonType } from "./common.js";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
export const add: <A extends SchemaObject | boolean>(schema: A,
|
|
4
|
+
export const add: <A extends SchemaObject | boolean>(schema: A, retrievalUri?: string, defaultSchemaVersion?: string) => string;
|
|
5
5
|
export const get: (url: string, context?: SchemaDocument) => Promise<SchemaDocument>;
|
|
6
6
|
export const markValidated: (id: string) => void;
|
|
7
7
|
export const uri: (doc: SchemaDocument) => string;
|
package/lib/schema.js
CHANGED
|
@@ -2,7 +2,7 @@ import curry from "just-curry-it";
|
|
|
2
2
|
import * as Pact from "@hyperjump/pact";
|
|
3
3
|
import * as Json from "@hyperjump/json";
|
|
4
4
|
import * as JsonPointer from "@hyperjump/json-pointer";
|
|
5
|
-
import { jsonTypeOf,
|
|
5
|
+
import { jsonTypeOf, resolveUri, toAbsoluteUri, uriFragment, pathRelative } from "./common.js";
|
|
6
6
|
import fetch from "./fetch.js";
|
|
7
7
|
import * as Keywords from "./keywords.js";
|
|
8
8
|
import * as MediaTypes from "./media-types.js";
|
|
@@ -15,11 +15,11 @@ const schemaStoreAlias = {};
|
|
|
15
15
|
|
|
16
16
|
const defaultDialectId = "https://json-schema.org/validation";
|
|
17
17
|
|
|
18
|
-
export const add = (schema,
|
|
18
|
+
export const add = (schema, retrievalUri = undefined, contextDialectId = undefined) => {
|
|
19
19
|
schema = JSON.parse(JSON.stringify(schema));
|
|
20
20
|
|
|
21
21
|
// Dialect / JSON Schema Version
|
|
22
|
-
const dialectId =
|
|
22
|
+
const dialectId = toAbsoluteUri(schema.$schema || contextDialectId || defaultDialectId);
|
|
23
23
|
delete schema.$schema;
|
|
24
24
|
|
|
25
25
|
if (!Keywords.hasDialect(dialectId)) {
|
|
@@ -29,11 +29,14 @@ export const add = (schema, url = undefined, contextDialectId = undefined) => {
|
|
|
29
29
|
// Identifiers
|
|
30
30
|
const idToken = Keywords.getKeywordName(dialectId, "https://json-schema.org/keyword/id")
|
|
31
31
|
|| Keywords.getKeywordName(dialectId, "https://json-schema.org/keyword/draft-04/id");
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (retrievalUri === undefined && !(idToken in schema)) {
|
|
33
|
+
throw Error(`Unable to determine an identifier for the schema. Use the '${idToken}' keyword or pass a retrievalUri when loading the schema.`);
|
|
34
|
+
}
|
|
35
|
+
const internalUrl = resolveUri(schema[idToken] || retrievalUri, retrievalUri);
|
|
36
|
+
const id = toAbsoluteUri(internalUrl);
|
|
34
37
|
delete schema[idToken];
|
|
35
|
-
if (
|
|
36
|
-
const externalId =
|
|
38
|
+
if (retrievalUri) {
|
|
39
|
+
const externalId = toAbsoluteUri(retrievalUri);
|
|
37
40
|
schemaStoreAlias[externalId] = id;
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -80,7 +83,7 @@ const processSchema = (subject, id, dialectId, pointer, anchors, dynamicAnchors)
|
|
|
80
83
|
anchors[anchor] = pointer;
|
|
81
84
|
} else {
|
|
82
85
|
delete subject[legacyIdToken].$schema;
|
|
83
|
-
subject[legacyIdToken] =
|
|
86
|
+
subject[legacyIdToken] = resolveUri(subject[legacyIdToken], id);
|
|
84
87
|
add(subject, undefined, dialectId);
|
|
85
88
|
return Reference.cons(subject[legacyIdToken], subject);
|
|
86
89
|
}
|
|
@@ -88,10 +91,10 @@ const processSchema = (subject, id, dialectId, pointer, anchors, dynamicAnchors)
|
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
// Embedded Schema
|
|
91
|
-
const embeddedDialectId = typeof subject.$schema === "string" ?
|
|
94
|
+
const embeddedDialectId = typeof subject.$schema === "string" ? toAbsoluteUri(subject.$schema) : dialectId;
|
|
92
95
|
const idToken = Keywords.getKeywordName(embeddedDialectId, "https://json-schema.org/keyword/id");
|
|
93
96
|
if (typeof subject[idToken] === "string") {
|
|
94
|
-
subject[idToken] =
|
|
97
|
+
subject[idToken] = resolveUri(subject[idToken], id);
|
|
95
98
|
add(subject, undefined, dialectId);
|
|
96
99
|
return Reference.cons(subject[idToken], subject);
|
|
97
100
|
}
|
|
@@ -142,7 +145,7 @@ export const markValidated = (id) => {
|
|
|
142
145
|
};
|
|
143
146
|
|
|
144
147
|
// Schema Retrieval
|
|
145
|
-
const nil =
|
|
148
|
+
const nil = {
|
|
146
149
|
id: undefined,
|
|
147
150
|
dialectId: undefined,
|
|
148
151
|
pointer: JsonPointer.nil,
|
|
@@ -151,12 +154,12 @@ const nil = Object.freeze({
|
|
|
151
154
|
anchors: {},
|
|
152
155
|
dynamicAnchors: {},
|
|
153
156
|
validated: true
|
|
154
|
-
}
|
|
157
|
+
};
|
|
155
158
|
|
|
156
159
|
export const get = async (url, contextDoc = nil) => {
|
|
157
|
-
const resolvedUrl =
|
|
158
|
-
const id =
|
|
159
|
-
const fragment =
|
|
160
|
+
const resolvedUrl = resolveUri(url, contextDoc.id);
|
|
161
|
+
const id = toAbsoluteUri(resolvedUrl);
|
|
162
|
+
const fragment = uriFragment(resolvedUrl);
|
|
160
163
|
|
|
161
164
|
if (!hasStoredSchema(id)) {
|
|
162
165
|
const response = await fetch(id, { headers: { Accept: "application/schema+json" } });
|
|
@@ -168,7 +171,7 @@ export const get = async (url, contextDoc = nil) => {
|
|
|
168
171
|
const [schema, contextDialectId] = await MediaTypes.parse(response);
|
|
169
172
|
|
|
170
173
|
// Try to determine the dialect from the meta-schema if it isn't already known
|
|
171
|
-
const dialectId =
|
|
174
|
+
const dialectId = toAbsoluteUri(schema.$schema || contextDialectId || defaultDialectId);
|
|
172
175
|
if (!Keywords.hasDialect(dialectId) && !hasStoredSchema(dialectId)) {
|
|
173
176
|
await get(dialectId);
|
|
174
177
|
}
|
|
@@ -177,12 +180,12 @@ export const get = async (url, contextDoc = nil) => {
|
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
const storedSchema = getStoredSchema(id);
|
|
180
|
-
const pointer = fragment[0]
|
|
181
|
-
const doc =
|
|
183
|
+
const pointer = fragment[0] === "/" ? fragment : getAnchorPointer(storedSchema, fragment);
|
|
184
|
+
const doc = {
|
|
182
185
|
...storedSchema,
|
|
183
186
|
pointer: pointer,
|
|
184
187
|
value: JsonPointer.get(pointer, storedSchema.schema)
|
|
185
|
-
}
|
|
188
|
+
};
|
|
186
189
|
|
|
187
190
|
return followReferences(doc);
|
|
188
191
|
};
|
|
@@ -205,13 +208,12 @@ export const typeOf = (doc, type) => jsonTypeOf(value(doc), type);
|
|
|
205
208
|
|
|
206
209
|
export const step = (key, doc) => {
|
|
207
210
|
const storedSchema = getStoredSchema(doc.id);
|
|
208
|
-
|
|
211
|
+
return followReferences({
|
|
209
212
|
...doc,
|
|
210
213
|
pointer: JsonPointer.append(`${key}`, doc.pointer),
|
|
211
214
|
value: value(doc)[key],
|
|
212
215
|
validated: storedSchema.validated
|
|
213
216
|
});
|
|
214
|
-
return followReferences(nextDoc);
|
|
215
217
|
};
|
|
216
218
|
|
|
217
219
|
export const keys = (doc) => Object.keys(value(doc));
|
|
@@ -254,16 +256,14 @@ export const toSchema = (schemaDoc, options = {}) => {
|
|
|
254
256
|
|
|
255
257
|
const dynamicAnchors = {};
|
|
256
258
|
for (const anchor in schemaDoc.dynamicAnchors) {
|
|
257
|
-
const pointer =
|
|
259
|
+
const pointer = uriFragment(schemaDoc.dynamicAnchors[anchor]);
|
|
258
260
|
dynamicAnchors[pointer] = anchor;
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
const schema = JSON.parse(Json.stringify(schemaDoc.schema, (key, value, pointer) => {
|
|
262
264
|
if (Reference.isReference(value)) {
|
|
263
265
|
const refValue = Reference.value(value);
|
|
264
|
-
if (
|
|
265
|
-
return;
|
|
266
|
-
} else {
|
|
266
|
+
if (fullOptions.includeEmbedded || !(idToken in refValue)) {
|
|
267
267
|
return Reference.value(value);
|
|
268
268
|
}
|
|
269
269
|
} else {
|
|
@@ -292,8 +292,8 @@ export const toSchema = (schemaDoc, options = {}) => {
|
|
|
292
292
|
const id = relativeUri(fullOptions.parentId, schemaDoc.id);
|
|
293
293
|
const dialect = fullOptions.parentDialect === schemaDoc.dialectId ? "" : schemaDoc.dialectId;
|
|
294
294
|
return {
|
|
295
|
-
...
|
|
296
|
-
...
|
|
295
|
+
...id && { [idToken]: id },
|
|
296
|
+
...dialect && { $schema: dialect },
|
|
297
297
|
...schema
|
|
298
298
|
};
|
|
299
299
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperjump/json-schema",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"clean": "xargs -a .gitignore rm -rf",
|
|
25
|
-
"lint": "eslint lib",
|
|
26
|
-
"test": "mocha 'lib/**/*.spec.ts' 'stable/**/*.spec.ts' 'draft-*/**/*.spec.ts' '
|
|
25
|
+
"lint": "eslint lib stable draft-* openapi-*",
|
|
26
|
+
"test": "mocha 'lib/**/*.spec.ts' 'stable/**/*.spec.ts' 'draft-*/**/*.spec.ts' 'openapi-*/**/*.spec.ts'"
|
|
27
27
|
},
|
|
28
28
|
"repository": "github:hyperjump-io/json-schema",
|
|
29
29
|
"keywords": [
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"@hyperjump/json": "^0.1.0",
|
|
65
65
|
"@hyperjump/json-pointer": "^0.9.5",
|
|
66
66
|
"@hyperjump/pact": "^0.2.4",
|
|
67
|
+
"@hyperjump/uri": "^1.0.0",
|
|
67
68
|
"content-type": "^1.0.4",
|
|
68
69
|
"fastest-stable-stringify": "^2.0.2",
|
|
69
70
|
"node-fetch": "^3.3.0"
|