@hyperjump/json-schema 1.0.0 → 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.
package/README.md CHANGED
@@ -4,6 +4,10 @@ A collection of modules for working with JSON Schemas.
4
4
 
5
5
  * Validate JSON-compatible values against a JSON Schema
6
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
7
11
  * Schemas can reference other schemas using a different dialect
8
12
  * Work directly with schemas on the filesystem or HTTP
9
13
  * Create custom keywords, vocabularies, and dialects
@@ -126,6 +130,31 @@ const isString = await validate(`file://${__dirname}/string.schema.yaml`);
126
130
  const output = isString("foo");
127
131
  ```
128
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.
139
+
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.
142
+
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.
146
+
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);
156
+ ```
157
+
129
158
  ## API
130
159
  These are available from any of the exports that refer to a version of JSON
131
160
  Schema, such as `@hyperjump/json-schema/draft-2020-12`.
package/lib/openapi.js ADDED
@@ -0,0 +1,29 @@
1
+ import { addMediaTypePlugin } from "./media-types.js";
2
+
3
+
4
+ const is31 = RegExp.prototype.test.bind(/^3\.1\.\d+(-.+)?$/);
5
+ const is30 = RegExp.prototype.test.bind(/^3\.0\.\d+(-.+)?$/);
6
+
7
+ addMediaTypePlugin("application/openapi+json", {
8
+ parse: async (response, contentTypeParameters) => {
9
+ const doc = await response.json();
10
+
11
+ let defaultDialect;
12
+ const version = doc.openapi || contentTypeParameters.version;
13
+
14
+ if (is30(version)) {
15
+ defaultDialect = "https://spec.openapis.org/oas/3.0/schema";
16
+ } else if (is31(version)) {
17
+ if (!("jsonSchemaDialect" in doc) || doc.jsonSchemaDialect === "https://spec.openapis.org/oas/3.1/dialect/base") {
18
+ defaultDialect = "https://spec.openapis.org/oas/3.1/schema-base";
19
+ } else {
20
+ defaultDialect = `https://spec.openapis.org/oas/3.1/schema-${encodeURIComponent(doc.jsonSchemaDialect)}`;
21
+ }
22
+ } else {
23
+ throw Error("Invalid OpenAPI document. Add the 'openapi' field and try again.");
24
+ }
25
+
26
+ return [doc, defaultDialect];
27
+ },
28
+ matcher: (path) => /(\/|\.)openapi\.json$/.test(path)
29
+ });
@@ -0,0 +1,174 @@
1
+ export default {
2
+ "id": "https://spec.openapis.org/oas/3.0/dialect",
3
+ "$schema": "http://json-schema.org/draft-04/schema#",
4
+
5
+ "type": "object",
6
+ "properties": {
7
+ "title": { "type": "string" },
8
+ "multipleOf": {
9
+ "type": "number",
10
+ "minimum": 0,
11
+ "exclusiveMinimum": true
12
+ },
13
+ "maximum": { "type": "number" },
14
+ "exclusiveMaximum": {
15
+ "type": "boolean",
16
+ "default": false
17
+ },
18
+ "minimum": { "type": "number" },
19
+ "exclusiveMinimum": {
20
+ "type": "boolean",
21
+ "default": false
22
+ },
23
+ "maxLength": { "$ref": "#/definitions/positiveInteger" },
24
+ "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
25
+ "pattern": {
26
+ "type": "string",
27
+ "format": "regex"
28
+ },
29
+ "maxItems": { "$ref": "#/definitions/positiveInteger" },
30
+ "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
31
+ "uniqueItems": {
32
+ "type": "boolean",
33
+ "default": false
34
+ },
35
+ "maxProperties": { "$ref": "#/definitions/positiveInteger" },
36
+ "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
37
+ "required": {
38
+ "type": "array",
39
+ "items": { "type": "string" },
40
+ "minItems": 1,
41
+ "uniqueItems": true
42
+ },
43
+ "enum": {
44
+ "type": "array",
45
+ "minItems": 1,
46
+ "uniqueItems": false
47
+ },
48
+ "type": { "enum": ["array", "boolean", "integer", "number", "object", "string"] },
49
+ "not": { "$ref": "#" },
50
+ "allOf": { "$ref": "#/definitions/schemaArray" },
51
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
52
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
53
+ "items": { "$ref": "#" },
54
+ "properties": {
55
+ "type": "object",
56
+ "additionalProperties": { "$ref": "#" }
57
+ },
58
+ "additionalProperties": {
59
+ "anyOf": [
60
+ { "type": "boolean" },
61
+ { "$ref": "#" }
62
+ ],
63
+ "default": {}
64
+ },
65
+ "description": { "type": "string" },
66
+ "format": { "type": "string" },
67
+ "default": {},
68
+ "nullable": {
69
+ "type": "boolean",
70
+ "default": false
71
+ },
72
+ "discriminator": { "$ref": "#/definitions/Discriminator" },
73
+ "readOnly": {
74
+ "type": "boolean",
75
+ "default": false
76
+ },
77
+ "writeOnly": {
78
+ "type": "boolean",
79
+ "default": false
80
+ },
81
+ "example": {},
82
+ "externalDocs": { "$ref": "#/definitions/ExternalDocumentation" },
83
+ "deprecated": {
84
+ "type": "boolean",
85
+ "default": false
86
+ },
87
+ "xml": { "$ref": "#/definitions/XML" },
88
+ "$ref": {
89
+ "type": "string",
90
+ "format": "uri"
91
+ }
92
+ },
93
+ "patternProperties": {
94
+ "^x-": {}
95
+ },
96
+ "additionalProperties": false,
97
+
98
+ "anyOf": [
99
+ {
100
+ "not": { "required": ["$ref"] }
101
+ },
102
+ { "maxProperties": 1 }
103
+ ],
104
+
105
+ "definitions": {
106
+ "schemaArray": {
107
+ "type": "array",
108
+ "minItems": 1,
109
+ "items": { "$ref": "#" }
110
+ },
111
+ "positiveInteger": {
112
+ "type": "integer",
113
+ "minimum": 0
114
+ },
115
+ "positiveIntegerDefault0": {
116
+ "allOf": [{ "$ref": "#/definitions/positiveInteger" }, { "default": 0 }]
117
+ },
118
+ "Discriminator": {
119
+ "type": "object",
120
+ "required": [
121
+ "propertyName"
122
+ ],
123
+ "properties": {
124
+ "propertyName": {
125
+ "type": "string"
126
+ },
127
+ "mapping": {
128
+ "type": "object",
129
+ "additionalProperties": {
130
+ "type": "string"
131
+ }
132
+ }
133
+ }
134
+ },
135
+ "ExternalDocumentation": {
136
+ "type": "object",
137
+ "required": ["url"],
138
+ "properties": {
139
+ "description": { "type": "string" },
140
+ "url": {
141
+ "type": "string",
142
+ "format": "uri-reference"
143
+ }
144
+ },
145
+ "patternProperties": {
146
+ "^x-": {}
147
+ },
148
+ "additionalProperties": false
149
+ },
150
+ "XML": {
151
+ "type": "object",
152
+ "properties": {
153
+ "name": { "type": "string" },
154
+ "namespace": {
155
+ "type": "string",
156
+ "format": "uri"
157
+ },
158
+ "prefix": { "type": "string" },
159
+ "attribute": {
160
+ "type": "boolean",
161
+ "default": false
162
+ },
163
+ "wrapped": {
164
+ "type": "boolean",
165
+ "default": false
166
+ }
167
+ },
168
+ "patternProperties": {
169
+ "^x-": {}
170
+ },
171
+ "additionalProperties": false
172
+ }
173
+ }
174
+ };
@@ -0,0 +1,4 @@
1
+ import metaData from "../lib/keywords/meta-data.js";
2
+
3
+
4
+ export default { id: "https://spec.openapis.org/oas/3.0/keyword/discriminator", ...metaData };
@@ -0,0 +1,4 @@
1
+ import metaData from "../lib/keywords/meta-data.js";
2
+
3
+
4
+ export default { id: "https://spec.openapis.org/oas/3.0/keyword/example", ...metaData };
@@ -0,0 +1,4 @@
1
+ import metaData from "../lib/keywords/meta-data.js";
2
+
3
+
4
+ export default { id: "https://spec.openapis.org/oas/3.0/keyword/externalDocs", ...metaData };
@@ -0,0 +1,60 @@
1
+ import type { Json } from "@hyperjump/json-pointer";
2
+ import type { JsonSchemaType } from "../lib/common.js";
3
+
4
+
5
+ export type OasSchema30 = {
6
+ $ref: string;
7
+ } | {
8
+ title?: string;
9
+ description?: string;
10
+ default?: Json;
11
+ multipleOf?: number;
12
+ maximum?: number;
13
+ exclusiveMaximum?: boolean;
14
+ minimum?: number;
15
+ exclusiveMinimum?: boolean;
16
+ maxLength?: number;
17
+ minLength?: number;
18
+ pattern?: string;
19
+ items?: OasSchema30;
20
+ maxItems?: number;
21
+ minItems?: number;
22
+ uniqueItems?: boolean;
23
+ maxProperties?: number;
24
+ minProperties?: number;
25
+ required?: string[];
26
+ additionalProperties?: boolean | OasSchema30;
27
+ properties?: Record<string, OasSchema30>;
28
+ enum?: Json[];
29
+ type?: JsonSchemaType;
30
+ nullable?: boolean;
31
+ format?: "date-time" | "email" | "hostname" | "ipv4" | "ipv6" | "uri" | "int32" | "int64" | "float" | "double" | "byte" | "binary" | "date" | "password";
32
+ allOf?: OasSchema30[];
33
+ anyOf?: OasSchema30[];
34
+ oneOf?: OasSchema30[];
35
+ not?: OasSchema30;
36
+ example: Json;
37
+ discriminator: Discriminator;
38
+ externalDocs: ExternalDocs;
39
+ xml: Xml;
40
+ };
41
+
42
+ type Discriminator = {
43
+ propertyName: string;
44
+ mappings: Record<string, string>;
45
+ };
46
+
47
+ type ExternalDocs = {
48
+ url: string;
49
+ description: string;
50
+ };
51
+
52
+ type Xml = {
53
+ name: string;
54
+ namespace: string;
55
+ prefix: string;
56
+ attribute: boolean;
57
+ wrapped: boolean;
58
+ };
59
+
60
+ export * from "../lib/index.js";
@@ -0,0 +1,77 @@
1
+ import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js";
2
+ import { addSchema } from "../lib/core.js";
3
+ import "../lib/openapi.js";
4
+
5
+ import dialectSchema from "./dialect.js";
6
+ import schema20210928 from "./schema/2021-09-28.js";
7
+
8
+ import discriminator from "./discriminator.js";
9
+ import example from "./example.js";
10
+ import externalDocs from "./externalDocs.js";
11
+ import nullable from "./nullable.js";
12
+ import type from "./type.js";
13
+ import xml from "./xml.js";
14
+
15
+
16
+ addKeyword(discriminator);
17
+ addKeyword(example);
18
+ addKeyword(externalDocs);
19
+ addKeyword(nullable);
20
+ addKeyword(type);
21
+ addKeyword(xml);
22
+
23
+ const jsonSchemaVersion = "https://spec.openapis.org/oas/3.0/dialect";
24
+
25
+ defineVocabulary(jsonSchemaVersion, {
26
+ "$ref": "https://json-schema.org/keyword/draft-04/ref",
27
+ "additionalProperties": "https://json-schema.org/keyword/additionalProperties",
28
+ "allOf": "https://json-schema.org/keyword/allOf",
29
+ "anyOf": "https://json-schema.org/keyword/anyOf",
30
+ "default": "https://json-schema.org/keyword/default",
31
+ "deprecated": "https://json-schema.org/keyword/deprecated",
32
+ "description": "https://json-schema.org/keyword/description",
33
+ "discriminator": "https://spec.openapis.org/oas/3.0/keyword/discriminator",
34
+ "enum": "https://json-schema.org/keyword/enum",
35
+ "example": "https://spec.openapis.org/oas/3.0/keyword/example",
36
+ "exclusiveMaximum": "https://json-schema.org/keyword/draft-04/exclusiveMaximum",
37
+ "exclusiveMinimum": "https://json-schema.org/keyword/draft-04/exclusiveMinimum",
38
+ "externalDocs": "https://spec.openapis.org/oas/3.0/keyword/externalDocs",
39
+ "format": "https://json-schema.org/keyword/format",
40
+ "items": "https://json-schema.org/keyword/draft-04/items",
41
+ "maxItems": "https://json-schema.org/keyword/maxItems",
42
+ "maxLength": "https://json-schema.org/keyword/maxLength",
43
+ "maxProperties": "https://json-schema.org/keyword/maxProperties",
44
+ "maximum": "https://json-schema.org/keyword/draft-04/maximum",
45
+ "minItems": "https://json-schema.org/keyword/minItems",
46
+ "minLength": "https://json-schema.org/keyword/minLength",
47
+ "minProperties": "https://json-schema.org/keyword/minProperties",
48
+ "minimum": "https://json-schema.org/keyword/draft-04/minimum",
49
+ "multipleOf": "https://json-schema.org/keyword/multipleOf",
50
+ "not": "https://json-schema.org/keyword/not",
51
+ "nullable": "https://spec.openapis.org/oas/3.0/keyword/nullable",
52
+ "oneOf": "https://json-schema.org/keyword/oneOf",
53
+ "pattern": "https://json-schema.org/keyword/pattern",
54
+ "properties": "https://json-schema.org/keyword/properties",
55
+ "readOnly": "https://json-schema.org/keyword/readOnly",
56
+ "required": "https://json-schema.org/keyword/required",
57
+ "title": "https://json-schema.org/keyword/title",
58
+ "type": "https://spec.openapis.org/oas/3.0/keyword/type",
59
+ "uniqueItems": "https://json-schema.org/keyword/uniqueItems",
60
+ "writeOnly": "https://json-schema.org/keyword/writeOnly",
61
+ "xml": "https://spec.openapis.org/oas/3.0/keyword/xml"
62
+ });
63
+
64
+ loadDialect(jsonSchemaVersion, {
65
+ [jsonSchemaVersion]: true
66
+ });
67
+
68
+ loadDialect("https://spec.openapis.org/oas/3.0/schema", {
69
+ [jsonSchemaVersion]: true
70
+ });
71
+
72
+ addSchema(dialectSchema);
73
+
74
+ addSchema(schema20210928, "https://spec.openapis.org/oas/3.0/schema");
75
+ addSchema(schema20210928, "https://spec.openapis.org/oas/3.0/schema/latest");
76
+
77
+ export * from "../draft-04/index.js";
@@ -0,0 +1,4 @@
1
+ import metaData from "../lib/keywords/meta-data.js";
2
+
3
+
4
+ export default { id: "https://spec.openapis.org/oas/3.0/keyword/nullable", ...metaData };