@hyperjump/json-schema 0.23.4 → 1.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 (219) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +487 -119
  3. package/draft-04/additionalItems.js +29 -0
  4. package/{lib/keywords → draft-04}/dependencies.js +9 -5
  5. package/draft-04/exclusiveMaximum.js +5 -0
  6. package/draft-04/exclusiveMinimum.js +5 -0
  7. package/draft-04/id.js +1 -0
  8. package/draft-04/index.d.ts +43 -0
  9. package/draft-04/index.js +69 -0
  10. package/draft-04/items.js +35 -0
  11. package/draft-04/maximum.js +26 -0
  12. package/draft-04/minimum.js +25 -0
  13. package/draft-04/ref.js +1 -0
  14. package/draft-04/schema.js +149 -0
  15. package/draft-06/contains.js +13 -0
  16. package/draft-06/index.d.ts +47 -0
  17. package/draft-06/index.js +66 -0
  18. package/draft-06/schema.js +154 -0
  19. package/{lib/draft-07.d.ts → draft-07/index.d.ts} +21 -19
  20. package/draft-07/index.js +72 -0
  21. package/draft-07/schema.js +172 -0
  22. package/{lib/keywords/contains-minContains-maxContains.js → draft-2019-09/contains.js} +14 -7
  23. package/{lib/draft-2019-09.d.ts → draft-2019-09/index.d.ts} +24 -22
  24. package/draft-2019-09/index.js +117 -0
  25. package/draft-2019-09/meta/applicator.js +55 -0
  26. package/draft-2019-09/meta/content.js +17 -0
  27. package/draft-2019-09/meta/core.js +57 -0
  28. package/draft-2019-09/meta/format.js +14 -0
  29. package/draft-2019-09/meta/meta-data.js +37 -0
  30. package/draft-2019-09/meta/validation.js +98 -0
  31. package/draft-2019-09/recursiveAnchor.js +1 -0
  32. package/draft-2019-09/recursiveRef.js +19 -0
  33. package/draft-2019-09/schema.js +42 -0
  34. package/draft-2020-12/dynamicAnchor.js +1 -0
  35. package/draft-2020-12/dynamicRef.js +35 -0
  36. package/draft-2020-12/index.d.ts +66 -0
  37. package/draft-2020-12/index.js +124 -0
  38. package/draft-2020-12/meta/applicator.js +46 -0
  39. package/draft-2020-12/meta/content.js +14 -0
  40. package/draft-2020-12/meta/core.js +54 -0
  41. package/draft-2020-12/meta/format-annotation.js +11 -0
  42. package/draft-2020-12/meta/format-assertion.js +11 -0
  43. package/draft-2020-12/meta/meta-data.js +34 -0
  44. package/draft-2020-12/meta/unevaluated.js +12 -0
  45. package/draft-2020-12/meta/validation.js +95 -0
  46. package/draft-2020-12/schema.js +44 -0
  47. package/lib/common.d.ts +5 -1
  48. package/lib/common.js +80 -9
  49. package/lib/configuration.d.ts +9 -0
  50. package/lib/configuration.js +18 -0
  51. package/lib/core.d.ts +48 -0
  52. package/lib/core.js +102 -0
  53. package/lib/experimental.d.ts +8 -0
  54. package/lib/experimental.js +4 -0
  55. package/lib/fetch.browser.js +1 -0
  56. package/lib/fetch.js +19 -0
  57. package/lib/index.d.ts +11 -42
  58. package/lib/index.js +130 -23
  59. package/lib/instance.d.ts +75 -0
  60. package/lib/instance.js +58 -0
  61. package/lib/invalid-schema-error.d.ts +8 -0
  62. package/lib/invalid-schema-error.js +7 -0
  63. package/lib/keywords/additionalProperties.js +19 -17
  64. package/lib/keywords/allOf.js +10 -7
  65. package/lib/keywords/anchor.js +1 -0
  66. package/lib/keywords/anyOf.js +10 -7
  67. package/lib/keywords/comment.js +4 -0
  68. package/lib/keywords/const.js +6 -3
  69. package/lib/keywords/contains.js +48 -5
  70. package/lib/keywords/contentEncoding.js +4 -0
  71. package/lib/keywords/contentMediaType.js +4 -0
  72. package/lib/keywords/contentSchema.js +4 -0
  73. package/lib/keywords/default.js +4 -0
  74. package/lib/keywords/definitions.js +7 -4
  75. package/lib/keywords/dependentRequired.js +6 -3
  76. package/lib/keywords/dependentSchemas.js +10 -6
  77. package/lib/keywords/deprecated.js +4 -0
  78. package/lib/keywords/description.js +4 -0
  79. package/lib/keywords/dynamicAnchor.js +1 -0
  80. package/lib/keywords/dynamicRef.js +13 -17
  81. package/lib/keywords/else.js +16 -11
  82. package/lib/keywords/enum.js +6 -3
  83. package/lib/keywords/examples.js +4 -0
  84. package/lib/keywords/exclusiveMaximum.js +5 -2
  85. package/lib/keywords/exclusiveMinimum.js +5 -2
  86. package/lib/keywords/format.js +4 -0
  87. package/lib/keywords/id.js +1 -0
  88. package/lib/keywords/if.js +8 -6
  89. package/lib/keywords/items.js +17 -19
  90. package/lib/keywords/maxContains.js +4 -0
  91. package/lib/keywords/maxItems.js +5 -2
  92. package/lib/keywords/maxLength.js +5 -2
  93. package/lib/keywords/maxProperties.js +5 -2
  94. package/lib/keywords/maximum.js +5 -2
  95. package/lib/keywords/meta-data.js +4 -0
  96. package/lib/keywords/minContains.js +4 -0
  97. package/lib/keywords/minItems.js +5 -2
  98. package/lib/keywords/minLength.js +5 -2
  99. package/lib/keywords/minProperties.js +5 -2
  100. package/lib/keywords/minimum.js +5 -2
  101. package/lib/keywords/multipleOf.js +5 -2
  102. package/lib/keywords/not.js +6 -4
  103. package/lib/keywords/oneOf.js +9 -6
  104. package/lib/keywords/pattern.js +5 -2
  105. package/lib/keywords/patternProperties.js +9 -5
  106. package/lib/keywords/prefixItems.js +28 -0
  107. package/lib/keywords/properties.js +11 -6
  108. package/lib/keywords/propertyDependencies.js +49 -0
  109. package/lib/keywords/propertyNames.js +7 -4
  110. package/lib/keywords/readOnly.js +4 -0
  111. package/lib/keywords/ref.js +9 -6
  112. package/lib/keywords/requireAllExcept.js +24 -0
  113. package/lib/keywords/required.js +5 -2
  114. package/lib/keywords/then.js +16 -11
  115. package/lib/keywords/title.js +4 -0
  116. package/lib/keywords/type.js +5 -2
  117. package/lib/keywords/unevaluatedItems.js +9 -5
  118. package/lib/keywords/unevaluatedProperties.js +9 -5
  119. package/lib/keywords/uniqueItems.js +6 -3
  120. package/lib/keywords/validation.js +123 -0
  121. package/lib/keywords/vocabulary.js +1 -0
  122. package/lib/keywords/writeOnly.js +4 -0
  123. package/lib/keywords.d.ts +19 -0
  124. package/lib/keywords.js +59 -0
  125. package/lib/media-types.d.ts +9 -0
  126. package/lib/media-types.js +26 -0
  127. package/lib/openapi.js +29 -0
  128. package/lib/pubsub.js +42 -0
  129. package/lib/reference.d.ts +11 -0
  130. package/lib/reference.js +11 -0
  131. package/lib/schema.d.ts +64 -0
  132. package/lib/schema.js +308 -0
  133. package/openapi-3-0/dialect.js +174 -0
  134. package/openapi-3-0/discriminator.js +4 -0
  135. package/openapi-3-0/example.js +4 -0
  136. package/openapi-3-0/externalDocs.js +4 -0
  137. package/openapi-3-0/index.d.ts +60 -0
  138. package/openapi-3-0/index.js +77 -0
  139. package/openapi-3-0/nullable.js +4 -0
  140. package/openapi-3-0/schema/2021-09-28.js +1404 -0
  141. package/openapi-3-0/type.js +16 -0
  142. package/openapi-3-0/xml.js +4 -0
  143. package/openapi-3-1/dialect/base.js +21 -0
  144. package/openapi-3-1/index.d.ts +88 -0
  145. package/openapi-3-1/index.js +48 -0
  146. package/openapi-3-1/meta/base.js +76 -0
  147. package/openapi-3-1/schema/2022-10-07.js +1440 -0
  148. package/openapi-3-1/schema-base/2022-10-07.js +23 -0
  149. package/package.json +34 -22
  150. package/{lib/draft-2020-12.d.ts → stable/index.d.ts} +26 -24
  151. package/stable/index.js +118 -0
  152. package/stable/meta/applicator.js +49 -0
  153. package/stable/meta/content.js +12 -0
  154. package/stable/meta/core.js +49 -0
  155. package/stable/meta/format-annotation.js +10 -0
  156. package/stable/meta/format-assertion.js +10 -0
  157. package/stable/meta/meta-data.js +16 -0
  158. package/stable/meta/unevaluated.js +11 -0
  159. package/stable/meta/validation.js +67 -0
  160. package/stable/validation.js +24 -0
  161. package/dist/json-schema-amd.js +0 -6644
  162. package/dist/json-schema-amd.js.map +0 -1
  163. package/dist/json-schema-amd.min.js +0 -2
  164. package/dist/json-schema-amd.min.js.map +0 -1
  165. package/dist/json-schema-cjs.js +0 -6642
  166. package/dist/json-schema-cjs.js.map +0 -1
  167. package/dist/json-schema-cjs.min.js +0 -2
  168. package/dist/json-schema-cjs.min.js.map +0 -1
  169. package/dist/json-schema-esm.js +0 -6638
  170. package/dist/json-schema-esm.js.map +0 -1
  171. package/dist/json-schema-esm.min.js +0 -2
  172. package/dist/json-schema-esm.min.js.map +0 -1
  173. package/dist/json-schema-iife.js +0 -6647
  174. package/dist/json-schema-iife.js.map +0 -1
  175. package/dist/json-schema-iife.min.js +0 -2
  176. package/dist/json-schema-iife.min.js.map +0 -1
  177. package/dist/json-schema-system.js +0 -6645
  178. package/dist/json-schema-system.js.map +0 -1
  179. package/dist/json-schema-system.min.js +0 -2
  180. package/dist/json-schema-system.min.js.map +0 -1
  181. package/dist/json-schema-umd.js +0 -6648
  182. package/dist/json-schema-umd.js.map +0 -1
  183. package/dist/json-schema-umd.min.js +0 -2
  184. package/dist/json-schema-umd.min.js.map +0 -1
  185. package/lib/draft-04.d.ts +0 -41
  186. package/lib/draft-04.js +0 -46
  187. package/lib/draft-06.d.ts +0 -45
  188. package/lib/draft-06.js +0 -51
  189. package/lib/draft-07.js +0 -55
  190. package/lib/draft-2019-09.js +0 -92
  191. package/lib/draft-2020-12.js +0 -103
  192. package/lib/index.mjs +0 -19
  193. package/lib/keywords/additionalItems.js +0 -27
  194. package/lib/keywords/additionalItems6.js +0 -23
  195. package/lib/keywords/additionalProperties6.js +0 -28
  196. package/lib/keywords/index.js +0 -53
  197. package/lib/keywords/items202012.js +0 -23
  198. package/lib/keywords/maximum-exclusiveMaximum.js +0 -20
  199. package/lib/keywords/minimum-exclusiveMinimum.js +0 -20
  200. package/lib/keywords/tupleItems.js +0 -24
  201. package/meta/draft/2019-09/meta/applicator.js +0 -55
  202. package/meta/draft/2019-09/meta/content.js +0 -17
  203. package/meta/draft/2019-09/meta/core.js +0 -57
  204. package/meta/draft/2019-09/meta/format.js +0 -14
  205. package/meta/draft/2019-09/meta/meta-data.js +0 -37
  206. package/meta/draft/2019-09/meta/validation.js +0 -98
  207. package/meta/draft/2019-09/schema.js +0 -42
  208. package/meta/draft/2020-12/meta/applicator.js +0 -49
  209. package/meta/draft/2020-12/meta/content.js +0 -17
  210. package/meta/draft/2020-12/meta/core.js +0 -57
  211. package/meta/draft/2020-12/meta/format-annotation.js +0 -14
  212. package/meta/draft/2020-12/meta/format-assertion.js +0 -14
  213. package/meta/draft/2020-12/meta/meta-data.js +0 -37
  214. package/meta/draft/2020-12/meta/unevaluated.js +0 -15
  215. package/meta/draft/2020-12/meta/validation.js +0 -98
  216. package/meta/draft/2020-12/schema.js +0 -44
  217. package/meta/draft-04/schema.js +0 -149
  218. package/meta/draft-06/schema.js +0 -154
  219. package/meta/draft-07/schema.js +0 -172
