json-schema-library 11.1.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/.mocharc.js +1 -0
- package/CHANGELOG.md +11 -0
- package/README.md +21 -13
- 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-jlib.ts +150 -0
- package/bowtie/bowtie.test.ts +267 -0
- package/bowtie/package.json +11 -0
- package/bowtie/tsconfig.json +12 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +66 -503
- package/dist/index.d.mts +66 -503
- 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 +0 -3
- package/package.json +14 -8
- package/src/Draft.ts +1 -1
- package/src/Keyword.ts +2 -3
- package/src/SchemaNode.ts +9 -0
- package/src/compileSchema.test.ts +52 -0
- package/src/compileSchema.ts +53 -3
- 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/draft04.ts +2 -2
- package/src/draft06/keywords/$ref.ts +15 -5
- package/src/draft06.ts +2 -2
- package/src/draft07.ts +2 -2
- package/src/draft2019-09/keywords/$ref.test.ts +3 -1
- package/src/draft2019-09/keywords/$ref.ts +44 -30
- 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/draft2019.ts +1 -1
- package/src/draft2020.ts +1 -1
- package/src/errors/errors.ts +4 -0
- package/src/formats/formats.ts +35 -28
- package/src/formats/hyperjump.d.ts +172 -0
- package/src/keywords/$ref.ts +50 -17
- package/src/keywords/oneOf.test.ts +3 -3
- package/src/keywords/properties.ts +1 -1
- package/src/keywords/propertyDependencies.ts +1 -1
- package/src/methods/getData.ts +1 -1
- package/src/settings.ts +27 -1
- package/src/validateNode.ts +4 -1
- 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
|
@@ -3,11 +3,13 @@ import { SchemaNode } from "../../types";
|
|
|
3
3
|
import { isObject } from "../../utils/isObject";
|
|
4
4
|
import { validateNode } from "../../validateNode";
|
|
5
5
|
|
|
6
|
+
const KEYWORD = "items";
|
|
7
|
+
|
|
6
8
|
export const itemsKeyword: Keyword = {
|
|
7
|
-
id:
|
|
8
|
-
keyword:
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
9
11
|
parse: parseItems,
|
|
10
|
-
addResolve: (node) => (node.prefixItems || node
|
|
12
|
+
addResolve: (node) => (node.prefixItems || node[KEYWORD]) != null,
|
|
11
13
|
resolve: itemsResolver,
|
|
12
14
|
addValidate: ({ schema }) => schema.items != null,
|
|
13
15
|
validate: validateItems
|
|
@@ -24,18 +26,38 @@ function itemsResolver({ node, key }: JsonSchemaResolverParams) {
|
|
|
24
26
|
|
|
25
27
|
export function parseItems(node: SchemaNode) {
|
|
26
28
|
const { schema, evaluationPath } = node;
|
|
27
|
-
|
|
29
|
+
const items = schema[KEYWORD];
|
|
30
|
+
if (items == null || typeof items === "boolean") {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (isObject(items)) {
|
|
28
35
|
const propertyNode = node.compileSchema(
|
|
29
|
-
|
|
30
|
-
`${evaluationPath}
|
|
31
|
-
`${node.schemaLocation}
|
|
36
|
+
items,
|
|
37
|
+
`${evaluationPath}/${KEYWORD}`,
|
|
38
|
+
`${node.schemaLocation}/${KEYWORD}`
|
|
32
39
|
);
|
|
33
40
|
node.items = propertyNode;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (Array.isArray(items)) {
|
|
45
|
+
node.prefixItems = items.map((itemSchema, index) =>
|
|
46
|
+
node.compileSchema(
|
|
47
|
+
itemSchema,
|
|
48
|
+
`${evaluationPath}/${KEYWORD}/${index}`,
|
|
49
|
+
`${node.schemaLocation}/${KEYWORD}/${index}`
|
|
50
|
+
)
|
|
37
51
|
);
|
|
52
|
+
return;
|
|
38
53
|
}
|
|
54
|
+
|
|
55
|
+
return node.createError("schema-error", {
|
|
56
|
+
pointer: evaluationPath,
|
|
57
|
+
schema,
|
|
58
|
+
value: undefined,
|
|
59
|
+
message: `Keyword '${KEYWORD}' must be an object or array - received '${typeof items}'`
|
|
60
|
+
});
|
|
39
61
|
}
|
|
40
62
|
|
|
41
63
|
function validateItems({ node, data, pointer = "#", path }: JsonSchemaValidatorParams) {
|
|
@@ -3,27 +3,40 @@ import { SchemaNode } from "../../types";
|
|
|
3
3
|
import { Keyword, JsonSchemaValidatorParams, ValidationReturnType } from "../../Keyword";
|
|
4
4
|
import { validateNode } from "../../validateNode";
|
|
5
5
|
|
|
6
|
+
const KEYWORD = "unevaluatedItems";
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* @draft >= 2019-09
|
|
8
10
|
* Similar to additionalItems, but can "see" into subschemas and across references
|
|
9
11
|
* https://json-schema.org/draft/2019-09/json-schema-core#rfc.section.9.3.1.3
|
|
10
12
|
*/
|
|
11
13
|
export const unevaluatedItemsKeyword: Keyword = {
|
|
12
|
-
id:
|
|
13
|
-
keyword:
|
|
14
|
+
id: KEYWORD,
|
|
15
|
+
keyword: KEYWORD,
|
|
14
16
|
parse: parseUnevaluatedItems,
|
|
15
|
-
addValidate: ({ schema }) => schema
|
|
17
|
+
addValidate: ({ schema }) => schema[KEYWORD] != null,
|
|
16
18
|
validate: validateUnevaluatedItems
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
export function parseUnevaluatedItems(node: SchemaNode) {
|
|
20
|
-
|
|
22
|
+
const { unevaluatedItems } = node.schema;
|
|
23
|
+
if (unevaluatedItems == null || typeof unevaluatedItems === "boolean") {
|
|
21
24
|
return;
|
|
22
25
|
}
|
|
26
|
+
|
|
27
|
+
if (!isObject(unevaluatedItems)) {
|
|
28
|
+
return node.createError("schema-error", {
|
|
29
|
+
pointer: node.evaluationPath,
|
|
30
|
+
schema: node.schema,
|
|
31
|
+
value: undefined,
|
|
32
|
+
message: `Keyword '${KEYWORD}' must be an object - received '${typeof unevaluatedItems}'`
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
node.unevaluatedItems = node.compileSchema(
|
|
24
37
|
node.schema.unevaluatedItems,
|
|
25
|
-
`${node.evaluationPath}
|
|
26
|
-
`${node.schemaLocation}
|
|
38
|
+
`${node.evaluationPath}/${KEYWORD}`,
|
|
39
|
+
`${node.schemaLocation}/${KEYWORD}`
|
|
27
40
|
);
|
|
28
41
|
}
|
|
29
42
|
|
|
@@ -160,7 +160,7 @@ export function getData(node: SchemaNode, data?: unknown, opts?: TemplateOptions
|
|
|
160
160
|
return defaultData;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
if (resolvedNode
|
|
163
|
+
if (isSchemaNode(resolvedNode)) {
|
|
164
164
|
defaultData = resolvedNode.getData(defaultData, opts) ?? defaultData;
|
|
165
165
|
currentNode = resolvedNode;
|
|
166
166
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { compileSchema as _compileSchema, type CompileOptions } from "../compileSchema";
|
|
2
|
+
import { strict as assert } from "assert";
|
|
3
|
+
import { draft2019 } from "../draft2019";
|
|
4
|
+
import { JsonSchema } from "../types";
|
|
5
|
+
|
|
6
|
+
const drafts = [draft2019];
|
|
7
|
+
function compileSchema(schema: JsonSchema, options: CompileOptions = {}) {
|
|
8
|
+
return _compileSchema(schema, { ...options, drafts });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("validateSchema (2019-09)", () => {
|
|
12
|
+
it("should error if `additionalItems` is of an invalid type", () => {
|
|
13
|
+
const { schemaErrors } = compileSchema({ additionalItems: [] });
|
|
14
|
+
assert.equal(schemaErrors?.length, 1);
|
|
15
|
+
});
|
|
16
|
+
it("should create annotation for `additionalItems` where items-array is missing", () => {
|
|
17
|
+
const { schemaAnnotations } = compileSchema({ additionalItems: true });
|
|
18
|
+
assert.equal(schemaAnnotations?.length, 1);
|
|
19
|
+
});
|
|
20
|
+
it("should error if `items` is of an invalid type", () => {
|
|
21
|
+
const { schemaErrors } = compileSchema({ items: 999 });
|
|
22
|
+
assert.equal(schemaErrors?.length, 1);
|
|
23
|
+
});
|
|
24
|
+
it("should error if `unevaluatedItems` is not an object or boolean", () => {
|
|
25
|
+
const { schemaErrors } = compileSchema({ unevaluatedItems: [] });
|
|
26
|
+
assert.equal(schemaErrors?.length, 1);
|
|
27
|
+
});
|
|
28
|
+
});
|
package/src/draft2019.ts
CHANGED
|
@@ -60,7 +60,7 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
|
|
|
60
60
|
*/
|
|
61
61
|
export const draft2019 = sanitizeKeywords({
|
|
62
62
|
version: "draft-2019-09",
|
|
63
|
-
$schemaRegEx: "draft[/-]2019
|
|
63
|
+
$schemaRegEx: "draft[/-]2019-?(09)?",
|
|
64
64
|
$schema: "https://json-schema.org/draft/2019-09/schema",
|
|
65
65
|
errors,
|
|
66
66
|
formats,
|
package/src/draft2020.ts
CHANGED
|
@@ -70,7 +70,7 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
|
|
|
70
70
|
*/
|
|
71
71
|
export const draft2020 = sanitizeKeywords({
|
|
72
72
|
version: "draft-2020-12",
|
|
73
|
-
$schemaRegEx: "draft[/-]2020
|
|
73
|
+
$schemaRegEx: "draft[/-]2020-?(12)?",
|
|
74
74
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
75
75
|
errors,
|
|
76
76
|
formats,
|
package/src/errors/errors.ts
CHANGED
|
@@ -19,12 +19,15 @@ export const errors = {
|
|
|
19
19
|
"format-duration-error": "Value `{{value}}` at `{{pointer}}` is not a valid duration",
|
|
20
20
|
"format-email-error": "Value `{{value}}` at `{{pointer}}` is not a valid email",
|
|
21
21
|
"format-hostname-error": "Value `{{value}}` at `{{pointer}}` is not a valid hostname",
|
|
22
|
+
"format-idn-hostname-error": "Value `{{value}}` at `{{pointer}}` is not a valid idn hostname",
|
|
22
23
|
"format-ipv4-error": "Value `{{value}}` at `{{pointer}}` is not a valid IPv4 address",
|
|
23
24
|
"format-ipv4-leading-zero-error":
|
|
24
25
|
"IPv4 addresses starting with zero are invalid, since they are interpreted as octals",
|
|
25
26
|
"format-ipv6-error": "Value `{{value}}` at `{{pointer}}` is not a valid IPv6 address",
|
|
26
27
|
"format-ipv6-leading-zero-error":
|
|
27
28
|
"IPv6 addresses starting with zero are invalid, since they are interpreted as octals",
|
|
29
|
+
"format-iri-error": "Value `{{value}}` at `{{pointer}}` is not a valid iri",
|
|
30
|
+
"format-iri-reference-error": "Value `{{value}}` at `{{pointer}}` is not a valid iri-reference",
|
|
28
31
|
"format-json-pointer-error": "Value `{{value}}` at `{{pointer}}` is not a valid json-pointer",
|
|
29
32
|
"format-regex-error": "Value `{{value}}` at `{{pointer}}` is not a valid regular expression",
|
|
30
33
|
"format-time-error": "Value `{{value}}` at `{{pointer}}` is not a valid time",
|
|
@@ -62,6 +65,7 @@ export const errors = {
|
|
|
62
65
|
"pattern-error": "Value in `{{pointer}}` should match `{{description}}`, but received `{{received}}`",
|
|
63
66
|
"pattern-properties-error":
|
|
64
67
|
"Property `{{key}}` does not match any patterns in `{{pointer}}`. Valid patterns are: {{patterns}}",
|
|
68
|
+
"ref-error": "Could not resolve $ref '{{ref}}' from '{{pointer}}'",
|
|
65
69
|
"required-property-error": "The required property `{{key}}` is missing at `{{pointer}}`",
|
|
66
70
|
/** return schema-warning with createSchemaWarning:true when a valid, but undefined property was found */
|
|
67
71
|
"schema-warning": "Failed retrieving a schema from '{{pointer}}' to key '{{key}}'",
|
package/src/formats/formats.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
/* eslint-disable no-control-regex */
|
|
2
|
-
import {
|
|
2
|
+
import { isAsciiIdn, isUri, isIdnEmail, isIri, isIriReference, isIdn } from "@hyperjump/json-schema-formats";
|
|
3
3
|
import validUrl from "valid-url";
|
|
4
|
+
import { getTypeOf } from "../utils/getTypeOf";
|
|
4
5
|
import { JsonSchemaValidatorParams, ValidationReturnType } from "../Keyword";
|
|
5
|
-
import { parse as parseIdnEmail } from "smtp-address-parser";
|
|
6
6
|
import settings from "../settings";
|
|
7
7
|
|
|
8
8
|
const { REGEX_FLAGS } = settings;
|
|
9
9
|
const isValidIPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
|
|
10
10
|
const isValidIPV6 =
|
|
11
11
|
/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i;
|
|
12
|
-
const isValidHostname =
|
|
13
|
-
/^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/;
|
|
14
12
|
const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
15
13
|
const matchTime =
|
|
16
14
|
/^(?<time>(?:([0-1]\d|2[0-3]):[0-5]\d:(?<second>[0-5]\d|60)))(?:\.\d+)?(?<offset>(?:z|[+-]([0-1]\d|2[0-3])(?::?[0-5]\d)?))$/i;
|
|
@@ -129,33 +127,30 @@ export const formats: Record<string, (options: JsonSchemaValidatorParams) => Val
|
|
|
129
127
|
return undefined;
|
|
130
128
|
},
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
* @draft 7
|
|
134
|
-
* [RFC6531] https://json-schema.org/draft-07/json-schema-validation.html#RFC6531
|
|
135
|
-
*/
|
|
136
|
-
"idn-email": ({ node, pointer, data }) => {
|
|
130
|
+
hostname: ({ node, pointer, data }) => {
|
|
137
131
|
const { schema } = node;
|
|
138
|
-
if (typeof data !== "string" || data
|
|
132
|
+
if (typeof data !== "string" || isAsciiIdn(data)) {
|
|
139
133
|
return undefined;
|
|
140
134
|
}
|
|
141
|
-
|
|
142
|
-
parseIdnEmail(data);
|
|
143
|
-
return undefined;
|
|
144
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
145
|
-
} catch (e) {
|
|
146
|
-
return node.createError("format-email-error", { value: data, pointer, schema });
|
|
147
|
-
}
|
|
135
|
+
return node.createError("format-hostname-error", { value: data, pointer, schema });
|
|
148
136
|
},
|
|
149
137
|
|
|
150
|
-
hostname: ({ node, pointer, data }) => {
|
|
151
|
-
|
|
152
|
-
if (typeof data !== "string") {
|
|
138
|
+
"idn-hostname": ({ node, pointer, data }) => {
|
|
139
|
+
if (typeof data !== "string" || isIdn(data)) {
|
|
153
140
|
return undefined;
|
|
154
141
|
}
|
|
155
|
-
|
|
142
|
+
return node.createError("format-idn-hostname-error", { value: data, pointer, schema: node.schema });
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @draft 7
|
|
147
|
+
* [RFC6531] https://json-schema.org/draft-07/json-schema-validation.html#RFC6531
|
|
148
|
+
*/
|
|
149
|
+
"idn-email": ({ node, pointer, data }) => {
|
|
150
|
+
if (typeof data !== "string" || data === "" || isIdnEmail(data)) {
|
|
156
151
|
return undefined;
|
|
157
152
|
}
|
|
158
|
-
return node.createError("format-
|
|
153
|
+
return node.createError("format-email-error", { value: data, pointer, schema: node.schema });
|
|
159
154
|
},
|
|
160
155
|
|
|
161
156
|
ipv4: ({ node, pointer, data }) => {
|
|
@@ -188,6 +183,22 @@ export const formats: Record<string, (options: JsonSchemaValidatorParams) => Val
|
|
|
188
183
|
return node.createError("format-ipv6-error", { value: data, pointer, schema });
|
|
189
184
|
},
|
|
190
185
|
|
|
186
|
+
iri: ({ node, pointer, data }) => {
|
|
187
|
+
const { schema } = node;
|
|
188
|
+
if (typeof data !== "string" || data === "" || isIri(data)) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
return node.createError("format-iri-error", { value: data, pointer, schema });
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
"iri-reference": ({ node, pointer, data }) => {
|
|
195
|
+
const { schema } = node;
|
|
196
|
+
if (typeof data !== "string" || data === "" || isIriReference(data)) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
return node.createError("format-iri-reference-error", { value: data, pointer, schema });
|
|
200
|
+
},
|
|
201
|
+
|
|
191
202
|
"json-pointer": ({ node, pointer, data }) => {
|
|
192
203
|
const { schema } = node;
|
|
193
204
|
if (typeof data !== "string" || data === "") {
|
|
@@ -281,14 +292,10 @@ export const formats: Record<string, (options: JsonSchemaValidatorParams) => Val
|
|
|
281
292
|
},
|
|
282
293
|
|
|
283
294
|
uri: ({ node, pointer, data }) => {
|
|
284
|
-
|
|
285
|
-
if (typeof data !== "string" || data === "") {
|
|
286
|
-
return undefined;
|
|
287
|
-
}
|
|
288
|
-
if (validUrl.isUri(data)) {
|
|
295
|
+
if (typeof data !== "string" || data === "" || isUri(data)) {
|
|
289
296
|
return undefined;
|
|
290
297
|
}
|
|
291
|
-
return node.createError("format-uri-error", { value: data, pointer, schema });
|
|
298
|
+
return node.createError("format-uri-error", { value: data, pointer, schema: node.schema });
|
|
292
299
|
},
|
|
293
300
|
|
|
294
301
|
"uri-reference": ({ node, pointer, data }) => {
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
declare module "@hyperjump/json-schema-formats" {
|
|
2
|
+
/**
|
|
3
|
+
* The 'date' format. Validates that a string represents a date according to
|
|
4
|
+
* [RFC 3339, section 5.6](https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6).
|
|
5
|
+
*
|
|
6
|
+
* @see [JSON Schema Core, section 7.3.1](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.1)
|
|
7
|
+
*/
|
|
8
|
+
export const isDate: (date: string) => boolean;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The 'time' format. Validates that a string represents a time according to
|
|
12
|
+
* [RFC 3339, section 5.6](https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6).
|
|
13
|
+
*
|
|
14
|
+
* **NOTE**: Leap seconds are only allowed on specific dates. Since there is no date
|
|
15
|
+
* in this context, leap seconds are never allowed.
|
|
16
|
+
*
|
|
17
|
+
* @see [JSON Schema Core, section 7.3.1](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.1)
|
|
18
|
+
*/
|
|
19
|
+
export const isTime: (time: string) => boolean;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The 'date-time' format. Validates that a string represents a date-time
|
|
23
|
+
* according to [RFC 3339, section 5.6](https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6).
|
|
24
|
+
*
|
|
25
|
+
* @see [JSON Schema Core, section 7.3.1](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.1)
|
|
26
|
+
*/
|
|
27
|
+
export const isDateTime: (dateTime: string) => boolean;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The 'duration' format. Validates that a string represents a duration
|
|
31
|
+
* according to [RFC 3339, Appendix A](https://www.rfc-editor.org/rfc/rfc3339.html#appendix-A).
|
|
32
|
+
*
|
|
33
|
+
* @see [JSON Schema Core, section 7.3.1](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.1)
|
|
34
|
+
*/
|
|
35
|
+
export const isDuration: (duration: string) => boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The 'email' format. Validates that a string represents an email as defined by
|
|
39
|
+
* the "Mailbox" ABNF rule in [RFC 5321, section 4.1.2](https://www.rfc-editor.org/rfc/rfc5321.html#section-4.1.2).
|
|
40
|
+
*
|
|
41
|
+
* @see [JSON Schema Core, section 7.3.2](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.2)
|
|
42
|
+
*/
|
|
43
|
+
export const isEmail: (email: string) => boolean;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The 'idn-email' format. Validates that a string represents an email as
|
|
47
|
+
* defined by the "Mailbox" ABNF rule in [RFC 6531, section 3.3](https://www.rfc-editor.org/rfc/rfc6531.html#section-3.3).
|
|
48
|
+
*
|
|
49
|
+
* @see [JSON Schema Core, section 7.3.2](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.2)
|
|
50
|
+
*/
|
|
51
|
+
export const isIdnEmail: (email: string) => boolean;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The 'hostname' format in draft-04 - draft-06. Validates that a string
|
|
55
|
+
* represents a hostname as defined by [RFC 1123, section 2.1](https://www.rfc-editor.org/rfc/rfc1123.html#section-2.1).
|
|
56
|
+
*
|
|
57
|
+
* **NOTE**: The 'hostname' format changed in draft-07. Use {@link isAsciiIdn} for
|
|
58
|
+
* draft-07 and later.
|
|
59
|
+
*
|
|
60
|
+
* @see [JSON Schema Core, section 7.3.3](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.3)
|
|
61
|
+
*/
|
|
62
|
+
export const isHostname: (hostname: string) => boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The 'hostname' format since draft-07. Validates that a string represents an
|
|
66
|
+
* IDNA2008 internationalized domain name consiting of only A-labels and NR-LDH
|
|
67
|
+
* labels as defined by [RFC 5890, section 2.3.2.1](https://www.rfc-editor.org/rfc/rfc5890.html#section-2.3.2.3).
|
|
68
|
+
*
|
|
69
|
+
* **NOTE**: The 'hostname' format changed in draft-07. Use {@link isHostname}
|
|
70
|
+
* for draft-06 and earlier.
|
|
71
|
+
*
|
|
72
|
+
* @see [JSON Schema Core, section 7.3.3](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.3)
|
|
73
|
+
*/
|
|
74
|
+
export const isAsciiIdn: (hostname: string) => boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The 'idn-hostname' format. Validates that a string represents an IDNA2008
|
|
78
|
+
* internationalized domain name as defined by [RFC 5890, section 2.3.2.1](https://www.rfc-editor.org/rfc/rfc5890.html#section-2.3.2.1).
|
|
79
|
+
*
|
|
80
|
+
* @see [JSON Schema Core, section 7.3.3](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.3)
|
|
81
|
+
*/
|
|
82
|
+
export const isIdn: (hostname: string) => boolean;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The 'ipv4' format. Validates that a string represents an IPv4 address
|
|
86
|
+
* according to the "dotted-quad" ABNF syntax as defined in
|
|
87
|
+
* [RFC 2673, section 3.2](https://www.rfc-editor.org/rfc/rfc2673.html#section-3.2).
|
|
88
|
+
*
|
|
89
|
+
* @see [JSON Schema Core, section 7.3.4](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.4)
|
|
90
|
+
*/
|
|
91
|
+
export const isIPv4: (ip: string) => boolean;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The 'ipv6' format. Validates that a string represents an IPv6 address as
|
|
95
|
+
* defined in [RFC 4291, section 2.2](https://www.rfc-editor.org/rfc/rfc4291.html#section-2.2).
|
|
96
|
+
*
|
|
97
|
+
* @see [JSON Schema Core, section 7.3.4](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.4)
|
|
98
|
+
*/
|
|
99
|
+
export const isIPv6: (ip: string) => boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The 'uri' format. Validates that a string represents a URI as defined by [RFC
|
|
103
|
+
* 3986](https://www.rfc-editor.org/rfc/rfc3986.html).
|
|
104
|
+
*
|
|
105
|
+
* @see [JSON Schema Core, section 7.3.5](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.5)
|
|
106
|
+
*/
|
|
107
|
+
export const isUri: (uri: string) => boolean;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The 'uri-reference' format. Validates that a string represents a URI
|
|
111
|
+
* Reference as defined by [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986.html).
|
|
112
|
+
*
|
|
113
|
+
* @see [JSON Schema Core, section 7.3.5](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.5)
|
|
114
|
+
*/
|
|
115
|
+
export const isUriReference: (uri: string) => boolean;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The 'iri' format. Validates that a string represents an IRI as defined by
|
|
119
|
+
* [RFC 3987](https://www.rfc-editor.org/rfc/rfc3987.html).
|
|
120
|
+
*
|
|
121
|
+
* @see [JSON Schema Core, section 7.3.5](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.5)
|
|
122
|
+
*/
|
|
123
|
+
export const isIri: (iri: string) => boolean;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The 'iri-reference' format. Validates that a string represents an IRI
|
|
127
|
+
* Reference as defined by [RFC 3987](https://www.rfc-editor.org/rfc/rfc3987.html).
|
|
128
|
+
*
|
|
129
|
+
* @see [JSON Schema Core, section 7.3.5](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.5)
|
|
130
|
+
*/
|
|
131
|
+
export const isIriReference: (iri: string) => boolean;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The 'uuid' format. Validates that a string represents a UUID address as
|
|
135
|
+
* defined by [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122.html).
|
|
136
|
+
*
|
|
137
|
+
* @see [JSON Schema Core, section 7.3.5](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.5)
|
|
138
|
+
*/
|
|
139
|
+
export const isUuid: (uuid: string) => boolean;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The 'uri-template' format. Validates that a string represents a URI Template
|
|
143
|
+
* as defined by [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.html).
|
|
144
|
+
*
|
|
145
|
+
* @see [JSON Schema Core, section 7.3.6](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.6)
|
|
146
|
+
*/
|
|
147
|
+
export const isUriTemplate: (uriTemplate: string) => boolean;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The 'json-pointer' format. Validates that a string represents a JSON Pointer
|
|
151
|
+
* as defined by [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901.html).
|
|
152
|
+
*
|
|
153
|
+
* @see [JSON Schema Core, section 7.3.7](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.7)
|
|
154
|
+
*/
|
|
155
|
+
export const isJsonPointer: (pointer: string) => boolean;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The 'relative-json-pointer' format. Validates that a string represents an IRI
|
|
159
|
+
* Reference as defined by [draft-bhutton-relative-json-pointer-00](https://datatracker.ietf.org/doc/html/draft-bhutton-relative-json-pointer-00).
|
|
160
|
+
*
|
|
161
|
+
* @see [JSON Schema Core, section 7.3.5](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.5)
|
|
162
|
+
*/
|
|
163
|
+
export const isRelativeJsonPointer: (pointer: string) => boolean;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The 'regex' format. Validates that a string represents a regular expression
|
|
167
|
+
* as defined by [ECMA-262](https://262.ecma-international.org/5.1/).
|
|
168
|
+
*
|
|
169
|
+
* @see [JSON Schema Core, section 7.3.8](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-7.3.8)
|
|
170
|
+
*/
|
|
171
|
+
export const isRegex: (regex: string) => boolean;
|
|
172
|
+
}
|
package/src/keywords/$ref.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SchemaNode } from "../types";
|
|
1
|
+
import { isJsonError, isSchemaNode, JsonError, SchemaNode } from "../types";
|
|
2
2
|
import { Keyword, JsonSchemaValidatorParams, ValidationPath, JsonSchemaReducerParams } from "../Keyword";
|
|
3
3
|
import { resolveUri } from "../utils/resolveUri";
|
|
4
4
|
import splitRef from "../utils/splitRef";
|
|
@@ -8,6 +8,7 @@ import { validateNode } from "../validateNode";
|
|
|
8
8
|
import { get, split } from "@sagold/json-pointer";
|
|
9
9
|
import { mergeNode } from "../mergeNode";
|
|
10
10
|
import { pick } from "../utils/pick";
|
|
11
|
+
import settings from "src/settings";
|
|
11
12
|
|
|
12
13
|
export const $refKeyword: Keyword = {
|
|
13
14
|
id: "$ref",
|
|
@@ -93,7 +94,12 @@ export function reduceRef({ node, data, key, pointer, path }: JsonSchemaReducerP
|
|
|
93
94
|
|
|
94
95
|
const resolvedNode = node.resolveRef({ pointer, path });
|
|
95
96
|
if (resolvedNode == null) {
|
|
96
|
-
return
|
|
97
|
+
return node.createError("ref-error", {
|
|
98
|
+
ref: node.schema.$ref ?? node.schema.$dynamicRef,
|
|
99
|
+
pointer,
|
|
100
|
+
schema: node.schema,
|
|
101
|
+
value: data
|
|
102
|
+
});
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
if (resolvedNode.schemaLocation === node.schemaLocation) {
|
|
@@ -107,6 +113,9 @@ export function reduceRef({ node, data, key, pointer, path }: JsonSchemaReducerP
|
|
|
107
113
|
export function resolveRef(this: SchemaNode, { pointer, path = [] }: { pointer?: string; path?: ValidationPath } = {}) {
|
|
108
114
|
if (this.schema.$dynamicRef) {
|
|
109
115
|
const nextNode = resolveRecursiveRef(this, path);
|
|
116
|
+
if (isJsonError(nextNode)) {
|
|
117
|
+
return nextNode;
|
|
118
|
+
}
|
|
110
119
|
path.push({ pointer: pointer!, node: nextNode! });
|
|
111
120
|
return nextNode;
|
|
112
121
|
}
|
|
@@ -116,7 +125,7 @@ export function resolveRef(this: SchemaNode, { pointer, path = [] }: { pointer?:
|
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
const resolvedNode = getRef(this);
|
|
119
|
-
if (resolvedNode
|
|
128
|
+
if (isSchemaNode(resolvedNode)) {
|
|
120
129
|
path.push({ pointer: pointer!, node: resolvedNode });
|
|
121
130
|
}
|
|
122
131
|
|
|
@@ -129,10 +138,16 @@ function validateRef({ node, data, pointer = "#", path }: JsonSchemaValidatorPar
|
|
|
129
138
|
// recursively resolveRef and validate
|
|
130
139
|
return validateNode(nextNode, data, pointer, path);
|
|
131
140
|
}
|
|
141
|
+
return node.createError("ref-error", {
|
|
142
|
+
ref: node.schema.$ref ?? node.schema.$dynamicRef,
|
|
143
|
+
pointer,
|
|
144
|
+
schema: node.schema,
|
|
145
|
+
value: data
|
|
146
|
+
});
|
|
132
147
|
}
|
|
133
148
|
|
|
134
149
|
// 1. https://json-schema.org/draft/2019-09/json-schema-core#scopes
|
|
135
|
-
function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode |
|
|
150
|
+
function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode | JsonError {
|
|
136
151
|
const history = path;
|
|
137
152
|
const refInCurrentScope = resolveUri(node.$id, node.schema.$dynamicRef);
|
|
138
153
|
|
|
@@ -163,14 +178,12 @@ function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode
|
|
|
163
178
|
return getRef(node, refInCurrentScope);
|
|
164
179
|
}
|
|
165
180
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
function compileNext(referencedNode: SchemaNode, sourceNode: SchemaNode) {
|
|
181
|
+
export function compileNext(referencedNode: SchemaNode, sourceNode: SchemaNode) {
|
|
169
182
|
let referencedSchema = referencedNode.schema;
|
|
170
183
|
if (isObject(referencedNode.schema)) {
|
|
171
184
|
referencedSchema = {
|
|
172
185
|
...omit(referencedNode.schema, "$id"),
|
|
173
|
-
...pick(sourceNode.schema, ...PROPERTIES_TO_MERGE)
|
|
186
|
+
...pick(sourceNode.schema, ...settings.PROPERTIES_TO_MERGE)
|
|
174
187
|
};
|
|
175
188
|
}
|
|
176
189
|
return referencedNode.compileSchema(
|
|
@@ -180,7 +193,7 @@ function compileNext(referencedNode: SchemaNode, sourceNode: SchemaNode) {
|
|
|
180
193
|
);
|
|
181
194
|
}
|
|
182
195
|
|
|
183
|
-
export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode |
|
|
196
|
+
export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | JsonError {
|
|
184
197
|
if ($ref == null) {
|
|
185
198
|
return node;
|
|
186
199
|
}
|
|
@@ -202,7 +215,12 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
202
215
|
// check for remote-host + pointer pair to switch rootSchema
|
|
203
216
|
const fragments = splitRef($ref);
|
|
204
217
|
if (fragments.length === 0) {
|
|
205
|
-
return
|
|
218
|
+
return node.createError("ref-error", {
|
|
219
|
+
ref: $ref,
|
|
220
|
+
pointer: node.evaluationPath,
|
|
221
|
+
schema: node.schema,
|
|
222
|
+
value: undefined
|
|
223
|
+
});
|
|
206
224
|
}
|
|
207
225
|
|
|
208
226
|
// resolve $ref as remote-host
|
|
@@ -212,6 +230,7 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
212
230
|
if (node.context.remotes[$ref]) {
|
|
213
231
|
return compileNext(node.context.remotes[$ref], node);
|
|
214
232
|
}
|
|
233
|
+
|
|
215
234
|
if ($ref[0] === "#") {
|
|
216
235
|
// support refOfUnknownKeyword
|
|
217
236
|
const rootSchema = node.context.rootNode.schema;
|
|
@@ -221,7 +240,12 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
221
240
|
}
|
|
222
241
|
}
|
|
223
242
|
// console.error("REF: UNFOUND 1", $ref);
|
|
224
|
-
return
|
|
243
|
+
return node.createError("ref-error", {
|
|
244
|
+
ref: $ref,
|
|
245
|
+
pointer: node.evaluationPath,
|
|
246
|
+
schema: node.schema,
|
|
247
|
+
value: undefined
|
|
248
|
+
});
|
|
225
249
|
}
|
|
226
250
|
|
|
227
251
|
if (fragments.length === 2) {
|
|
@@ -252,16 +276,25 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
252
276
|
// @ts-expect-error random path
|
|
253
277
|
currentNode = currentNode[property];
|
|
254
278
|
if (currentNode == null) {
|
|
255
|
-
console.error("REF: FAILED RESOLVING ref json-pointer", fragments[1]);
|
|
256
|
-
return
|
|
279
|
+
// console.error("REF: FAILED RESOLVING ref json-pointer", fragments[1]);
|
|
280
|
+
return node.createError("ref-error", {
|
|
281
|
+
ref: $ref,
|
|
282
|
+
pointer: node.evaluationPath,
|
|
283
|
+
schema: node.schema,
|
|
284
|
+
value: undefined,
|
|
285
|
+
host: fragments[0],
|
|
286
|
+
local: fragments[1]
|
|
287
|
+
});
|
|
257
288
|
}
|
|
258
289
|
}
|
|
259
290
|
return currentNode;
|
|
260
291
|
}
|
|
261
|
-
|
|
262
|
-
console.error("REF: UNFOUND 2", $ref);
|
|
263
|
-
return undefined;
|
|
264
292
|
}
|
|
265
293
|
|
|
266
|
-
|
|
294
|
+
return node.createError("ref-error", {
|
|
295
|
+
ref: $ref,
|
|
296
|
+
pointer: node.evaluationPath,
|
|
297
|
+
schema: node.schema,
|
|
298
|
+
value: undefined
|
|
299
|
+
});
|
|
267
300
|
}
|
|
@@ -483,7 +483,7 @@ describe("keyword : oneof-fuzzy : validate", () => {
|
|
|
483
483
|
required: ["type", "children"],
|
|
484
484
|
properties: {
|
|
485
485
|
type: {
|
|
486
|
-
options: { hidden: true },
|
|
486
|
+
"x-options": { hidden: true },
|
|
487
487
|
type: "string",
|
|
488
488
|
const: "parent"
|
|
489
489
|
},
|
|
@@ -500,7 +500,7 @@ describe("keyword : oneof-fuzzy : validate", () => {
|
|
|
500
500
|
required: ["type"],
|
|
501
501
|
properties: {
|
|
502
502
|
type: {
|
|
503
|
-
options: { hidden: true },
|
|
503
|
+
"x-options": { hidden: true },
|
|
504
504
|
type: "string",
|
|
505
505
|
const: "one"
|
|
506
506
|
}
|
|
@@ -512,7 +512,7 @@ describe("keyword : oneof-fuzzy : validate", () => {
|
|
|
512
512
|
required: ["type"],
|
|
513
513
|
properties: {
|
|
514
514
|
type: {
|
|
515
|
-
options: { hidden: true },
|
|
515
|
+
"x-options": { hidden: true },
|
|
516
516
|
type: "string",
|
|
517
517
|
const: "two"
|
|
518
518
|
}
|