@hyperjump/json-schema 1.16.5 → 1.17.1

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 (61) hide show
  1. package/README.md +99 -11
  2. package/annotations/annotated-instance.js +1 -1
  3. package/draft-04/format.js +31 -0
  4. package/draft-04/index.js +3 -1
  5. package/draft-06/format.js +34 -0
  6. package/draft-06/index.js +3 -1
  7. package/draft-07/format.js +42 -0
  8. package/draft-07/index.js +3 -1
  9. package/draft-2019-09/format-assertion.js +44 -0
  10. package/draft-2019-09/format.js +44 -0
  11. package/draft-2019-09/index.js +5 -1
  12. package/draft-2020-12/format-assertion.js +43 -0
  13. package/draft-2020-12/format.js +44 -0
  14. package/draft-2020-12/index.js +6 -2
  15. package/formats/handlers/date-time.js +7 -0
  16. package/formats/handlers/date.js +7 -0
  17. package/formats/handlers/draft-04/hostname.js +7 -0
  18. package/formats/handlers/duration.js +7 -0
  19. package/formats/handlers/email.js +7 -0
  20. package/formats/handlers/hostname.js +7 -0
  21. package/formats/handlers/idn-email.js +7 -0
  22. package/formats/handlers/idn-hostname.js +7 -0
  23. package/formats/handlers/ipv4.js +7 -0
  24. package/formats/handlers/ipv6.js +7 -0
  25. package/formats/handlers/iri-reference.js +7 -0
  26. package/formats/handlers/iri.js +7 -0
  27. package/formats/handlers/json-pointer.js +7 -0
  28. package/formats/handlers/regex.js +7 -0
  29. package/formats/handlers/relative-json-pointer.js +7 -0
  30. package/formats/handlers/time.js +7 -0
  31. package/formats/handlers/uri-reference.js +7 -0
  32. package/formats/handlers/uri-template.js +7 -0
  33. package/formats/handlers/uri.js +7 -0
  34. package/formats/handlers/uuid.js +7 -0
  35. package/formats/index.js +11 -0
  36. package/formats/lite.js +38 -0
  37. package/lib/configuration.js +7 -2
  38. package/lib/experimental.d.ts +11 -2
  39. package/lib/experimental.js +1 -0
  40. package/lib/index.d.ts +2 -0
  41. package/lib/index.js +3 -1
  42. package/lib/keywords/dynamicRef.js +1 -1
  43. package/lib/keywords/format.js +35 -2
  44. package/lib/keywords.js +25 -3
  45. package/openapi-3-0/index.js +1 -1
  46. package/package.json +8 -5
  47. package/v1/extension-tests/conditional.json +289 -0
  48. package/v1/extension-tests/itemPattern.json +462 -0
  49. package/{stable → v1}/index.d.ts +2 -2
  50. package/{stable → v1}/index.js +25 -30
  51. package/{stable → v1}/meta/applicator.js +1 -3
  52. package/{stable → v1}/meta/content.js +1 -3
  53. package/{stable → v1}/meta/core.js +5 -4
  54. package/v1/meta/format.js +8 -0
  55. package/{stable → v1}/meta/meta-data.js +1 -3
  56. package/{stable → v1}/meta/unevaluated.js +1 -3
  57. package/{stable → v1}/meta/validation.js +1 -3
  58. package/v1/schema.js +24 -0
  59. package/stable/meta/format-annotation.js +0 -10
  60. package/stable/meta/format-assertion.js +0 -10
  61. package/stable/validation.js +0 -24
