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.
Files changed (58) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +112 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.cts +93 -16
  5. package/dist/index.d.mts +93 -16
  6. package/dist/index.mjs +1 -1
  7. package/dist/jlib.js +3 -3
  8. package/index.ts +10 -1
  9. package/package.json +11 -8
  10. package/src/Draft.ts +1 -1
  11. package/src/Keyword.ts +36 -10
  12. package/src/SchemaNode.ts +75 -16
  13. package/src/compileSchema.ts +53 -4
  14. package/src/errors/errors.ts +4 -1
  15. package/src/keywords/$defs.ts +34 -8
  16. package/src/keywords/$ref.ts +12 -0
  17. package/src/keywords/additionalProperties.ts +19 -8
  18. package/src/keywords/allOf.ts +44 -18
  19. package/src/keywords/anyOf.ts +38 -19
  20. package/src/keywords/contains.ts +21 -9
  21. package/src/keywords/dependencies.ts +37 -17
  22. package/src/keywords/dependentRequired.ts +56 -38
  23. package/src/keywords/dependentSchemas.ts +37 -13
  24. package/src/keywords/deprecated.ts +32 -8
  25. package/src/keywords/enum.ts +30 -8
  26. package/src/keywords/exclusiveMaximum.ts +21 -2
  27. package/src/keywords/exclusiveMinimum.ts +22 -3
  28. package/src/keywords/format.ts +21 -2
  29. package/src/keywords/ifthenelse.ts +49 -5
  30. package/src/keywords/items.ts +27 -13
  31. package/src/keywords/maxItems.ts +22 -2
  32. package/src/keywords/maxLength.ts +30 -9
  33. package/src/keywords/maxProperties.ts +30 -9
  34. package/src/keywords/maximum.ts +28 -8
  35. package/src/keywords/minItems.ts +30 -9
  36. package/src/keywords/minLength.ts +30 -9
  37. package/src/keywords/minProperties.ts +26 -5
  38. package/src/keywords/minimum.ts +32 -13
  39. package/src/keywords/multipleOf.ts +33 -12
  40. package/src/keywords/not.ts +23 -10
  41. package/src/keywords/oneOf.ts +29 -9
  42. package/src/keywords/pattern.ts +35 -9
  43. package/src/keywords/properties.ts +35 -12
  44. package/src/keywords/propertyDependencies.test.ts +180 -0
  45. package/src/keywords/propertyDependencies.ts +173 -0
  46. package/src/keywords/propertyNames.ts +26 -14
  47. package/src/keywords/required.ts +31 -8
  48. package/src/keywords/type.ts +53 -16
  49. package/src/keywords/unevaluatedItems.ts +24 -8
  50. package/src/keywords/unevaluatedProperties.ts +24 -7
  51. package/src/keywords/uniqueItems.ts +23 -4
  52. package/src/mergeNode.ts +9 -4
  53. package/src/settings.ts +2 -1
  54. package/src/types.ts +1 -1
  55. package/src/utils/isListOfStrings.ts +3 -0
  56. package/src/validate.test.ts +0 -2
  57. package/src/validateNode.ts +2 -2
  58. package/src/validateSchema.test.ts +312 -0