package/README.md CHANGED
@@ -1,24 +1,20 @@
1
- # Hyperjump - JSON Schema Validator
2
- JSON Schema Validator (JSV) is built on [JSON Schema Core](https://github.com/hyperjump-io/json-schema-core).
3
-
4
- * Supported JSON Schema Dialects
5
- * draft-04 | draft-06 | draft-07 | Draft 2019-09 | Draft 2020-12
6
- * Create your own dialect with [JSC](https://github.com/hyperjump-io/json-schema-core)
7
- * Schemas can reference other schemas using a different draft
8
- * Supported vocabularies (new in Draft 2019-09)
9
- * Draft 2019-09: core | applicator | validation | meta-data | content
10
- * Draft 2020-12: core | applicator | validation | meta-data | content |
11
- format-annotations
12
- * Create your own keywords and vocabularies with [JSC](https://github.com/hyperjump-io/json-schema-core)
13
- * Output formats
14
- * FLAG, BASIC, DETAILED, VERBOSE
15
- * Create your own output format with [JSC](https://github.com/hyperjump-io/json-schema-core)
16
- * Load schemas from filesystem (file://), network (http(s)://), or JavaScript
17
- * Build non-validation JSON-Schema based tools with [JSC](https://github.com/hyperjump-io/json-schema-core)
1
+ # Hyperjump - JSON Schema
2
+
3
+ A collection of modules for working with JSON Schemas.
4
+
5
+ * Validate JSON-compatible values against a JSON Schema
6
+ * Dialects: draft-2020-12, draft-2019-09, draft-07, draft-06, draft-04
7
+ * OpenAPI
8
+ * Versions/Dialects: 3.0, 3.1
9
+ * Validate an OpenAPI document
10
+ * Validate values against a schema from an OpenAPI document
11
+ * Schemas can reference other schemas using a different dialect
12
+ * Work directly with schemas on the filesystem or HTTP
13
+ * Create custom keywords, vocabularies, and dialects
14
+ * Provides utilities for building non-validation JSON Schema tooling
18
15
 
19
16
  ## Install
20
- JSV includes support for node.js JavaScript (CommonJS and ES Modules),
21
- TypeScript, and browsers.
17
+ Includes support for node.js (ES Modules, TypeScript) and browsers.
22
18
 
23
19
  ### Node.js
24
20
  ```bash
@@ -26,7 +22,7 @@ npm install @hyperjump/json-schema
26
22
  ```
27
23
 
28
24
  ### Browser
29
- When in a browser context, JSV is designed to use the browser's `fetch`
25
+ When in a browser context, this library is designed to use the browser's `fetch`
30
26
  implementation instead of a node.js fetch clone. The Webpack bundler does this
31
27
  properly without any extra configuration, but if you are using the Rollup
32
28
  bundler you will need to include the `browser: true` option in your Rollup
@@ -36,72 +32,187 @@ configuration.
36
32
  plugins: [
37
33
  resolve({
38
34
  browser: true
39
- }),
40
- commonjs()
35
+ })
41
36
  ]
42
37
  ```
43
38
 
44
39
  ### Versioning
45
- This project is in beta and there may be breaking changes at any time. When it's
46
- stable enough, I'll publish v1.0.0 and follow semantic versioning from there on
47
- out.
40
+ The API for this library is divided into two categories: Stable and
41
+ Experimental. The Stable API strictly follows semantic versioning, but the
42
+ Experimental API may have backward-incompatible changes between minor versions.
48
43
 
49
44
  ## Usage
45
+ This library supports many versions of JSON Schema. Use the pattern
46
+ `@hyperjump/json-schema/*` to import the version you need.
47
+
50
48
  ```javascript
51
- const JsonSchema = require("@hyperjump/json-schema");
49
+ import { addSchema, validate } from "@hyperjump/json-schema/draft-2020-12";
50
+ ```
52
51
 
52
+ You can import support for additional versions as needed.
53
53
 
54
- // Example: Inline schema
55
- const schemaJson = {
56
- "$schema": "https://json-schema.org/draft/2020-12/schema",
57
- "$id": "http://example.com/schemas/string",
58
- "type": "string"
59
- };
60
- JsonSchema.add(schemaJson);
61
- const schema = await JsonSchema.get("http://example.com/schemas/string");
54
+ ```javascript
55
+ import { addSchema, validate } from "@hyperjump/json-schema/draft-2020-12";
56
+ import "@hyperjump/json-schema/draft-07";
57
+ ```
62
58
 
63
- // Example: Fetch from the web
64
- const schema = await JsonSchema.get("http://example.com/schemas/string");
59
+ **Note**: The default export (`@hyperjump/json-schema`) is reserved for the
60
+ stable version of JSON Schema that will hopefully be released in 2023.
65
61
 
66
- // Example: Fetch from file
67
- const schema = await JsonSchema.get("file:///path/to/my/schemas/string.schema.json");
62
+ **Validate schema from JavaScript**
63
+ ```javascript
64
+ addSchema({
65
+ $schema: "https://json-schema.org/draft/2020-12/schema",
66
+ type: "string"
67
+ }, "http://example.com/schemas/string");
68
68
 
69
- // Example: Validate instance
70
- const output = await JsonSchema.validate(schema, "foo");
69
+ const output = await validate("http://example.com/schemas/string", "foo");
71
70
  if (output.valid) {
72
71
  console.log("Instance is valid :-)");
73
72
  } else {
74
73
  console.log("Instance is invalid :-(");
75
74
  }
75
+ ```
76
+
77
+ **Compile schema**
78
+
79
+ If you need to validate multiple instances against the same schema, you can
80
+ compile the schema into a reusable validation function.
81
+
82
+ ```javascript
83
+ const isString = await validate("http://example.com/schemas/string");
84
+ const output1 = isString("foo");
85
+ const output2 = isString(42);
86
+ ```
87
+
88
+ **Fetching schemas**
89
+
90
+ You can fetch schemas from the web or from the file system, but when fetching
91
+ from the file system, there are limitations for security reasons. If your schema
92
+ has an identifier with an http(s) scheme (**https**://example.com), it's not
93
+ allowed to reference schemas with a file scheme
94
+ (**file**:///path/to/my/schemas).
95
+
96
+ ```javascript
97
+ const output = await validate("http://example.com/schemas/string", "foo");
98
+ ```
99
+
100
+ ```javascript
101
+ const output = await validate(`file://${__dirname}/string.schema.json`, "foo");
102
+ ```
103
+
104
+ **Media type plugins**
105
+
106
+ There is a plugin system for adding support for different media types. By
107
+ default it's configured to accept schemas that have the
108
+ `application/schema+json` Content-Type (web) or a `.schema.json` file extension
109
+ (filesystem). If, for example, you want to fetch schemas that are written in
110
+ YAML, you can add a MediaTypePlugin to support that.
111
+
112
+ ```javascript
113
+ import { addMediaTypePlugin, validate } from "@hyperjump/json-schema/draft-2020-12";
114
+ import YAML from "yaml";
115
+
116
+
117
+ // Add support for JSON Schemas written in YAML
118
+ addMediaTypePlugin("application/schema+yaml", {
119
+ parse: async (response) => [YAML.parse(await response.text()), undefined],
120
+ matcher: (path) => path.endsWith(".schema.yaml")
121
+ });
122
+
123
+ // Example: Fetch schema with Content-Type: application/schema+yaml from the web
124
+ const isString = await validate("http://example.com/schemas/string");
125
+
126
+ // Example: Fetch from file with JSON Schema YAML file extension
127
+ const isString = await validate(`file://${__dirname}/string.schema.yaml`);
76
128
 
77
- // Example: Precompile validator
78
- const isString = await JsonSchema.validate(schema);
129
+ // Then validate against your schema like normal
79
130
  const output = isString("foo");
131
+ ```
132
+
133
+ **Open API**
134
+
135
+ The OpenAPI 3.0 and 3.1 meta-schemas are pre-loaded and the OpenAPI JSON Schema
136
+ dialects for each of those versions is supported. A document with a Content-Type
137
+ of `application/openapi+json` (web) or a file extension of `openapi.json`
138
+ (filesystem) is understood as an OpenAPI document.
80
139
 
81
- // Example: Specify output format
82
- const output = await JsonSchema.validate(schema, "foo", JsonSchema.VERBOSE);
140
+ Use the pattern `@hyperjump/json-schema/*` to import the version you need. The
141
+ available versions are `openapi-3-0` for 3.0 and `openapi-3-1` for 3.1.
83
142
 
84
- // Example: Specify meta-validation output format
85
- JsonSchema.setMetaOutputFormat(JsonSchema.FLAG);
143
+ YAML support isn't built in, but you can add it by writing a MediaTypePlugin.
144
+ You can use the one at `lib/openapi.js` as an example and replacing the JSON
145
+ parts with YAML.
86
146
 
87
- // Example: Disable meta-validation
88
- JsonSchema.setShouldMetaValidate(false);
147
+ ```javascript
148
+ import { addSchema, validate } from "@hyperjump/json-schema/openapi-3-1";
149
+
150
+
151
+ // Validate an OpenAPI document
152
+ const output = await validate("https://spec.openapis.org/oas/3.1/schema-base", openapi);
153
+
154
+ // Validate an instance against a schema in an OpenAPI document
155
+ const output = await validate(`file://${__dirname}/example.openapi.json#/components/schemas/foo`, 42);
89
156
  ```
90
157
 
91
- ### Media Types
92
- JSV has a plugin system for adding support for different media types. By default
93
- it's configured to accept schemas that have the `application/schema+json`
94
- Content-Type (web) or a `.schema.json` file extension (filesystem). If for
95
- example, you want to fetch schemas that are written in YAML, you can add a
96
- MediaTypePlugin to support that.
158
+ ## API
159
+ These are available from any of the exports that refer to a version of JSON
160
+ Schema, such as `@hyperjump/json-schema/draft-2020-12`.
161
+
162
+ * **addSchema**: (schema: object, retrievalUri?: string, defaultDialectId?: string) => void
163
+
164
+ Load a schema manually rather than fetching it from the filesystem or over
165
+ the network.
166
+ * **validate**: (schemaURI: string, instance: any, outputFormat: OutputFormat = FLAG) => Promise<OutputUnit>
167
+
168
+ Validate an instance against a schema. This function is curried to allow
169
+ compiling the schema once and applying it to multiple instances.
170
+ * **validate**: (schemaURI: string) => Promise<(instance: any, outputFormat: OutputFormat = FLAG) => OutputUnit>
171
+
172
+ Compiling a schema to a validation function.
173
+ * **FLAG**: "FLAG"
97
174
 
98
- * **Core.addMediaTypePlugin**: (contentType: string, plugin: MediaTypePlugin) => void
175
+ An identifier for the `FLAG` output format as defined by the 2019-09 and
176
+ 2020-12 specifications.
177
+ * **InvalidSchemaError**: Error & { output: OutputUnit }
178
+
179
+ This error is thrown if the schema being compiled is found to be invalid.
180
+ The `output` field contains an `OutputUnit` with information about the
181
+ error. You can use the `setMetaOutputFormat` configuration to set the output
182
+ format that is returned in `output`.
183
+ * **setMetaOutputFormat**: (outputFormat: OutputFormat) => void
184
+
185
+ Set the output format used for validating schemas.
186
+ * **getMetaOutputFormat**: () => OutputFormat
187
+
188
+ Get the output format used for validating schemas.
189
+ * **setShouldMetaValidate**: (isEnabled: boolean) => void
190
+
191
+ Enable or disable validating schemas.
192
+ * **getShouldMetaValidate**: (isEnabled: boolean) => void
193
+
194
+ Determine if validating schemas is enabled.
195
+ * **addMediaTypePlugin**: (contentType: string, plugin: MediaTypePlugin) => void
99
196
 
100
197
  Add a custom media type handler to support things like YAML or to change the
101
198
  way JSON is supported.
199
+
200
+ **Type Definitions**
201
+
202
+ The following types are used in the above definitions
203
+
204
+ * **OutputFormat**: **FLAG**
205
+
206
+ Only the `FLAG` output format is part of the Stable API. Additional output
207
+ formats are included as part of the Experimental API.
208
+ * **OutputUnit**: { valid: boolean }
209
+
210
+ Output is an experimental feature of the JSON Schema specification. There
211
+ may be additional fields present in the OutputUnit, but only the `valid`
212
+ property should be considered part of the Stable API.
102
213
  * **MediaTypePlugin**: object
103
214
 
104
- * parse: (response: Response, mediaTypeParameters: object) => [SchemaObject, string]
215
+ * parse: (response: Response, mediaTypeParameters: object) => [object | boolean, string?]
105
216
 
106
217
  Given a fetch Response object, parse the body of the request. Return the
107
218
  parsed schema and an optional default dialectId.
@@ -110,96 +221,353 @@ MediaTypePlugin to support that.
110
221
  Given a filesystem path, return whether or not the file should be
111
222
  considered a member of this media type.
112
223
 
224
+ ## Experimental
225
+ The JSON Schema specification includes several features that are experimental in
226
+ nature including the Vocabulary System, Output Formats, and Annotations. This
227
+ implementation aims to support only the latest version of experimental features
228
+ as they evolve. There will not be a major version bump if there needs to be
229
+ backward incompatible changes to the Experimental API.
230
+
231
+ ### Usage
232
+ All experimental features are segregated into exports that include the word
233
+ "experimental" so you never accidentally depend on something that could change
234
+ or be removed in future releases.
235
+
113
236
  ```javascript
114
- const JsonSchema = require("@hyperjump/json-schema");
115
- const YAML = require("yaml");
237
+ import { BASIC } from "@hyperjump/json-schema/experimental";
238
+ ```
116
239
 
240
+ **Change the validation output format**
117
241
 
118
- // Add support for JSON Schemas written in YAML
119
- JsonSchema.addMediaTypePlugin("application/schema+yaml", {
120
- parse: async (response) => [YAML.parse(await response.text()), undefined],
121
- matcher: (path) => path.endsWith(".schema.yaml")
122
- });
242
+ The `FLAG` output format isn't very informative. You can change the output
243
+ format used for validation to get more information.
123
244
 
124
- // Example: Fetch schema with Content-Type: application/schema+yaml from the web
125
- const schema = await JsonSchema.get("http://example.com/schemas/string");
245
+ ```javascript
246
+ const output = await validate("https://example.com/schema1", 42, BASIC);
247
+ ```
126
248
 
127
- // Example: Fetch from file with JSON Schema YAML file extension
128
- const schema = await JsonSchema.get("file:///path/to/my/schemas/string.schema.yaml");
249
+ **Change the schema validation output format**
129
250
 
130
- // Then validate against your schema like normal
251
+ The output format used for validating schemas can be changed as well.
252
+
253
+ ```javascript
254
+ setMetaOutputFormat(BASIC);
255
+ try {
256
+ const output = await validate("https://example.com/invalid-schema");
257
+ } catch (error) {
258
+ console.log(error.output);
259
+ }
131
260
  ```
132
261
 
133
- ## TypeScript
134
- Although the package is written in JavaScript, type definitions are included for
135
- TypeScript support. The following example shows the types you might want to
136
- know.
262
+ **Keywords, Vocabularies, and Dialects**
137
263
 
138
- ```typescript
139
- import JsonSchema, { InvalidSchemaError } from "@hyperjump/json-schema";
140
- import type { SchemaDocument, Validator, Result, Draft202012Schema } from "@hyperjump/json-schema";
264
+ In order to create and use a custom keyword, you need to define your keyword's
265
+ behavior, create a vocabulary that includes that keyword, and then create a
266
+ dialect that includes your vocabulary.
141
267
 
268
+ ```javascript
269
+ import { addSchema, validate } from "@hyperjump/json-schema/draft-2020-12";
270
+ import { addKeyword, defineVocabulary, Validation } from "@hyperjump/json-schema/experimental";
271
+ import * as Schema from "@hyperjump/json-schema/schema/experimental";
142
272
 
143
- const schemaJson: Draft202012Schema = {
144
- "$id": "https://json-schema.hyperjump.io/schema",
145
- "$schema": "https://json-schema.org/draft/2020-12/schema",
146
273
 
147
- "type": "string"
148
- };
149
- JsonSchema.add(schemaJson);
274
+ // Define a keyword that's an array of schemas that are applied sequentially
275
+ // using implication: A -> B -> C -> D
276
+ addKeyword({
277
+ id: "https://example.com/keyword/implication",
150
278
 
151
- const schema: SchemaDocument = await JsonSchema.get("https://json-schema.hyperjump.io/schema");
152
- try {
153
- const isString: Validator = await JsonSchema.validate(schema);
154
- const result: Result = isString("foo");
155
- console.log("isString:", result.valid);
156
- } catch (error: unknown) {
157
- if (error instanceof InvalidSchemaError) {
158
- console.log(error.output);
159
- } else {
160
- console.log(error);
279
+ compile: (schema, ast) => {
280
+ return Schema.map(async (itemSchema) => Validation.compile(await itemSchema, ast), schema);
281
+ },
282
+
283
+ interpret: (implies, instance, ast, dynamicAnchors) => {
284
+ return implies.reduce((acc, schema) => {
285
+ return !acc || Validation.interpret(schema, instance, ast, dynamicAnchors);
286
+ }, true);
161
287
  }
162
- }
288
+ });
289
+
290
+ // Create a vocabulary with this keyword and call it "implies"
291
+ defineVocabulary("https://example.com/vocab/logic", {
292
+ "implies": "https://example.com/keyword/implication"
293
+ });
294
+
295
+ // Create a vocabulary schema for this vocabulary
296
+ addSchema({
297
+ "$id": "https://example.com/meta/logic",
298
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
299
+
300
+ "$dynamicAnchor": "meta",
301
+ "properties": {
302
+ "implies": {
303
+ "type": "array",
304
+ "items": { "$dynamicRef": "meta" },
305
+ "minItems": 2
306
+ }
307
+ }
308
+ });
309
+
310
+ // Create a dialect schema adding this vocabulary to the standard JSON Schema
311
+ // vocabularies
312
+ addSchema({
313
+ "$id": "https://example.com/dialect/logic",
314
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
315
+
316
+ "$vocabulary": {
317
+ "https://json-schema.org/vocab/core": true,
318
+ "https://json-schema.org/vocab/applicator": true,
319
+ "https://json-schema.org/vocab/unevaluated": true,
320
+ "https://json-schema.org/vocab/validation": true,
321
+ "https://json-schema.org/vocab/meta-data": true,
322
+ "https://json-schema.org/vocab/format-annotation": true,
323
+ "https://json-schema.org/vocab/content": true,
324
+ "https://example.com/vocab/logic": true
325
+ },
326
+
327
+ "$dynamicAnchor": "meta",
328
+
329
+ "allOf": [
330
+ { "$ref": "https://json-schema.org/draft/2020-12/schema" },
331
+ { "$ref": "/meta/logic" }
332
+ ]
333
+ });
334
+
335
+ // Use your dialect to validate a JSON instance
336
+ addSchema({
337
+ "$schema": "https://example.com/dialect/logic",
338
+
339
+ "type": "number",
340
+ "implies": [
341
+ { "minimum": 10 },
342
+ { "multipleOf": 2 }
343
+ ]
344
+ }, "https://example.com/schema1");
345
+ const output = await validate("https://example.com/schema1", 42);
163
346
  ```
164
347
 
165
- ## API
166
- * **add**: (schema: object, url?: URI, dialectId?: string) => SDoc
348
+ **Custom Meta Schema**
167
349
 
168
- Load a schema. See [JSC - $id](https://github.com/hyperjump-io/json-schema-core#id)
169
- and [JSC - $schema](https://github.com/hyperjump-io/json-schema-core#schema-1)
170
- for more information.
171
- * **get**: (url: URI, contextDoc?: SDoc, recursive: boolean = false) => Promise<SDoc>
350
+ You can use a custom meta-schema to restrict users to a subset of JSON Schema
351
+ functionality. This example requires that no unknown keywords are used in the
352
+ schema.
172
353
 
173
- Fetch a schema. Schemas can come from an HTTP request, a file, or a schema
174
- that was added with `add`.
175
- * **validate**: (schema: SDoc, instance: any, outputFormat: OutputFormat = FLAG) => Promise<OutputUnit>
354
+ ```javascript
355
+ addSchema({
356
+ "$id": "https://example.com/meta-schema1",
357
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
176
358
 
177
- Validate an instance against a schema. The function is curried to allow
178
- compiling the schema once and applying it to multiple instances.
179
- * **compile**: (schema: SDoc) => Promise<CompiledSchema>
359
+ "$vocabulary": {
360
+ "https://json-schema.org/draft/2020-12/vocab/core": true,
361
+ "https://json-schema.org/draft/2020-12/vocab/applicator": true,
362
+ "https://json-schema.org/draft/2020-12/vocab/unevaluated": true,
363
+ "https://json-schema.org/draft/2020-12/vocab/validation": true,
364
+ "https://json-schema.org/draft/2020-12/vocab/meta-data": true,
365
+ "https://json-schema.org/draft/2020-12/vocab/format-annotation": true,
366
+ "https://json-schema.org/draft/2020-12/vocab/content": true
367
+ },
368
+
369
+ "$dynamicAnchor": "meta",
370
+
371
+ "$ref": "https://json-schema.org/draft/2020-12/schema",
372
+ "unevaluatedProperties": false
373
+ });
374
+
375
+ addSchema({
376
+ $schema: "https://example.com/meta-schema1",
377
+ type: "number",
378
+ foo: 42
379
+ }, "https://example.com/schema1");
380
+
381
+ const output = await validate("https://example.com/schema1", 42); // Expect InvalidSchemaError
382
+ ```
383
+
384
+ ### API
385
+ These are available from the `@hyperjump/json-schema/experimental` export.
180
386
 
181
- Compile a schema to be interpreted later. A compiled schema is a JSON
182
- serializable structure that can be serialized an restored for later use.
183
- * **interpret**: (schema: CompiledSchema, instance: any, outputFormat: OutputFormat = FLAG) => OutputUnit
387
+ * **compile**: (schema: SchemaDocument) => Promise<CompiledSchema>
388
+
389
+ Return a compiled schema. This is useful if you're creating tooling for
390
+ something other than validation.
391
+ * **interpret**: (schema: CompiledSchema, instance: Instance, outputFormat: OutputFormat = BASIC) => OutputUnit
184
392
 
185
393
  A curried function for validating an instance against a compiled schema.
186
- * **setMetaOutputFormat**: (outputFormat: OutputFormat = DETAILED) => undefined
394
+ This can be useful for creating custom output formats.
395
+ * **addKeyword**: (keywordHandler: Keyword) => void
396
+
397
+ Define a keyword for use in a vocabulary.
398
+ * **defineVocabulary**: (id: string, keywords: { [keyword: string]: string }) => void
399
+
400
+ Define a vocabulary that maps keyword name to keyword URIs defined using
401
+ `addKeyword`.
402
+ * **getKeyword**: (keywordId: string) => Keyword
403
+
404
+ Get a keyword object by its URI. This is useful for building non-validation
405
+ tooling.
406
+ * **getKeywordName**: (dialectId: string, keywordId: string) => string
407
+
408
+ Determine a keyword's name given its URI a dialect URI. This is useful when
409
+ defining a keyword that depends on the value of another keyword (such as how
410
+ `contains` depends on `minContains` and `maxContains`).
411
+ * **loadDialect**: (dialectId: string, dialect: { [vocabularyId: string] }) => void
412
+
413
+ Define a dialect. In most cases, dialects are loaded automatically from the
414
+ `$vocabulary` keyword in the meta-schema. The only time you would need to
415
+ load a dialect manually is if you're creating a distinct version of JSON
416
+ Schema rather than creating a dialect of an existing version of JSON Schema.
417
+ * **Validation**: Keyword
418
+
419
+ A Keyword object that represents a "validate" operation. You would use this
420
+ for compiling and evaluating sub-schemas when defining a custom keyword.
421
+
422
+ **Type Definitions**
423
+
424
+ The following types are used in the above definitions
425
+
426
+ * **OutputFormat**: **FLAG** | **BASIC** | **DETAILED** | **VERBOSE**
427
+
428
+ In addition to the `FLAG` output format in the Stable API, the Experimental
429
+ API includes support for the `BASIC`, `DETAILED`, and `VERBOSE` formats as
430
+ specified in the 2019-09 specification (with some minor customizations).
431
+ This implementation doesn't include annotations or human readable error
432
+ messages. The output can be processed to create human readable error
433
+ messages as needed.
434
+ * **Keyword**: object
435
+ * id: string
436
+
437
+ A URI that uniquely identifies the keyword. It should use a domain you
438
+ own to avoid conflict with keywords defined by others.
439
+ * compile: (schema: SchemaDocument, ast: AST, parentSchema: SchemaDocument) => Promise<A>
440
+
441
+ This function takes the keyword value, does whatever preprocessing it
442
+ can on it without an instance, and returns the result. The returned
443
+ value will be passed to the `interpret` function. The `ast` parameter is
444
+ needed for compiling sub-schemas. The `parentSchema` parameter is
445
+ primarily useful for looking up the value of an adjacent keyword that
446
+ might effect this one.
447
+ * interpret: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => boolean
448
+
449
+ This function takes the value returned by the `compile` function and the
450
+ instance value that is being validated and returns whether the value is
451
+ valid or not. The other parameters are only needed for validating
452
+ sub-schemas.
453
+ * collectEvaluatedProperties?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => string[] | false
454
+
455
+ If the keyword is an applicator, it will need to implements this
456
+ function for `unevaluatedProperties` to work as expected.
457
+ * collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => Set<number> | false
458
+
459
+ If the keyword is an applicator, it will need to implements this
460
+ function for `unevaluatedItems` to work as expected.
461
+
462
+ ### Schema
463
+ These functions are available from the
464
+ `@hyperjump/json-schema/schema/experimental` export.
465
+
466
+ This library uses SchemaDocument objects to represent a value in a schema.
467
+ You'll work with these objects if you create a custom keyword. This module is a
468
+ set of functions for working with SchemaDocuments.
469
+
470
+ * **Schema.add**: (schema: object, retrievalUri?: string, dialectId?: string) => string
471
+
472
+ Load a schema. Returns the identifier for the schema.
473
+ * **Schema.get**: (url: string, contextDoc?: SchemaDocument) => Promise<SchemaDocument>
474
+
475
+ Fetch a schema. Schemas can come from an HTTP request, a file, or a schema
476
+ that was added with `Schema.add`.
477
+ * **Schema.uri**: (doc: SchemaDocument) => string
478
+
479
+ Returns a URI for the value the SchemaDocument represents.
480
+ * **Schema.value**: (doc: SchemaDocument) => any
481
+
482
+ Returns the value the SchemaDocument represents.
483
+ * **Schema.typeOf**: (doc: SchemaDocument, type: string) => boolean
484
+
485
+ Determines if the JSON type of the given doc matches the given type.
486
+ * **Schema.has**: (key: string, doc: SchemaDocument) => Promise<SchemaDocument>
487
+
488
+ Similar to `key in schema`.
489
+ * **Schema.step**: (key: string, doc: SchemaDocument) => Promise<SchemaDocument>
490
+
491
+ Similar to `schema[key]`, but returns an SchemaDocument.
492
+ * **Schema.entries**: (doc: SchemaDocument) => Promise<[[string, SchemaDocument]]>
493
+
494
+ Similar to `Object.entries`, but returns SchemaDocuments for values.
495
+ * **Schema.keys**: (doc: SchemaDocument) => [string]
496
+
497
+ Similar to `Object.keys`.
498
+ * **Schema.map**: (fn: (item: Promise<SchemaDocument>, index: integer) => T, doc: SchemaDocument) => Promise<[T]>
499
+
500
+ A `map` function for an SchemaDocument whose value is an array.
501
+ * **Schema.length**: (doc: SchemaDocument) => number
502
+
503
+ Similar to `Array.prototype.length`.
504
+ * **Schema.toSchema**: (doc: SchemaDocument, options: ToSchemaOptions) => object
505
+
506
+ Get a raw schema from a Schema Document.
507
+
508
+ **Type Definitions**
509
+
510
+ The following types are used in the above definitions
511
+
512
+ * **ToSchemaOptions**: object
513
+
514
+ * parentId: string (default: "") -- `file://` URIs will be generated
515
+ relative to this path.
516
+ * parentDialect: string (default: "") -- If the dialect of the schema
517
+ * matches this value, the `$schema` keyword will be omitted.
518
+ * includeEmbedded: boolean (default: true) -- If false, embedded schemas
519
+ will be unbundled from the schema.
520
+
521
+ ### Instance
522
+ These functions are available from the
523
+ `@hyperjump/json-schema/instance/experimental` export.
524
+
525
+ This library uses InstanceDocument objects to represent a value in an instance.
526
+ You'll work with these objects if you create a custom keyword. This module is a
527
+ set of functions for working with InstanceDocuments.
528
+
529
+ * **Instance.cons**: (instance: any, uri?: string) => InstanceDocument
530
+
531
+ Construct an InstanceDocument from a value.
532
+ * **Instance.get**: (url: string, contextDoc: InstanceDocument) => InstanceDocument
533
+
534
+ Apply a same-resource reference to a InstanceDocument.
535
+ * **Instance.uri**: (doc: InstanceDocument) => string
536
+
537
+ Returns a URI for the value the InstanceDocument represents.
538
+ * **Instance.value**: (doc: InstanceDocument) => any
539
+
540
+ Returns the value the InstanceDocument represents.
541
+ * **Instance.has**: (key: string, doc: InstanceDocument) => any
542
+
543
+ Similar to `key in instance`.
544
+ * **Instance.typeOf**: (doc: InstanceDocument, type: string) => boolean
545
+
546
+ Determines if the JSON type of the given doc matches the given type.
547
+ * **Instance.step**: (key: string, doc: InstanceDocument) => InstanceDocument
548
+
549
+ Similar to `schema[key]`, but returns a InstanceDocument.
550
+ * **Instance.entries**: (doc: InstanceDocument) => [string, InstanceDocument]
551
+
552
+ Similar to `Object.entries`, but returns IDocs for values.
553
+ * **Instance.keys**: (doc: InstanceDocument) => [string]
554
+
555
+ Similar to `Object.keys`.
556
+ * **Instance.map**: (fn: (item: InstanceDocument, index: integer) => T, doc: InstanceDocument) => [T]
187
557
 
188
- Set the output format for meta-validation. Meta-validation output is only
189
- returned if meta-validation results in an error.
190
- * **setShouldMetaValidate**: (isEnabled: boolean) => undefined
558
+ A `map` function for an InstanceDocument whose value is an array.
559
+ * **Instance.reduce**: (fn: (accumulator: T, item: InstanceDocument, index: integer) => T, initial: T, doc: InstanceDocument) => T
191
560
 
192
- Enable or disable meta-validation.
193
- * **OutputFormat**: [**FLAG** | **BASIC** | **DETAILED** | **VERBOSE**]
561
+ A `reduce` function for an InstanceDocument whose value is an array.
562
+ * **Instance.every**: (fn: (doc: InstanceDocument, index: integer) => boolean, doc: InstanceDocument) => boolean
194
563
 
195
- See [JSC - Output](https://github.com/hyperjump-io/json-schema-core#output)
196
- for more information on output formats.
564
+ An `every` function for an InstanceDocument whose value is an array.
565
+ * **Instance.some**: (fn: (doc: InstanceDocument, index: integer) => boolean, doc: InstanceDocument) => boolean
197
566
 
198
- ## Not (yet) Supported
199
- This implementation supports all required features of JSON Schema. The following
200
- optional features are not supported yet.
567
+ A `some` function for an InstanceDocument whose value is an array.
568
+ * **Instance.length**: (doc: InstanceDocument) => number
201
569
 
202
- * The format-assertion vocabulary
570
+ Similar to `Array.prototype.length`.
203
571
 
204
572
  ## Contributing
205
573