@@ -0,0 +1,7 @@
1
+ import { isIdnEmail } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/idn-email",
6
+ handler: (email) => typeof email !== "string" || isIdnEmail(email)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isIdn } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/idn-hostname",
6
+ handler: (hostname) => typeof hostname !== "string" || isIdn(hostname)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isIPv4 } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/ipv4",
6
+ handler: (ip) => typeof ip !== "string" || isIPv4(ip)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isIPv6 } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/ipv6",
6
+ handler: (ip) => typeof ip !== "string" || isIPv6(ip)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isIriReference } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/iri-reference",
6
+ handler: (uri) => typeof uri !== "string" || isIriReference(uri)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isIri } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/iri",
6
+ handler: (uri) => typeof uri !== "string" || isIri(uri)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isJsonPointer } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/json-pointer",
6
+ handler: (pointer) => typeof pointer !== "string" || isJsonPointer(pointer)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isRegex } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/regex",
6
+ handler: (regex) => typeof regex !== "string" || isRegex(regex)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isRelativeJsonPointer } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/relative-json-pointer",
6
+ handler: (pointer) => typeof pointer !== "string" || isRelativeJsonPointer(pointer)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isTime } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/time",
6
+ handler: (time) => typeof time !== "string" || isTime(time)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isUriReference } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/uri-reference",
6
+ handler: (uri) => typeof uri !== "string" || isUriReference(uri)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isUriTemplate } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/uri-template",
6
+ handler: (uriTemplate) => typeof uriTemplate !== "string" || isUriTemplate(uriTemplate)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isUri } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/uri",
6
+ handler: (uri) => typeof uri !== "string" || isUri(uri)
7
+ };
@@ -0,0 +1,7 @@
1
+ import { isUuid } from "@hyperjump/json-schema-formats";
2
+
3
+
4
+ export default {
5
+ id: "https://json-schema.org/format/uuid",
6
+ handler: (uuid) => typeof uuid !== "string" || isUuid(uuid)
7
+ };
@@ -0,0 +1,11 @@
1
+ import { addFormat } from "../lib/keywords.js";
2
+ import "./lite.js";
3
+
4
+ import idnEmail from "./handlers/idn-email.js";
5
+ import hostname from "./handlers/hostname.js";
6
+ import idnHostname from "./handlers/idn-hostname.js";
7
+
8
+
9
+ addFormat(idnEmail);
10
+ addFormat(hostname);
11
+ addFormat(idnHostname);
@@ -0,0 +1,38 @@
1
+ import { addFormat } from "../lib/keywords.js";
2
+
3
+ import draft04Hostname from "./handlers/draft-04/hostname.js";
4
+ import dateTime from "./handlers/date-time.js";
5
+ import date from "./handlers/date.js";
6
+ import time from "./handlers/time.js";
7
+ import duration from "./handlers/duration.js";
8
+ import email from "./handlers/email.js";
9
+ import ipv4 from "./handlers/ipv4.js";
10
+ import ipv6 from "./handlers/ipv6.js";
11
+ import uri from "./handlers/uri.js";
12
+ import uriReference from "./handlers/uri-reference.js";
13
+ import iri from "./handlers/iri.js";
14
+ import iriReference from "./handlers/iri-reference.js";
15
+ import uuid from "./handlers/uuid.js";
16
+ import uriTemplate from "./handlers/uri-template.js";
17
+ import jsonPointer from "./handlers/json-pointer.js";
18
+ import relativeJsonPointer from "./handlers/relative-json-pointer.js";
19
+ import regex from "./handlers/regex.js";
20
+
21
+
22
+ addFormat(draft04Hostname);
23
+ addFormat(dateTime);
24
+ addFormat(date);
25
+ addFormat(time);
26
+ addFormat(duration);
27
+ addFormat(email);
28
+ addFormat(ipv4);
29
+ addFormat(ipv6);
30
+ addFormat(uri);
31
+ addFormat(uriReference);
32
+ addFormat(iri);
33
+ addFormat(iriReference);
34
+ addFormat(uuid);
35
+ addFormat(uriTemplate);
36
+ addFormat(jsonPointer);
37
+ addFormat(relativeJsonPointer);
38
+ addFormat(regex);
@@ -1,12 +1,17 @@
1
1
  let metaSchemaOutputFormat;
2
- let shouldValidateSchema = true;
3
-
4
2
  export const getMetaSchemaOutputFormat = () => metaSchemaOutputFormat;
5
3
  export const setMetaSchemaOutputFormat = (format) => {
6
4
  metaSchemaOutputFormat = format;
7
5
  };
8
6
 
7
+ let shouldValidateSchema = true;
9
8
  export const getShouldValidateSchema = () => shouldValidateSchema;
