@hyperjump/json-schema 1.2.3 → 1.2.5
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/lib/core.js +21 -12
- package/lib/fetch.js +4 -3
- package/lib/index.js +2 -0
- package/lib/keywords/meta-data.js +4 -1
- package/lib/keywords/unknown.js +4 -0
- package/lib/keywords/validation.js +6 -3
- package/lib/keywords.js +5 -13
- package/lib/media-types.d.ts +1 -0
- package/lib/openapi.js +4 -2
- package/openapi-3-0/index.d.ts +11 -11
- package/openapi-3-0/schema/2021-09-28.js +4 -40
- package/openapi-3-1/index.d.ts +11 -11
- package/package.json +2 -3
package/lib/core.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import curry from "just-curry-it";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { subscribe, unsubscribe } from "./pubsub.js";
|
|
3
|
+
import {
|
|
4
|
+
setMetaSchemaOutputFormat,
|
|
5
|
+
getShouldValidateSchema,
|
|
6
|
+
isExperimentalKeywordEnabled,
|
|
7
|
+
setExperimentalKeywordEnabled,
|
|
8
|
+
getMetaSchemaOutputFormat
|
|
9
|
+
} from "./configuration.js";
|
|
4
10
|
import * as Instance from "./instance.js";
|
|
5
11
|
import { InvalidSchemaError } from "./invalid-schema-error.js";
|
|
6
12
|
import * as Schema from "./schema.js";
|
|
@@ -8,7 +14,7 @@ import Validation from "./keywords/validation.js";
|
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
export const FLAG = "FLAG", BASIC = "BASIC", DETAILED = "DETAILED", VERBOSE = "VERBOSE";
|
|
11
|
-
|
|
17
|
+
setMetaSchemaOutputFormat(FLAG);
|
|
12
18
|
|
|
13
19
|
export const validate = async (url, value = undefined, outputFormat = undefined) => {
|
|
14
20
|
const compiled = await compile(url);
|
|
@@ -30,9 +36,12 @@ export const interpret = curry(({ ast, schemaUri }, value, outputFormat = FLAG)
|
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
const output = [];
|
|
33
|
-
const subscriptionToken =
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
const subscriptionToken = subscribe("result", outputHandler(outputFormat, output));
|
|
40
|
+
try {
|
|
41
|
+
Validation.interpret(schemaUri, value, ast, {});
|
|
42
|
+
} finally {
|
|
43
|
+
unsubscribe("result", subscriptionToken);
|
|
44
|
+
}
|
|
36
45
|
|
|
37
46
|
return output[0];
|
|
38
47
|
});
|
|
@@ -70,26 +79,26 @@ const outputHandler = (outputFormat, output) => {
|
|
|
70
79
|
};
|
|
71
80
|
|
|
72
81
|
const metaValidators = {};
|
|
73
|
-
|
|
74
|
-
if (
|
|
82
|
+
subscribe("validate.metaValidate", async (message, schema) => {
|
|
83
|
+
if (getShouldValidateSchema() && !schema.validated) {
|
|
75
84
|
Schema.markValidated(schema.id);
|
|
76
85
|
|
|
77
86
|
// Compile
|
|
78
87
|
if (!(schema.dialectId in metaValidators)) {
|
|
79
88
|
// Dynamic references are experimental, but are necessary for meta-validation
|
|
80
89
|
const dyanmicRefKeywordId = "https://json-schema.org/keyword/dynamicRef";
|
|
81
|
-
const isDynamicRefEnabled =
|
|
82
|
-
|
|
90
|
+
const isDynamicRefEnabled = isExperimentalKeywordEnabled(dyanmicRefKeywordId);
|
|
91
|
+
setExperimentalKeywordEnabled(dyanmicRefKeywordId, true);
|
|
83
92
|
|
|
84
93
|
const compiledSchema = await compile(schema.dialectId);
|
|
85
94
|
metaValidators[schema.dialectId] = interpret(compiledSchema);
|
|
86
95
|
|
|
87
|
-
|
|
96
|
+
setExperimentalKeywordEnabled(dyanmicRefKeywordId, isDynamicRefEnabled);
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
// Interpret
|
|
91
100
|
const schemaInstance = Instance.cons(schema.schema, schema.id);
|
|
92
|
-
const metaResults = metaValidators[schema.dialectId](schemaInstance,
|
|
101
|
+
const metaResults = metaValidators[schema.dialectId](schemaInstance, getMetaSchemaOutputFormat());
|
|
93
102
|
if (!metaResults.valid) {
|
|
94
103
|
throw new InvalidSchemaError(metaResults);
|
|
95
104
|
}
|
package/lib/fetch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
|
-
import fetch,
|
|
2
|
+
import { fetch, Response } from "undici";
|
|
3
3
|
import Url from "url";
|
|
4
4
|
import * as MediaTypes from "./media-types.js";
|
|
5
5
|
|
|
@@ -9,10 +9,11 @@ export default async (url, options) => {
|
|
|
9
9
|
const filePath = Url.fileURLToPath(url);
|
|
10
10
|
const fd = await fs.open(filePath);
|
|
11
11
|
const stream = fd.createReadStream();
|
|
12
|
-
|
|
13
|
-
url: url,
|
|
12
|
+
const response = new Response(stream, {
|
|
14
13
|
headers: { "Content-Type": MediaTypes.getContentType(filePath) }
|
|
15
14
|
});
|
|
15
|
+
Object.defineProperty(response, "url", { value: url });
|
|
16
|
+
return response;
|
|
16
17
|
} else {
|
|
17
18
|
return fetch(url, options);
|
|
18
19
|
}
|
package/lib/index.js
CHANGED
|
@@ -57,6 +57,7 @@ import type from "./keywords/type.js";
|
|
|
57
57
|
import unevaluatedItems from "./keywords/unevaluatedItems.js";
|
|
58
58
|
import unevaluatedProperties from "./keywords/unevaluatedProperties.js";
|
|
59
59
|
import uniqueItems from "./keywords/uniqueItems.js";
|
|
60
|
+
import unknown from "./keywords/unknown.js";
|
|
60
61
|
import vocabulary from "./keywords/vocabulary.js";
|
|
61
62
|
import writeOnly from "./keywords/writeOnly.js";
|
|
62
63
|
|
|
@@ -125,6 +126,7 @@ addKeyword(type);
|
|
|
125
126
|
addKeyword(unevaluatedItems);
|
|
126
127
|
addKeyword(unevaluatedProperties);
|
|
127
128
|
addKeyword(uniqueItems);
|
|
129
|
+
addKeyword(unknown);
|
|
128
130
|
addKeyword(vocabulary);
|
|
129
131
|
addKeyword(writeOnly);
|
|
130
132
|
|
|
@@ -27,7 +27,7 @@ const compile = async (schema, ast) => {
|
|
|
27
27
|
|
|
28
28
|
const schemaValue = Schema.value(schema);
|
|
29
29
|
if (!["object", "boolean"].includes(typeof schemaValue)) {
|
|
30
|
-
throw Error(`No schema found at '${
|
|
30
|
+
throw Error(`No schema found at '${url}'`);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
ast[url] = [
|
|
@@ -38,12 +38,15 @@ const compile = async (schema, ast) => {
|
|
|
38
38
|
Pact.map(async ([keyword, keywordSchema]) => {
|
|
39
39
|
const keywordId = getKeywordId(schema.dialectId, keyword);
|
|
40
40
|
if (!keywordId) {
|
|
41
|
-
throw Error(`Encountered unknown keyword '${keyword}' at ${
|
|
41
|
+
throw Error(`Encountered unknown keyword '${keyword}' at ${url}`);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const keywordHandler = getKeyword(keywordId);
|
|
45
|
+
if (!keywordHandler) {
|
|
46
|
+
throw Error(`Encountered unsupported keyword ${keyword} at '${url}'. You can provide an implementation for the '${keywordId}' keyword using the 'addKeyword' function.`);
|
|
47
|
+
}
|
|
45
48
|
if (keywordHandler.experimental && !isExperimentalKeywordEnabled(keywordId)) {
|
|
46
|
-
throw Error(`Encountered experimental keyword ${keyword} at '${
|
|
49
|
+
throw Error(`Encountered experimental keyword ${keyword} at '${url}'. You can enable this keyword with: setExperimentalKeywordEnabled('${keywordId}', true)`);
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
const keywordAst = await keywordHandler.compile(keywordSchema, ast, schema);
|
package/lib/keywords.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import metaData from "./keywords/meta-data.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
const _keywords = {};
|
|
5
|
-
export const getKeyword = (id) => _keywords[id]
|
|
2
|
+
export const getKeyword = (id) => _keywords[id];
|
|
6
3
|
|
|
7
4
|
export const addKeyword = (keywordHandler) => {
|
|
8
5
|
_keywords[keywordHandler.id] = keywordHandler;
|
|
@@ -16,7 +13,7 @@ export const defineVocabulary = (id, keywords) => {
|
|
|
16
13
|
const _dialects = {};
|
|
17
14
|
const _allowUnknownKeywords = {};
|
|
18
15
|
export const getKeywordId = (dialectId, keyword) => _dialects[dialectId]?.[keyword]
|
|
19
|
-
|| _allowUnknownKeywords[dialectId] &&
|
|
16
|
+
|| _allowUnknownKeywords[dialectId] && "https://json-schema.org/keyword/unknown";
|
|
20
17
|
export const getKeywordName = (dialectId, keywordId) => {
|
|
21
18
|
for (const keyword in _dialects[dialectId]) {
|
|
22
19
|
if (_dialects[dialectId][keyword] === keywordId) {
|
|
@@ -36,14 +33,9 @@ export const loadDialect = (dialectId, dialect, allowUnknownKeywords = false) =>
|
|
|
36
33
|
if (vocabularyId in _vocabularies) {
|
|
37
34
|
Object.entries(_vocabularies[vocabularyId])
|
|
38
35
|
.forEach(([keyword, keywordId]) => {
|
|
39
|
-
if (!(keywordId in _keywords)) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw Error(`The '${keywordId}' keyword is not supported. This keyword was included in the '${vocabularyId}' vocabulary which is required by the '${dialectId}' dialect.`);
|
|
43
|
-
} else {
|
|
44
|
-
// Allow keyword to be ignored
|
|
45
|
-
keywordId = `https://json-schema.org/keyword/unknown#${keyword}`;
|
|
46
|
-
}
|
|
36
|
+
if (!(keywordId in _keywords) && !isRequired) {
|
|
37
|
+
// Allow keyword to be ignored
|
|
38
|
+
keywordId = "https://json-schema.org/keyword/unknown";
|
|
47
39
|
}
|
|
48
40
|
_dialects[dialectId][keyword] = keywordId;
|
|
49
41
|
});
|
package/lib/media-types.d.ts
CHANGED
package/lib/openapi.js
CHANGED
|
@@ -11,7 +11,9 @@ addMediaTypePlugin("application/openapi+json", {
|
|
|
11
11
|
let defaultDialect;
|
|
12
12
|
const version = doc.openapi || contentTypeParameters.version;
|
|
13
13
|
|
|
14
|
-
if (
|
|
14
|
+
if (!version) {
|
|
15
|
+
throw Error("Invalid OpenAPI document. Add the 'openapi' field and try again.");
|
|
16
|
+
} else if (is30(version)) {
|
|
15
17
|
defaultDialect = "https://spec.openapis.org/oas/3.0/schema";
|
|
16
18
|
} else if (is31(version)) {
|
|
17
19
|
if (!("jsonSchemaDialect" in doc) || doc.jsonSchemaDialect === "https://spec.openapis.org/oas/3.1/dialect/base") {
|
|
@@ -20,7 +22,7 @@ addMediaTypePlugin("application/openapi+json", {
|
|
|
20
22
|
defaultDialect = `https://spec.openapis.org/oas/3.1/schema-${encodeURIComponent(doc.jsonSchemaDialect)}`;
|
|
21
23
|
}
|
|
22
24
|
} else {
|
|
23
|
-
throw Error(
|
|
25
|
+
throw Error(`Encountered unsupported OpenAPI version '${version}' in ${response.url}`);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
return [doc, defaultDialect];
|
package/openapi-3-0/index.d.ts
CHANGED
|
@@ -33,28 +33,28 @@ export type OasSchema30 = {
|
|
|
33
33
|
anyOf?: OasSchema30[];
|
|
34
34
|
oneOf?: OasSchema30[];
|
|
35
35
|
not?: OasSchema30;
|
|
36
|
-
example
|
|
37
|
-
discriminator
|
|
38
|
-
externalDocs
|
|
39
|
-
xml
|
|
36
|
+
example?: Json;
|
|
37
|
+
discriminator?: Discriminator;
|
|
38
|
+
externalDocs?: ExternalDocs;
|
|
39
|
+
xml?: Xml;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
type Discriminator = {
|
|
43
43
|
propertyName: string;
|
|
44
|
-
mappings
|
|
44
|
+
mappings?: Record<string, string>;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
type ExternalDocs = {
|
|
48
48
|
url: string;
|
|
49
|
-
description
|
|
49
|
+
description?: string;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
type Xml = {
|
|
53
|
-
name
|
|
54
|
-
namespace
|
|
55
|
-
prefix
|
|
56
|
-
attribute
|
|
57
|
-
wrapped
|
|
53
|
+
name?: string;
|
|
54
|
+
namespace?: string;
|
|
55
|
+
prefix?: string;
|
|
56
|
+
attribute?: boolean;
|
|
57
|
+
wrapped?: boolean;
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
export * from "../lib/index.js";
|
|
@@ -193,16 +193,7 @@ export default {
|
|
|
193
193
|
"schemas": {
|
|
194
194
|
"type": "object",
|
|
195
195
|
"patternProperties": {
|
|
196
|
-
"^[a-zA-Z0-9\\.\\-_]+$": {
|
|
197
|
-
"oneOf": [
|
|
198
|
-
{
|
|
199
|
-
"$ref": "#/definitions/Schema"
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
"$ref": "#/definitions/Reference"
|
|
203
|
-
}
|
|
204
|
-
]
|
|
205
|
-
}
|
|
196
|
+
"^[a-zA-Z0-9\\.\\-_]+$": { "$ref": "#/definitions/Schema" }
|
|
206
197
|
}
|
|
207
198
|
},
|
|
208
199
|
"responses": {
|
|
@@ -384,16 +375,7 @@ export default {
|
|
|
384
375
|
"MediaType": {
|
|
385
376
|
"type": "object",
|
|
386
377
|
"properties": {
|
|
387
|
-
"schema": {
|
|
388
|
-
"oneOf": [
|
|
389
|
-
{
|
|
390
|
-
"$ref": "#/definitions/Schema"
|
|
391
|
-
},
|
|
392
|
-
{
|
|
393
|
-
"$ref": "#/definitions/Reference"
|
|
394
|
-
}
|
|
395
|
-
]
|
|
396
|
-
},
|
|
378
|
+
"schema": { "$ref": "#/definitions/Schema" },
|
|
397
379
|
"example": {
|
|
398
380
|
},
|
|
399
381
|
"examples": {
|
|
@@ -481,16 +463,7 @@ export default {
|
|
|
481
463
|
"type": "boolean",
|
|
482
464
|
"default": false
|
|
483
465
|
},
|
|
484
|
-
"schema": {
|
|
485
|
-
"oneOf": [
|
|
486
|
-
{
|
|
487
|
-
"$ref": "#/definitions/Schema"
|
|
488
|
-
},
|
|
489
|
-
{
|
|
490
|
-
"$ref": "#/definitions/Reference"
|
|
491
|
-
}
|
|
492
|
-
]
|
|
493
|
-
},
|
|
466
|
+
"schema": { "$ref": "#/definitions/Schema" },
|
|
494
467
|
"content": {
|
|
495
468
|
"type": "object",
|
|
496
469
|
"additionalProperties": {
|
|
@@ -853,16 +826,7 @@ export default {
|
|
|
853
826
|
"type": "boolean",
|
|
854
827
|
"default": false
|
|
855
828
|
},
|
|
856
|
-
"schema": {
|
|
857
|
-
"oneOf": [
|
|
858
|
-
{
|
|
859
|
-
"$ref": "#/definitions/Schema"
|
|
860
|
-
},
|
|
861
|
-
{
|
|
862
|
-
"$ref": "#/definitions/Reference"
|
|
863
|
-
}
|
|
864
|
-
]
|
|
865
|
-
},
|
|
829
|
+
"schema": { "$ref": "#/definitions/Schema" },
|
|
866
830
|
"content": {
|
|
867
831
|
"type": "object",
|
|
868
832
|
"additionalProperties": {
|
package/openapi-3-1/index.d.ts
CHANGED
|
@@ -61,28 +61,28 @@ export type OasSchema31 = boolean | {
|
|
|
61
61
|
contentMediaType?: string;
|
|
62
62
|
contentEncoding?: string;
|
|
63
63
|
contentSchema?: OasSchema31;
|
|
64
|
-
example
|
|
65
|
-
discriminator
|
|
66
|
-
externalDocs
|
|
67
|
-
xml
|
|
64
|
+
example?: Json;
|
|
65
|
+
discriminator?: Discriminator;
|
|
66
|
+
externalDocs?: ExternalDocs;
|
|
67
|
+
xml?: Xml;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
type Discriminator = {
|
|
71
71
|
propertyName: string;
|
|
72
|
-
mappings
|
|
72
|
+
mappings?: Record<string, string>;
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
type ExternalDocs = {
|
|
76
76
|
url: string;
|
|
77
|
-
description
|
|
77
|
+
description?: string;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
type Xml = {
|
|
81
|
-
name
|
|
82
|
-
namespace
|
|
83
|
-
prefix
|
|
84
|
-
attribute
|
|
85
|
-
wrapped
|
|
81
|
+
name?: string;
|
|
82
|
+
namespace?: string;
|
|
83
|
+
prefix?: string;
|
|
84
|
+
attribute?: boolean;
|
|
85
|
+
wrapped?: boolean;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
export * from "../lib/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperjump/json-schema",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "A JSON Schema validator with support for custom keywords, vocabularies, and dialects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./stable/index.js",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"eslint-plugin-import": "*",
|
|
57
57
|
"json-schema-test-suite": "github:json-schema-org/JSON-Schema-Test-Suite",
|
|
58
58
|
"mocha": "*",
|
|
59
|
-
"nock": "*",
|
|
60
59
|
"ts-node": "*",
|
|
61
60
|
"typescript": "*",
|
|
62
61
|
"yaml": "*"
|
|
@@ -67,7 +66,7 @@
|
|
|
67
66
|
"@hyperjump/uri": "^1.0.0",
|
|
68
67
|
"content-type": "^1.0.4",
|
|
69
68
|
"fastest-stable-stringify": "^2.0.2",
|
|
70
|
-
"
|
|
69
|
+
"undici": "^5.19.1",
|
|
71
70
|
"uuid": "^9.0.0"
|
|
72
71
|
},
|
|
73
72
|
"engines": {
|