json-schema-library 5.2.1 → 6.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 (115) hide show
  1. package/.prettierignore +1 -0
  2. package/.prettierrc +7 -0
  3. package/README.md +9 -8
  4. package/dist/index.d.ts +13 -44
  5. package/dist/jsonSchemaLibrary.js +1 -1
  6. package/dist/lib/addValidator.d.ts +2 -1
  7. package/dist/lib/compile/index.d.ts +11 -0
  8. package/dist/lib/config/strings.d.ts +1 -39
  9. package/dist/lib/cores/CoreInterface.d.ts +28 -9
  10. package/dist/lib/cores/Draft04.d.ts +2 -2
  11. package/dist/lib/cores/Draft06.d.ts +15 -0
  12. package/dist/lib/cores/Draft07.d.ts +15 -0
  13. package/dist/lib/cores/JsonEditor.d.ts +2 -2
  14. package/dist/lib/draft06/addSchema.d.ts +7 -0
  15. package/dist/lib/draft06/compile/index.d.ts +15 -0
  16. package/dist/lib/draft06/validation/keyword.d.ts +3 -0
  17. package/dist/lib/draft06/validation/type.d.ts +17 -0
  18. package/dist/lib/draft06/validation/typeKeywordMapping.d.ts +13 -0
  19. package/dist/lib/getChildSchemaSelection.d.ts +7 -5
  20. package/dist/lib/getSchema.d.ts +1 -1
  21. package/dist/lib/getTemplate.d.ts +5 -1
  22. package/dist/lib/getTypeOf.d.ts +2 -1
  23. package/dist/lib/schema/getTypeId.d.ts +1 -1
  24. package/dist/lib/step.d.ts +4 -4
  25. package/dist/lib/types.d.ts +10 -2
  26. package/dist/lib/utils/filter.d.ts +0 -1
  27. package/dist/lib/utils/merge.d.ts +3 -0
  28. package/dist/lib/validate.d.ts +1 -1
  29. package/dist/lib/validation/format.d.ts +6 -0
  30. package/dist/lib/validation/keyword.d.ts +2 -27
  31. package/dist/lib/validation/type.d.ts +3 -10
  32. package/dist/lib/validation/typeKeywordMapping.d.ts +4 -4
  33. package/dist/module/index.js +13 -6
  34. package/dist/module/lib/addValidator.js +3 -4
  35. package/dist/module/lib/compile/getRef.js +1 -1
  36. package/dist/module/lib/compile/index.js +11 -0
  37. package/dist/module/lib/config/strings.js +15 -2
  38. package/dist/module/lib/cores/CoreInterface.js +22 -0
  39. package/dist/module/lib/cores/Draft06.js +61 -0
  40. package/dist/module/lib/cores/Draft07.js +61 -0
  41. package/dist/module/lib/createSchemaOf.js +1 -1
  42. package/dist/module/lib/draft06/addSchema.js +11 -0
  43. package/dist/module/lib/draft06/compile/index.js +65 -0
  44. package/dist/module/lib/draft06/validation/keyword.js +156 -0
  45. package/dist/module/lib/draft06/validation/type.js +30 -0
  46. package/dist/module/lib/draft06/validation/typeKeywordMapping.js +15 -0
  47. package/dist/module/lib/each.js +1 -1
  48. package/dist/module/lib/eachSchema.js +3 -3
  49. package/dist/module/lib/getChildSchemaSelection.js +7 -6
  50. package/dist/module/lib/getSchema.js +2 -1
  51. package/dist/module/lib/getTemplate.js +57 -23
  52. package/dist/module/lib/resolveAllOf.js +3 -4
  53. package/dist/module/lib/resolveOneOf.fuzzy.js +13 -3
  54. package/dist/module/lib/resolveOneOf.strict.js +11 -2
  55. package/dist/module/lib/resolveRef.strict.js +8 -0
  56. package/dist/module/lib/schema/getTypeDefs.js +12 -1
  57. package/dist/module/lib/schema/getTypeId.js +1 -1
  58. package/dist/module/lib/step.js +62 -11
  59. package/dist/module/lib/types.js +7 -1
  60. package/dist/module/lib/utils/filter.js +3 -5
  61. package/dist/module/lib/utils/merge.js +3 -0
  62. package/dist/module/lib/validate.js +33 -8
  63. package/dist/module/lib/validateAsync.js +7 -7
  64. package/dist/module/lib/validation/errors.js +15 -2
  65. package/dist/module/lib/validation/format.js +105 -4
  66. package/dist/module/lib/validation/keyword.js +77 -30
  67. package/dist/module/lib/validation/type.js +2 -1
  68. package/dist/module/remotes/draft06.json +155 -0
  69. package/dist/module/remotes/draft07.json +172 -0
  70. package/dist/module/remotes/index.js +0 -1
  71. package/dist/remotes/index.d.ts +0 -1
  72. package/index.ts +14 -5
  73. package/lib/addValidator.ts +5 -5
  74. package/lib/compile/getRef.ts +1 -1
  75. package/lib/compile/index.ts +11 -1
  76. package/lib/config/strings.ts +17 -3
  77. package/lib/cores/CoreInterface.ts +37 -10
  78. package/lib/cores/Draft04.ts +2 -4
  79. package/lib/cores/Draft06.ts +76 -0
  80. package/lib/cores/Draft07.ts +75 -0
  81. package/lib/cores/JsonEditor.ts +2 -4
  82. package/lib/createSchemaOf.ts +1 -3
  83. package/lib/draft06/addSchema.ts +14 -0
  84. package/lib/draft06/compile/index.ts +68 -0
  85. package/lib/draft06/validation/keyword.ts +177 -0
  86. package/lib/draft06/validation/type.ts +43 -0
  87. package/lib/draft06/validation/typeKeywordMapping.ts +15 -0
  88. package/lib/each.ts +8 -3
  89. package/lib/eachSchema.ts +3 -3
  90. package/lib/getChildSchemaSelection.ts +14 -7
  91. package/lib/getSchema.ts +15 -7
  92. package/lib/getTemplate.ts +148 -38
  93. package/lib/getTypeOf.ts +2 -1
  94. package/lib/resolveAllOf.ts +9 -5
  95. package/lib/resolveOneOf.fuzzy.ts +25 -8
  96. package/lib/resolveOneOf.strict.ts +17 -4
  97. package/lib/resolveRef.strict.ts +9 -0
  98. package/lib/schema/getTypeDefs.ts +14 -1
  99. package/lib/schema/getTypeId.ts +2 -2
  100. package/lib/step.ts +103 -22
  101. package/lib/types.ts +21 -4
  102. package/lib/utils/filter.ts +4 -6
  103. package/lib/utils/merge.ts +4 -0
  104. package/lib/validate.ts +45 -15
  105. package/lib/validateAsync.ts +13 -12
  106. package/lib/validation/errors.ts +15 -2
  107. package/lib/validation/format.ts +113 -4
  108. package/lib/validation/keyword.ts +147 -78
  109. package/lib/validation/type.ts +5 -1
  110. package/package.json +73 -63
  111. package/remotes/draft06.json +155 -0
  112. package/remotes/draft07.json +172 -0
  113. package/remotes/draft2019-09.json +86 -0
  114. package/remotes/index.ts +0 -2
  115. package/tsconfig.json +2 -9