10
9
  export const setShouldValidateSchema = (isEnabled) => {
11
10
  shouldValidateSchema = isEnabled;
12
11
  };
12
+
13
+ let shouldValidateFormat;
14
+ export const getShouldValidateFormat = () => shouldValidateFormat;
15
+ export const setShouldValidateFormat = (isEnabled) => {
16
+ shouldValidateFormat = isEnabled;
17
+ };
@@ -1,4 +1,5 @@
1
1
  import type { Browser, Document } from "@hyperjump/browser";
2
+ import type { Json } from "@hyperjump/json-pointer";
2
3
  import type { Validator, Output, OutputUnit, OutputFormat, SchemaObject } from "./index.js";
3
4
  import type { JsonNode } from "./instance.js";
4
5
 
@@ -61,11 +62,19 @@ export const getKeywordName: (dialectId: string, keywordId: string) => string;
61
62
  export const getKeyword: <A>(id: string) => Keyword<A>;
62
63
  export const getKeywordByName: <A>(keywordName: string, dialectId: string) => Keyword<A>;
63
64
  export const getKeywordId: (keywordName: string, dialectId: string) => string;
65
+ export const addFormat: (format: Format) => void;
66
+ export const setFormatHandler: (keywordUri: string, formatName: string, formatUri: string) => void;
67
+ export const removeFormatHandler: (keywordUri: string, formatName: string) => void;
64
68
  export const defineVocabulary: (id: string, keywords: Record<string, string>) => void;
65
69
  export const loadDialect: (dialectId: string, dialect: Record<string, boolean>, allowUnknownKeywords?: boolean) => void;
66
70
  export const unloadDialect: (dialectId: string) => void;
67
71
  export const hasDialect: (dialectId: string) => boolean;
68
72
 
