json-schema-library 11.0.4 → 11.1.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/CHANGELOG.md +11 -0
- package/README.md +112 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +93 -16
- package/dist/index.d.mts +93 -16
- package/dist/index.mjs +1 -1
- package/dist/jlib.js +3 -3
- package/index.ts +10 -1
- package/package.json +11 -8
- package/src/Draft.ts +1 -1
- package/src/Keyword.ts +36 -10
- package/src/SchemaNode.ts +75 -16
- package/src/compileSchema.ts +53 -4
- package/src/errors/errors.ts +4 -1
- package/src/keywords/$defs.ts +34 -8
- package/src/keywords/$ref.ts +12 -0
- 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 +35 -12
- 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/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 +2 -2
- package/src/validateSchema.test.ts +312 -0
package/src/keywords/format.ts
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
2
|
+
import { SchemaNode } from "../SchemaNode";
|
|
3
|
+
|
|
4
|
+
const KEYWORD = "format";
|
|
2
5
|
|
|
3
6
|
export const formatKeyword: Keyword = {
|
|
4
|
-
id:
|
|
5
|
-
keyword:
|
|
7
|
+
id: KEYWORD,
|
|
8
|
+
keyword: KEYWORD,
|
|
9
|
+
parse: parseFormat,
|
|
6
10
|
addValidate: ({ schema }) => schema?.format != null,
|
|
7
11
|
validate: validateFormat
|
|
8
12
|
};
|
|
9
13
|
|
|
14
|
+
function parseFormat(node: SchemaNode) {
|
|
15
|
+
const format = node.schema[KEYWORD];
|
|
16
|
+
if (format == null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (typeof format !== "string") {
|
|
20
|
+
return node.createError("schema-error", {
|
|
21
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
22
|
+
schema: node.schema,
|
|
23
|
+
value: format,
|
|
24
|
+
message: `Keyword '${KEYWORD}' must be a string - received '${typeof format}'`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
function validateFormat(options: JsonSchemaValidatorParams) {
|
|
11
30
|
const { node } = options;
|
|
12
31
|
const formatValidator = node.context.formats[node.schema.format];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mergeSchema } from "../utils/mergeSchema";
|
|
2
|
-
import { Keyword, JsonSchemaReducerParams, JsonSchemaValidatorParams } from "../Keyword";
|
|
3
|
-
import { SchemaNode } from "../types";
|
|
2
|
+
import { Keyword, JsonSchemaReducerParams, JsonSchemaValidatorParams, ValidationAnnotation } from "../Keyword";
|
|
3
|
+
import { isBooleanSchema, isJsonSchema, SchemaNode } from "../types";
|
|
4
4
|
import { validateNode } from "../validateNode";
|
|
5
5
|
|
|
6
6
|
export const ifKeyword: Keyword = {
|
|
@@ -15,15 +15,59 @@ export const ifKeyword: Keyword = {
|
|
|
15
15
|
|
|
16
16
|
export function parseIfThenElse(node: SchemaNode) {
|
|
17
17
|
const { schema, evaluationPath } = node;
|
|
18
|
+
const errors: ValidationAnnotation[] = [];
|
|
18
19
|
if (schema.if != null) {
|
|
19
|
-
|
|
20
|
+
if (isJsonSchema(schema.if) || isBooleanSchema(schema.if)) {
|
|
21
|
+
node.if = node.compileSchema(schema.if, `${evaluationPath}/if`);
|
|
22
|
+
if (node.if.schemaValidation) {
|
|
23
|
+
errors.push(...node.if.schemaValidation);
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
errors.push(
|
|
27
|
+
node.createError("schema-error", {
|
|
28
|
+
pointer: `${node.schemaLocation}/if`,
|
|
29
|
+
schema: node.schema,
|
|
30
|
+
value: schema.if,
|
|
31
|
+
message: `Keyword 'if' must be valid JSON Schema - received '${typeof schema.if}'`
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
}
|
|
20
35
|
}
|
|
21
36
|
if (schema.then != null) {
|
|
22
|
-
|
|
37
|
+
if (isJsonSchema(schema.then) || isBooleanSchema(schema.then)) {
|
|
38
|
+
node.then = node.compileSchema(schema.then, `${evaluationPath}/then`);
|
|
39
|
+
if (node.then.schemaValidation) {
|
|
40
|
+
errors.push(...node.then.schemaValidation);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
errors.push(
|
|
44
|
+
node.createError("schema-error", {
|
|
45
|
+
pointer: `${node.schemaLocation}/then`,
|
|
46
|
+
schema: node.schema,
|
|
47
|
+
value: schema.then,
|
|
48
|
+
message: `Keyword 'then' must be valid JSON Schema - received '${typeof schema.then}'`
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
}
|
|
23
52
|
}
|
|
24
53
|
if (schema.else != null) {
|
|
25
|
-
|
|
54
|
+
if (isJsonSchema(schema.else) || isBooleanSchema(schema.else)) {
|
|
55
|
+
node.else = node.compileSchema(schema.else, `${evaluationPath}/else`);
|
|
56
|
+
if (node.else.schemaValidation) {
|
|
57
|
+
errors.push(...node.else.schemaValidation);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
errors.push(
|
|
61
|
+
node.createError("schema-error", {
|
|
62
|
+
pointer: `${node.schemaLocation}/else`,
|
|
63
|
+
schema: node.schema,
|
|
64
|
+
value: schema.else,
|
|
65
|
+
message: `Keyword 'else' must be valid JSON Schema - received '${typeof schema.else}'`
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
}
|
|
26
69
|
}
|
|
70
|
+
return errors;
|
|
27
71
|
}
|
|
28
72
|
|
|
29
73
|
function reduceIf({ node, data, pointer, path }: JsonSchemaReducerParams) {
|
package/src/keywords/items.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Keyword, JsonSchemaResolverParams, JsonSchemaValidatorParams, ValidationReturnType } from "../Keyword";
|
|
2
|
-
import { SchemaNode } from "../types";
|
|
3
|
-
import { isObject } from "../utils/isObject";
|
|
2
|
+
import { isBooleanSchema, isJsonSchema, SchemaNode } from "../types";
|
|
4
3
|
import { validateNode } from "../validateNode";
|
|
5
4
|
|
|
5
|
+
const KEYWORD = "items";
|
|
6
|
+
|
|
6
7
|
export const itemsKeyword: Keyword = {
|
|
7
|
-
id:
|
|
8
|
-
keyword:
|
|
8
|
+
id: KEYWORD,
|
|
9
|
+
keyword: KEYWORD,
|
|
9
10
|
parse: parseItems,
|
|
10
|
-
addResolve: (node) => node
|
|
11
|
+
addResolve: (node) => node[KEYWORD] != null,
|
|
11
12
|
resolve: itemsResolver,
|
|
12
|
-
addValidate: (
|
|
13
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
13
14
|
validate: validateItems
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -23,14 +24,27 @@ function itemsResolver({ node, key }: JsonSchemaResolverParams) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function parseItems(node: SchemaNode) {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const items = node.schema[KEYWORD];
|
|
28
|
+
if (items == null) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!(isJsonSchema(items) || isBooleanSchema(items))) {
|
|
32
|
+
return node.createError("schema-error", {
|
|
33
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
34
|
+
schema: node.schema,
|
|
35
|
+
value: items,
|
|
36
|
+
message: `Keyword '${KEYWORD}' must be a valid JSON Schema - received '${typeof items}'`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (items !== true) {
|
|
41
|
+
// @todo remove skipping boolean schema
|
|
42
|
+
node[KEYWORD] = node.compileSchema(
|
|
43
|
+
items,
|
|
44
|
+
`${node.evaluationPath}/${KEYWORD}`,
|
|
45
|
+
`${node.schemaLocation}/${KEYWORD}`
|
|
32
46
|
);
|
|
33
|
-
node.
|
|
47
|
+
return node[KEYWORD].schemaValidation;
|
|
34
48
|
}
|
|
35
49
|
}
|
|
36
50
|
|
package/src/keywords/maxItems.ts
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
2
|
+
import { isNumber, SchemaNode } from "../types";
|
|
3
|
+
|
|
4
|
+
const KEYWORD = "maxItems";
|
|
2
5
|
|
|
3
6
|
export const maxItemsKeyword: Keyword = {
|
|
4
|
-
id:
|
|
5
|
-
keyword:
|
|
7
|
+
id: KEYWORD,
|
|
8
|
+
keyword: KEYWORD,
|
|
9
|
+
parse: parseMaxItems,
|
|
6
10
|
addValidate: ({ schema }) => !isNaN(schema.maxItems),
|
|
7
11
|
validate: validateMaxItems
|
|
8
12
|
};
|
|
9
13
|
|
|
14
|
+
function parseMaxItems(node: SchemaNode) {
|
|
15
|
+
const max = node.schema[KEYWORD];
|
|
16
|
+
if (max == null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!isNumber(max)) {
|
|
20
|
+
return node.createError("schema-error", {
|
|
21
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
22
|
+
schema: node.schema,
|
|
23
|
+
value: max,
|
|
24
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof max}'`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
node[KEYWORD] = max;
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
function validateMaxItems({ node, data, pointer }: JsonSchemaValidatorParams) {
|
|
11
31
|
const { schema } = node;
|
|
12
32
|
if (Array.isArray(data) && schema.maxItems < data.length) {
|
|
@@ -1,25 +1,46 @@
|
|
|
1
1
|
import ucs2decode from "../utils/punycode.ucs2decode";
|
|
2
2
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
3
|
+
import { SchemaNode } from "../SchemaNode";
|
|
4
|
+
import { isNumber } from "../types";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const KEYWORD = "maxLength";
|
|
7
|
+
|
|
8
|
+
export const maxLengthKeyword: Keyword<"maxLength"> = {
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
11
|
+
parse: parseMaxLength,
|
|
12
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
8
13
|
validate: validateMaxLength
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
function
|
|
16
|
+
function parseMaxLength(node: SchemaNode) {
|
|
17
|
+
const max = node.schema[KEYWORD];
|
|
18
|
+
if (max == null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!isNumber(max)) {
|
|
22
|
+
return node.createError("schema-error", {
|
|
23
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
24
|
+
schema: node.schema,
|
|
25
|
+
value: max,
|
|
26
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof max}'`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
node[KEYWORD] = max;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function validateMaxLength({ node, data, pointer = "#" }: JsonSchemaValidatorParams<"maxLength">) {
|
|
12
33
|
if (typeof data !== "string") {
|
|
13
34
|
return;
|
|
14
35
|
}
|
|
15
|
-
const
|
|
36
|
+
const maxLength = node[KEYWORD];
|
|
16
37
|
const length = ucs2decode(data).length;
|
|
17
|
-
if (
|
|
38
|
+
if (maxLength < length) {
|
|
18
39
|
return node.createError("max-length-error", {
|
|
19
|
-
maxLength:
|
|
40
|
+
maxLength: maxLength,
|
|
20
41
|
length,
|
|
21
42
|
pointer,
|
|
22
|
-
schema,
|
|
43
|
+
schema: node.schema,
|
|
23
44
|
value: data
|
|
24
45
|
});
|
|
25
46
|
}
|
|
@@ -1,25 +1,46 @@
|
|
|
1
1
|
import { isObject } from "../utils/isObject";
|
|
2
2
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
3
|
+
import { SchemaNode } from "../SchemaNode";
|
|
4
|
+
import { isNumber } from "../types";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const KEYWORD = "maxProperties";
|
|
7
|
+
|
|
8
|
+
export const maxPropertiesKeyword: Keyword<typeof KEYWORD> = {
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
11
|
+
parse: parseMaxProperties,
|
|
12
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
8
13
|
validate: validateMaxProperties
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
function
|
|
16
|
+
function parseMaxProperties(node: SchemaNode) {
|
|
17
|
+
const max = node.schema[KEYWORD];
|
|
18
|
+
if (max == null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!isNumber(max)) {
|
|
22
|
+
return node.createError("schema-error", {
|
|
23
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
24
|
+
schema: node.schema,
|
|
25
|
+
value: max,
|
|
26
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof max}'`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
node[KEYWORD] = max;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function validateMaxProperties({ node, data, pointer = "#" }: JsonSchemaValidatorParams<typeof KEYWORD>) {
|
|
12
33
|
if (!isObject(data)) {
|
|
13
34
|
return;
|
|
14
35
|
}
|
|
15
|
-
const
|
|
36
|
+
const maxProperties = node[KEYWORD];
|
|
16
37
|
const propertyCount = Object.keys(data).length;
|
|
17
|
-
if (
|
|
38
|
+
if (maxProperties < propertyCount) {
|
|
18
39
|
return node.createError("max-properties-error", {
|
|
19
|
-
maxProperties:
|
|
40
|
+
maxProperties: maxProperties,
|
|
20
41
|
length: propertyCount,
|
|
21
42
|
pointer,
|
|
22
|
-
schema,
|
|
43
|
+
schema: node.schema,
|
|
23
44
|
value: data
|
|
24
45
|
});
|
|
25
46
|
}
|
package/src/keywords/maximum.ts
CHANGED
|
@@ -1,31 +1,51 @@
|
|
|
1
1
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
2
|
-
import { isNumber } from "../types";
|
|
2
|
+
import { isNumber, SchemaNode } from "../types";
|
|
3
|
+
|
|
4
|
+
const KEYWORD = "maximum";
|
|
3
5
|
|
|
4
6
|
export const maximumKeyword: Keyword = {
|
|
5
|
-
id:
|
|
6
|
-
keyword:
|
|
7
|
-
|
|
7
|
+
id: KEYWORD,
|
|
8
|
+
keyword: KEYWORD,
|
|
9
|
+
parse: parseMaximum,
|
|
10
|
+
addValidate: (node) => node.maximum != null,
|
|
8
11
|
validate: validateMaximum
|
|
9
12
|
};
|
|
10
13
|
|
|
14
|
+
function parseMaximum(node: SchemaNode) {
|
|
15
|
+
const max = node.schema[KEYWORD];
|
|
16
|
+
if (max == null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!isNumber(max)) {
|
|
20
|
+
return node.createError("schema-error", {
|
|
21
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
22
|
+
schema: node.schema,
|
|
23
|
+
value: max,
|
|
24
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof max}'`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
node[KEYWORD] = max;
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
function validateMaximum({ node, data, pointer }: JsonSchemaValidatorParams) {
|
|
12
31
|
if (!isNumber(data)) {
|
|
13
32
|
return undefined;
|
|
14
33
|
}
|
|
15
34
|
|
|
35
|
+
const max = node[KEYWORD];
|
|
16
36
|
const { schema } = node;
|
|
17
|
-
if (
|
|
37
|
+
if (max && max < data) {
|
|
18
38
|
return node.createError("maximum-error", {
|
|
19
|
-
maximum:
|
|
39
|
+
maximum: max,
|
|
20
40
|
length: data,
|
|
21
41
|
value: data,
|
|
22
42
|
pointer,
|
|
23
43
|
schema
|
|
24
44
|
});
|
|
25
45
|
}
|
|
26
|
-
if (
|
|
46
|
+
if (max && schema.exclusiveMaximum === true && max === data) {
|
|
27
47
|
return node.createError("maximum-error", {
|
|
28
|
-
maximum:
|
|
48
|
+
maximum: max,
|
|
29
49
|
length: data,
|
|
30
50
|
pointer,
|
|
31
51
|
schema,
|
package/src/keywords/minItems.ts
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
import { JsonSchemaValidatorParams, Keyword } from "../Keyword";
|
|
2
|
+
import { SchemaNode } from "../SchemaNode";
|
|
3
|
+
import { isNumber } from "../types";
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const KEYWORD = "minItems";
|
|
6
|
+
|
|
7
|
+
export const minItemsKeyword: Keyword<"minItems"> = {
|
|
8
|
+
id: KEYWORD,
|
|
9
|
+
keyword: KEYWORD,
|
|
10
|
+
parse: parseMinItems,
|
|
11
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
7
12
|
validate: validateMinItems
|
|
8
13
|
};
|
|
9
14
|
|
|
10
|
-
function
|
|
15
|
+
function parseMinItems(node: SchemaNode) {
|
|
16
|
+
const min = node.schema[KEYWORD];
|
|
17
|
+
if (min == null) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!isNumber(min)) {
|
|
21
|
+
return node.createError("schema-error", {
|
|
22
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
23
|
+
schema: node.schema,
|
|
24
|
+
value: min,
|
|
25
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof min}'`
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
node[KEYWORD] = min;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateMinItems({ node, data, pointer }: JsonSchemaValidatorParams<"minItems">) {
|
|
11
32
|
if (!Array.isArray(data)) {
|
|
12
33
|
return;
|
|
13
34
|
}
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
35
|
+
const minItems = node[KEYWORD];
|
|
36
|
+
if (minItems > data.length) {
|
|
16
37
|
return node.createError("min-items-error", {
|
|
17
|
-
minItems:
|
|
38
|
+
minItems: minItems,
|
|
18
39
|
length: data.length,
|
|
19
40
|
pointer,
|
|
20
|
-
schema,
|
|
41
|
+
schema: node.schema,
|
|
21
42
|
value: data
|
|
22
43
|
});
|
|
23
44
|
}
|
|
@@ -1,27 +1,48 @@
|
|
|
1
1
|
import ucs2decode from "../utils/punycode.ucs2decode";
|
|
2
2
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
3
|
+
import { SchemaNode } from "../SchemaNode";
|
|
4
|
+
import { isNumber } from "../types";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const KEYWORD = "minLength";
|
|
7
|
+
|
|
8
|
+
export const minLengthKeyword: Keyword<"minLength"> = {
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
11
|
+
parse: parseMinLength,
|
|
12
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
8
13
|
validate: validateMinLength
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
function
|
|
16
|
+
function parseMinLength(node: SchemaNode) {
|
|
17
|
+
const min = node.schema[KEYWORD];
|
|
18
|
+
if (min == null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!isNumber(min)) {
|
|
22
|
+
return node.createError("schema-error", {
|
|
23
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
24
|
+
schema: node.schema,
|
|
25
|
+
value: min,
|
|
26
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof min}'`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
node[KEYWORD] = min;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function validateMinLength({ node, data, pointer = "#" }: JsonSchemaValidatorParams<"minLength">) {
|
|
12
33
|
if (typeof data !== "string") {
|
|
13
34
|
return;
|
|
14
35
|
}
|
|
15
|
-
const { schema } = node;
|
|
16
36
|
const length = ucs2decode(data).length;
|
|
17
|
-
|
|
37
|
+
const minLength = node[KEYWORD];
|
|
38
|
+
if (minLength <= length) {
|
|
18
39
|
return;
|
|
19
40
|
}
|
|
20
41
|
return node.createError("min-length-error", {
|
|
21
|
-
minLength
|
|
42
|
+
minLength,
|
|
22
43
|
length,
|
|
23
44
|
pointer,
|
|
24
|
-
schema,
|
|
45
|
+
schema: node.schema,
|
|
25
46
|
value: data
|
|
26
47
|
});
|
|
27
48
|
}
|
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import { isObject } from "../utils/isObject";
|
|
2
2
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
3
|
+
import { SchemaNode } from "../SchemaNode";
|
|
4
|
+
import { isNumber } from "../types";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const KEYWORD = "minProperties";
|
|
7
|
+
|
|
8
|
+
export const minPropertiesKeyword: Keyword<"minProperties"> = {
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
11
|
+
parse: parseMinItems,
|
|
12
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
8
13
|
validate: validateMinProperties
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
function
|
|
16
|
+
function parseMinItems(node: SchemaNode) {
|
|
17
|
+
const min = node.schema[KEYWORD];
|
|
18
|
+
if (min == null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!isNumber(min)) {
|
|
22
|
+
return node.createError("schema-error", {
|
|
23
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
24
|
+
schema: node.schema,
|
|
25
|
+
value: min,
|
|
26
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof min}'`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
node[KEYWORD] = min;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function validateMinProperties({ node, data, pointer = "#" }: JsonSchemaValidatorParams<"minProperties">) {
|
|
12
33
|
if (!isObject(data)) {
|
|
13
34
|
return;
|
|
14
35
|
}
|
package/src/keywords/minimum.ts
CHANGED
|
@@ -1,33 +1,52 @@
|
|
|
1
1
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
2
|
-
import { isNumber } from "../types";
|
|
2
|
+
import { isNumber, SchemaNode } from "../types";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const KEYWORD = "minimum";
|
|
5
|
+
|
|
6
|
+
export const minimumKeyword: Keyword<"minimum"> = {
|
|
7
|
+
id: KEYWORD,
|
|
8
|
+
keyword: KEYWORD,
|
|
9
|
+
parse: parseMinimum,
|
|
10
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
8
11
|
validate: validateMinimum
|
|
9
12
|
};
|
|
10
13
|
|
|
11
|
-
function
|
|
14
|
+
function parseMinimum(node: SchemaNode) {
|
|
15
|
+
const min = node.schema[KEYWORD];
|
|
16
|
+
if (min == null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!isNumber(min)) {
|
|
20
|
+
return node.createError("schema-error", {
|
|
21
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
22
|
+
schema: node.schema,
|
|
23
|
+
value: min,
|
|
24
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof min}'`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
node[KEYWORD] = min;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function validateMinimum({ node, data, pointer }: JsonSchemaValidatorParams<"minimum">) {
|
|
12
31
|
if (!isNumber(data)) {
|
|
13
32
|
return undefined;
|
|
14
33
|
}
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
34
|
+
const min = node[KEYWORD];
|
|
35
|
+
if (min > data) {
|
|
17
36
|
return node.createError("minimum-error", {
|
|
18
|
-
minimum:
|
|
37
|
+
minimum: min,
|
|
19
38
|
length: data,
|
|
20
39
|
pointer,
|
|
21
|
-
schema,
|
|
40
|
+
schema: node.schema,
|
|
22
41
|
value: data
|
|
23
42
|
});
|
|
24
43
|
}
|
|
25
|
-
if (schema.exclusiveMinimum === true && schema.minimum === data) {
|
|
44
|
+
if (node.schema.exclusiveMinimum === true && node.schema.minimum === data) {
|
|
26
45
|
return node.createError("minimum-error", {
|
|
27
|
-
minimum:
|
|
46
|
+
minimum: min,
|
|
28
47
|
length: data,
|
|
29
48
|
pointer,
|
|
30
|
-
schema,
|
|
49
|
+
schema: node.schema,
|
|
31
50
|
value: data
|
|
32
51
|
});
|
|
33
52
|
}
|
|
@@ -1,39 +1,60 @@
|
|
|
1
1
|
import { getPrecision } from "../utils/getPrecision";
|
|
2
2
|
import { Keyword, JsonSchemaValidatorParams } from "../Keyword";
|
|
3
|
+
import { SchemaNode } from "../SchemaNode";
|
|
4
|
+
import { isNumber } from "../types";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const KEYWORD = "multipleOf";
|
|
7
|
+
|
|
8
|
+
export const multipleOfKeyword: Keyword<"multipleOf"> = {
|
|
9
|
+
id: KEYWORD,
|
|
10
|
+
keyword: KEYWORD,
|
|
11
|
+
parse: parseMultipleOf,
|
|
12
|
+
addValidate: (node) => node[KEYWORD] != null,
|
|
8
13
|
validate: validateMultipleOf
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
function
|
|
16
|
+
function parseMultipleOf(node: SchemaNode) {
|
|
17
|
+
const multipleOf = node.schema[KEYWORD];
|
|
18
|
+
if (multipleOf == null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!isNumber(multipleOf)) {
|
|
22
|
+
return node.createError("schema-error", {
|
|
23
|
+
pointer: `${node.schemaLocation}/${KEYWORD}`,
|
|
24
|
+
schema: node.schema,
|
|
25
|
+
value: multipleOf,
|
|
26
|
+
message: `Keyword '${KEYWORD}' must be a number - received '${typeof multipleOf}'`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
node[KEYWORD] = multipleOf;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function validateMultipleOf({ node, data, pointer }: JsonSchemaValidatorParams<"multipleOf">) {
|
|
12
33
|
if (typeof data !== "number") {
|
|
13
34
|
return undefined;
|
|
14
35
|
}
|
|
15
|
-
const
|
|
36
|
+
const multipleOf = node[KEYWORD];
|
|
16
37
|
const valuePrecision = getPrecision(data);
|
|
17
|
-
const multiplePrecision = getPrecision(
|
|
38
|
+
const multiplePrecision = getPrecision(multipleOf);
|
|
18
39
|
if (valuePrecision > multiplePrecision) {
|
|
19
40
|
// value with higher precision then multipleOf-precision can never be multiple
|
|
20
41
|
return node.createError("multiple-of-error", {
|
|
21
|
-
multipleOf
|
|
42
|
+
multipleOf,
|
|
22
43
|
value: data,
|
|
23
44
|
pointer,
|
|
24
|
-
schema
|
|
45
|
+
schema: node.schema
|
|
25
46
|
});
|
|
26
47
|
}
|
|
27
48
|
|
|
28
49
|
const precision = Math.pow(10, multiplePrecision);
|
|
29
50
|
const val = Math.round(data * precision);
|
|
30
|
-
const multiple = Math.round(
|
|
51
|
+
const multiple = Math.round(multipleOf * precision);
|
|
31
52
|
if ((val % multiple) / precision !== 0) {
|
|
32
53
|
return node.createError("multiple-of-error", {
|
|
33
|
-
multipleOf:
|
|
54
|
+
multipleOf: multipleOf,
|
|
34
55
|
value: data,
|
|
35
56
|
pointer,
|
|
36
|
-
schema
|
|
57
|
+
schema: node.schema
|
|
37
58
|
});
|
|
38
59
|
}
|
|
39
60
|
|