@hyperjump/json-schema 1.6.7 → 1.7.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 (104) hide show
  1. package/README.md +247 -255
  2. package/annotations/annotated-instance.js +3 -3
  3. package/annotations/index.d.ts +7 -1
  4. package/annotations/index.js +3 -3
  5. package/bundle/index.d.ts +1 -5
  6. package/bundle/index.js +112 -156
  7. package/draft-04/additionalItems.js +6 -7
  8. package/draft-04/dependencies.js +5 -5
  9. package/draft-04/index.js +2 -2
  10. package/draft-04/items.js +5 -5
  11. package/draft-04/maximum.js +8 -8
  12. package/draft-04/minimum.js +8 -8
  13. package/draft-06/contains.js +2 -2
  14. package/draft-06/index.js +3 -2
  15. package/draft-07/index.js +3 -2
  16. package/draft-2019-09/index.js +9 -11
  17. package/draft-2020-12/dynamicRef.js +5 -5
  18. package/draft-2020-12/index.js +11 -13
  19. package/lib/common.d.ts +1 -1
  20. package/lib/common.js +44 -60
  21. package/lib/configuration.js +0 -6
  22. package/lib/core.js +32 -30
  23. package/lib/experimental.d.ts +75 -5
  24. package/lib/experimental.js +2 -2
  25. package/lib/index.d.ts +43 -11
  26. package/lib/index.js +11 -11
  27. package/lib/instance.d.ts +1 -17
  28. package/lib/instance.js +3 -3
  29. package/lib/keywords/additionalProperties.js +12 -13
  30. package/lib/keywords/allOf.js +3 -3
  31. package/lib/keywords/anyOf.js +3 -3
  32. package/lib/keywords/conditional.js +6 -7
  33. package/lib/keywords/const.js +2 -2
  34. package/lib/keywords/contains.js +14 -35
  35. package/lib/keywords/contentSchema.js +1 -1
  36. package/lib/keywords/definitions.js +2 -2
  37. package/lib/keywords/dependentRequired.js +4 -4
  38. package/lib/keywords/dependentSchemas.js +5 -5
  39. package/lib/keywords/dynamicRef.js +10 -5
  40. package/lib/keywords/else.js +5 -6
  41. package/lib/keywords/enum.js +4 -4
  42. package/lib/keywords/exclusiveMaximum.js +3 -3
  43. package/lib/keywords/exclusiveMinimum.js +3 -3
  44. package/lib/keywords/if.js +1 -1
  45. package/lib/keywords/itemPattern.js +17 -14
  46. package/lib/keywords/items.js +6 -7
  47. package/lib/keywords/maxItems.js +3 -3
  48. package/lib/keywords/maxLength.js +3 -3
  49. package/lib/keywords/maxProperties.js +3 -3
  50. package/lib/keywords/maximum.js +3 -3
  51. package/lib/keywords/meta-data.js +1 -1
  52. package/lib/keywords/minItems.js +3 -3
  53. package/lib/keywords/minLength.js +3 -3
  54. package/lib/keywords/minProperties.js +3 -3
  55. package/lib/keywords/minimum.js +3 -3
  56. package/lib/keywords/multipleOf.js +3 -3
  57. package/lib/keywords/not.js +1 -1
  58. package/lib/keywords/oneOf.js +3 -3
  59. package/lib/keywords/pattern.js +3 -3
  60. package/lib/keywords/patternProperties.js +5 -5
  61. package/lib/keywords/prefixItems.js +5 -5
  62. package/lib/keywords/properties.js +5 -5
  63. package/lib/keywords/propertyDependencies.js +6 -7
  64. package/lib/keywords/propertyNames.js +2 -2
  65. package/lib/keywords/ref.js +2 -7
  66. package/lib/keywords/requireAllExcept.js +8 -9
  67. package/lib/keywords/required.js +3 -3
  68. package/lib/keywords/then.js +5 -5
  69. package/lib/keywords/type.js +9 -3
  70. package/lib/keywords/unevaluatedItems.js +4 -4
  71. package/lib/keywords/unevaluatedProperties.js +4 -5
  72. package/lib/keywords/uniqueItems.js +3 -3
  73. package/lib/keywords/validation.js +11 -23
  74. package/lib/keywords.js +27 -14
  75. package/lib/openapi.js +19 -6
  76. package/lib/schema.js +236 -227
  77. package/openapi-3-0/index.js +5 -5
  78. package/openapi-3-0/type.js +13 -7
  79. package/openapi-3-1/index.js +22 -21
  80. package/openapi-3-1/{schema-base/2022-10-07.js → schema-base.js} +12 -2
  81. package/openapi-3-1/schema-draft-04.js +33 -0
  82. package/openapi-3-1/schema-draft-06.js +33 -0
  83. package/openapi-3-1/schema-draft-07.js +33 -0
  84. package/openapi-3-1/schema-draft-2019-09.js +33 -0
  85. package/openapi-3-1/schema-draft-2020-12.js +33 -0
  86. package/package.json +11 -11
  87. package/stable/index.js +10 -10
  88. package/annotations/validation-error.d.ts +0 -8
  89. package/draft-2019-09/contains.js +0 -44
  90. package/lib/configuration.d.ts +0 -9
  91. package/lib/context-uri.browser.js +0 -1
  92. package/lib/context-uri.js +0 -4
  93. package/lib/core.d.ts +0 -48
  94. package/lib/fetch.browser.js +0 -1
  95. package/lib/fetch.js +0 -20
  96. package/lib/invalid-schema-error.d.ts +0 -8
  97. package/lib/keywords.d.ts +0 -19
  98. package/lib/media-types.d.ts +0 -11
  99. package/lib/media-types.js +0 -48
  100. package/lib/reference.d.ts +0 -11
  101. package/lib/reference.js +0 -11
  102. package/lib/schema.d.ts +0 -60
  103. /package/openapi-3-0/{schema/2021-09-28.js → schema.js} +0 -0
  104. /package/openapi-3-1/{schema/2022-10-07.js → schema.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { some } from "@hyperjump/pact";
2
2
  import * as Instance from "../lib/instance.js";
3
- import Validation from "../lib/keywords/validation.js";
3
+ import { Validation } from "../lib/experimental.js";
4
4
 
5
5
 
6
6
  const id = "https://json-schema.org/keyword/draft-06/contains";
@@ -8,7 +8,7 @@ const id = "https://json-schema.org/keyword/draft-06/contains";
8
8
  const compile = (schema, ast) => Validation.compile(schema, ast);
9
9
 
10
10
  const interpret = (contains, instance, ast, dynamicAnchors, quiet) => {
11
- return !Instance.typeOf(instance, "array") || some((item) => Validation.interpret(contains, item, ast, dynamicAnchors, quiet), Instance.iter(instance));
11
+ return Instance.typeOf(instance) !== "array" || some((item) => Validation.interpret(contains, item, ast, dynamicAnchors, quiet), Instance.iter(instance));
12
12
  };
13
13
 
14
14
  export default { id, compile, interpret };
package/draft-06/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js";
2
- import { addSchema } from "../lib/core.js";
2
+ import { registerSchema } from "../lib/index.js";
3
+
3
4
  import metaSchema from "./schema.js";
4
5
  import additionalItems from "../draft-04/additionalItems.js";
5
6
  import contains from "./contains.js";
@@ -61,6 +62,6 @@ loadDialect(jsonSchemaVersion, {
61
62
  [jsonSchemaVersion]: true
62
63
  }, true);
63
64
 
64
- addSchema(metaSchema);
65
+ registerSchema(metaSchema);
65
66
 
66
67
  export * from "../lib/index.js";
package/draft-07/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js";
2
- import { addSchema } from "../lib/core.js";
2
+ import { registerSchema } from "../lib/index.js";
3
+
3
4
  import metaSchema from "./schema.js";
4
5
  import additionalItems from "../draft-04/additionalItems.js";
5
6
  import contains from "../draft-06/contains.js";
@@ -67,6 +68,6 @@ loadDialect(jsonSchemaVersion, {
67
68
  [jsonSchemaVersion]: true
68
69
  }, true);
69
70
 
70
- addSchema(metaSchema);
71
+ registerSchema(metaSchema);
71
72
 
72
73
  export * from "../lib/index.js";
@@ -1,5 +1,5 @@
1
1
  import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js";
2
- import { addSchema } from "../lib/core.js";
2
+ import { registerSchema } from "../lib/index.js";
3
3
 
4
4
  import metaSchema from "./schema.js";
5
5
  import coreMetaSchema from "./meta/core.js";
@@ -10,14 +10,12 @@ import formatMetaSchema from "./meta/format.js";
10
10
  import contentMetaSchema from "./meta/content.js";
11
11
 
12
12
  import additionalItems from "../draft-04/additionalItems.js";
13
- import contains from "./contains.js";
14
13
  import items from "../draft-04/items.js";
15
14
  import recursiveAnchor from "./recursiveAnchor.js";
16
15
  import recursiveRef from "../draft-2020-12/dynamicRef.js";
17
16
 
18
17
 
19
18
  addKeyword(additionalItems);
20
- addKeyword(contains);
21
19
  addKeyword(items);
22
20
  addKeyword(recursiveAnchor);
23
21
  addKeyword(recursiveRef);
@@ -38,7 +36,7 @@ defineVocabulary("https://json-schema.org/draft/2019-09/vocab/applicator", {
38
36
  "additionalProperties": "https://json-schema.org/keyword/additionalProperties",
39
37
  "allOf": "https://json-schema.org/keyword/allOf",
40
38
  "anyOf": "https://json-schema.org/keyword/anyOf",
41
- "contains": "https://json-schema.org/keyword/draft-2019-09/contains",
39
+ "contains": "https://json-schema.org/keyword/contains",
42
40
  "dependentSchemas": "https://json-schema.org/keyword/dependentSchemas",
43
41
  "if": "https://json-schema.org/keyword/if",
44
42
  "then": "https://json-schema.org/keyword/then",
@@ -105,12 +103,12 @@ loadDialect("https://json-schema.org/draft/2019-09/schema", {
105
103
  "https://json-schema.org/draft/2019-09/vocab/content": true
106
104
  }, true);
107
105
 
108
- addSchema(metaSchema);
109
- addSchema(coreMetaSchema);
110
- addSchema(applicatorMetaSchema);
111
- addSchema(validationMetaSchema);
112
- addSchema(metaDataMetaSchema);
113
- addSchema(formatMetaSchema);
114
- addSchema(contentMetaSchema);
106
+ registerSchema(metaSchema);
107
+ registerSchema(coreMetaSchema);
108
+ registerSchema(applicatorMetaSchema);
109
+ registerSchema(validationMetaSchema);
110
+ registerSchema(metaDataMetaSchema);
111
+ registerSchema(formatMetaSchema);
112
+ registerSchema(contentMetaSchema);
115
113
 
116
114
  export * from "../lib/index.js";
@@ -1,15 +1,15 @@
1
- import Validation from "../lib/keywords/validation.js";
2
- import * as Schema from "../lib/schema.js";
1
+ import * as Browser from "@hyperjump/browser";
2
+ import { Validation, canonicalUri } from "../lib/experimental.js";
3
3
  import { uriFragment } from "../lib/common.js";
4
4
 
5
5
 
6
6
  const id = "https://json-schema.org/keyword/draft-2020-12/dynamicRef";
7
7
 
8
8
  const compile = async (dynamicRef, ast) => {
9
- const fragment = uriFragment(Schema.value(dynamicRef));
10
- const referencedSchema = await Schema.get(Schema.value(dynamicRef), dynamicRef);
9
+ const fragment = uriFragment(Browser.value(dynamicRef));
10
+ const referencedSchema = await Browser.get(Browser.value(dynamicRef), dynamicRef);
11
11
  await Validation.compile(referencedSchema, ast);
12
- return [referencedSchema.id, fragment, Schema.uri(referencedSchema)];
12
+ return [referencedSchema.document.baseUri, fragment, canonicalUri(referencedSchema)];
13
13
  };
14
14
 
15
15
  const evaluate = (strategy) => ([id, fragment, ref], instance, ast, dynamicAnchors, quiet) => {
@@ -1,5 +1,5 @@
1
1
  import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js";
2
- import { addSchema } from "../lib/core.js";
2
+ import { registerSchema } from "../lib/index.js";
3
3
 
4
4
  import metaSchema from "./schema.js";
5
5
  import coreMetaSchema from "./meta/core.js";
@@ -11,12 +11,10 @@ import formatAssertionMetaSchema from "./meta/format-assertion.js";
11
11
  import contentMetaSchema from "./meta/content.js";
12
12
  import unevaluatedMetaSchema from "./meta/unevaluated.js";
13
13
 
14
- import contains from "../draft-2019-09/contains.js";
15
14
  import dynamicAnchor from "./dynamicAnchor.js";
16
15
  import dynamicRef from "./dynamicRef.js";
17
16
 
18
17
 
19
- addKeyword(contains);
20
18
  addKeyword(dynamicRef);
21
19
  addKeyword(dynamicAnchor);
22
20
 
@@ -35,7 +33,7 @@ defineVocabulary("https://json-schema.org/draft/2020-12/vocab/applicator", {
35
33
  "additionalProperties": "https://json-schema.org/keyword/additionalProperties",
36
34
  "allOf": "https://json-schema.org/keyword/allOf",
37
35
  "anyOf": "https://json-schema.org/keyword/anyOf",
38
- "contains": "https://json-schema.org/keyword/draft-2019-09/contains",
36
+ "contains": "https://json-schema.org/keyword/contains",
39
37
  "dependentSchemas": "https://json-schema.org/keyword/dependentSchemas",
40
38
  "if": "https://json-schema.org/keyword/if",
41
39
  "then": "https://json-schema.org/keyword/then",
@@ -111,14 +109,14 @@ loadDialect("https://json-schema.org/draft/2020-12/schema", {
111
109
  "https://json-schema.org/draft/2020-12/vocab/unevaluated": true
112
110
  }, true);
113
111
 
114
- addSchema(metaSchema);
115
- addSchema(coreMetaSchema);
116
- addSchema(applicatorMetaSchema);
117
- addSchema(validationMetaSchema);
118
- addSchema(metaDataMetaSchema);
119
- addSchema(formatAnnotationMetaSchema);
120
- addSchema(formatAssertionMetaSchema);
121
- addSchema(contentMetaSchema);
122
- addSchema(unevaluatedMetaSchema);
112
+ registerSchema(metaSchema);
113
+ registerSchema(coreMetaSchema);
114
+ registerSchema(applicatorMetaSchema);
115
+ registerSchema(validationMetaSchema);
116
+ registerSchema(metaDataMetaSchema);
117
+ registerSchema(formatAnnotationMetaSchema);
118
+ registerSchema(formatAssertionMetaSchema);
119
+ registerSchema(contentMetaSchema);
120
+ registerSchema(unevaluatedMetaSchema);
123
121
 
124
122
  export * from "../lib/index.js";
package/lib/common.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type JsonType = "object" | "array" | "string" | "number" | "boolean" | "null";
2
2
  export type JsonSchemaType = JsonType | "integer";
3
3
 
4
- export const pathRelative: (from: string, to: string) => string;
4
+ export const toRelativeIri: (from: string, to: string) => string;
5
5
 
6
6
  export type Replacer = (key: string, value: unknown) => unknown;
7
7
  export const jsonStringify: (value: unknown, replacer?: Replacer, space?: string) => string;
package/lib/common.js CHANGED
@@ -1,18 +1,29 @@
1
- import { resolveIri, parseIriReference } from "@hyperjump/uri";
1
+ import { resolveIri, parseIriReference, parseAbsoluteIri } from "@hyperjump/uri";
2
2
  import * as JsonPointer from "@hyperjump/json-pointer";
3
3
 
4
4
 
5
- const isObject = (value) => typeof value === "object" && !Array.isArray(value) && value !== null;
6
- const isType = {
7
- null: (value) => value === null,
8
- boolean: (value) => typeof value === "boolean",
9
- object: isObject,
10
- array: (value) => Array.isArray(value),
11
- number: (value) => typeof value === "number",
12
- integer: (value) => Number.isInteger(value),
13
- string: (value) => typeof value === "string"
5
+ export const jsonTypeOf = (value) => {
6
+ const jsType = typeof value;
7
+
8
+ switch (jsType) {
9
+ case "number":
10
+ case "string":
11
+ case "boolean":
12
+ case "undefined":
13
+ return jsType;
14
+ case "object":
15
+ if (Array.isArray(value)) {
16
+ return "array";
17
+ } else if (value === null) {
18
+ return "null";
19
+ } else if (Object.getPrototypeOf(value) === Object.prototype) {
20
+ return "object";
21
+ }
22
+ default:
23
+ const type = jsType === "object" ? Object.getPrototypeOf(value).constructor.name || "anonymous" : jsType;
24
+ throw Error(`Not a JSON compatible type: ${type}`);
25
+ }
14
26
  };
15
- export const jsonTypeOf = (value, type) => isType[type](value);
16
27
 
17
28
  export const resolveUri = (uri, baseUri) => {
18
29
  const resolved = resolveIri(uri, baseUri);
@@ -30,70 +41,43 @@ export const toAbsoluteUri = (uri) => {
30
41
 
31
42
  export const uriFragment = (uri) => decodeURIComponent(parseIriReference(uri).fragment || "");
32
43
 
33
- const CHAR_BACKWARD_SLASH = 47;
44
+ export const toRelativeIri = (from, to) => {
45
+ const fromUri = parseAbsoluteIri(from);
46
+ const toUri = parseAbsoluteIri(to);
34
47
 
35
- export const pathRelative = (from, to) => {
36
- if (from === to) {
37
- return "";
48
+ if (toUri.scheme !== fromUri.scheme) {
49
+ return to;
38
50
  }
39
51
 
40
- let toStart = 1;
41
- const fromLen = from.length - 1;
42
- const toLen = to.length - toStart;
43
-
44
- // Compare paths to find the longest common path from root
45
- const length = fromLen < toLen ? fromLen : toLen;
46
- let lastCommonSep = -1;
47
- let i = 0;
48
- for (; i < length; i++) {
49
- const fromCode = from.charCodeAt(i + 1);
50
- if (fromCode !== to.charCodeAt(toStart + i)) {
51
- break;
52
- } else if (fromCode === CHAR_BACKWARD_SLASH) {
53
- lastCommonSep = i;
54
- }
52
+ if (toUri.authority !== fromUri.authority) {
53
+ return to;
55
54
  }
56
55
 
57
- if (toLen > length) {
58
- if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {
59
- return to.slice(toStart + i + 1);
60
- }
61
- if (i === 0) {
62
- return to.slice(toStart + i);
63
- }
64
- }
65
- if (fromLen > length) {
66
- if (from.charCodeAt(i + 1) === CHAR_BACKWARD_SLASH) {
67
- lastCommonSep = i;
68
- } else if (length === 0) {
69
- lastCommonSep = 0;
70
- }
56
+ if (from === to) {
57
+ return "";
71
58
  }
72
59
 
73
- let out = "";
74
- // Generate the relative path based on the path difference between `to` and `from`
75
- for (i = lastCommonSep + 2; i <= from.length; ++i) {
76
- if (i === from.length || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {
77
- out += out.length === 0 ? ".." : "/..";
78
- }
79
- }
60
+ const fromSegments = fromUri.path.split("/");
61
+ const toSegments = toUri.path.split("/");
80
62
 
81
- toStart += lastCommonSep;
63
+ let position = 0;
64
+ while (fromSegments[position] === toSegments[position] && position < fromSegments.length - 1 && position < toSegments.length - 1) {
65
+ position++;
66
+ }
82
67
 
83
- // Lastly, append the rest of the destination (`to`) path that comes after
84
- // the common path parts
85
- if (out.length > 0) {
86
- return `${out}${to.slice(toStart, to.length)}`;
68
+ const segments = [];
69
+ for (let index = position + 1; index < fromSegments.length; index++) {
70
+ segments.push("..");
87
71
  }
88
72
 
89
- if (to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
90
- ++toStart;
73
+ for (let index = position; index < toSegments.length; index++) {
74
+ segments.push(toSegments[index]);
91
75
  }
92
76
 
93
- return to.slice(toStart, to.length);
77
+ return segments.join("/");
94
78
  };
95
79
 
96
- const defaultReplacer = (key, value) => value;
80
+ const defaultReplacer = (_key, value) => value;
97
81
  export const jsonStringify = (value, replacer = defaultReplacer, space = "") => {
98
82
  return stringifyValue(value, replacer, space, "", JsonPointer.nil, 1);
99
83
  };
@@ -1,6 +1,5 @@
1
1
  let metaSchemaOutputFormat;
2
2
  let shouldValidateSchema = true;
3
- const enabledExperimentalKeywords = {};
4
3
 
5
4
  export const getMetaSchemaOutputFormat = () => metaSchemaOutputFormat;
6
5
  export const setMetaSchemaOutputFormat = (format) => {
@@ -11,8 +10,3 @@ export const getShouldValidateSchema = () => shouldValidateSchema;
11
10
  export const setShouldValidateSchema = (isEnabled) => {
12
11
  shouldValidateSchema = isEnabled;
13
12
  };
14
-
15
- export const isExperimentalKeywordEnabled = (keywordId) => enabledExperimentalKeywords[keywordId];
16
- export const setExperimentalKeywordEnabled = (keywordId, isEnabled) => {
17
- enabledExperimentalKeywords[keywordId] = isEnabled;
18
- };
package/lib/core.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import curry from "just-curry-it";
2
+ import { resolveIri, toAbsoluteIri } from "@hyperjump/uri";
2
3
  import { subscribe, unsubscribe } from "./pubsub.js";
3
4
  import {
4
5
  setMetaSchemaOutputFormat,
5
6
  getShouldValidateSchema,
6
- isExperimentalKeywordEnabled,
7
- setExperimentalKeywordEnabled,
8
7
  getMetaSchemaOutputFormat
9
8
  } from "./configuration.js";
10
9
  import * as Instance from "./instance.js";
11
10
  import { InvalidSchemaError } from "./invalid-schema-error.js";
12
- import * as Schema from "./schema.js";
11
+ import { getSchema, registerSchema, unregisterSchema as schemaUnregister } from "./schema.js";
12
+ import { getKeywordName } from "./keywords.js";
13
13
  import Validation from "./keywords/validation.js";
14
14
 
15
15
 
@@ -17,14 +17,14 @@ export const FLAG = "FLAG", BASIC = "BASIC", DETAILED = "DETAILED", VERBOSE = "V
17
17
  setMetaSchemaOutputFormat(FLAG);
18
18
 
19
19
  export const validate = async (url, value = undefined, outputFormat = undefined) => {
20
- const compiled = await compile(url);
20
+ const schema = await getSchema(url);
21
+ const compiled = await compile(schema);
21
22
  const interpretAst = (value, outputFormat) => interpret(compiled, Instance.cons(value), outputFormat);
22
23
 
23
24
  return value === undefined ? interpretAst : interpretAst(value, outputFormat);
24
25
  };
25
26
 
26
- export const compile = async (url) => {
27
- const schema = await Schema.get(url);
27
+ export const compile = async (schema) => {
28
28
  const ast = { metaData: {} };
29
29
  const schemaUri = await Validation.compile(schema, ast);
30
30
  return { ast, schemaUri };
@@ -79,39 +79,41 @@ const outputHandler = (outputFormat, output) => {
79
79
  };
80
80
 
81
81
  const metaValidators = {};
82
- subscribe("validate.metaValidate", async (message, schema) => {
83
- if (getShouldValidateSchema() && !schema.validated) {
84
- Schema.markValidated(schema.id);
82
+ subscribe("validate.metaValidate", async (_message, schema) => {
83
+ if (getShouldValidateSchema() && !schema.document.validated) {
84
+ schema.document.validated = true;
85
85
 
86
86
  // Compile
87
- if (!(schema.dialectId in metaValidators)) {
88
- // Dynamic references are experimental, but are necessary for meta-validation
89
- const dyanmicRefKeywordId = "https://json-schema.org/keyword/dynamicRef";
90
- const isDynamicRefEnabled = isExperimentalKeywordEnabled(dyanmicRefKeywordId);
91
- setExperimentalKeywordEnabled(dyanmicRefKeywordId, true);
92
-
93
- // itemPattern is experimental, but is necessary for meta-validation
94
- const itemPatternKeywordId = "https://json-schema.org/keyword/itemPattern";
95
- const isItemPatternEnabled = isExperimentalKeywordEnabled(itemPatternKeywordId);
96
- setExperimentalKeywordEnabled(itemPatternKeywordId, true);
97
-
98
- const compiledSchema = await compile(schema.dialectId);
99
- metaValidators[schema.dialectId] = interpret(compiledSchema);
100
-
101
- setExperimentalKeywordEnabled(itemPatternKeywordId, isItemPatternEnabled);
102
- setExperimentalKeywordEnabled(dyanmicRefKeywordId, isDynamicRefEnabled);
87
+ if (!(schema.document.dialectId in metaValidators)) {
88
+ const metaSchema = await getSchema(schema.document.dialectId);
89
+ const compiledSchema = await compile(metaSchema);
90
+ metaValidators[schema.document.dialectId] = interpret(compiledSchema);
103
91
  }
104
92
 
105
93
  // Interpret
106
- const schemaInstance = Instance.cons(schema.schema, schema.id);
107
- const metaResults = metaValidators[schema.dialectId](schemaInstance, getMetaSchemaOutputFormat());
94
+ const schemaInstance = Instance.cons(schema.document.root, schema.document.baseUri);
95
+ const metaResults = metaValidators[schema.document.dialectId](schemaInstance, getMetaSchemaOutputFormat());
108
96
  if (!metaResults.valid) {
109
97
  throw new InvalidSchemaError(metaResults);
110
98
  }
111
99
  }
112
100
  });
113
101
 
114
- export const addSchema = (schema, url = undefined, defaultSchemaVersion = undefined) => {
115
- const id = Schema.add(schema, url, defaultSchemaVersion);
116
- delete metaValidators[id];
102
+ /**
103
+ * @deprecated since 1.7.0. Use registerSchema instead.
104
+ */
105
+ export const addSchema = (schema, retrievalUri = undefined, contextDialectId = undefined) => {
106
+ const dialectId = typeof schema.$schema === "string" ? toAbsoluteIri(schema.$schema) : contextDialectId;
107
+ const idToken = getKeywordName(dialectId, "https://json-schema.org/keyword/id")
108
+ || getKeywordName(dialectId, "https://json-schema.org/keyword/draft-04/id");
109
+ const id = typeof schema[idToken] === "string" ? resolveIri(schema[idToken], retrievalUri) : retrievalUri;
110
+
111
+ unregisterSchema(id);
112
+
113
+ registerSchema(schema, retrievalUri, contextDialectId);
114
+ };
115
+
116
+ export const unregisterSchema = (uri) => {
117
+ schemaUnregister(uri);
118
+ delete metaValidators[uri];
117
119
  };
@@ -1,8 +1,78 @@
1
- import type { Keyword } from "./keywords.js";
1
+ import type { Browser, Document } from "@hyperjump/browser";
2
+ import type { Validator, OutputUnit, OutputFormat, SchemaObject } from "./index.js";
3
+ import type { JsonDocument } from "./instance.js";
2
4
 
3
5
 
4
- export { compile, interpret, BASIC, DETAILED, VERBOSE } from "./core.js";
5
- export { isExperimentalKeywordEnabled, setExperimentalKeywordEnabled } from "./configuration.js";
6
- export { getKeyword, addKeyword, defineVocabulary, getKeywordName, loadDialect } from "./keywords.js";
7
- export type { Keyword } from "./keywords.js";
6
+ // Compile/interpret
7
+ export const compile: (url: string) => Promise<CompiledSchema>;
8
+ export const interpret: (
9
+ (compiledSchema: CompiledSchema, value: unknown, outputFormat?: OutputFormat) => OutputUnit
10
+ ) & (
11
+ (compiledSchema: CompiledSchema) => Validator
12
+ );
13
+
14
+ export type CompiledSchema = {
15
+ schemaUri: string;
16
+ ast: AST;
17
+ };
18
+
19
+ type AST = {
20
+ metaData: Record<string, MetaData>;
21
+ } & Record<string, Node<Node<unknown>[] | boolean>>;
22
+
23
+ type Node<A> = [keywordId: string, schemaUri: string, keywordValue: A];
24
+
25
+ type MetaData = {
26
+ id: string;
27
+ dynamicAnchors: Anchors;
28
+ anchors: Anchors;
29
+ };
30
+
31
+ type Anchors = Record<string, string>;
32
+
33
+ // Output Formats
34
+ export const BASIC: "BASIC";
35
+ export const DETAILED: "DETAILED";
36
+ export const VERBOSE: "VERBOSE";
37
+
38
+ // Schema
39
+ export const getSchema: (uri: string, browser?: Browser) => Promise<Browser<SchemaDocument>>;
40
+ export const buildSchemaDocument: (schema: SchemaObject | boolean, retrievalUri?: string, contextDialectId?: string) => SchemaDocument;
41
+ export const canonicalUri: (browser: Browser<SchemaDocument>) => string;
42
+ export const toSchema: (browser: Browser<SchemaDocument>, options?: ToSchemaOptions) => SchemaObject;
43
+
44
+ export type ToSchemaOptions = {
45
+ contextDialectId?: string;
46
+ includeDialect?: "auto" | "always" | "never";
47
+ selfIdentify?: boolean;
48
+ contextUri?: string;
49
+ includeEmbedded?: boolean;
50
+ };
51
+
52
+ export type SchemaDocument = Document & {
53
+ dialectId: string;
54
+ anchors: Record<string, string>;
55
+ dynamicAnchors: Record<string, string>;
56
+ };
57
+
58
+ // Vocabulary System
59
+ export const addKeyword: <A>(keywordHandler: Keyword<A>) => void;
60
+ export const getKeywordName: (dialectId: string, keywordId: string) => string;
61
+ export const getKeyword: <A>(id: string) => Keyword<A>;
62
+ export const getKeywordByName: <A>(keywordName: string, dialectId: string) => Keyword<A>;
63
+ export const getKeywordId: (keywordName: string, dialectId: string) => string;
64
+ export const defineVocabulary: (id: string, keywords: { [keyword: string]: string }) => void;
65
+ export const loadDialect: (dialectId: string, dialect: { [vocabularyId: string]: boolean }, allowUnknownKeywords?: boolean) => void;
66
+ export const hasDialect: (dialectId: string) => boolean;
67
+
68
+ export type Keyword<A> = {
69
+ id: string;
70
+ compile: (schema: Browser<SchemaDocument>, ast: AST, parentSchema: Browser<SchemaDocument>) => Promise<A>;
71
+ interpret: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => boolean;
72
+ collectEvaluatedProperties?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors, isTop?: boolean) => Set<string> | false;
73
+ collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors, isTop?: boolean) => Set<number> | false;
74
+ collectExternalIds?: (visited: Set<string>, parentSchema: Browser<SchemaDocument>, schema: Browser<SchemaDocument>) => Promise<Set<string>>;
75
+ annotation?: <B>(compiledKeywordValue: A) => B;
76
+ };
77
+
8
78
  export const Validation: Keyword<string>;
@@ -1,4 +1,4 @@
1
1
  export { compile, interpret, BASIC, DETAILED, VERBOSE } from "./core.js";
2
- export { isExperimentalKeywordEnabled, setExperimentalKeywordEnabled } from "./configuration.js";
3
- export { getKeyword, addKeyword, defineVocabulary, getKeywordName, loadDialect } from "./keywords.js";
2
+ export { getKeyword, getKeywordByName, addKeyword, defineVocabulary, getKeywordName, getKeywordId, loadDialect, hasDialect } from "./keywords.js";
3
+ export { getSchema, toSchema, canonicalUri, buildSchemaDocument } from "./schema.js";
4
4
  export { default as Validation } from "./keywords/validation.js";
package/lib/index.d.ts CHANGED
@@ -1,11 +1,43 @@
1
- export { addSchema, validate, FLAG } from "./core.js";
2
- export type { Validator, OutputFormat, CompiledSchema, OutputUnit } from "./core.js";
3
- export {
4
- getMetaSchemaOutputFormat,
5
- setMetaSchemaOutputFormat,
6
- getShouldValidateSchema,
7
- setShouldValidateSchema
8
- } from "./configuration.js";
9
- export { InvalidSchemaError } from "./invalid-schema-error.js";
10
- export { addMediaTypePlugin } from "./media-types.js";
11
- export type { MediaTypePlugin } from "./media-types.js";
1
+ export type SchemaFragment = string | number | boolean | null | SchemaObject | SchemaFragment[];
2
+ export type SchemaObject = {
3
+ [keyword: string]: SchemaFragment;
4
+ };
5
+
6
+ export const registerSchema: (schema: SchemaObject | boolean, retrievalUri?: string, contextDialectId?: string) => void;
7
+ export const unregisterSchema: (retrievalUri: string) => void;
8
+
9
+ /**
10
+ * @deprecated since 1.7.0. Use registerSchema instead.
11
+ */
12
+ export const addSchema: typeof registerSchema;
13
+
14
+ export const validate: (
15
+ (url: string, value: unknown, outputFormat?: OutputFormat) => Promise<OutputUnit>
16
+ ) & (
17
+ (url: string) => Promise<Validator>
18
+ );
19
+
20
+ export type Validator = (value: unknown, outputFormat?: OutputFormat) => OutputUnit;
21
+
22
+ export type OutputUnit = {
23
+ keyword: string;
24
+ absoluteKeywordLocation: string;
25
+ instanceLocation: string;
26
+ valid: boolean;
27
+ errors?: OutputUnit[];
28
+ };
29
+
30
+ export const FLAG: "FLAG";
31
+
32
+ export type OutputFormat = "FLAG" | "BASIC" | "DETAILED" | "VERBOSE";
33
+
34
+ export const setMetaSchemaOutputFormat: (format: OutputFormat) => void;
35
+ export const getMetaSchemaOutputFormat: () => OutputFormat;
36
+ export const setShouldValidateSchema: (isEnabled: boolean) => void;
37
+ export const getShouldValidateSchema: () => boolean;
38
+
39
+ export class InvalidSchemaError extends Error {
40
+ public output: OutputUnit;
41
+
42
+ public constructor(output: OutputUnit);
43
+ }
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { addMediaTypePlugin } from "@hyperjump/browser";
1
2
  import { addKeyword } from "./keywords.js";
2
- import { addMediaTypePlugin } from "./media-types.js";
3
+ import { schemaPlugin } from "./schema.js";
3
4
 
4
5
  import additionalProperties from "./keywords/additionalProperties.js";
5
6
  import allOf from "./keywords/allOf.js";
@@ -64,13 +65,7 @@ import vocabulary from "./keywords/vocabulary.js";
64
65
  import writeOnly from "./keywords/writeOnly.js";
65
66
 
66
67
 
67
- addMediaTypePlugin("application/schema+json", {
68
- parse: async (response, contentTypeParameters) => [
69
- await response.json(),
70
- contentTypeParameters.schema || contentTypeParameters.profile
71
- ],
72
- matcher: (path) => path.endsWith(".schema.json")
73
- });
68
+ addMediaTypePlugin("application/schema+json", schemaPlugin);
74
69
 
75
70
  addKeyword(additionalProperties);
76
71
  addKeyword(allOf);
@@ -134,7 +129,12 @@ addKeyword(unknown);
134
129
  addKeyword(vocabulary);
135
130
  addKeyword(writeOnly);
136
131
 
137
- export { addSchema, validate, FLAG } from "./core.js";
138
- export { getMetaSchemaOutputFormat, setMetaSchemaOutputFormat, getShouldValidateSchema, setShouldValidateSchema } from "./configuration.js";
132
+ export { addSchema, unregisterSchema, validate, FLAG } from "./core.js";
133
+ export { registerSchema } from "./schema.js";
134
+ export {
135
+ getMetaSchemaOutputFormat,
136
+ setMetaSchemaOutputFormat,
137
+ getShouldValidateSchema,
138
+ setShouldValidateSchema
139
+ } from "./configuration.js";
139
140
  export { InvalidSchemaError } from "./invalid-schema-error.js";
140
- export { addMediaTypePlugin } from "./media-types.js";
package/lib/instance.d.ts CHANGED
@@ -8,23 +8,7 @@ export const get: (url: string, context?: JsonDocument) => JsonDocument;
8
8
  export const uri: (doc: JsonDocument) => string;
9
9
  export const value: <A extends Json>(doc: JsonDocument<A>) => A;
10
10
  export const has: (key: string, doc: JsonDocument<JsonObject>) => boolean;
11
- export const typeOf: (
12
- (doc: JsonDocument, type: "null") => doc is JsonDocument<null>
13
- ) & (
14
- (doc: JsonDocument, type: "boolean") => doc is JsonDocument<boolean>
15
- ) & (
16
- (doc: JsonDocument, type: "object") => doc is JsonDocument<JsonObject>
17
- ) & (
18
- (doc: JsonDocument, type: "array") => doc is JsonDocument<Json[]>
19
- ) & (
20
- (doc: JsonDocument, type: "number" | "integer") => doc is JsonDocument<number>
21
- ) & (
22
- (doc: JsonDocument, type: "string") => doc is JsonDocument<string>
23
- ) & (
24
- (doc: JsonDocument, type: JsonType) => boolean
25
- ) & (
26
- (doc: JsonDocument) => (type: JsonType) => boolean
27
- );
11
+ export const typeOf: (doc: JsonDocument) => JsonType;
28
12
  export const step: (key: string, doc: JsonDocument<JsonObject | Json[]>) => JsonDocument<typeof doc.value>;
29
13
  export const iter: (doc: JsonDocument<JsonObject>) => Generator<JsonDocument>;
30
14
  export const keys: (doc: JsonDocument<JsonObject>) => Generator<string>;