73
+ export type Format = {
74
+ id: string;
75
+ handler: (value: Json) => boolean;
76
+ };
77
+
69
78
  export type Keyword<A, Context extends ValidationContext = ValidationContext> = {
70
79
  id: string;
71
80
  compile: (schema: Browser<SchemaDocument>, ast: AST, parentSchema: Browser<SchemaDocument>) => Promise<A>;
@@ -83,8 +92,8 @@ export type ValidationContext = {
83
92
  // Evaluation Plugins
84
93
  export type EvaluationPlugin<Context extends ValidationContext = ValidationContext> = {
85
94
  beforeSchema?(url: string, instance: JsonNode, context: Context): void;
86
- beforeKeyword?(keywordNode: Node<unknown>, instance: JsonNode, context: Context, schemaContext: Context, keyword: Keyword): void;
87
- afterKeyword?(keywordNode: Node<unknown>, instance: JsonNode, context: Context, valid: boolean, schemaContext: Context, keyword: Keyword): void;
95
+ beforeKeyword?(keywordNode: Node<unknown>, instance: JsonNode, context: Context, schemaContext: Context, keyword: Keyword<unknown>): void;
96
+ afterKeyword?(keywordNode: Node<unknown>, instance: JsonNode, context: Context, valid: boolean, schemaContext: Context, keyword: Keyword<unknown>): void;
88
97
  afterSchema?(url: string, instance: JsonNode, context: Context, valid: boolean): void;
89
98
  };
90
99
 
@@ -1,6 +1,7 @@
1
1
  export { compile, interpret, BASIC, DETAILED } from "./core.js";
2
2
  export {
3
3
  addKeyword, getKeyword, getKeywordByName, getKeywordName, getKeywordId,
4
+ addFormat, setFormatHandler, removeFormatHandler,
4
5
  defineVocabulary,
5
6
  loadDialect, unloadDialect, hasDialect
6
7
  } from "./keywords.js";
package/lib/index.d.ts CHANGED
@@ -54,6 +54,8 @@ export const setMetaSchemaOutputFormat: (format: OutputFormat) => void;
54
54
  export const getMetaSchemaOutputFormat: () => OutputFormat;
55
55
  export const setShouldValidateSchema: (isEnabled: boolean) => void;
56
56
  export const getShouldValidateSchema: () => boolean;
57
+ export const setShouldValidateFormat: (isEnabled: boolean) => void;
58
+ export const getShouldValidateFormat: () => boolean;
57
59
 
58
60
  export class InvalidSchemaError extends Error {
59
61
  public output: Output & { valid: false };
package/lib/index.js CHANGED
@@ -135,6 +135,8 @@ export {
135
135
  getMetaSchemaOutputFormat,
136
136
  setMetaSchemaOutputFormat,
137
137
  getShouldValidateSchema,
138
- setShouldValidateSchema
138
+ setShouldValidateSchema,
139
+ getShouldValidateFormat,
140
+ setShouldValidateFormat
139
141
  } from "./configuration.js";
140
142
  export { InvalidSchemaError } from "./invalid-schema-error.js";
@@ -10,7 +10,7 @@ const compile = async (schema, ast) => {
10
10
  const self = await Browser.get(schema.document.baseUri, schema);
11
11
  await Validation.compile(self, ast);
12
12
 
13
- return reference;
13
+ return reference.startsWith("#") ? reference.slice(1) : reference;
14
14
  };
15
15
 
16
16
  const interpret = (fragment, instance, context) => {
@@ -1,10 +1,43 @@
1
1
  import * as Browser from "@hyperjump/browser";
2
+ import * as Instance from "../instance.js";
3
+ import { getFormatHandler } from "../keywords.js";
2
4
 
3
5
 
4
6
  const id = "https://json-schema.org/keyword/format";
5
7
 
6
8
  const compile = (schema) => Browser.value(schema);
7
- const interpret = () => true;
9
+
10
+ const interpret = (format, instance) => {
11
+ const handler = getFormatHandler(formats[format]);
12
+ if (!handler) {
13
+ throw Error(`The '${format}' format is not supported.`);
14
+ }
15
+
16
+ return handler(Instance.value(instance));
17
+ };
18
+
8
19
  const annotation = (format) => format;
9
20
 
10
- export default { id, compile, interpret, annotation };
21
+ const formats = {
22
+ "date-time": "https://json-schema.org/format/date-time",
23
+ "date": "https://json-schema.org/format/date",
24
+ "time": "https://json-schema.org/format/time",
25
+ "duration": "https://json-schema.org/format/duration",
26
+ "email": "https://json-schema.org/format/email",
27
+ "idn-email": "https://json-schema.org/format/idn-email",
28
+ "hostname": "https://json-schema.org/format/hostname",
29
+ "idn-hostname": "https://json-schema.org/format/idn-hostname",
30
+ "ipv4": "https://json-schema.org/format/ipv4",
31
+ "ipv6": "https://json-schema.org/format/ipv6",
32
+ "uri": "https://json-schema.org/format/uri",
33
+ "uri-reference": "https://json-schema.org/format/uri-reference",
34
+ "iri": "https://json-schema.org/format/iri",
35
+ "iri-reference": "https://json-schema.org/format/iri-reference",
36
+ "uuid": "https://json-schema.org/format/uuid",
37
+ "uri-template": "https://json-schema.org/format/uri-template",
38
+ "json-pointer": "https://json-schema.org/format/json-pointer",
39
+ "relative-json-pointer": "https://json-schema.org/format/relative-json-pointer",
40
+ "regex": "https://json-schema.org/format/regex"
41
+ };
42
+
43
+ export default { id, compile, interpret, annotation, formats };
package/lib/keywords.js CHANGED
@@ -34,6 +34,23 @@ export const defineVocabulary = (id, keywords) => {
34
34
  _vocabularies[id] = keywords;
35
35
  };
36
36
 
37
+ const _formats = {};
38
+ export const addFormat = (format) => {
39
+ _formats[format.id] = format.handler;
40
+ };
41
+
42
+ export const getFormatHandler = (formatUri) => {
43
+ return _formats[formatUri];
44
+ };
45
+
46
+ export const setFormatHandler = (keywordUri, formatName, formatUri) => {
47
+ _keywords[keywordUri].formats[formatName] = formatUri;
48
+ };
49
+
50
+ export const removeFormatHandler = (keywordUri, formatName) => {
51
+ delete _keywords[keywordUri].formats[formatName];
52
+ };
53
+
37
54
  const _dialects = {};
38
55
 
39
56
  export const getKeywordId = (keyword, dialectId) => {
@@ -74,9 +91,14 @@ export const loadDialect = (dialectId, dialect, allowUnknownKeywords = false, is
74
91
  if (vocabularyId in _vocabularies) {
75
92
  for (const keyword in _vocabularies[vocabularyId]) {
76
93
  let keywordId = _vocabularies[vocabularyId][keyword];
77
- if (!(keywordId in _keywords) && !dialect[vocabularyId]) {
78
- // Allow keyword to be ignored
79
- keywordId = `https://json-schema.org/keyword/unknown#${keyword}`;
94
+ if (!dialect[vocabularyId]) {
95
+ if (vocabularyId === "https://json-schema.org/draft/2019-09/vocab/format") {
96
+ // Handle inconsistent 2019-09 behavior
97
+ keywordId = "https://json-schema.org/keyword/draft-2019-09/format";
98
+ } else if (!(keywordId in _keywords)) {
99
+ // Allow keyword to be ignored
100
+ keywordId = `https://json-schema.org/keyword/unknown#${keyword}`;
101
+ }
80
102
  }
81
103
  _dialects[dialectId].keywords[keyword] = keywordId;
82
104
  }
@@ -38,7 +38,7 @@ defineVocabulary(jsonSchemaVersion, {
38
38
  "exclusiveMaximum": "https://json-schema.org/keyword/draft-04/exclusiveMaximum",
39
39
  "exclusiveMinimum": "https://json-schema.org/keyword/draft-04/exclusiveMinimum",
40
40
  "externalDocs": "https://spec.openapis.org/oas/3.0/keyword/externalDocs",
41
- "format": "https://json-schema.org/keyword/format",
41
+ "format": "https://json-schema.org/keyword/draft-04/format",
42
42
  "items": "https://json-schema.org/keyword/draft-04/items",
43
43
  "maxItems": "https://json-schema.org/keyword/maxItems",
44
44
  "maxLength": "https://json-schema.org/keyword/maxLength",
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@hyperjump/json-schema",
3
- "version": "1.16.5",
3
+ "version": "1.17.1",
4
4
  "description": "A JSON Schema validator with support for custom keywords, vocabularies, and dialects",
5
5
  "type": "module",
6
- "main": "./stable/index.js",
6
+ "main": "./v1/index.js",
7
7
  "exports": {
8
- ".": "./stable/index.js",
8
+ ".": "./v1/index.js",
9
9
  "./draft-04": "./draft-04/index.js",
10
10
  "./draft-06": "./draft-06/index.js",
11
11
  "./draft-07": "./draft-07/index.js",
@@ -18,11 +18,13 @@
18
18
  "./instance/experimental": "./lib/instance.js",
19
19
  "./annotations/experimental": "./annotations/index.js",
20
20
  "./annotated-instance/experimental": "./annotations/annotated-instance.js",
21
- "./bundle": "./bundle/index.js"
21
+ "./bundle": "./bundle/index.js",
22
+ "./formats": "./formats/index.js",
23
+ "./formats-lite": "./formats/lite.js"
22
24
  },
23
25
  "scripts": {
24
26
  "clean": "xargs -a .gitignore rm -rf",
25
- "lint": "eslint lib stable draft-* openapi-* bundle annotations",
27
+ "lint": "eslint lib v1 draft-* openapi-* bundle annotations",
26
28
  "test": "vitest --watch=false",
27
29
  "check-types": "tsc --noEmit"
28
30
  },
@@ -67,6 +69,7 @@
67
69
  },
68
70
  "dependencies": {
69
71
  "@hyperjump/json-pointer": "^1.1.0",
72
+ "@hyperjump/json-schema-formats": "^1.0.0",
70
73
  "@hyperjump/pact": "^1.2.0",
71
74
  "@hyperjump/uri": "^1.2.0",
72
75
  "content-type": "^1.0.4",