@@ -1,13 +1,12 @@
1
1
  import copy from "./utils/copy";
2
2
  import merge from "./utils/merge";
3
- import errors from "./validation/errors";
4
3
  export default function resolveAllOf(core, data, schema = core.rootSchema, pointer = "#") {
5
4
  let mergedSchema = copy(schema);
6
5
  for (let i = 0; i < schema.allOf.length; i += 1) {
7
6
  const allOfSchema = core.resolveRef(schema.allOf[i]);
8
- if (core.isValid(data, allOfSchema, pointer) === false) {
9
- return errors.allOfError({ value: data, pointer, allOf: JSON.stringify(schema.allOf) });
10
- }
7
+ // if (core.isValid(data, allOfSchema, pointer) === false) {
8
+ // return errors.allOfError({ value: data, pointer, allOf: JSON.stringify(schema.allOf) });
9
+ // }
11
10
  mergedSchema = merge(mergedSchema, allOfSchema);
12
11
  }
13
12
  delete mergedSchema.allOf;
@@ -2,6 +2,7 @@ import { errorOrPromise } from "./utils/filter";
2
2
  import flattenArray from "./utils/flattenArray";
3
3
  import getTypeOf from "./getTypeOf";
4
4
  import settings from "./config/settings";
5
+ import { isJSONError } from "./types";
5
6
  const { DECLARATOR_ONEOF } = settings;
6
7
  /**
7
8
  * Returns a ranking for the data and given schema
@@ -52,7 +53,7 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
52
53
  for (let i = 0; i < schema.oneOf.length; i += 1) {
53
54
  const one = core.resolveRef(schema.oneOf[i]);
54
55
  const oneOfPropertySchema = core.step(oneOfProperty, one, data, pointer);
55
- if (oneOfPropertySchema && oneOfPropertySchema.type === "error") {
56
+ if (isJSONError(oneOfPropertySchema)) {
56
57
  return oneOfPropertySchema;
57
58
  }
58
59
  let result = flattenArray(core.validate(oneOfValue, oneOfPropertySchema, pointer));
@@ -64,7 +65,12 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
64
65
  return one; // return resolved schema
65
66
  }
66
67
  }
67
- return core.errors.oneOfPropertyError({ property: oneOfProperty, value: oneOfValue, pointer, errors });
68
+ return core.errors.oneOfPropertyError({
69
+ property: oneOfProperty,
70
+ value: oneOfValue,
71
+ pointer,
72
+ errors
73
+ });
68
74
  }
69
75
  // keyword: oneOf
70
76
  const matches = [];
@@ -90,7 +96,11 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
90
96
  }
91
97
  }
92
98
  if (schemaOfItem === undefined) {
93
- return core.errors.oneOfError({ value: JSON.stringify(data), pointer, oneOf: schema.oneOf });
99
+ return core.errors.oneOfError({
100
+ value: JSON.stringify(data),
101
+ pointer,
102
+ oneOf: schema.oneOf
103
+ });
94
104
  }
95
105
  return schemaOfItem;
96
106
  }
@@ -27,7 +27,16 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
27
27
  return matches[0];
28
28
  }
29
29
  if (matches.length > 1) {
30
- return core.errors.multipleOneOfError({ value: data, pointer, matches });
30
+ return core.errors.multipleOneOfError({
31
+ value: data,
32
+ pointer,
33
+ matches,
34
+ });
31
35
  }
32
- return core.errors.oneOfError({ value: JSON.stringify(data), pointer, oneOf: schema.oneOf, errors });
36
+ return core.errors.oneOfError({
37
+ value: JSON.stringify(data),
38
+ pointer,
39
+ oneOf: schema.oneOf,
40
+ errors,
41
+ });
33
42
  }
@@ -2,6 +2,14 @@ export default function resolveRef(schema, rootSchema) {
2
2
  if (schema == null || schema.$ref == null) {
3
3
  return schema;
4
4
  }
5
+ if (schema.getRoot) {
6
+ // we actually always need to resolve the schema like this, since returned subschemas
7
+ // must resolve relative from their schema
8
+ const resolvedSchema = schema.getRoot().getRef(schema);
9
+ // console.log(schema.$ref, "=>", resolvedSchema);
10
+ return resolvedSchema;
11
+ }
12
+ // tryout - this should never be called, except we missed something
5
13
  const resolvedSchema = rootSchema.getRef(schema);
6
14
  return resolvedSchema;
7
15
  }
@@ -14,7 +14,18 @@ export default function getTypeDefs(schema) {
14
14
  if (id == null) {
15
15
  return defs;
16
16
  }
17
- const type = types[id];
17
+ let type;
18
+ if (Array.isArray(id)) {
19
+ // since types can also be declared as a set of types, merge the definitions
20
+ // maybe this will require a more sophisticated approach
21
+ type = {};
22
+ for (let i = 0, l = id.length; i < l; i += 1) {
23
+ Object.assign(type, types[id[i]]);
24
+ }
25
+ }
26
+ else {
27
+ type = types[id];
28
+ }
18
29
  if (type.definitions == null) {
19
30
  return defs;
20
31
  }
@@ -15,7 +15,7 @@ export default function getTypeId(schema) {
15
15
  if (schema.enum) {
16
16
  return "enum";
17
17
  }
18
- if (types[schema.type]) {
18
+ if (types[schema.type] || Array.isArray(schema.type)) {
19
19
  return schema.type;
20
20
  }
21
21
  const ids = typeKeywords.filter(type => schema[type]);
@@ -2,6 +2,7 @@ import getTypeOf from "./getTypeOf";
2
2
  import createSchemaOf from "./createSchemaOf";
3
3
  import errors from "./validation/errors";
4
4
  import merge from "./utils/merge";
5
+ import { isJSONError } from "./types";
5
6
  const stepType = {
6
7
  array: (core, key, schema, data, pointer) => {
7
8
  const itemsType = getTypeOf(schema.items);
@@ -23,11 +24,27 @@ const stepType = {
23
24
  return core.resolveRef(schema.items);
24
25
  }
25
26
  if (itemsType === "array") {
27
+ // @draft >= 7 bool schema, items:[true, false]
28
+ if (schema.items[key] === true) {
29
+ return createSchemaOf(data[key]);
30
+ }
31
+ // @draft >= 7 bool schema, items:[true, false]
32
+ if (schema.items[key] === false) {
33
+ return errors.invalidDataError({
34
+ key,
35
+ value: data[key],
36
+ pointer
37
+ });
38
+ }
26
39
  if (schema.items[key]) {
27
40
  return core.resolveRef(schema.items[key]);
28
41
  }
29
42
  if (schema.additionalItems === false) {
30
- return errors.additionalItemsError({ key, value: data[key], pointer });
43
+ return errors.additionalItemsError({
44
+ key,
45
+ value: data[key],
46
+ pointer
47
+ });
31
48
  }
32
49
  if (schema.additionalItems === true || schema.additionalItems === undefined) {
33
50
  return createSchemaOf(data[key]);
@@ -50,21 +67,21 @@ const stepType = {
50
67
  const oneOfSchema = core.resolveOneOf(data, schema, pointer);
51
68
  // resolveOneOf does currently not apply merge with base schema
52
69
  schema = merge(schema, oneOfSchema);
53
- if (schema && schema.type === "error") {
70
+ if (isJSONError(schema)) {
54
71
  return schema;
55
72
  }
56
73
  }
57
74
  if (Array.isArray(schema.anyOf)) {
58
75
  // update current schema
59
76
  schema = core.resolveAnyOf(data, schema, pointer);
60
- if (schema && schema.type === "error") {
77
+ if (isJSONError(schema)) {
61
78
  return schema;
62
79
  }
63
80
  }
64
81
  if (Array.isArray(schema.allOf)) {
65
82
  // update current schema
66
83
  schema = core.resolveAllOf(data, schema, pointer);
67
- if (schema && schema.type === "error") {
84
+ if (isJSONError(schema)) {
68
85
  return schema;
69
86
  }
70
87
  }
@@ -73,7 +90,7 @@ const stepType = {
73
90
  if (schema.properties && schema.properties[key] !== undefined) {
74
91
  // @todo patternProperties also validate properties
75
92
  targetSchema = core.resolveRef(schema.properties[key]);
76
- if (targetSchema && targetSchema.type === "error") {
93
+ if (isJSONError(targetSchema)) {
77
94
  return targetSchema;
78
95
  }
79
96
  // check if there is a oneOf selection, which must be resolved
@@ -81,8 +98,10 @@ const stepType = {
81
98
  // @special case: this is a mix of a schema and optional definitions
82
99
  // we resolve the schema here and add the original schema to `oneOfSchema`
83
100
  let resolvedSchema = core.resolveOneOf(data[key], targetSchema, `${pointer}/${key}`);
101
+ const oneOfIndex = targetSchema.oneOf.findIndex((s) => s === resolvedSchema);
84
102
  resolvedSchema = JSON.parse(JSON.stringify(resolvedSchema));
85
103
  resolvedSchema.variableSchema = true;
104
+ resolvedSchema.oneOfIndex = oneOfIndex;
86
105
  resolvedSchema.oneOfSchema = targetSchema;
87
106
  return resolvedSchema;
88
107
  }
@@ -91,6 +110,20 @@ const stepType = {
91
110
  return targetSchema;
92
111
  }
93
112
  }
113
+ // @draft <= 07
114
+ const { dependencies } = schema;
115
+ if (getTypeOf(dependencies) === "object") {
116
+ const dependentProperties = Object.keys(dependencies).filter((propertyName) =>
117
+ // data[propertyName] !== undefined &&
118
+ getTypeOf(dependencies[propertyName]) === "object");
119
+ for (let i = 0, l = dependentProperties.length; i < l; i += 1) {
120
+ const dependentProperty = dependentProperties[i];
121
+ const schema = step(core, key, dependencies[dependentProperty], data);
122
+ if (!isJSONError(schema)) {
123
+ return schema;
124
+ }
125
+ }
126
+ }
94
127
  // find matching property key
95
128
  if (getTypeOf(schema.patternProperties) === "object") {
96
129
  let regex;
@@ -106,9 +139,14 @@ const stepType = {
106
139
  return schema.additionalProperties;
107
140
  }
108
141
  if (schema.additionalProperties === true) {
109
- return createSchemaOf(data);
142
+ return createSchemaOf(data[key]);
110
143
  }
111
- return errors.unknownPropertyError({ property: key, value: data, pointer });
144
+ return errors.unknownPropertyError({
145
+ property: key,
146
+ value: data,
147
+ // pointer: `${pointer}/${key}`,
148
+ pointer: `${pointer}`
149
+ });
112
150
  }
113
151
  };
114
152
  /**
@@ -118,14 +156,27 @@ const stepType = {
118
156
  * This helper determines the location of the property within the schema (additional properties, oneOf, ...) and
119
157
  * returns the correct schema.
120
158
  *
121
- * @param core - validator
122
- * @param key - property-name or array-index
123
- * @param schema - json schema of current data
159
+ * @param core - validator
160
+ * @param key - property-name or array-index
161
+ * @param schema - json schema of current data
124
162
  * @param data - parent of key
125
- * @param [pointer]
163
+ * @param [pointer] - pointer to schema and data (parent of key)
126
164
  * @return Schema or Error if failed resolving key
127
165
  */
128
166
  export default function step(core, key, schema, data, pointer = "#") {
167
+ // @draft >= 4 ?
168
+ if (Array.isArray(schema.type)) {
169
+ const dataType = getTypeOf(data);
170
+ if (schema.type.includes(dataType)) {
171
+ return stepType[dataType](core, key, schema, data, pointer);
172
+ }
173
+ return core.errors.typeError({
174
+ value: data,
175
+ pointer,
176
+ expected: schema.type,
177
+ received: dataType
178
+ });
179
+ }
129
180
  const expectedType = schema.type || getTypeOf(data);
130
181
  if (stepType[expectedType]) {
131
182
  return stepType[expectedType](core, key, schema, data, pointer);
@@ -1 +1,7 @@
1
- export {};
1
+ /**
2
+ * ts type guard for json error
3
+ * @returns true if passed type is a JSONError
4
+ */
5
+ export function isJSONError(error) {
6
+ return (error === null || error === void 0 ? void 0 : error.type) === "error";
7
+ }
@@ -1,12 +1,10 @@
1
+ import { isJSONError } from "../types";
1
2
  export function isPromise(obj) {
2
3
  return obj instanceof Promise;
3
4
  }
4
- export function isError(obj) {
5
- return obj && obj.type === "error";
6
- }
7
5
  export function errorOrPromise(error) {
8
- return isError(error) || isPromise(error);
6
+ return isJSONError(error) || isPromise(error);
9
7
  }
10
8
  export function errorsOnly(error) {
11
- return isError(error);
9
+ return isJSONError(error);
12
10
  }
@@ -1,3 +1,6 @@
1
1
  import deepmerge from "deepmerge";
2
2
  const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
3
+ /**
4
+ * returns a new json-schema, where properties are combined and arrays are replaced
5
+ */
3
6
  export default (a, b) => deepmerge(a, b, { arrayMerge: overwriteMerge });
@@ -1,11 +1,14 @@
1
1
  import getTypeOf from "./getTypeOf";
2
2
  import { errorOrPromise } from "./utils/filter";
3
3
  import flattenArray from "./utils/flattenArray";
4
+ import { isJSONError } from "./types";
5
+ import equal from "fast-deep-equal";
4
6
  function getJsonSchemaType(value, expectedType) {
5
- let jsType = getTypeOf(value);
6
- if (jsType === "number" && (expectedType === "integer" ||
7
- (Array.isArray(expectedType) && expectedType.includes("integer")))) {
8
- jsType = Number.isInteger(value) ? "integer" : "number";
7
+ const jsType = getTypeOf(value);
8
+ if (jsType === "number" &&
9
+ (expectedType === "integer" ||
10
+ (Array.isArray(expectedType) && expectedType.includes("integer")))) {
11
+ return Number.isInteger(value) ? "integer" : "number";
9
12
  }
10
13
  return jsType;
11
14
  }
@@ -19,14 +22,36 @@ function getJsonSchemaType(value, expectedType) {
19
22
  * @return list of errors or empty
20
23
  */
21
24
  export default function validate(core, value, schema = core.rootSchema, pointer = "#") {
22
- if (schema.type === "error") {
25
+ schema = core.resolveRef(schema);
26
+ // this is a high level v7 schema validation
27
+ if (getTypeOf(schema) === "boolean") {
28
+ if (schema) {
29
+ return [];
30
+ }
31
+ return [core.errors.invalidDataError({ value, pointer })];
32
+ }
33
+ if (isJSONError(schema)) {
23
34
  return [schema];
24
35
  }
25
- schema = core.resolveRef(schema);
36
+ // @draft >= 6 const
37
+ if (schema.const !== undefined) {
38
+ if (equal(schema.const, value)) {
39
+ return [];
40
+ }
41
+ return [core.errors.constError({ value, expected: schema.const, pointer })];
42
+ }
26
43
  const receivedType = getJsonSchemaType(value, schema.type);
27
44
  const expectedType = schema.type || receivedType;
28
- if (receivedType !== expectedType && (!Array.isArray(expectedType) || !expectedType.includes(receivedType))) {
29
- return [core.errors.typeError({ received: receivedType, expected: expectedType, value, pointer })];
45
+ if (receivedType !== expectedType &&
46
+ (!Array.isArray(expectedType) || !expectedType.includes(receivedType))) {
47
+ return [
48
+ core.errors.typeError({
49
+ received: receivedType,
50
+ expected: expectedType,
51
+ value,
52
+ pointer
53
+ })
54
+ ];
30
55
  }
31
56
  if (core.validateType[receivedType] == null) {
32
57
  return [core.errors.invalidTypeError({ receivedType, pointer })];
@@ -1,5 +1,6 @@
1
- import { isError, errorsOnly } from "./utils/filter";
1
+ import { errorsOnly } from "./utils/filter";
2
2
  import flattenArray from "./utils/flattenArray";
3
+ import { isJSONError } from "./types";
3
4
  function createErrorNotification(onError) {
4
5
  return function notifyError(error) {
5
6
  if (Array.isArray(error)) {
@@ -7,7 +8,7 @@ function createErrorNotification(onError) {
7
8
  error.forEach(notifyError);
8
9
  return error;
9
10
  }
10
- if (isError(error)) {
11
+ if (isJSONError(error)) {
11
12
  onError(error);
12
13
  }
13
14
  return error;
@@ -35,16 +36,15 @@ export default function validateAsync(core, value, options) {
35
36
  if (errors[i] instanceof Promise) {
36
37
  errors[i].then(notifyError);
37
38
  }
38
- else if (isError(errors[i])) {
39
+ else if (isJSONError(errors[i])) {
39
40
  onError(errors[i]);
40
41
  }
41
42
  }
42
43
  }
43
- return Promise
44
- .all(errors)
44
+ return Promise.all(errors)
45
45
  .then(flattenArray)
46
- .then(resolvedErrors => resolvedErrors.filter(errorsOnly))
47
- .catch(e => {
46
+ .then((resolvedErrors) => resolvedErrors.filter(errorsOnly))
47
+ .catch((e) => {
48
48
  console.log("Failed resolving promises", e.message);
49
49
  console.log(e.stack);
50
50
  throw e;
@@ -5,17 +5,30 @@ const errors = {
5
5
  additionalPropertiesError: createCustomError("AdditionalPropertiesError"),
6
6
  anyOfError: createCustomError("AnyOfError"),
7
7
  allOfError: createCustomError("AllOfError"),
8
+ constError: createCustomError("ConstError"),
9
+ containsError: createCustomError("ContainsError"),
10
+ containsArrayError: createCustomError("ContainsArrayError"),
11
+ containsAnyError: createCustomError("ContainsAnyError"),
8
12
  enumError: createCustomError("EnumError"),
9
- formatUrlError: createCustomError("FormatUrlError"),
10
- formatUriError: createCustomError("FormatUriError"),
13
+ formatURLError: createCustomError("FormatURLError"),
14
+ formatURIError: createCustomError("FormatURIError"),
15
+ formatURIReferenceError: createCustomError("FormatURIReferenceError"),
16
+ formatURITemplateError: createCustomError("FormatURITemplateError"),
17
+ formatDateError: createCustomError("FormatDateaError"),
11
18
  formatDateTimeError: createCustomError("FormatDateTimeError"),
12
19
  formatEmailError: createCustomError("FormatEmailError"),
13
20
  formatHostnameError: createCustomError("FormatHostnameError"),
14
21
  formatIPV4Error: createCustomError("FormatIPV4Error"),
22
+ formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"),
15
23
  formatIPV6Error: createCustomError("FormatIPV6Error"),
24
+ formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"),
25
+ formatJSONPointerError: createCustomError("FormatJSONPointerError"),
16
26
  formatRegExError: createCustomError("FormatRegExError"),
27
+ formatTimeError: createCustomError("FormatTimeError"),
17
28
  invalidSchemaError: createCustomError("InvalidSchemaError"),
29
+ invalidDataError: createCustomError("InvalidDataError"),
18
30
  invalidTypeError: createCustomError("InvalidTypeError"),
31
+ invalidPropertyNameError: createCustomError("InvalidPropertyNameError"),
19
32
  maximumError: createCustomError("MaximumError"),
20
33
  maxItemsError: createCustomError("MaxItemsError"),
21
34
  maxLengthError: createCustomError("MaxLengthError"),
@@ -1,13 +1,43 @@
1
- /* eslint-disable max-len */
1
+ /* eslint-disable max-len, no-control-regex */
2
2
  import errors from "./errors";
3
3
  import validUrl from "valid-url";
4
+ // referenced
5
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
4
6
  // https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8
5
7
  const isValidDateTime = new RegExp("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([Zz])|([\\+|\\-]([01][0-9]|2[0-3]):[0-5][0-9]))$");
6
8
  const isValidIPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
7
9
  const isValidIPV6 = /^((([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;
8
10
  const isValidHostname = /^(?=.{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])?)*\.?$/;
11
+ const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
12
+ const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
13
+ const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
14
+ const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
15
+ const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
16
+ const isValidURIRef = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
17
+ // uri-template: https://tools.ietf.org/html/rfc6570
18
+ const isValidURITemplate = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
9
19
  // Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref
10
20
  export default {
21
+ date: (core, schema, value, pointer) => {
22
+ if (typeof value !== "string") {
23
+ return undefined;
24
+ }
25
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
26
+ // full-date from http://tools.ietf.org/html/rfc3339#section-5.6
27
+ const matches = value.match(matchDate);
28
+ if (!matches) {
29
+ return errors.formatDateTimeError({ value, pointer });
30
+ }
31
+ const year = +matches[1];
32
+ const month = +matches[2];
33
+ const day = +matches[3];
34
+ // https://tools.ietf.org/html/rfc3339#appendix-C
35
+ const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
36
+ if (month >= 1 && month <= 12 && day >= 1 && day <= (month == 2 && isLeapYear ? 29 : DAYS[month])) {
37
+ return undefined;
38
+ }
39
+ return errors.formatDateError({ value, pointer });
40
+ },
11
41
  "date-time": (core, schema, value, pointer) => {
12
42
  if (typeof value !== "string") {
13
43
  return undefined;
@@ -56,6 +86,10 @@ export default {
56
86
  if (typeof value !== "string" || value === "") {
57
87
  return undefined;
58
88
  }
89
+ if (value && value[0] === "0") {
90
+ // leading zeroes should be rejected, as they are treated as octals
91
+ return errors.formatIPV4LeadingZeroError({ value, pointer });
92
+ }
59
93
  if (value.length <= 15 && isValidIPV4.test(value)) {
60
94
  return undefined;
61
95
  }
@@ -65,25 +99,92 @@ export default {
65
99
  if (typeof value !== "string" || value === "") {
66
100
  return undefined;
67
101
  }
102
+ if (value && value[0] === "0") {
103
+ // leading zeroes should be rejected, as they are treated as octals
104
+ return errors.formatIPV6LeadingZeroError({ value, pointer });
105
+ }
68
106
  if (value.length <= 45 && isValidIPV6.test(value)) {
69
107
  return undefined;
70
108
  }
71
109
  return errors.formatIPV6Error({ value, pointer });
72
110
  },
111
+ "json-pointer": (core, schema, value, pointer) => {
112
+ if (typeof value !== "string" || value === "") {
113
+ return undefined;
114
+ }
115
+ if (isValidJSONPointer.test(value)) {
116
+ return undefined;
117
+ }
118
+ return errors.formatJSONPointerError({ value, pointer });
119
+ },
120
+ "relative-json-pointer": (core, schema, value, pointer) => {
121
+ if (typeof value !== "string" || value === "") {
122
+ return undefined;
123
+ }
124
+ if (isValidRelativeJSONPointer.test(value)) {
125
+ return undefined;
126
+ }
127
+ return errors.formatJSONPointerError({ value, pointer });
128
+ },
73
129
  regex: (core, schema, value, pointer) => {
74
130
  if (typeof value === "string" && /\\Z$/.test(value) === false) {
131
+ try {
132
+ new RegExp(value);
133
+ return undefined;
134
+ }
135
+ catch (e) { } // eslint-disable-line no-empty
136
+ return errors.formatRegExError({ value, pointer });
137
+ }
138
+ // v7 tests, ignore non-regex values
139
+ if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) {
75
140
  return undefined;
76
141
  }
77
142
  return errors.formatRegExError({ value, pointer });
78
143
  },
79
- uri: (core, schema, value, pointer) => {
144
+ time: (core, schema, value, pointer) => {
80
145
  if (typeof value !== "string") {
81
146
  return undefined;
82
147
  }
83
- if (value === "" || validUrl.isUri(value)) {
148
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
149
+ const matches = value.match(matchTime);
150
+ if (!matches) {
151
+ return errors.formatDateTimeError({ value, pointer });
152
+ }
153
+ const hour = +matches[1];
154
+ const minute = +matches[2];
155
+ const second = +matches[3];
156
+ const timeZone = !!matches[5];
157
+ if (((hour <= 23 && minute <= 59 && second <= 59) || (hour == 23 && minute == 59 && second == 60)) && timeZone) {
158
+ return undefined;
159
+ }
160
+ return errors.formatTimeError({ value, pointer });
161
+ },
162
+ uri: (core, schema, value, pointer) => {
163
+ if (typeof value !== "string" || value === "") {
164
+ return undefined;
165
+ }
166
+ if (validUrl.isUri(value)) {
167
+ return undefined;
168
+ }
169
+ return errors.formatURIError({ value, pointer });
170
+ },
171
+ "uri-reference": (core, schema, value, pointer) => {
172
+ if (typeof value !== "string" || value === "") {
173
+ return undefined;
174
+ }
175
+ if (isValidURIRef.test(value)) {
176
+ return undefined;
177
+ }
178
+ return errors.formatURIReferenceError({ value, pointer });
179
+ },
180
+ "uri-template": (core, schema, value, pointer) => {
181
+ if (typeof value !== "string" || value === "") {
182
+ return undefined;
183
+ }
184
+ if (isValidURITemplate.test(value)) {
84
185
  return undefined;
85
186
  }
86
- return errors.formatUriError({ value, pointer });
187
+ return errors.formatURITemplateError({ value, pointer });
87
188
  },
88
189
  url: (core, schema, value, pointer) => {
89
190
  if (value === "" || validUrl.isWebUri(value)) {