json-schema-library 5.3.0 → 7.0.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 (161) hide show
  1. package/.editorconfig +1 -0
  2. package/.prettierignore +1 -0
  3. package/.prettierrc +7 -0
  4. package/CHANGELOG.md +106 -0
  5. package/README.md +811 -199
  6. package/TASKS.md +3 -81
  7. package/dist/index.d.ts +29 -71
  8. package/dist/jsonSchemaLibrary.js +1 -1
  9. package/dist/lib/SchemaService.d.ts +6 -8
  10. package/dist/lib/{addSchema.d.ts → addRemoteSchema.d.ts} +2 -1
  11. package/dist/lib/addValidator.d.ts +3 -2
  12. package/dist/lib/compile/getRef.d.ts +2 -1
  13. package/dist/lib/compile/index.d.ts +15 -1
  14. package/dist/lib/compile/types.d.ts +5 -0
  15. package/dist/lib/config/strings.d.ts +1 -39
  16. package/dist/lib/draft/index.d.ts +132 -0
  17. package/dist/lib/draft04/index.d.ts +7 -0
  18. package/dist/lib/draft06/compile/index.d.ts +16 -0
  19. package/dist/lib/draft06/index.d.ts +7 -0
  20. package/dist/lib/draft06/validation/keyword.d.ts +3 -0
  21. package/dist/lib/draft06/validation/type.d.ts +10 -0
  22. package/dist/lib/draft06/validation/typeKeywordMapping.d.ts +13 -0
  23. package/dist/lib/draft07/index.d.ts +7 -0
  24. package/dist/lib/each.d.ts +3 -2
  25. package/dist/lib/eachSchema.d.ts +2 -4
  26. package/dist/lib/getChildSchemaSelection.d.ts +7 -5
  27. package/dist/lib/getSchema.d.ts +2 -2
  28. package/dist/lib/getTemplate.d.ts +1 -1
  29. package/dist/lib/getTypeOf.d.ts +2 -1
  30. package/dist/lib/isValid.d.ts +1 -1
  31. package/dist/lib/jsoneditor/index.d.ts +7 -0
  32. package/dist/lib/resolveAllOf.d.ts +1 -1
  33. package/dist/lib/resolveAnyOf.d.ts +1 -1
  34. package/dist/lib/resolveOneOf.fuzzy.d.ts +1 -1
  35. package/dist/lib/resolveOneOf.strict.d.ts +2 -2
  36. package/dist/lib/{resolveRef.withOverwrite.d.ts → resolveRef.merge.d.ts} +0 -0
  37. package/dist/lib/schema/getTypeDefs.d.ts +6 -4
  38. package/dist/lib/schema/getTypeId.d.ts +1 -1
  39. package/dist/lib/schema/types.d.ts +7 -57
  40. package/dist/lib/step.d.ts +5 -5
  41. package/dist/lib/types.d.ts +11 -3
  42. package/dist/lib/utils/createCustomError.d.ts +8 -11
  43. package/dist/lib/utils/filter.d.ts +4 -4
  44. package/dist/lib/utils/flattenArray.d.ts +1 -1
  45. package/dist/lib/utils/merge.d.ts +3 -0
  46. package/dist/lib/utils/punycode.ucs2decode.d.ts +1 -1
  47. package/dist/lib/validate.d.ts +2 -2
  48. package/dist/lib/validateAsync.d.ts +1 -1
  49. package/dist/lib/validation/errors.d.ts +1 -3
  50. package/dist/lib/validation/format.d.ts +4 -11
  51. package/dist/lib/validation/keyword.d.ts +2 -27
  52. package/dist/lib/validation/type.d.ts +3 -10
  53. package/dist/lib/validation/typeKeywordMapping.d.ts +4 -4
  54. package/dist/module/index.js +23 -32
  55. package/dist/module/lib/SchemaService.js +7 -4
  56. package/dist/module/lib/{addSchema.js → addRemoteSchema.js} +2 -4
  57. package/dist/module/lib/addValidator.js +3 -4
  58. package/dist/module/lib/compile/getRef.js +1 -1
  59. package/dist/module/lib/compile/index.js +43 -18
  60. package/dist/module/lib/compile/types.js +1 -0
  61. package/dist/module/lib/config/strings.js +15 -2
  62. package/dist/module/lib/createSchemaOf.js +1 -1
  63. package/dist/module/lib/draft/index.js +133 -0
  64. package/dist/module/lib/draft04/index.js +90 -0
  65. package/dist/module/lib/draft06/compile/index.js +77 -0
  66. package/dist/module/lib/draft06/index.js +96 -0
  67. package/dist/module/lib/draft06/validation/keyword.js +168 -0
  68. package/dist/module/lib/draft06/validation/type.js +31 -0
  69. package/dist/module/lib/draft06/validation/typeKeywordMapping.js +15 -0
  70. package/dist/module/lib/draft07/index.js +96 -0
  71. package/dist/module/lib/each.js +2 -2
  72. package/dist/module/lib/eachSchema.js +28 -19
  73. package/dist/module/lib/getChildSchemaSelection.js +7 -6
  74. package/dist/module/lib/getSchema.js +4 -2
  75. package/dist/module/lib/getTemplate.js +42 -15
  76. package/dist/module/lib/jsoneditor/index.js +16 -0
  77. package/dist/module/lib/resolveAllOf.js +3 -4
  78. package/dist/module/lib/resolveOneOf.fuzzy.js +13 -3
  79. package/dist/module/lib/resolveOneOf.strict.js +49 -2
  80. package/dist/module/lib/{resolveRef.withOverwrite.js → resolveRef.merge.js} +0 -0
  81. package/dist/module/lib/resolveRef.strict.js +8 -0
  82. package/dist/module/lib/schema/getTypeDefs.js +14 -3
  83. package/dist/module/lib/schema/getTypeId.js +10 -6
  84. package/dist/module/lib/schema/types.js +33 -9
  85. package/dist/module/lib/step.js +67 -13
  86. package/dist/module/lib/types.js +7 -1
  87. package/dist/module/lib/utils/createCustomError.js +4 -4
  88. package/dist/module/lib/utils/filter.js +3 -5
  89. package/dist/module/lib/utils/flattenArray.js +4 -3
  90. package/dist/module/lib/utils/merge.js +4 -0
  91. package/dist/module/lib/utils/punycode.ucs2decode.js +4 -3
  92. package/dist/module/lib/validate.js +34 -8
  93. package/dist/module/lib/validateAsync.js +7 -7
  94. package/dist/module/lib/validation/errors.js +16 -3
  95. package/dist/module/lib/validation/format.js +115 -8
  96. package/dist/module/lib/validation/keyword.js +79 -32
  97. package/dist/module/lib/validation/type.js +2 -1
  98. package/index.ts +46 -32
  99. package/lib/SchemaService.ts +18 -11
  100. package/lib/{addSchema.ts → addRemoteSchema.ts} +3 -5
  101. package/lib/addValidator.ts +15 -12
  102. package/lib/compile/getRef.ts +3 -4
  103. package/lib/compile/index.ts +65 -19
  104. package/lib/compile/types.ts +6 -0
  105. package/lib/config/strings.ts +17 -3
  106. package/lib/createSchemaOf.ts +1 -3
  107. package/lib/draft/index.ts +201 -0
  108. package/lib/draft04/index.ts +95 -0
  109. package/lib/draft06/compile/index.ts +104 -0
  110. package/lib/draft06/index.ts +101 -0
  111. package/lib/draft06/validation/keyword.ts +199 -0
  112. package/lib/draft06/validation/type.ts +47 -0
  113. package/lib/draft06/validation/typeKeywordMapping.ts +15 -0
  114. package/lib/draft07/index.ts +101 -0
  115. package/lib/each.ts +11 -4
  116. package/lib/eachSchema.ts +45 -32
  117. package/lib/getChildSchemaSelection.ts +14 -7
  118. package/lib/getSchema.ts +18 -9
  119. package/lib/getTemplate.ts +155 -42
  120. package/lib/getTypeOf.ts +2 -1
  121. package/lib/isValid.ts +7 -3
  122. package/lib/jsoneditor/index.ts +20 -0
  123. package/lib/resolveAllOf.ts +10 -6
  124. package/lib/resolveAnyOf.ts +7 -3
  125. package/lib/resolveOneOf.fuzzy.ts +26 -9
  126. package/lib/resolveOneOf.strict.ts +63 -5
  127. package/lib/{resolveRef.withOverwrite.ts → resolveRef.merge.ts} +0 -0
  128. package/lib/resolveRef.strict.ts +9 -0
  129. package/lib/schema/getTypeDefs.ts +19 -7
  130. package/lib/schema/getTypeId.ts +11 -8
  131. package/lib/schema/types.ts +41 -9
  132. package/lib/step.ts +109 -25
  133. package/lib/types.ts +23 -5
  134. package/lib/utils/createCustomError.ts +8 -13
  135. package/lib/utils/filter.ts +7 -9
  136. package/lib/utils/flattenArray.ts +5 -4
  137. package/lib/utils/merge.ts +5 -0
  138. package/lib/utils/punycode.ucs2decode.ts +6 -5
  139. package/lib/validate.ts +47 -16
  140. package/lib/validateAsync.ts +15 -14
  141. package/lib/validation/errors.ts +17 -6
  142. package/lib/validation/format.ts +147 -13
  143. package/lib/validation/keyword.ts +172 -103
  144. package/lib/validation/type.ts +5 -1
  145. package/package.json +75 -63
  146. package/{dist/module/remotes/draft04.json → remotes/draft06.json} +47 -42
  147. package/remotes/draft07.json +172 -0
  148. package/remotes/draft2019-09.json +86 -0
  149. package/tsconfig.json +3 -10
  150. package/dist/lib/cores/CoreInterface.d.ts +0 -23
  151. package/dist/lib/cores/Draft04.d.ts +0 -13
  152. package/dist/lib/cores/JsonEditor.d.ts +0 -13
  153. package/dist/module/lib/cores/CoreInterface.js +0 -51
  154. package/dist/module/lib/cores/Draft04.js +0 -52
  155. package/dist/module/lib/cores/JsonEditor.js +0 -51
  156. package/dist/module/remotes/index.js +0 -10
  157. package/dist/remotes/index.d.ts +0 -5
  158. package/lib/cores/CoreInterface.ts +0 -76
  159. package/lib/cores/Draft04.ts +0 -66
  160. package/lib/cores/JsonEditor.ts +0 -64
  161. package/remotes/index.ts +0 -11
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";
5
- import Core from "./cores/CoreInterface";
6
-
7
-
8
- function getJsonSchemaType(value, expectedType) {
9
- let jsType = getTypeOf(value);
4
+ import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
5
+ import { Draft as Core } from "./draft";
6
+ import equal from "fast-deep-equal";
10
7
 
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) {
@@ -47,5 +77,6 @@ export default function validate(core: Core, value: any, schema: JSONSchema = co
47
77
 
48
78
  const errors = flattenArray(core.validateType[receivedType](core, schema, value, pointer));
49
79
  // also promises may be passed along (validateAsync)
80
+ // @ts-ignore
50
81
  return errors.filter(errorOrPromise);
51
82
  }
@@ -1,24 +1,22 @@
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";
4
- import Core from "./cores/CoreInterface";
5
-
3
+ import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
4
+ import { Draft as Core } from "./draft";
6
5
 
7
6
  function createErrorNotification(onError: OnError) {
8
- return function notifyError(error) {
7
+ return function notifyError(error: JSONError | JSONError[]) {
9
8
  if (Array.isArray(error)) {
10
9
  error = flattenArray(error);
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;
@@ -1,23 +1,35 @@
1
1
  /* eslint no-invalid-this: 0 */
2
- import createCustomError, { CreateError } from "../utils/createCustomError";
2
+ import { createCustomError, CreateError } from "../utils/createCustomError";
3
3
 
4
-
5
- const errors: { [p: string]: CreateError } = {
4
+ const errors: Record<string, CreateError> = {
6
5
  additionalItemsError: createCustomError("AdditionalItemsError"),
7
6
  additionalPropertiesError: createCustomError("AdditionalPropertiesError"),
8
7
  anyOfError: createCustomError("AnyOfError"),
9
8
  allOfError: createCustomError("AllOfError"),
9
+ constError: createCustomError("ConstError"),
10
+ containsError: createCustomError("ContainsError"),
11
+ containsArrayError: createCustomError("ContainsArrayError"),
12
+ containsAnyError: createCustomError("ContainsAnyError"),
10
13
  enumError: createCustomError("EnumError"),
11
- formatUrlError: createCustomError("FormatUrlError"),
12
- formatUriError: createCustomError("FormatUriError"),
14
+ formatURLError: createCustomError("FormatURLError"),
15
+ formatURIError: createCustomError("FormatURIError"),
16
+ formatURIReferenceError: createCustomError("FormatURIReferenceError"),
17
+ formatURITemplateError: createCustomError("FormatURITemplateError"),
18
+ formatDateError: createCustomError("FormatDateaError"),
13
19
  formatDateTimeError: createCustomError("FormatDateTimeError"),
14
20
  formatEmailError: createCustomError("FormatEmailError"),
15
21
  formatHostnameError: createCustomError("FormatHostnameError"),
16
22
  formatIPV4Error: createCustomError("FormatIPV4Error"),
23
+ formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"),
17
24
  formatIPV6Error: createCustomError("FormatIPV6Error"),
25
+ formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"),
26
+ formatJSONPointerError: createCustomError("FormatJSONPointerError"),
18
27
  formatRegExError: createCustomError("FormatRegExError"),
28
+ formatTimeError: createCustomError("FormatTimeError"),
19
29
  invalidSchemaError: createCustomError("InvalidSchemaError"),
30
+ invalidDataError: createCustomError("InvalidDataError"),
20
31
  invalidTypeError: createCustomError("InvalidTypeError"),
32
+ invalidPropertyNameError: createCustomError("InvalidPropertyNameError"),
21
33
  maximumError: createCustomError("MaximumError"),
22
34
  maxItemsError: createCustomError("MaxItemsError"),
23
35
  maxLengthError: createCustomError("MaxLengthError"),
@@ -44,5 +56,4 @@ const errors: { [p: string]: CreateError } = {
44
56
  valueNotEmptyError: createCustomError("ValueNotEmptyError")
45
57
  };
46
58
 
47
-
48
59
  export default errors;
@@ -1,16 +1,68 @@
1
- /* eslint-disable max-len */
1
+ /* eslint-disable max-len, no-control-regex */
2
2
  import errors from "./errors";
3
+ import { JSONError, JSONSchema } from "../types";
4
+ import { Draft } from "../draft";
3
5
  import validUrl from "valid-url";
4
6
 
7
+ // referenced
8
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
9
+
5
10
  // https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8
6
- 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]))$");
11
+ const isValidDateTime = new RegExp(
12
+ "^([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]))$"
13
+ );
7
14
  const isValidIPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
8
- 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
- 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])?)*\.?$/;
15
+ const isValidIPV6 =
16
+ /^((([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;
17
+ const isValidHostname =
18
+ /^(?=.{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])?)*\.?$/;
19
+ const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
20
+ const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
21
+ const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
10
22
 
23
+ const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
24
+ const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
25
+ const isValidURIRef =
26
+ /^(?:[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;
27
+ // uri-template: https://tools.ietf.org/html/rfc6570
28
+ const isValidURITemplate =
29
+ /^(?:(?:[^\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;
11
30
 
12
31
  // Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref
13
- export default {
32
+ const formatValidators: Record<
33
+ string,
34
+ (
35
+ draft: Draft,
36
+ schema: JSONSchema,
37
+ value: unknown,
38
+ pointer: string
39
+ ) => undefined | JSONError | JSONError[]
40
+ > = {
41
+ date: (core, schema, value, pointer) => {
42
+ if (typeof value !== "string") {
43
+ return undefined;
44
+ }
45
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
46
+ // full-date from http://tools.ietf.org/html/rfc3339#section-5.6
47
+ const matches = value.match(matchDate);
48
+ if (!matches) {
49
+ return errors.formatDateTimeError({ value, pointer });
50
+ }
51
+ const year = +matches[1];
52
+ const month = +matches[2];
53
+ const day = +matches[3];
54
+ // https://tools.ietf.org/html/rfc3339#appendix-C
55
+ const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
56
+ if (
57
+ month >= 1 &&
58
+ month <= 12 &&
59
+ day >= 1 &&
60
+ day <= (month == 2 && isLeapYear ? 29 : DAYS[month])
61
+ ) {
62
+ return undefined;
63
+ }
64
+ return errors.formatDateError({ value, pointer });
65
+ },
14
66
 
15
67
  "date-time": (core, schema, value, pointer) => {
16
68
  if (typeof value !== "string") {
@@ -33,17 +85,17 @@ export default {
33
85
  if (value[0] === '"') {
34
86
  return errors.formatEmailError({ value, pointer });
35
87
  }
36
- const [name, host, ...rest] = value.split('@');
37
- if (!name || !host || rest.length !== 0 || name.length > 64 || host.length > 253 ) {
88
+ const [name, host, ...rest] = value.split("@");
89
+ if (!name || !host || rest.length !== 0 || name.length > 64 || host.length > 253) {
38
90
  return errors.formatEmailError({ value, pointer });
39
91
  }
40
- if (name[0] === '.' || name.endsWith('.') || name.includes('..')) {
92
+ if (name[0] === "." || name.endsWith(".") || name.includes("..")) {
41
93
  return errors.formatEmailError({ value, pointer });
42
94
  }
43
95
  if (!/^[a-z0-9.-]+$/i.test(host) || !/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+$/i.test(name)) {
44
96
  return errors.formatEmailError({ value, pointer });
45
97
  }
46
- if (!host.split('.').every(part => /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i.test(part))) {
98
+ if (!host.split(".").every((part) => /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i.test(part))) {
47
99
  return errors.formatEmailError({ value, pointer });
48
100
  }
49
101
  return undefined;
@@ -63,6 +115,10 @@ export default {
63
115
  if (typeof value !== "string" || value === "") {
64
116
  return undefined;
65
117
  }
118
+ if (value && value[0] === "0") {
119
+ // leading zeroes should be rejected, as they are treated as octals
120
+ return errors.formatIPV4LeadingZeroError({ value, pointer });
121
+ }
66
122
  if (value.length <= 15 && isValidIPV4.test(value)) {
67
123
  return undefined;
68
124
  }
@@ -73,33 +129,111 @@ export default {
73
129
  if (typeof value !== "string" || value === "") {
74
130
  return undefined;
75
131
  }
132
+ if (value && value[0] === "0") {
133
+ // leading zeroes should be rejected, as they are treated as octals
134
+ return errors.formatIPV6LeadingZeroError({ value, pointer });
135
+ }
76
136
  if (value.length <= 45 && isValidIPV6.test(value)) {
77
137
  return undefined;
78
138
  }
79
139
  return errors.formatIPV6Error({ value, pointer });
80
140
  },
81
141
 
142
+ "json-pointer": (core, schema, value, pointer) => {
143
+ if (typeof value !== "string" || value === "") {
144
+ return undefined;
145
+ }
146
+ if (isValidJSONPointer.test(value)) {
147
+ return undefined;
148
+ }
149
+ return errors.formatJSONPointerError({ value, pointer });
150
+ },
151
+
152
+ "relative-json-pointer": (core, schema, value, pointer) => {
153
+ if (typeof value !== "string" || value === "") {
154
+ return undefined;
155
+ }
156
+ if (isValidRelativeJSONPointer.test(value)) {
157
+ return undefined;
158
+ }
159
+ return errors.formatJSONPointerError({ value, pointer });
160
+ },
161
+
82
162
  regex: (core, schema, value, pointer) => {
83
163
  if (typeof value === "string" && /\\Z$/.test(value) === false) {
164
+ try {
165
+ new RegExp(value);
166
+ return undefined;
167
+ } catch (e) {} // eslint-disable-line no-empty
168
+
169
+ return errors.formatRegExError({ value, pointer });
170
+ }
171
+ // v7 tests, ignore non-regex values
172
+ if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) {
84
173
  return undefined;
85
174
  }
86
175
  return errors.formatRegExError({ value, pointer });
87
176
  },
88
177
 
89
- uri: (core, schema, value, pointer) => {
178
+ time: (core, schema, value, pointer) => {
90
179
  if (typeof value !== "string") {
91
180
  return undefined;
92
181
  }
93
- if (value === "" || validUrl.isUri(value)) {
182
+ // https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
183
+ const matches = value.match(matchTime);
184
+ if (!matches) {
185
+ return errors.formatDateTimeError({ value, pointer });
186
+ }
187
+ const hour = +matches[1];
188
+ const minute = +matches[2];
189
+ const second = +matches[3];
190
+ const timeZone = !!matches[5];
191
+ if (
192
+ ((hour <= 23 && minute <= 59 && second <= 59) ||
193
+ (hour == 23 && minute == 59 && second == 60)) &&
194
+ timeZone
195
+ ) {
196
+ return undefined;
197
+ }
198
+ return errors.formatTimeError({ value, pointer });
199
+ },
200
+
201
+ uri: (core, schema, value, pointer) => {
202
+ if (typeof value !== "string" || value === "") {
203
+ return undefined;
204
+ }
205
+ if (validUrl.isUri(value)) {
206
+ return undefined;
207
+ }
208
+ return errors.formatURIError({ value, pointer });
209
+ },
210
+
211
+ "uri-reference": (core, schema, value, pointer) => {
212
+ if (typeof value !== "string" || value === "") {
213
+ return undefined;
214
+ }
215
+ if (isValidURIRef.test(value)) {
216
+ return undefined;
217
+ }
218
+ return errors.formatURIReferenceError({ value, pointer });
219
+ },
220
+
221
+ "uri-template": (core, schema, value, pointer) => {
222
+ if (typeof value !== "string" || value === "") {
94
223
  return undefined;
95
224
  }
96
- return errors.formatUriError({ value, pointer });
225
+ if (isValidURITemplate.test(value)) {
226
+ return undefined;
227
+ }
228
+ return errors.formatURITemplateError({ value, pointer });
97
229
  },
98
230
 
99
- url: (core, schema, value, pointer) => {
231
+ url: (core, schema, value: string, pointer) => {
100
232
  if (value === "" || validUrl.isWebUri(value)) {
101
233
  return undefined;
102
234
  }
103
235
  return errors.formatUrlError({ value, pointer });
104
236
  }
105
237
  };
238
+
239
+ export default formatValidators;