@@ -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: "format",
5
- keyword: "format",
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
- node.if = node.compileSchema(schema.if, `${evaluationPath}/if`);
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
- node.then = node.compileSchema(schema.then, `${evaluationPath}/then`);
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
- node.else = node.compileSchema(schema.else, `${evaluationPath}/else`);
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) {
@@ -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: "items",
8
- keyword: "items",
8
+ id: KEYWORD,
9
+ keyword: KEYWORD,
9
10
  parse: parseItems,
10
- addResolve: (node) => node.items != null,
11
+ addResolve: (node) => node[KEYWORD] != null,
11
12
  resolve: itemsResolver,
12
- addValidate: ({ schema }) => schema.items != null,
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 { schema, evaluationPath } = node;
27
- if (isObject(schema.items)) {
28
- const propertyNode = node.compileSchema(
29
- schema.items,
30
- `${evaluationPath}/items`,
31
- `${node.schemaLocation}/items`
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.items = propertyNode;
47
+ return node[KEYWORD].schemaValidation;
34
48
  }
35
49
  }
36
50
 
@@ -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: "maxItems",
5
- keyword: "maxItems",
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
- export const maxLengthKeyword: Keyword = {
5
- id: "maxLength",
6
- keyword: "maxLength",
7
- addValidate: ({ schema }) => !isNaN(schema.maxLength),
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 validateMaxLength({ node, data, pointer = "#" }: JsonSchemaValidatorParams) {
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 { schema } = node;
36
+ const maxLength = node[KEYWORD];
16
37
  const length = ucs2decode(data).length;
17
- if (schema.maxLength < length) {
38
+ if (maxLength < length) {
18
39
  return node.createError("max-length-error", {
19
- maxLength: schema.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
- export const maxPropertiesKeyword: Keyword = {
5
- id: "maxProperties",
6
- keyword: "maxProperties",
7
- addValidate: ({ schema }) => !isNaN(schema.maxProperties),
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 validateMaxProperties({ node, data, pointer = "#" }: JsonSchemaValidatorParams) {
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 { schema } = node;
36
+ const maxProperties = node[KEYWORD];
16
37
  const propertyCount = Object.keys(data).length;
17
- if (isNaN(schema.maxProperties) === false && schema.maxProperties < propertyCount) {
38
+ if (maxProperties < propertyCount) {
18
39
  return node.createError("max-properties-error", {
19
- maxProperties: schema.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
  }
@@ -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: "maximum",
6
- keyword: "maximum",
7
- addValidate: ({ schema }) => !isNaN(schema.maximum),
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 (schema.maximum && schema.maximum < data) {
37
+ if (max && max < data) {
18
38
  return node.createError("maximum-error", {
19
- maximum: schema.maximum,
39
+ maximum: max,
20
40
  length: data,
21
41
  value: data,
22
42
  pointer,
23
43
  schema
24
44
  });
25
45
  }
26
- if (schema.maximum && schema.exclusiveMaximum === true && schema.maximum === data) {
46
+ if (max && schema.exclusiveMaximum === true && max === data) {
27
47
  return node.createError("maximum-error", {
28
- maximum: schema.maximum,
48
+ maximum: max,
29
49
  length: data,
30
50
  pointer,
31
51
  schema,
@@ -1,23 +1,44 @@
1
1
  import { JsonSchemaValidatorParams, Keyword } from "../Keyword";
2
+ import { SchemaNode } from "../SchemaNode";
3
+ import { isNumber } from "../types";
2
4
 
3
- export const minItemsKeyword: Keyword = {
4
- id: "minItems",
5
- keyword: "minItems",
6
- addValidate: ({ schema }) => !isNaN(schema.minItems),
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 validateMinItems({ node, data, pointer }: JsonSchemaValidatorParams) {
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 { schema } = node;
15
- if (schema.minItems > data.length) {
35
+ const minItems = node[KEYWORD];
36
+ if (minItems > data.length) {
16
37
  return node.createError("min-items-error", {
17
- minItems: schema.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
- export const minLengthKeyword: Keyword = {
5
- id: "minLength",
6
- keyword: "minLength",
7
- addValidate: ({ schema }) => !isNaN(schema.minLength),
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 validateMinLength({ node, data, pointer = "#" }: JsonSchemaValidatorParams) {
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
- if (schema.minLength <= length) {
37
+ const minLength = node[KEYWORD];
38
+ if (minLength <= length) {
18
39
  return;
19
40
  }
20
41
  return node.createError("min-length-error", {
21
- minLength: schema.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
- export const minPropertiesKeyword: Keyword = {
5
- id: "minProperties",
6
- keyword: "minProperties",
7
- addValidate: ({ schema }) => !isNaN(schema.minProperties),
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 validateMinProperties({ node, data, pointer = "#" }: JsonSchemaValidatorParams) {
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
  }
@@ -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
- export const minimumKeyword: Keyword = {
5
- id: "minimum",
6
- keyword: "minimum",
7
- addValidate: ({ schema }) => !isNaN(schema.minimum),
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 validateMinimum({ node, data, pointer }: JsonSchemaValidatorParams) {
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 { schema } = node;
16
- if (schema.minimum > data) {
34
+ const min = node[KEYWORD];
35
+ if (min > data) {
17
36
  return node.createError("minimum-error", {
18
- minimum: schema.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: schema.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
- export const multipleOfKeyword: Keyword = {
5
- id: "multipleOf",
6
- keyword: "multipleOf",
7
- addValidate: ({ schema }) => !isNaN(schema.multipleOf),
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 validateMultipleOf({ node, data, pointer }: JsonSchemaValidatorParams) {
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 { schema } = node;
36
+ const multipleOf = node[KEYWORD];
16
37
  const valuePrecision = getPrecision(data);
17
- const multiplePrecision = getPrecision(schema.multipleOf);
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: schema.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(schema.multipleOf * precision);
51
+ const multiple = Math.round(multipleOf * precision);
31
52
  if ((val % multiple) / precision !== 0) {
32
53
  return node.createError("multiple-of-error", {
33
- multipleOf: schema.multipleOf,
54
+ multipleOf: multipleOf,
34
55
  value: data,
35
56
  pointer,
36
- schema
57
+ schema: node.schema
37
58
  });
38
59
  }
39
60