json-schema-library 11.0.5 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.js +1 -0
- package/CHANGELOG.md +17 -0
- package/README.md +120 -0
- package/bowtie/.editorconfig +21 -0
- package/bowtie/.prettierrc +6 -0
- package/bowtie/BOWTIE.md +54 -0
- package/bowtie/Dockerfile +6 -0
- package/bowtie/bowtie-api.ts +101 -0
- package/bowtie/bowtie.test.ts +76 -0
- package/bowtie/bowtie.ts +156 -0
- package/bowtie/package.json +11 -0
- package/bowtie/tsconfig.json +12 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +39 -470
- package/dist/index.d.mts +39 -470
- package/dist/index.mjs +1 -1
- package/dist/jlib.js +2 -13
- package/dist/remotes/index.cjs +1 -0
- package/dist/remotes/index.d.cts +7 -0
- package/dist/remotes/index.d.mts +7 -0
- package/dist/remotes/index.mjs +1 -0
- package/dist/types-B2wwNWyo.d.cts +513 -0
- package/dist/types-BhTU1l2h.d.mts +513 -0
- package/index.ts +10 -4
- package/package.json +14 -8
- package/src/Keyword.ts +37 -12
- package/src/SchemaNode.ts +84 -16
- package/src/compileSchema.ts +56 -4
- package/src/draft04/keywords/$ref.ts +22 -14
- package/src/draft04/keywords/exclusiveMaximum.ts +14 -0
- package/src/draft04/keywords/exclusiveMinimum.ts +14 -0
- package/src/draft04/validateSchema.test.ts +20 -0
- package/src/draft06/keywords/$ref.ts +15 -5
- package/src/draft2019-09/keywords/$ref.test.ts +3 -1
- package/src/draft2019-09/keywords/$ref.ts +40 -16
- package/src/draft2019-09/keywords/additionalItems.ts +33 -10
- package/src/draft2019-09/keywords/items.ts +32 -10
- package/src/draft2019-09/keywords/unevaluatedItems.ts +19 -6
- package/src/draft2019-09/methods/getData.ts +1 -1
- package/src/draft2019-09/validateSchema.test.ts +28 -0
- package/src/errors/errors.ts +8 -1
- package/src/formats/formats.ts +35 -28
- package/src/formats/hyperjump.d.ts +172 -0
- package/src/keywords/$defs.ts +34 -8
- package/src/keywords/$ref.ts +59 -13
- package/src/keywords/additionalProperties.ts +19 -8
- package/src/keywords/allOf.ts +44 -18
- package/src/keywords/anyOf.ts +38 -19
- package/src/keywords/contains.ts +21 -9
- package/src/keywords/dependencies.ts +37 -17
- package/src/keywords/dependentRequired.ts +56 -38
- package/src/keywords/dependentSchemas.ts +37 -13
- package/src/keywords/deprecated.ts +32 -8
- package/src/keywords/enum.ts +30 -8
- package/src/keywords/exclusiveMaximum.ts +21 -2
- package/src/keywords/exclusiveMinimum.ts +22 -3
- package/src/keywords/format.ts +21 -2
- package/src/keywords/ifthenelse.ts +49 -5
- package/src/keywords/items.ts +27 -13
- package/src/keywords/maxItems.ts +22 -2
- package/src/keywords/maxLength.ts +30 -9
- package/src/keywords/maxProperties.ts +30 -9
- package/src/keywords/maximum.ts +28 -8
- package/src/keywords/minItems.ts +30 -9
- package/src/keywords/minLength.ts +30 -9
- package/src/keywords/minProperties.ts +26 -5
- package/src/keywords/minimum.ts +32 -13
- package/src/keywords/multipleOf.ts +33 -12
- package/src/keywords/not.ts +23 -10
- package/src/keywords/oneOf.ts +29 -9
- package/src/keywords/pattern.ts +35 -9
- package/src/keywords/properties.ts +34 -11
- package/src/keywords/propertyDependencies.test.ts +180 -0
- package/src/keywords/propertyDependencies.ts +173 -0
- package/src/keywords/propertyNames.ts +26 -14
- package/src/keywords/required.ts +31 -8
- package/src/keywords/type.ts +53 -16
- package/src/keywords/unevaluatedItems.ts +24 -8
- package/src/keywords/unevaluatedProperties.ts +24 -7
- package/src/keywords/uniqueItems.ts +23 -4
- package/src/mergeNode.ts +9 -4
- package/src/methods/getData.ts +1 -1
- package/src/settings.ts +2 -1
- package/src/types.ts +1 -1
- package/src/utils/isListOfStrings.ts +3 -0
- package/src/validate.test.ts +0 -2
- package/src/validateNode.ts +6 -3
- package/src/validateSchema.test.ts +312 -0
- package/tsconfig.json +11 -4
- package/tsconfig.test.json +9 -2
- package/tsdown.config.ts +1 -1
- package/Dockerfile +0 -6
- package/bowtie_jlib.js +0 -140
- package/remotes/draft04.json +0 -150
- package/remotes/draft06.json +0 -155
- package/remotes/draft07.json +0 -155
- package/remotes/draft2019-09.json +0 -42
- package/remotes/draft2019-09_meta_applicator.json +0 -53
- package/remotes/draft2019-09_meta_content.json +0 -14
- package/remotes/draft2019-09_meta_core.json +0 -54
- package/remotes/draft2019-09_meta_format.json +0 -11
- package/remotes/draft2019-09_meta_meta-data.json +0 -34
- package/remotes/draft2019-09_meta_validation.json +0 -95
- package/remotes/draft2020-12.json +0 -55
- package/remotes/draft2020-12_meta_applicator.json +0 -45
- package/remotes/draft2020-12_meta_content.json +0 -14
- package/remotes/draft2020-12_meta_core.json +0 -48
- package/remotes/draft2020-12_meta_format_annotation.json +0 -11
- package/remotes/draft2020-12_meta_format_assertion.json +0 -11
- package/remotes/draft2020-12_meta_meta_data.json +0 -34
- package/remotes/draft2020-12_meta_unevaluated.json +0 -12
- package/remotes/draft2020-12_meta_validation.json +0 -87
- package/remotes/index.ts +0 -48
package/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";
|
|
@@ -72,6 +72,18 @@ export function parseRef(node: SchemaNode) {
|
|
|
72
72
|
node.$ref = `#${node.$ref}`;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
// validate simple ref to definitions
|
|
77
|
+
if (node.$ref?.startsWith("#/$defs/")) {
|
|
78
|
+
if (get(node.getNodeRoot().schema, node.$ref) == null) {
|
|
79
|
+
return node.createError("schema-error", {
|
|
80
|
+
pointer: `${node.schemaLocation}/$ref`,
|
|
81
|
+
schema: node.schema,
|
|
82
|
+
value: node.schema.$ref,
|
|
83
|
+
message: `Invalid $ref to missing target '${node.schema.ref}'`
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
export function reduceRef({ node, data, key, pointer, path }: JsonSchemaReducerParams) {
|
|
@@ -81,7 +93,12 @@ export function reduceRef({ node, data, key, pointer, path }: JsonSchemaReducerP
|
|
|
81
93
|
|
|
82
94
|
const resolvedNode = node.resolveRef({ pointer, path });
|
|
83
95
|
if (resolvedNode == null) {
|
|
84
|
-
return
|
|
96
|
+
return node.createError("ref-error", {
|
|
97
|
+
ref: node.schema.$ref ?? node.schema.$dynamicRef,
|
|
98
|
+
pointer,
|
|
99
|
+
schema: node.schema,
|
|
100
|
+
value: data
|
|
101
|
+
});
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
if (resolvedNode.schemaLocation === node.schemaLocation) {
|
|
@@ -95,6 +112,9 @@ export function reduceRef({ node, data, key, pointer, path }: JsonSchemaReducerP
|
|
|
95
112
|
export function resolveRef(this: SchemaNode, { pointer, path = [] }: { pointer?: string; path?: ValidationPath } = {}) {
|
|
96
113
|
if (this.schema.$dynamicRef) {
|
|
97
114
|
const nextNode = resolveRecursiveRef(this, path);
|
|
115
|
+
if (isJsonError(nextNode)) {
|
|
116
|
+
return nextNode;
|
|
117
|
+
}
|
|
98
118
|
path.push({ pointer: pointer!, node: nextNode! });
|
|
99
119
|
return nextNode;
|
|
100
120
|
}
|
|
@@ -104,7 +124,7 @@ export function resolveRef(this: SchemaNode, { pointer, path = [] }: { pointer?:
|
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
const resolvedNode = getRef(this);
|
|
107
|
-
if (resolvedNode
|
|
127
|
+
if (isSchemaNode(resolvedNode)) {
|
|
108
128
|
path.push({ pointer: pointer!, node: resolvedNode });
|
|
109
129
|
}
|
|
110
130
|
|
|
@@ -117,10 +137,16 @@ function validateRef({ node, data, pointer = "#", path }: JsonSchemaValidatorPar
|
|
|
117
137
|
// recursively resolveRef and validate
|
|
118
138
|
return validateNode(nextNode, data, pointer, path);
|
|
119
139
|
}
|
|
140
|
+
return node.createError("ref-error", {
|
|
141
|
+
ref: node.schema.$ref ?? node.schema.$dynamicRef,
|
|
142
|
+
pointer,
|
|
143
|
+
schema: node.schema,
|
|
144
|
+
value: data
|
|
145
|
+
});
|
|
120
146
|
}
|
|
121
147
|
|
|
122
148
|
// 1. https://json-schema.org/draft/2019-09/json-schema-core#scopes
|
|
123
|
-
function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode |
|
|
149
|
+
function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode | JsonError {
|
|
124
150
|
const history = path;
|
|
125
151
|
const refInCurrentScope = resolveUri(node.$id, node.schema.$dynamicRef);
|
|
126
152
|
|
|
@@ -168,7 +194,7 @@ function compileNext(referencedNode: SchemaNode, sourceNode: SchemaNode) {
|
|
|
168
194
|
);
|
|
169
195
|
}
|
|
170
196
|
|
|
171
|
-
export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode |
|
|
197
|
+
export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | JsonError {
|
|
172
198
|
if ($ref == null) {
|
|
173
199
|
return node;
|
|
174
200
|
}
|
|
@@ -190,7 +216,12 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
190
216
|
// check for remote-host + pointer pair to switch rootSchema
|
|
191
217
|
const fragments = splitRef($ref);
|
|
192
218
|
if (fragments.length === 0) {
|
|
193
|
-
return
|
|
219
|
+
return node.createError("ref-error", {
|
|
220
|
+
ref: $ref,
|
|
221
|
+
pointer: node.evaluationPath,
|
|
222
|
+
schema: node.schema,
|
|
223
|
+
value: undefined
|
|
224
|
+
});
|
|
194
225
|
}
|
|
195
226
|
|
|
196
227
|
// resolve $ref as remote-host
|
|
@@ -200,6 +231,7 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
200
231
|
if (node.context.remotes[$ref]) {
|
|
201
232
|
return compileNext(node.context.remotes[$ref], node);
|
|
202
233
|
}
|
|
234
|
+
|
|
203
235
|
if ($ref[0] === "#") {
|
|
204
236
|
// support refOfUnknownKeyword
|
|
205
237
|
const rootSchema = node.context.rootNode.schema;
|
|
@@ -209,7 +241,12 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
209
241
|
}
|
|
210
242
|
}
|
|
211
243
|
// console.error("REF: UNFOUND 1", $ref);
|
|
212
|
-
return
|
|
244
|
+
return node.createError("ref-error", {
|
|
245
|
+
ref: $ref,
|
|
246
|
+
pointer: node.evaluationPath,
|
|
247
|
+
schema: node.schema,
|
|
248
|
+
value: undefined
|
|
249
|
+
});
|
|
213
250
|
}
|
|
214
251
|
|
|
215
252
|
if (fragments.length === 2) {
|
|
@@ -240,16 +277,25 @@ export function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | undefi
|
|
|
240
277
|
// @ts-expect-error random path
|
|
241
278
|
currentNode = currentNode[property];
|
|
242
279
|
if (currentNode == null) {
|
|
243
|
-
console.error("REF: FAILED RESOLVING ref json-pointer", fragments[1]);
|
|
244
|
-
return
|
|
280
|
+
// console.error("REF: FAILED RESOLVING ref json-pointer", fragments[1]);
|
|
281
|
+
return node.createError("ref-error", {
|
|
282
|
+
ref: $ref,
|
|
283
|
+
pointer: node.evaluationPath,
|
|
284
|
+
schema: node.schema,
|
|
285
|
+
value: undefined,
|
|
286
|
+
host: fragments[0],
|
|
287
|
+
local: fragments[1]
|
|
288
|
+
});
|
|
245
289
|
}
|
|
246
290
|
}
|
|
247
291
|
return currentNode;
|
|
248
292
|
}
|
|
249
|
-
|
|
250
|
-
console.error("REF: UNFOUND 2", $ref);
|
|
251
|
-
return undefined;
|
|
252
293
|
}
|
|
253
294
|
|
|
254
|
-
|
|
295
|
+
return node.createError("ref-error", {
|
|
296
|
+
ref: $ref,
|
|
297
|
+
pointer: node.evaluationPath,
|
|
298
|
+
schema: node.schema,
|
|
299
|
+
value: undefined
|
|
300
|
+
});
|
|
255
301
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import settings from "../settings";
|
|
2
2
|
import { isObject } from "../utils/isObject";
|
|
3
|
-
import { Keyword, JsonSchemaResolverParams, JsonSchemaValidatorParams, ValidationReturnType} from "../Keyword";
|
|
4
|
-
import { SchemaNode } from "../types";
|
|
3
|
+
import { Keyword, JsonSchemaResolverParams, JsonSchemaValidatorParams, ValidationReturnType } from "../Keyword";
|
|
4
|
+
import { isBooleanSchema, SchemaNode } from "../types";
|
|
5
5
|
import { getValue } from "../utils/getValue";
|
|
6
6
|
import { validateNode } from "../validateNode";
|
|
7
7
|
|
|
@@ -24,13 +24,24 @@ export const additionalPropertiesKeyword: Keyword = {
|
|
|
24
24
|
// must come as last resolver
|
|
25
25
|
export function parseAdditionalProperties(node: SchemaNode) {
|
|
26
26
|
const { schema, evaluationPath, schemaLocation } = node;
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
schema.additionalProperties,
|
|
30
|
-
`${evaluationPath}/additionalProperties`,
|
|
31
|
-
`${schemaLocation}/additionalProperties`
|
|
32
|
-
);
|
|
27
|
+
if (schema.additionalProperties == null || isBooleanSchema(schema.additionalProperties)) {
|
|
28
|
+
return;
|
|
33
29
|
}
|
|
30
|
+
|
|
31
|
+
if (!isObject(schema.additionalProperties)) {
|
|
32
|
+
return node.createError("schema-error", {
|
|
33
|
+
pointer: node.schemaLocation,
|
|
34
|
+
schema,
|
|
35
|
+
value: schema.additionalProperties,
|
|
36
|
+
message: `keyword 'additionalProperties' must be a valid JSON Schema - receoved: ${typeof schema.additionalProperties}`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
node.additionalProperties = node.compileSchema(
|
|
41
|
+
schema.additionalProperties,
|
|
42
|
+
`${evaluationPath}/additionalProperties`,
|
|
43
|
+
`${schemaLocation}/additionalProperties`
|
|
44
|
+
);
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
function additionalPropertyResolver({ node, data, key }: JsonSchemaResolverParams) {
|
package/src/keywords/allOf.ts
CHANGED
|
@@ -1,29 +1,55 @@
|
|
|
1
1
|
import { mergeSchema } from "../utils/mergeSchema";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Keyword,
|
|
4
|
+
JsonSchemaReducerParams,
|
|
5
|
+
JsonSchemaValidatorParams,
|
|
6
|
+
ValidationReturnType,
|
|
7
|
+
ValidationAnnotation
|
|
8
|
+
} from "../Keyword";
|
|
3
9
|
import { SchemaNode } from "../types";
|
|
4
10
|
import { validateNode } from "../validateNode";
|
|
5
11
|
|
|
12
|
+
const KEYWORD = "allOf";
|
|
13
|
+
|
|
6
14
|
export const allOfKeyword: Keyword = {
|
|
7
|
-
id:
|
|
8
|
-
keyword:
|
|
15
|
+
id: KEYWORD,
|
|
16
|
+
keyword: KEYWORD,
|
|
9
17
|
parse: parseAllOf,
|
|
10
|
-
addReduce: (node: SchemaNode) => node
|
|
18
|
+
addReduce: (node: SchemaNode) => node[KEYWORD] != null,
|
|
11
19
|
reduce: reduceAllOf,
|
|
12
|
-
addValidate: (node) => node
|
|
20
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
13
21
|
validate: validateAllOf
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
export function parseAllOf(node: SchemaNode) {
|
|
17
|
-
const { schema, evaluationPath } = node;
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
const { schema, evaluationPath, schemaLocation } = node;
|
|
26
|
+
if (schema[KEYWORD] == null) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(schema[KEYWORD])) {
|
|
30
|
+
return node.createError("schema-error", {
|
|
31
|
+
pointer: schemaLocation,
|
|
32
|
+
schema,
|
|
33
|
+
value: schema[KEYWORD],
|
|
34
|
+
message: `Keyword '${KEYWORD}' must be an array - received '${typeof schema[KEYWORD]}'`
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (schema[KEYWORD].length === 0) {
|
|
38
|
+
return;
|
|
22
39
|
}
|
|
40
|
+
|
|
41
|
+
node[KEYWORD] = schema[KEYWORD].map((s, index) =>
|
|
42
|
+
node.compileSchema(s, `${evaluationPath}/${KEYWORD}/${index}`, `${schemaLocation}/${KEYWORD}/${index}`)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return node[KEYWORD].reduce((errors, node) => {
|
|
46
|
+
if (node.schemaValidation) errors.push(...node.schemaValidation);
|
|
47
|
+
return errors;
|
|
48
|
+
}, [] as ValidationAnnotation[]);
|
|
23
49
|
}
|
|
24
50
|
|
|
25
51
|
function reduceAllOf({ node, data, key, pointer, path }: JsonSchemaReducerParams) {
|
|
26
|
-
if (node
|
|
52
|
+
if (node[KEYWORD] == null) {
|
|
27
53
|
return;
|
|
28
54
|
}
|
|
29
55
|
|
|
@@ -31,15 +57,15 @@ function reduceAllOf({ node, data, key, pointer, path }: JsonSchemaReducerParams
|
|
|
31
57
|
// dynamic schema parts
|
|
32
58
|
let mergedSchema = {};
|
|
33
59
|
let dynamicId = "";
|
|
34
|
-
for (let i = 0; i < node.
|
|
35
|
-
const { node: schemaNode } = node
|
|
60
|
+
for (let i = 0; i < node[KEYWORD].length; i += 1) {
|
|
61
|
+
const { node: schemaNode } = node[KEYWORD][i].reduceNode(data, { key, pointer, path });
|
|
36
62
|
if (schemaNode) {
|
|
37
63
|
const nestedDynamicId = schemaNode.dynamicId?.replace(node.dynamicId, "") ?? "";
|
|
38
|
-
const localDynamicId = nestedDynamicId === "" ?
|
|
64
|
+
const localDynamicId = nestedDynamicId === "" ? `${KEYWORD}/${i}` : nestedDynamicId;
|
|
39
65
|
dynamicId += `${dynamicId === "" ? "" : ","}${localDynamicId}`;
|
|
40
66
|
|
|
41
|
-
const schema = mergeSchema(node
|
|
42
|
-
mergedSchema = mergeSchema(mergedSchema, schema,
|
|
67
|
+
const schema = mergeSchema(node[KEYWORD][i].schema, schemaNode.schema);
|
|
68
|
+
mergedSchema = mergeSchema(mergedSchema, schema, KEYWORD, "contains");
|
|
43
69
|
}
|
|
44
70
|
}
|
|
45
71
|
|
|
@@ -52,11 +78,11 @@ function reduceAllOf({ node, data, key, pointer, path }: JsonSchemaReducerParams
|
|
|
52
78
|
}
|
|
53
79
|
|
|
54
80
|
function validateAllOf({ node, data, pointer, path }: JsonSchemaValidatorParams) {
|
|
55
|
-
if (!Array.isArray(node
|
|
81
|
+
if (!Array.isArray(node[KEYWORD]) || node[KEYWORD].length === 0) {
|
|
56
82
|
return;
|
|
57
83
|
}
|
|
58
84
|
const errors: ValidationReturnType = [];
|
|
59
|
-
node.
|
|
85
|
+
node[KEYWORD].forEach((allOfNode) => {
|
|
60
86
|
errors.push(...validateNode(allOfNode, data, pointer, path));
|
|
61
87
|
});
|
|
62
88
|
return errors;
|
package/src/keywords/anyOf.ts
CHANGED
|
@@ -1,45 +1,64 @@
|
|
|
1
1
|
import { mergeSchema } from "../utils/mergeSchema";
|
|
2
|
-
import { Keyword, JsonSchemaReducerParams, JsonSchemaValidatorParams } from "../Keyword";
|
|
2
|
+
import { Keyword, JsonSchemaReducerParams, JsonSchemaValidatorParams, ValidationAnnotation } from "../Keyword";
|
|
3
3
|
import { SchemaNode } from "../types";
|
|
4
4
|
import { validateNode } from "../validateNode";
|
|
5
5
|
|
|
6
|
+
const KEYWORD = "anyOf";
|
|
7
|
+
|
|
6
8
|
export const anyOfKeyword: Keyword = {
|
|
7
|
-
id:
|
|
8
|
-
keyword:
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
9
11
|
parse: parseAnyOf,
|
|
10
|
-
addReduce: (node) => node
|
|
12
|
+
addReduce: (node) => node[KEYWORD] != null,
|
|
11
13
|
reduce: reduceAnyOf,
|
|
12
|
-
addValidate: (node) => node
|
|
14
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
13
15
|
validate: validateAnyOf
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export function parseAnyOf(node: SchemaNode) {
|
|
17
19
|
const { schema, evaluationPath, schemaLocation } = node;
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
node.compileSchema(s, `${evaluationPath}/anyOf/${index}`, `${schemaLocation}/anyOf/${index}`)
|
|
21
|
-
);
|
|
20
|
+
if (schema[KEYWORD] == null) {
|
|
21
|
+
return;
|
|
22
22
|
}
|
|
23
|
+
if (!Array.isArray(schema[KEYWORD])) {
|
|
24
|
+
return node.createError("schema-error", {
|
|
25
|
+
pointer: schemaLocation,
|
|
26
|
+
schema,
|
|
27
|
+
value: schema[KEYWORD],
|
|
28
|
+
message: `Keyword '${KEYWORD}' must be an array - received '${typeof schema[KEYWORD]}'`
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (schema[KEYWORD].length === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
node[KEYWORD] = schema[KEYWORD].map((s, index) =>
|
|
35
|
+
node.compileSchema(s, `${evaluationPath}/${KEYWORD}/${index}`, `${schemaLocation}/${KEYWORD}/${index}`)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return node[KEYWORD].reduce((errors, node) => {
|
|
39
|
+
if (node.schemaValidation) errors.push(...node.schemaValidation);
|
|
40
|
+
return errors;
|
|
41
|
+
}, [] as ValidationAnnotation[]);
|
|
23
42
|
}
|
|
24
43
|
|
|
25
44
|
function reduceAnyOf({ node, data, pointer, path }: JsonSchemaReducerParams) {
|
|
26
|
-
if (node
|
|
45
|
+
if (node[KEYWORD] == null) {
|
|
27
46
|
return;
|
|
28
47
|
}
|
|
29
48
|
|
|
30
49
|
let mergedSchema = {};
|
|
31
50
|
let dynamicId = "";
|
|
32
|
-
for (let i = 0; i < node.
|
|
33
|
-
if (validateNode(node
|
|
34
|
-
const { node: schemaNode } = node
|
|
51
|
+
for (let i = 0; i < node[KEYWORD].length; i += 1) {
|
|
52
|
+
if (validateNode(node[KEYWORD][i], data, pointer, path).length === 0) {
|
|
53
|
+
const { node: schemaNode } = node[KEYWORD][i].reduceNode(data);
|
|
35
54
|
|
|
36
55
|
if (schemaNode) {
|
|
37
56
|
const nestedDynamicId = schemaNode.dynamicId?.replace(node.dynamicId, "") ?? "";
|
|
38
|
-
const localDynamicId = nestedDynamicId === "" ?
|
|
57
|
+
const localDynamicId = nestedDynamicId === "" ? `${KEYWORD}/${i}` : nestedDynamicId;
|
|
39
58
|
dynamicId += `${dynamicId === "" ? "" : ","}${localDynamicId}`;
|
|
40
59
|
|
|
41
|
-
const schema = mergeSchema(node
|
|
42
|
-
mergedSchema = mergeSchema(mergedSchema, schema,
|
|
60
|
+
const schema = mergeSchema(node[KEYWORD][i].schema, schemaNode.schema);
|
|
61
|
+
mergedSchema = mergeSchema(mergedSchema, schema, KEYWORD);
|
|
43
62
|
}
|
|
44
63
|
}
|
|
45
64
|
}
|
|
@@ -52,13 +71,13 @@ function reduceAnyOf({ node, data, pointer, path }: JsonSchemaReducerParams) {
|
|
|
52
71
|
}
|
|
53
72
|
|
|
54
73
|
function validateAnyOf({ node, data, pointer, path }: JsonSchemaValidatorParams) {
|
|
55
|
-
if (node
|
|
74
|
+
if (node[KEYWORD] == null) {
|
|
56
75
|
return;
|
|
57
76
|
}
|
|
58
|
-
for (const anyOf of node
|
|
77
|
+
for (const anyOf of node[KEYWORD]) {
|
|
59
78
|
if (validateNode(anyOf, data, pointer, path).length === 0) {
|
|
60
79
|
return undefined;
|
|
61
80
|
}
|
|
62
81
|
}
|
|
63
|
-
return node.createError("any-of-error", { pointer, schema: node.schema, value: data, anyOf: node.schema
|
|
82
|
+
return node.createError("any-of-error", { pointer, schema: node.schema, value: data, anyOf: node.schema[KEYWORD] });
|
|
64
83
|
}
|
package/src/keywords/contains.ts
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { isObject } from "../utils/isObject";
|
|
2
|
-
import { SchemaNode } from "../types";
|
|
2
|
+
import { isBooleanSchema, isJsonSchema, SchemaNode } from "../types";
|
|
3
3
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
4
4
|
import { validateNode } from "../validateNode";
|
|
5
5
|
|
|
6
|
+
const KEYWORD = "contains";
|
|
7
|
+
|
|
6
8
|
export const containsKeyword: Keyword = {
|
|
7
|
-
id:
|
|
8
|
-
keyword:
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
9
11
|
parse: parseContains,
|
|
10
|
-
addValidate: (node) => node
|
|
12
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
11
13
|
validate: validateContains,
|
|
12
|
-
addReduce: (node) => node
|
|
14
|
+
addReduce: (node) => node[KEYWORD] != null,
|
|
13
15
|
reduce: ({ node }) => {
|
|
14
16
|
return node.compileSchema(
|
|
15
17
|
{
|
|
16
18
|
items: {
|
|
17
|
-
anyOf: [node
|
|
19
|
+
anyOf: [node[KEYWORD]!.schema] // we tested for contains in addReduce
|
|
18
20
|
}
|
|
19
21
|
},
|
|
20
22
|
node.evaluationPath,
|
|
@@ -24,11 +26,21 @@ export const containsKeyword: Keyword = {
|
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export function parseContains(node: SchemaNode) {
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
+
const contains = node.schema[KEYWORD];
|
|
30
|
+
if (contains == null) {
|
|
29
31
|
return;
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
if (!(isJsonSchema(contains) || isBooleanSchema(contains))) {
|
|
34
|
+
return node.createError("schema-error", {
|
|
35
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
36
|
+
schema: node.schema,
|
|
37
|
+
value: contains,
|
|
38
|
+
message: `Keyword '${KEYWORD}' must be a valid JSON Schema - received '${typeof contains}'`
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
node[KEYWORD] = node.compileSchema(contains, `${node.evaluationPath}/${KEYWORD}`);
|
|
43
|
+
return node[KEYWORD].schemaValidation;
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
function validateContains({ node, data, pointer, path }: JsonSchemaValidatorParams) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isSchemaNode, SchemaNode } from "../types";
|
|
1
|
+
import { isBooleanSchema, isJsonSchema, isSchemaNode, SchemaNode } from "../types";
|
|
2
2
|
import { Keyword, JsonSchemaReducerParams, JsonSchemaValidatorParams, ValidationAnnotation } from "../Keyword";
|
|
3
3
|
import { isObject } from "../utils/isObject";
|
|
4
4
|
import { mergeNode } from "../mergeNode";
|
|
@@ -6,38 +6,58 @@ import { hasProperty } from "../utils/hasProperty";
|
|
|
6
6
|
import { validateDependentRequired } from "./dependentRequired";
|
|
7
7
|
import { validateDependentSchemas } from "./dependentSchemas";
|
|
8
8
|
import sanitizeErrors from "../utils/sanitizeErrors";
|
|
9
|
+
import { isListOfStrings } from "../utils/isListOfStrings";
|
|
10
|
+
|
|
11
|
+
const KEYWORD = "dependencies";
|
|
9
12
|
|
|
10
13
|
export const dependenciesKeyword: Keyword = {
|
|
11
|
-
id:
|
|
12
|
-
keyword:
|
|
14
|
+
id: KEYWORD,
|
|
15
|
+
keyword: KEYWORD,
|
|
13
16
|
parse: parseDependencies,
|
|
14
17
|
order: -9,
|
|
15
|
-
addReduce: (node) => node.schema
|
|
18
|
+
addReduce: (node) => node.schema[KEYWORD] != null, // because we remap this has to be tested on schema
|
|
16
19
|
reduce: reduceDependencies,
|
|
17
|
-
addValidate: (node) => node.schema
|
|
20
|
+
addValidate: (node) => node.schema[KEYWORD] != null, // because we remap this has to be tested on schema
|
|
18
21
|
validate: validateDependencies
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
export function parseDependencies(node: SchemaNode) {
|
|
22
25
|
const { dependencies } = node.schema;
|
|
23
26
|
if (!isObject(dependencies)) {
|
|
24
|
-
return
|
|
27
|
+
return node.createError("schema-error", {
|
|
28
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
29
|
+
schema: node.schema,
|
|
30
|
+
value: dependencies,
|
|
31
|
+
message: `Keyword '${KEYWORD}' must be an object - received ${typeof dependencies}`
|
|
32
|
+
});
|
|
25
33
|
}
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
|
|
35
|
+
const errors: ValidationAnnotation[] = [];
|
|
36
|
+
for (const property of Object.keys(dependencies)) {
|
|
28
37
|
const schema = dependencies[property] as string[];
|
|
29
|
-
if (
|
|
38
|
+
if (isJsonSchema(schema) || isBooleanSchema(schema)) {
|
|
30
39
|
node.dependentSchemas = node.dependentSchemas ?? {};
|
|
31
40
|
node.dependentSchemas[property] = node.compileSchema(
|
|
32
41
|
schema,
|
|
33
|
-
`${node.evaluationPath}
|
|
34
|
-
`${node.schemaLocation}
|
|
42
|
+
`${node.evaluationPath}/${KEYWORD}/${property}`,
|
|
43
|
+
`${node.schemaLocation}/${KEYWORD}/${property}`
|
|
35
44
|
);
|
|
36
|
-
} else {
|
|
45
|
+
} else if (isListOfStrings(schema)) {
|
|
37
46
|
node.dependentRequired = node.dependentRequired ?? {};
|
|
38
47
|
node.dependentRequired[property] = schema;
|
|
48
|
+
} else {
|
|
49
|
+
errors.push(
|
|
50
|
+
node.createError("schema-error", {
|
|
51
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
52
|
+
schema: node.schema,
|
|
53
|
+
value: dependencies,
|
|
54
|
+
message: `Keyword '${KEYWORD}[string]' must be JSON Schema or string[]`
|
|
55
|
+
})
|
|
56
|
+
);
|
|
39
57
|
}
|
|
40
|
-
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return errors;
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
export function reduceDependencies({ node, data, key, pointer, path }: JsonSchemaReducerParams) {
|
|
@@ -66,7 +86,7 @@ export function reduceDependencies({ node, data, key, pointer, path }: JsonSchem
|
|
|
66
86
|
required.push(...dependentRequired[propertyName]);
|
|
67
87
|
|
|
68
88
|
// @dynamicId
|
|
69
|
-
const localDynamicId =
|
|
89
|
+
const localDynamicId = `${KEYWORD}/${propertyName}`;
|
|
70
90
|
dynamicId += `${dynamicId === "" ? "" : ","}${localDynamicId}`;
|
|
71
91
|
});
|
|
72
92
|
}
|
|
@@ -90,7 +110,7 @@ export function reduceDependencies({ node, data, key, pointer, path }: JsonSchem
|
|
|
90
110
|
// but probably not how json-schema spec defines this behaviour (resolve only within sub-schema)
|
|
91
111
|
const reducedDependency = { ...dependency, schema: { ...dependency.schema, required } }.reduceNode(data, {
|
|
92
112
|
key,
|
|
93
|
-
pointer: `${pointer}
|
|
113
|
+
pointer: `${pointer}/${KEYWORD}/${propertyName}`,
|
|
94
114
|
path
|
|
95
115
|
}).node as SchemaNode;
|
|
96
116
|
|
|
@@ -98,7 +118,7 @@ export function reduceDependencies({ node, data, key, pointer, path }: JsonSchem
|
|
|
98
118
|
|
|
99
119
|
// @dynamicId
|
|
100
120
|
const nestedDynamicId = reducedDependency.dynamicId?.replace(node.dynamicId, "") ?? "";
|
|
101
|
-
const localDynamicId = nestedDynamicId === "" ?
|
|
121
|
+
const localDynamicId = nestedDynamicId === "" ? `${KEYWORD}/${propertyName}` : nestedDynamicId;
|
|
102
122
|
dynamicId += `${dynamicId === "" ? "" : ","}${localDynamicId}`;
|
|
103
123
|
});
|
|
104
124
|
}
|
|
@@ -113,7 +133,7 @@ export function reduceDependencies({ node, data, key, pointer, path }: JsonSchem
|
|
|
113
133
|
|
|
114
134
|
required = workingNode.schema.required ? workingNode.schema.required.concat(...required) : required;
|
|
115
135
|
required = required.filter((r: string, index: number, list: string[]) => list.indexOf(r) === index);
|
|
116
|
-
workingNode = mergeNode(workingNode, workingNode,
|
|
136
|
+
workingNode = mergeNode(workingNode, workingNode, KEYWORD) as SchemaNode;
|
|
117
137
|
return workingNode.compileSchema(
|
|
118
138
|
{ ...workingNode.schema, required },
|
|
119
139
|
workingNode.evaluationPath,
|