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
package/lib/step.ts CHANGED
@@ -2,13 +2,17 @@ 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 { JSONSchema, JSONPointer, JSONError } from "./types";
5
+ import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
6
6
  import Core from "./cores/CoreInterface";
7
7
 
8
-
9
8
  const stepType = {
10
-
11
- array: (core: Core, key: string|number, schema: JSONSchema, data: any, pointer: JSONPointer): JSONSchema|JSONError => {
9
+ array: (
10
+ core: Core,
11
+ key: string | number,
12
+ schema: JSONSchema,
13
+ data: any,
14
+ pointer: JSONPointer
15
+ ): JSONSchema | JSONError => {
12
16
  const itemsType = getTypeOf(schema.items);
13
17
 
14
18
  if (itemsType === "object") {
@@ -33,12 +37,29 @@ const stepType = {
33
37
  }
34
38
 
35
39
  if (itemsType === "array") {
40
+ // @draft >= 7 bool schema, items:[true, false]
41
+ if (schema.items[key] === true) {
42
+ return createSchemaOf(data[key]);
43
+ }
44
+ // @draft >= 7 bool schema, items:[true, false]
45
+ if (schema.items[key] === false) {
46
+ return errors.invalidDataError({
47
+ key,
48
+ value: data[key],
49
+ pointer
50
+ });
51
+ }
52
+
36
53
  if (schema.items[key]) {
37
54
  return core.resolveRef(schema.items[key]);
38
55
  }
39
56
 
40
57
  if (schema.additionalItems === false) {
41
- return errors.additionalItemsError({ key, value: data[key], pointer });
58
+ return errors.additionalItemsError({
59
+ key,
60
+ value: data[key],
61
+ pointer
62
+ });
42
63
  }
43
64
 
44
65
  if (schema.additionalItems === true || schema.additionalItems === undefined) {
@@ -49,7 +70,13 @@ const stepType = {
49
70
  return schema.additionalItems;
50
71
  }
51
72
 
52
- throw new Error(`Invalid schema ${JSON.stringify(schema, null, 4)} for ${JSON.stringify(data, null, 4)}`);
73
+ throw new Error(
74
+ `Invalid schema ${JSON.stringify(schema, null, 4)} for ${JSON.stringify(
75
+ data,
76
+ null,
77
+ 4
78
+ )}`
79
+ );
53
80
  }
54
81
 
55
82
  if (schema.additionalItems !== false && data[key]) {
@@ -61,14 +88,19 @@ const stepType = {
61
88
  return new Error(`Invalid array schema for ${key} at ${pointer}`) as JSONError;
62
89
  },
63
90
 
64
- object: (core: Core, key: string|number, schema: JSONSchema, data: any, pointer: JSONPointer): JSONSchema|JSONError => {
65
-
91
+ object: (
92
+ core: Core,
93
+ key: string | number,
94
+ schema: JSONSchema,
95
+ data: any,
96
+ pointer: JSONPointer
97
+ ): JSONSchema | JSONError => {
66
98
  if (Array.isArray(schema.oneOf)) {
67
99
  // update current schema
68
100
  const oneOfSchema = core.resolveOneOf(data, schema, pointer);
69
101
  // resolveOneOf does currently not apply merge with base schema
70
102
  schema = merge(schema, oneOfSchema);
71
- if (schema && schema.type === "error") {
103
+ if (isJSONError(schema)) {
72
104
  return schema;
73
105
  }
74
106
  }
@@ -76,7 +108,7 @@ const stepType = {
76
108
  if (Array.isArray(schema.anyOf)) {
77
109
  // update current schema
78
110
  schema = core.resolveAnyOf(data, schema, pointer);
79
- if (schema && schema.type === "error") {
111
+ if (isJSONError(schema)) {
80
112
  return schema;
81
113
  }
82
114
  }
@@ -84,7 +116,7 @@ const stepType = {
84
116
  if (Array.isArray(schema.allOf)) {
85
117
  // update current schema
86
118
  schema = core.resolveAllOf(data, schema, pointer);
87
- if (schema && schema.type === "error") {
119
+ if (isJSONError(schema)) {
88
120
  return schema;
89
121
  }
90
122
  }
@@ -96,7 +128,7 @@ const stepType = {
96
128
  // @todo patternProperties also validate properties
97
129
 
98
130
  targetSchema = core.resolveRef(schema.properties[key]);
99
- if (targetSchema && targetSchema.type === "error") {
131
+ if (isJSONError(targetSchema)) {
100
132
  return targetSchema;
101
133
  }
102
134
 
@@ -104,9 +136,17 @@ const stepType = {
104
136
  if (targetSchema && Array.isArray(targetSchema.oneOf)) {
105
137
  // @special case: this is a mix of a schema and optional definitions
106
138
  // we resolve the schema here and add the original schema to `oneOfSchema`
107
- let resolvedSchema = core.resolveOneOf(data[key], targetSchema, `${pointer}/${key}`);
139
+ let resolvedSchema = core.resolveOneOf(
140
+ data[key],
141
+ targetSchema,
142
+ `${pointer}/${key}`
143
+ );
144
+
145
+ const oneOfIndex = targetSchema.oneOf.findIndex((s) => s === resolvedSchema);
146
+
108
147
  resolvedSchema = JSON.parse(JSON.stringify(resolvedSchema));
109
148
  resolvedSchema.variableSchema = true;
149
+ resolvedSchema.oneOfIndex = oneOfIndex;
110
150
  resolvedSchema.oneOfSchema = targetSchema;
111
151
  return resolvedSchema;
112
152
  }
@@ -117,6 +157,24 @@ const stepType = {
117
157
  }
118
158
  }
119
159
 
160
+ // @draft <= 07
161
+ const { dependencies } = schema;
162
+ if (getTypeOf(dependencies) === "object") {
163
+ const dependentProperties = Object.keys(dependencies).filter(
164
+ (propertyName) =>
165
+ // data[propertyName] !== undefined &&
166
+ getTypeOf(dependencies[propertyName]) === "object"
167
+ );
168
+
169
+ for (let i = 0, l = dependentProperties.length; i < l; i += 1) {
170
+ const dependentProperty = dependentProperties[i];
171
+ const schema = step(core, key, dependencies[dependentProperty], data);
172
+ if (!isJSONError(schema)) {
173
+ return schema;
174
+ }
175
+ }
176
+ }
177
+
120
178
  // find matching property key
121
179
  if (getTypeOf(schema.patternProperties) === "object") {
122
180
  let regex;
@@ -134,14 +192,18 @@ const stepType = {
134
192
  }
135
193
 
136
194
  if (schema.additionalProperties === true) {
137
- return createSchemaOf(data);
195
+ return createSchemaOf(data[key]);
138
196
  }
139
197
 
140
- return errors.unknownPropertyError({ property: key, value: data, pointer });
198
+ return errors.unknownPropertyError({
199
+ property: key,
200
+ value: data,
201
+ // pointer: `${pointer}/${key}`,
202
+ pointer: `${pointer}`
203
+ });
141
204
  }
142
205
  };
143
206
 
144
-
145
207
  /**
146
208
  * Returns the json-schema of the given object property or array item.
147
209
  * e.g. it steps by one key into the data
@@ -149,15 +211,34 @@ const stepType = {
149
211
  * This helper determines the location of the property within the schema (additional properties, oneOf, ...) and
150
212
  * returns the correct schema.
151
213
  *
152
- * @param core - validator
153
- * @param key - property-name or array-index
154
- * @param schema - json schema of current data
214
+ * @param core - validator
215
+ * @param key - property-name or array-index
216
+ * @param schema - json schema of current data
155
217
  * @param data - parent of key
156
- * @param [pointer]
218
+ * @param [pointer] - pointer to schema and data (parent of key)
157
219
  * @return Schema or Error if failed resolving key
158
220
  */
159
- export default function step(core: Core, key: string|number, schema: JSONSchema, data?: any, pointer: JSONPointer = "#"):
160
- JSONSchema|JSONError {
221
+ export default function step(
222
+ core: Core,
223
+ key: string | number,
224
+ schema: JSONSchema,
225
+ data?: any,
226
+ pointer: JSONPointer = "#"
227
+ ): JSONSchema | JSONError {
228
+ // @draft >= 4 ?
229
+ if (Array.isArray(schema.type)) {
230
+ const dataType = getTypeOf(data);
231
+ if (schema.type.includes(dataType)) {
232
+ return stepType[dataType](core, key, schema, data, pointer);
233
+ }
234
+ return core.errors.typeError({
235
+ value: data,
236
+ pointer,
237
+ expected: schema.type,
238
+ received: dataType
239
+ });
240
+ }
241
+
161
242
  const expectedType = schema.type || getTypeOf(data);
162
243
  if (stepType[expectedType]) {
163
244
  return stepType[expectedType](core, key, schema, data, pointer);
package/lib/types.ts CHANGED
@@ -1,19 +1,36 @@
1
1
  import Core from "./cores/CoreInterface";
2
2
 
3
-
4
- export type JSONSchema = { [p:string]: any };
3
+ export type JSONSchema = { [p: string]: any };
5
4
 
6
5
  export type JSONPointer = string;
7
6
 
8
7
  export type JSONError = {
9
- type: "error"|"string";
8
+ type: "error";
10
9
  name: string;
11
10
  code: string;
12
11
  message: string;
13
12
  data?: { [p: string]: any };
14
13
  [p: string]: any;
14
+ };
15
+
16
+ /**
17
+ * ts type guard for json error
18
+ * @returns true if passed type is a JSONError
19
+ */
20
+ export function isJSONError(error): error is JSONError {
21
+ return error?.type === "error";
15
22
  }
16
23
 
17
24
  export interface JSONValidator {
18
- (core: Core, schema: JSONSchema, value: any, pointer: JSONPointer): void|undefined|JSONError|Array<JSONError>;
25
+ (core: Core, schema: JSONSchema, value: any, pointer: JSONPointer):
26
+ | void
27
+ | undefined
28
+ | JSONError
29
+ | JSONError[];
30
+ }
31
+
32
+ export interface JSONTypeValidator {
33
+ (core: Core, schema: JSONSchema, value: any, pointer: JSONPointer): Array<
34
+ void | undefined | JSONError | JSONError[]
35
+ >;
19
36
  }
@@ -1,15 +1,13 @@
1
+ import { isJSONError } from "../types";
2
+
1
3
  export function isPromise(obj) {
2
4
  return obj instanceof Promise;
3
5
  }
4
6
 
5
- export function isError(obj) {
6
- return obj && obj.type === "error";
7
- }
8
-
9
7
  export function errorOrPromise(error) {
10
- return isError(error) || isPromise(error);
8
+ return isJSONError(error) || isPromise(error);
11
9
  }
12
10
 
13
11
  export function errorsOnly(error) {
14
- return isError(error);
12
+ return isJSONError(error);
15
13
  }
@@ -1,3 +1,7 @@
1
1
  import deepmerge from "deepmerge";
2
2
  const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
3
+
4
+ /**
5
+ * returns a new json-schema, where properties are combined and arrays are replaced
6
+ */
3
7
  export default <T, K>(a: T, b: K): T & K => deepmerge(a, b, { arrayMerge: overwriteMerge });
package/lib/validate.ts CHANGED
@@ -1,23 +1,22 @@
1
- import getTypeOf from "./getTypeOf";
1
+ import getTypeOf, { JSType } from "./getTypeOf";
2
2
  import { errorOrPromise } from "./utils/filter";
3
3
  import flattenArray from "./utils/flattenArray";
4
- import { JSONSchema, JSONPointer, JSONError } from "./types";
4
+ import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
5
5
  import Core from "./cores/CoreInterface";
6
+ import equal from "fast-deep-equal";
6
7
 
7
-
8
- function getJsonSchemaType(value, expectedType) {
9
- let jsType = getTypeOf(value);
10
-
8
+ function getJsonSchemaType(value: unknown, expectedType: string | string[]): JSType | "integer" {
9
+ const jsType = getTypeOf(value);
11
10
  if (
12
- jsType === "number" && (expectedType === "integer" ||
13
- (Array.isArray(expectedType) && expectedType.includes("integer")))
11
+ jsType === "number" &&
12
+ (expectedType === "integer" ||
13
+ (Array.isArray(expectedType) && expectedType.includes("integer")))
14
14
  ) {
15
- jsType = Number.isInteger(value) ? "integer" : "number";
15
+ return Number.isInteger(value) ? "integer" : "number";
16
16
  }
17
17
  return jsType;
18
18
  }
19
19
 
20
-
21
20
  /**
22
21
  * Validate data by a json schema
23
22
  *
@@ -27,18 +26,49 @@ function getJsonSchemaType(value, expectedType) {
27
26
  * @param [pointer] - json pointer pointing to value (used for error-messages only)
28
27
  * @return list of errors or empty
29
28
  */
30
- export default function validate(core: Core, value: any, schema: JSONSchema = core.rootSchema, pointer: JSONPointer = "#"): Array<JSONError> {
31
- if (schema.type === "error") {
29
+ export default function validate(
30
+ core: Core,
31
+ value: unknown,
32
+ schema: JSONSchema = core.rootSchema,
33
+ pointer: JSONPointer = "#"
34
+ ): Array<JSONError> {
35
+ schema = core.resolveRef(schema);
36
+
37
+ // this is a high level v7 schema validation
38
+ if (getTypeOf(schema) === "boolean") {
39
+ if (schema) {
40
+ return [];
41
+ }
42
+ return [core.errors.invalidDataError({ value, pointer })];
43
+ }
44
+
45
+ if (isJSONError(schema)) {
32
46
  return [schema as JSONError];
33
47
  }
34
48
 
35
- schema = core.resolveRef(schema);
49
+ // @draft >= 6 const
50
+ if (schema.const !== undefined) {
51
+ if (equal(schema.const, value)) {
52
+ return [];
53
+ }
54
+ return [core.errors.constError({ value, expected: schema.const, pointer })];
55
+ }
36
56
 
37
57
  const receivedType = getJsonSchemaType(value, schema.type);
38
58
  const expectedType = schema.type || receivedType;
39
59
 
40
- if (receivedType !== expectedType && (!Array.isArray(expectedType) || !expectedType.includes(receivedType))) {
41
- return [core.errors.typeError({ received: receivedType, expected: expectedType, value, pointer })];
60
+ if (
61
+ receivedType !== expectedType &&
62
+ (!Array.isArray(expectedType) || !expectedType.includes(receivedType))
63
+ ) {
64
+ return [
65
+ core.errors.typeError({
66
+ received: receivedType,
67
+ expected: expectedType,
68
+ value,
69
+ pointer
70
+ })
71
+ ];
42
72
  }
43
73
 
44
74
  if (core.validateType[receivedType] == null) {
@@ -1,9 +1,8 @@
1
- import { isError, errorsOnly } from "./utils/filter";
1
+ import { errorsOnly } from "./utils/filter";
2
2
  import flattenArray from "./utils/flattenArray";
3
- import { JSONSchema, JSONPointer, JSONError } from "./types";
3
+ import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
4
4
  import Core from "./cores/CoreInterface";
5
5
 
6
-
7
6
  function createErrorNotification(onError: OnError) {
8
7
  return function notifyError(error) {
9
8
  if (Array.isArray(error)) {
@@ -11,14 +10,13 @@ function createErrorNotification(onError: OnError) {
11
10
  error.forEach(notifyError);
12
11
  return error;
13
12
  }
14
- if (isError(error)) {
13
+ if (isJSONError(error)) {
15
14
  onError(error);
16
15
  }
17
16
  return error;
18
17
  };
19
18
  }
20
19
 
21
-
22
20
  export interface OnError {
23
21
  (error: JSONError): void;
24
22
  }
@@ -27,7 +25,7 @@ export type Options = {
27
25
  schema?: JSONSchema;
28
26
  pointer?: JSONPointer;
29
27
  onError?: OnError;
30
- }
28
+ };
31
29
 
32
30
  /**
33
31
  * @async
@@ -41,7 +39,11 @@ export type Options = {
41
39
  * @param options.onError - will be called for each error as soon as it is resolved
42
40
  * @return list of errors or empty
43
41
  */
44
- export default function validateAsync(core: Core, value: any, options?: Options): Promise<Array<JSONError>> {
42
+ export default function validateAsync(
43
+ core: Core,
44
+ value: any,
45
+ options?: Options
46
+ ): Promise<Array<JSONError>> {
45
47
  const { schema, pointer, onError } = { schema: core.rootSchema, pointer: "#", ...options };
46
48
 
47
49
  let errors: Array<JSONError> = core.validate(value, schema, pointer);
@@ -51,17 +53,16 @@ export default function validateAsync(core: Core, value: any, options?: Options)
51
53
  for (let i = 0; i < errors.length; i += 1) {
52
54
  if (errors[i] instanceof Promise) {
53
55
  errors[i].then(notifyError);
54
- } else if (isError(errors[i])) {
56
+ } else if (isJSONError(errors[i])) {
55
57
  onError(errors[i]);
56
58
  }
57
59
  }
58
60
  }
59
61
 
60
- return Promise
61
- .all(errors)
62
+ return Promise.all(errors)
62
63
  .then(flattenArray)
63
- .then(resolvedErrors => resolvedErrors.filter(errorsOnly))
64
- .catch(e => {
64
+ .then((resolvedErrors) => resolvedErrors.filter(errorsOnly))
65
+ .catch((e) => {
65
66
  console.log("Failed resolving promises", e.message);
66
67
  console.log(e.stack);
67
68
  throw e;
@@ -7,17 +7,30 @@ const errors: { [p: string]: CreateError } = {
7
7
  additionalPropertiesError: createCustomError("AdditionalPropertiesError"),
8
8
  anyOfError: createCustomError("AnyOfError"),
9
9
  allOfError: createCustomError("AllOfError"),
10
+ constError: createCustomError("ConstError"),
11
+ containsError: createCustomError("ContainsError"),
12
+ containsArrayError: createCustomError("ContainsArrayError"),
13
+ containsAnyError: createCustomError("ContainsAnyError"),
10
14
  enumError: createCustomError("EnumError"),
11
- formatUrlError: createCustomError("FormatUrlError"),
12
- formatUriError: createCustomError("FormatUriError"),
15
+ formatURLError: createCustomError("FormatURLError"),
16
+ formatURIError: createCustomError("FormatURIError"),
17
+ formatURIReferenceError: createCustomError("FormatURIReferenceError"),
18
+ formatURITemplateError: createCustomError("FormatURITemplateError"),
19
+ formatDateError: createCustomError("FormatDateaError"),
13
20
  formatDateTimeError: createCustomError("FormatDateTimeError"),
14
21
  formatEmailError: createCustomError("FormatEmailError"),
15
22
  formatHostnameError: createCustomError("FormatHostnameError"),
16
23
  formatIPV4Error: createCustomError("FormatIPV4Error"),
24
+ formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"),
17
25
  formatIPV6Error: createCustomError("FormatIPV6Error"),
26
+ formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"),
27
+ formatJSONPointerError: createCustomError("FormatJSONPointerError"),
18
28
  formatRegExError: createCustomError("FormatRegExError"),
29
+ formatTimeError: createCustomError("FormatTimeError"),
19
30
  invalidSchemaError: createCustomError("InvalidSchemaError"),
31
+ invalidDataError: createCustomError("InvalidDataError"),
20
32
  invalidTypeError: createCustomError("InvalidTypeError"),
33
+ invalidPropertyNameError: createCustomError("InvalidPropertyNameError"),
21
34
  maximumError: createCustomError("MaximumError"),
22
35
  maxItemsError: createCustomError("MaxItemsError"),
23
36
  maxLengthError: createCustomError("MaxLengthError"),
@@ -1,17 +1,50 @@
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
4
 
5
+ // referenced
6
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
7
+
5
8
  // https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8
6
9
  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]))$");
7
10
  const isValidIPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
8
11
  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;
9
12
  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])?)*\.?$/;
13
+ const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
14
+ const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
15
+ const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
16
+
17
+ const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
18
+ const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
19
+ 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;
20
+ // uri-template: https://tools.ietf.org/html/rfc6570
21
+ 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;
10
22
 
11
23
 
12
24
  // Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref
13
25
  export default {
14
26
 
27
+ date: (core, schema, value, pointer) => {
28
+ if (typeof value !== "string") {
29
+ return undefined;
30
+ }
31
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
32
+ // full-date from http://tools.ietf.org/html/rfc3339#section-5.6
33
+ const matches = value.match(matchDate);
34
+ if (!matches) {
35
+ return errors.formatDateTimeError({ value, pointer });
36
+ }
37
+ const year = +matches[1];
38
+ const month = +matches[2];
39
+ const day = +matches[3];
40
+ // https://tools.ietf.org/html/rfc3339#appendix-C
41
+ const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
42
+ if (month >= 1 && month <= 12 && day >= 1 && day <= (month == 2 && isLeapYear ? 29 : DAYS[month])) {
43
+ return undefined;
44
+ }
45
+ return errors.formatDateError({ value, pointer });
46
+ },
47
+
15
48
  "date-time": (core, schema, value, pointer) => {
16
49
  if (typeof value !== "string") {
17
50
  return undefined;
@@ -63,6 +96,10 @@ export default {
63
96
  if (typeof value !== "string" || value === "") {
64
97
  return undefined;
65
98
  }
99
+ if (value && value[0] === "0") {
100
+ // leading zeroes should be rejected, as they are treated as octals
101
+ return errors.formatIPV4LeadingZeroError({ value, pointer });
102
+ }
66
103
  if (value.length <= 15 && isValidIPV4.test(value)) {
67
104
  return undefined;
68
105
  }
@@ -73,27 +110,99 @@ export default {
73
110
  if (typeof value !== "string" || value === "") {
74
111
  return undefined;
75
112
  }
113
+ if (value && value[0] === "0") {
114
+ // leading zeroes should be rejected, as they are treated as octals
115
+ return errors.formatIPV6LeadingZeroError({ value, pointer });
116
+ }
76
117
  if (value.length <= 45 && isValidIPV6.test(value)) {
77
118
  return undefined;
78
119
  }
79
120
  return errors.formatIPV6Error({ value, pointer });
80
121
  },
81
122
 
123
+ "json-pointer": (core, schema, value, pointer) => {
124
+ if (typeof value !== "string" || value === "") {
125
+ return undefined;
126
+ }
127
+ if (isValidJSONPointer.test(value)) {
128
+ return undefined;
129
+ }
130
+ return errors.formatJSONPointerError({ value, pointer });
131
+ },
132
+
133
+ "relative-json-pointer": (core, schema, value, pointer) => {
134
+ if (typeof value !== "string" || value === "") {
135
+ return undefined;
136
+ }
137
+ if (isValidRelativeJSONPointer.test(value)) {
138
+ return undefined;
139
+ }
140
+ return errors.formatJSONPointerError({ value, pointer });
141
+ },
142
+
82
143
  regex: (core, schema, value, pointer) => {
83
144
  if (typeof value === "string" && /\\Z$/.test(value) === false) {
145
+ try {
146
+ new RegExp(value);
147
+ return undefined;
148
+ } catch (e) {} // eslint-disable-line no-empty
149
+
150
+ return errors.formatRegExError({ value, pointer });
151
+ }
152
+ // v7 tests, ignore non-regex values
153
+ if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) {
84
154
  return undefined;
85
155
  }
86
156
  return errors.formatRegExError({ value, pointer });
87
157
  },
88
158
 
89
- uri: (core, schema, value, pointer) => {
159
+ time: (core, schema, value, pointer) => {
90
160
  if (typeof value !== "string") {
91
161
  return undefined;
92
162
  }
93
- if (value === "" || validUrl.isUri(value)) {
163
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
164
+ const matches = value.match(matchTime);
165
+ if (!matches) {
166
+ return errors.formatDateTimeError({ value, pointer });
167
+ }
168
+ const hour = +matches[1];
169
+ const minute = +matches[2];
170
+ const second = +matches[3];
171
+ const timeZone = !!matches[5];
172
+ if (((hour <= 23 && minute <= 59 && second <= 59) || (hour == 23 && minute == 59 && second == 60)) && timeZone) {
173
+ return undefined
174
+ }
175
+ return errors.formatTimeError({ value, pointer });
176
+ },
177
+
178
+ uri: (core, schema, value, pointer) => {
179
+ if (typeof value !== "string" || value === "") {
180
+ return undefined;
181
+ }
182
+ if (validUrl.isUri(value)) {
183
+ return undefined;
184
+ }
185
+ return errors.formatURIError({ value, pointer });
186
+ },
187
+
188
+ "uri-reference": (core, schema, value, pointer) => {
189
+ if (typeof value !== "string" || value === "") {
190
+ return undefined;
191
+ }
192
+ if (isValidURIRef.test(value)) {
193
+ return undefined;
194
+ }
195
+ return errors.formatURIReferenceError({ value, pointer });
196
+ },
197
+
198
+ "uri-template": (core, schema, value, pointer) => {
199
+ if (typeof value !== "string" || value === "") {
200
+ return undefined;
201
+ }
202
+ if (isValidURITemplate.test(value)) {
94
203
  return undefined;
95
204
  }
96
- return errors.formatUriError({ value, pointer });
205
+ return errors.formatURITemplateError({ value, pointer });
97
206
  },
98
207
 
99
208
  url: (core, schema, value, pointer) => {