@scalar/openapi-parser 0.4.0 → 0.5.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/CHANGELOG.md +12 -0
- package/dist/configuration.d.ts +3907 -0
- package/dist/index.d.ts +29 -5877
- package/dist/lib/Validator/Validator.d.ts +33 -0
- package/dist/pipeline.d.ts +445 -0
- package/dist/schemas/v2.0/schema.json.js +1623 -0
- package/dist/schemas/v3.0/schema.json.js +1673 -0
- package/dist/schemas/v3.1/schema.json.js +1452 -0
- package/dist/src/configuration.js +22 -0
- package/dist/src/index.js +27 -0
- package/dist/src/lib/Validator/Validator.js +94 -0
- package/dist/src/pipeline.js +131 -0
- package/dist/src/polyfills/path.js +83 -0
- package/dist/src/utils/betterAjvErrors/helpers.js +89 -0
- package/dist/src/utils/betterAjvErrors/index.js +15 -0
- package/dist/src/utils/betterAjvErrors/utils.js +17 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/additional-prop.js +18 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/base.js +23 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/default.js +18 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/enum.js +41 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/pattern.js +18 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/required.js +17 -0
- package/dist/src/utils/betterAjvErrors/validation-errors/unevaluated-prop.js +18 -0
- package/dist/src/utils/dereference.js +18 -0
- package/dist/src/utils/details.js +22 -0
- package/dist/src/utils/escapeJsonPointer.js +5 -0
- package/dist/src/utils/filter.js +12 -0
- package/dist/src/utils/getEntrypoint.js +5 -0
- package/dist/src/utils/getListOfReferences.js +17 -0
- package/dist/src/utils/getSegmentsFromPath.js +10 -0
- package/dist/src/utils/isFilesystem.js +5 -0
- package/dist/src/utils/isJson.js +10 -0
- package/dist/src/utils/isObject.js +3 -0
- package/dist/src/utils/isYaml.js +16 -0
- package/dist/src/utils/load/load.js +46 -0
- package/dist/src/utils/load/plugins/fetchUrlsPlugin.js +38 -0
- package/dist/src/utils/load/plugins/readFilesPlugin.js +50 -0
- package/dist/src/utils/makeFilesystem.js +22 -0
- package/dist/src/utils/normalize.js +18 -0
- package/dist/src/utils/resolveReferences.js +106 -0
- package/dist/src/utils/toJson.js +3 -0
- package/dist/src/utils/toYaml.js +5 -0
- package/dist/src/utils/transformErrors.js +19 -0
- package/dist/src/utils/traverse.js +20 -0
- package/dist/src/utils/unescapeJsonPointer.js +5 -0
- package/dist/src/utils/upgrade.js +17 -0
- package/dist/src/utils/upgradeFromThreeToThreeOne.js +71 -0
- package/dist/src/utils/upgradeFromTwoToThree.js +5 -0
- package/dist/src/utils/validate.js +15 -0
- package/dist/types/index.d.ts +42 -0
- package/dist/types/openapi.d.ts +747 -0
- package/dist/utils/dereference.d.ts +8 -0
- package/dist/utils/details.d.ts +12 -0
- package/dist/utils/escapeJsonPointer.d.ts +8 -0
- package/dist/utils/filter.d.ts +8 -0
- package/dist/utils/getEntrypoint.d.ts +8 -0
- package/dist/utils/getListOfReferences.d.ts +10 -0
- package/dist/utils/getSegmentsFromPath.d.ts +6 -0
- package/dist/utils/isFilesystem.d.ts +6 -0
- package/dist/utils/isJson.d.ts +3 -0
- package/dist/utils/isObject.d.ts +6 -0
- package/dist/utils/isYaml.d.ts +3 -0
- package/dist/utils/load/load.d.ts +16 -0
- package/dist/utils/load/plugins/fetchUrlsPlugin.d.ts +10 -0
- package/dist/utils/load/plugins/readFilesPlugin.d.ts +5 -0
- package/dist/utils/normalize.d.ts +9 -0
- package/dist/utils/resolveReferences.d.ts +14 -0
- package/dist/utils/toJson.d.ts +5 -0
- package/dist/utils/toYaml.d.ts +5 -0
- package/dist/utils/transformErrors.d.ts +8 -0
- package/dist/utils/traverse.d.ts +8 -0
- package/dist/utils/unescapeJsonPointer.d.ts +9 -0
- package/dist/utils/upgrade.d.ts +8 -0
- package/dist/utils/upgradeFromThreeToThreeOne.d.ts +10 -0
- package/dist/utils/upgradeFromTwoToThree.d.ts +10 -0
- package/dist/utils/validate.d.ts +8 -0
- package/dist/webpack-stats.json +1 -1
- package/package.json +12 -8
- package/dist/index.js +0 -16818
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Swagger20 from '../schemas/v2.0/schema.json.js';
|
|
2
|
+
import OpenApi30 from '../schemas/v3.0/schema.json.js';
|
|
3
|
+
import OpenApi31 from '../schemas/v3.1/schema.json.js';
|
|
4
|
+
|
|
5
|
+
const OpenApiSpecifications = {
|
|
6
|
+
"2.0": Swagger20,
|
|
7
|
+
"3.0": OpenApi30,
|
|
8
|
+
"3.1": OpenApi31
|
|
9
|
+
};
|
|
10
|
+
const OpenApiVersions = Object.keys(
|
|
11
|
+
OpenApiSpecifications
|
|
12
|
+
);
|
|
13
|
+
const ERRORS = {
|
|
14
|
+
EMPTY_OR_INVALID: "Cannot find JSON, YAML or filename in data",
|
|
15
|
+
// URI_MUST_BE_STRING: 'uri parameter or $id attribute must be a string',
|
|
16
|
+
OPENAPI_VERSION_NOT_SUPPORTED: "Cannot find supported Swagger/OpenAPI version in specification, version must be a string.",
|
|
17
|
+
INVALID_REFERENCE: "Can\u2019t resolve reference: %s",
|
|
18
|
+
EXTERNAL_REFERENCE_NOT_FOUND: "Can\u2019t resolve external reference: %s",
|
|
19
|
+
FILE_DOES_NOT_EXIST: "File does not exist: %s"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { ERRORS, OpenApiSpecifications, OpenApiVersions };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { openapi } from './pipeline.js';
|
|
2
|
+
export { Validator, jsonSchemaVersions } from './lib/Validator/Validator.js';
|
|
3
|
+
export { resolveReferences } from './utils/resolveReferences.js';
|
|
4
|
+
export { dereference } from './utils/dereference.js';
|
|
5
|
+
export { details } from './utils/details.js';
|
|
6
|
+
export { escapeJsonPointer } from './utils/escapeJsonPointer.js';
|
|
7
|
+
export { filter } from './utils/filter.js';
|
|
8
|
+
export { getEntrypoint } from './utils/getEntrypoint.js';
|
|
9
|
+
export { getListOfReferences } from './utils/getListOfReferences.js';
|
|
10
|
+
export { getSegmentsFromPath } from './utils/getSegmentsFromPath.js';
|
|
11
|
+
export { isFilesystem } from './utils/isFilesystem.js';
|
|
12
|
+
export { isJson } from './utils/isJson.js';
|
|
13
|
+
export { isObject } from './utils/isObject.js';
|
|
14
|
+
export { isYaml } from './utils/isYaml.js';
|
|
15
|
+
export { load } from './utils/load/load.js';
|
|
16
|
+
export { fetchUrlsPlugin, fetchUrlsPluginDefaultConfiguration } from './utils/load/plugins/fetchUrlsPlugin.js';
|
|
17
|
+
export { readFilesPlugin } from './utils/load/plugins/readFilesPlugin.js';
|
|
18
|
+
export { normalize } from './utils/normalize.js';
|
|
19
|
+
export { toJson } from './utils/toJson.js';
|
|
20
|
+
export { toYaml } from './utils/toYaml.js';
|
|
21
|
+
export { transformErrors } from './utils/transformErrors.js';
|
|
22
|
+
export { traverse } from './utils/traverse.js';
|
|
23
|
+
export { unescapeJsonPointer } from './utils/unescapeJsonPointer.js';
|
|
24
|
+
export { upgrade } from './utils/upgrade.js';
|
|
25
|
+
export { upgradeFromThreeToThreeOne } from './utils/upgradeFromThreeToThreeOne.js';
|
|
26
|
+
export { upgradeFromTwoToThree } from './utils/upgradeFromTwoToThree.js';
|
|
27
|
+
export { validate } from './utils/validate.js';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import Ajv04 from 'ajv-draft-04';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import Ajv2020 from 'ajv/dist/2020';
|
|
4
|
+
import { ERRORS, OpenApiVersions, OpenApiSpecifications } from '../../configuration.js';
|
|
5
|
+
import { details } from '../../utils/details.js';
|
|
6
|
+
import { resolveReferences } from '../../utils/resolveReferences.js';
|
|
7
|
+
import { transformErrors } from '../../utils/transformErrors.js';
|
|
8
|
+
|
|
9
|
+
const jsonSchemaVersions = {
|
|
10
|
+
"http://json-schema.org/draft-04/schema#": Ajv04,
|
|
11
|
+
"https://json-schema.org/draft/2020-12/schema": Ajv2020
|
|
12
|
+
};
|
|
13
|
+
class Validator {
|
|
14
|
+
constructor() {
|
|
15
|
+
// Object with function *or* object { errors: string }
|
|
16
|
+
this.ajvValidators = {};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Checks whether a specification is valid and all references can be resolved.
|
|
20
|
+
*/
|
|
21
|
+
async validate(filesystem) {
|
|
22
|
+
const entrypoint = filesystem.find((file) => file.isEntrypoint);
|
|
23
|
+
const specification = entrypoint?.specification;
|
|
24
|
+
this.specification = specification;
|
|
25
|
+
if (this.specification?.info && !this.specification.info.version) {
|
|
26
|
+
this.specification.info.version = "0.0.1";
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
if (specification === void 0 || specification === null) {
|
|
30
|
+
return {
|
|
31
|
+
valid: false,
|
|
32
|
+
errors: transformErrors(entrypoint, ERRORS.EMPTY_OR_INVALID)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const { version, specificationType, specificationVersion } = details(specification);
|
|
36
|
+
this.version = version;
|
|
37
|
+
this.specificationVersion = specificationVersion;
|
|
38
|
+
this.specificationType = specificationType;
|
|
39
|
+
if (!version) {
|
|
40
|
+
return {
|
|
41
|
+
valid: false,
|
|
42
|
+
errors: transformErrors(
|
|
43
|
+
entrypoint,
|
|
44
|
+
ERRORS.OPENAPI_VERSION_NOT_SUPPORTED
|
|
45
|
+
)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const validateSchema = await this.getAjvValidator(version);
|
|
49
|
+
const schemaResult = validateSchema(specification);
|
|
50
|
+
if (validateSchema.errors) {
|
|
51
|
+
if (validateSchema.errors.length > 0) {
|
|
52
|
+
return {
|
|
53
|
+
valid: false,
|
|
54
|
+
errors: transformErrors(entrypoint, validateSchema.errors)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const resolvedReferences = resolveReferences(filesystem);
|
|
59
|
+
return {
|
|
60
|
+
valid: schemaResult && resolvedReferences.valid,
|
|
61
|
+
errors: [...schemaResult.errors ?? [], ...resolvedReferences.errors],
|
|
62
|
+
schema: resolvedReferences.schema
|
|
63
|
+
};
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
errors: transformErrors(entrypoint, error.message ?? error)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ajv JSON schema validator
|
|
73
|
+
*/
|
|
74
|
+
async getAjvValidator(version) {
|
|
75
|
+
if (this.ajvValidators[version]) {
|
|
76
|
+
return this.ajvValidators[version];
|
|
77
|
+
}
|
|
78
|
+
const schema = OpenApiSpecifications[version];
|
|
79
|
+
const AjvClass = jsonSchemaVersions[schema.$schema];
|
|
80
|
+
const ajv = new AjvClass({
|
|
81
|
+
// Ajv is a bit too strict in its strict validation of OpenAPI schemas.
|
|
82
|
+
// Switch strict mode off.
|
|
83
|
+
strict: false
|
|
84
|
+
});
|
|
85
|
+
addFormats(ajv);
|
|
86
|
+
if (version === "3.1") {
|
|
87
|
+
ajv.addFormat("media-range", true);
|
|
88
|
+
}
|
|
89
|
+
return this.ajvValidators[version] = ajv.compile(schema);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
Validator.supportedVersions = OpenApiVersions;
|
|
93
|
+
|
|
94
|
+
export { Validator, jsonSchemaVersions };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { makeFilesystem } from './utils/makeFilesystem.js';
|
|
2
|
+
import { upgrade } from './utils/upgrade.js';
|
|
3
|
+
import { validate } from './utils/validate.js';
|
|
4
|
+
import { dereference } from './utils/dereference.js';
|
|
5
|
+
import { filter } from './utils/filter.js';
|
|
6
|
+
import { getEntrypoint } from './utils/getEntrypoint.js';
|
|
7
|
+
import { details } from './utils/details.js';
|
|
8
|
+
import { toJson } from './utils/toJson.js';
|
|
9
|
+
import { toYaml } from './utils/toYaml.js';
|
|
10
|
+
import { load } from './utils/load/load.js';
|
|
11
|
+
|
|
12
|
+
function openapi() {
|
|
13
|
+
return {
|
|
14
|
+
load: loadAction
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function loadAction(specification, options) {
|
|
18
|
+
const queue = {
|
|
19
|
+
specification,
|
|
20
|
+
tasks: [
|
|
21
|
+
{
|
|
22
|
+
action: load,
|
|
23
|
+
options
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
get: () => getAction(queue),
|
|
29
|
+
files: () => filesAction(queue),
|
|
30
|
+
details: () => detailsAction(queue),
|
|
31
|
+
filter: (callback) => filterAction(queue, callback),
|
|
32
|
+
upgrade: () => upgradeAction(queue),
|
|
33
|
+
validate: () => validateAction(queue),
|
|
34
|
+
dereference: () => dereferenceAction(queue),
|
|
35
|
+
toJson: () => toJsonAction(queue),
|
|
36
|
+
toYaml: () => toYamlAction(queue)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function upgradeAction(queue) {
|
|
40
|
+
queue.tasks.push({
|
|
41
|
+
action: upgrade
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
get: () => getAction(queue),
|
|
45
|
+
files: () => filesAction(queue),
|
|
46
|
+
details: () => detailsAction(queue),
|
|
47
|
+
filter: (callback) => filterAction(queue, callback),
|
|
48
|
+
validate: () => validateAction(queue),
|
|
49
|
+
dereference: () => dereferenceAction(queue),
|
|
50
|
+
toJson: () => toJsonAction(queue),
|
|
51
|
+
toYaml: () => toYamlAction(queue)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function validateAction(queue) {
|
|
55
|
+
const filesystem = await workThroughQueue(queue);
|
|
56
|
+
return {
|
|
57
|
+
...await validate(filesystem),
|
|
58
|
+
filter: (callback) => filterAction(queue, callback),
|
|
59
|
+
get: () => getAction(queue),
|
|
60
|
+
files: () => filesAction(queue),
|
|
61
|
+
details: () => detailsAction(queue),
|
|
62
|
+
dereference: () => dereferenceAction(queue),
|
|
63
|
+
toJson: () => toJsonAction(queue),
|
|
64
|
+
toYaml: () => toYamlAction(queue)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function dereferenceAction(queue) {
|
|
68
|
+
queue.tasks.push({
|
|
69
|
+
action: dereference
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
get: () => getAction(queue),
|
|
73
|
+
files: () => filesAction(queue),
|
|
74
|
+
filter: (callback) => filterAction(queue, callback),
|
|
75
|
+
toJson: () => toJsonAction(queue),
|
|
76
|
+
toYaml: () => toYamlAction(queue)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function filterAction(queue, callback) {
|
|
80
|
+
queue.tasks.push({
|
|
81
|
+
action: filter,
|
|
82
|
+
options: callback
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
get: () => getAction(queue),
|
|
86
|
+
files: () => filesAction(queue),
|
|
87
|
+
details: () => detailsAction(queue),
|
|
88
|
+
filter: () => filterAction(queue, callback),
|
|
89
|
+
upgrade: () => upgradeAction(queue),
|
|
90
|
+
validate: () => validateAction(queue),
|
|
91
|
+
dereference: () => dereferenceAction(queue),
|
|
92
|
+
toJson: () => toJsonAction(queue),
|
|
93
|
+
toYaml: () => toYamlAction(queue)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async function getAction(queue) {
|
|
97
|
+
const filesystem = await workThroughQueue(queue);
|
|
98
|
+
return getEntrypoint(filesystem).specification;
|
|
99
|
+
}
|
|
100
|
+
async function filesAction(queue) {
|
|
101
|
+
return await workThroughQueue(queue);
|
|
102
|
+
}
|
|
103
|
+
async function detailsAction(queue) {
|
|
104
|
+
const filesystem = await workThroughQueue(queue);
|
|
105
|
+
return details(getEntrypoint(filesystem).specification);
|
|
106
|
+
}
|
|
107
|
+
async function toJsonAction(queue) {
|
|
108
|
+
const filesystem = await workThroughQueue(queue);
|
|
109
|
+
return toJson(getEntrypoint(filesystem).specification);
|
|
110
|
+
}
|
|
111
|
+
async function toYamlAction(queue) {
|
|
112
|
+
const filesystem = await workThroughQueue(queue);
|
|
113
|
+
return toYaml(getEntrypoint(filesystem).specification);
|
|
114
|
+
}
|
|
115
|
+
async function workThroughQueue(queue) {
|
|
116
|
+
let specification = queue.specification;
|
|
117
|
+
for (const { action, options } of queue.tasks) {
|
|
118
|
+
if (typeof action !== "function") {
|
|
119
|
+
console.warn("[queue] The given action is not a function:", action);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (action.constructor.name === "AsyncFunction") {
|
|
123
|
+
specification = await action(specification, options);
|
|
124
|
+
} else {
|
|
125
|
+
specification = action(specification, options);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return makeFilesystem(specification);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { openapi };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
function normalizeArray(parts, allowAboveRoot) {
|
|
2
|
+
var up = 0;
|
|
3
|
+
for (var i = parts.length - 1; i >= 0; i--) {
|
|
4
|
+
var last = parts[i];
|
|
5
|
+
if (last === ".") {
|
|
6
|
+
parts.splice(i, 1);
|
|
7
|
+
} else if (last === "..") {
|
|
8
|
+
parts.splice(i, 1);
|
|
9
|
+
up++;
|
|
10
|
+
} else if (up) {
|
|
11
|
+
parts.splice(i, 1);
|
|
12
|
+
up--;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (allowAboveRoot) {
|
|
16
|
+
for (; up--; up) {
|
|
17
|
+
parts.unshift("..");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return parts;
|
|
21
|
+
}
|
|
22
|
+
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
|
|
23
|
+
var splitPath = function(filename) {
|
|
24
|
+
return splitPathRe.exec(filename).slice(1);
|
|
25
|
+
};
|
|
26
|
+
function normalize(path) {
|
|
27
|
+
var isPathAbsolute = isAbsolute(path), trailingSlash = substr(path, -1) === "/";
|
|
28
|
+
path = normalizeArray(
|
|
29
|
+
filter(path.split("/"), function(p) {
|
|
30
|
+
return !!p;
|
|
31
|
+
}),
|
|
32
|
+
!isPathAbsolute
|
|
33
|
+
).join("/");
|
|
34
|
+
if (!path && !isPathAbsolute) {
|
|
35
|
+
path = ".";
|
|
36
|
+
}
|
|
37
|
+
if (path && trailingSlash) {
|
|
38
|
+
path += "/";
|
|
39
|
+
}
|
|
40
|
+
return (isPathAbsolute ? "/" : "") + path;
|
|
41
|
+
}
|
|
42
|
+
function isAbsolute(path) {
|
|
43
|
+
return path.charAt(0) === "/";
|
|
44
|
+
}
|
|
45
|
+
function join(...paths) {
|
|
46
|
+
return normalize(
|
|
47
|
+
filter(paths, function(p, index) {
|
|
48
|
+
if (typeof p !== "string") {
|
|
49
|
+
throw new TypeError("Arguments to path.join must be strings");
|
|
50
|
+
}
|
|
51
|
+
return p;
|
|
52
|
+
}).join("/")
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
function dirname(path) {
|
|
56
|
+
var result = splitPath(path), root = result[0], dir = result[1];
|
|
57
|
+
if (!root && !dir) {
|
|
58
|
+
return ".";
|
|
59
|
+
}
|
|
60
|
+
if (dir) {
|
|
61
|
+
dir = dir.substr(0, dir.length - 1);
|
|
62
|
+
}
|
|
63
|
+
return root + dir;
|
|
64
|
+
}
|
|
65
|
+
function filter(xs, f) {
|
|
66
|
+
if (xs.filter)
|
|
67
|
+
return xs.filter(f);
|
|
68
|
+
var res = [];
|
|
69
|
+
for (var i = 0; i < xs.length; i++) {
|
|
70
|
+
if (f(xs[i], i, xs))
|
|
71
|
+
res.push(xs[i]);
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
var substr = "ab".substr(-1) === "b" ? function(str, start, len) {
|
|
76
|
+
return str.substr(start, len);
|
|
77
|
+
} : function(str, start, len) {
|
|
78
|
+
if (start < 0)
|
|
79
|
+
start = str.length + start;
|
|
80
|
+
return str.substr(start, len);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export { dirname, isAbsolute, join, normalize };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getErrors, isRequiredError, isAnyOfError, isEnumError, getSiblings, notUndefined, concatAll, getChildren } from './utils.js';
|
|
2
|
+
import EnumValidationError from './validation-errors/enum.js';
|
|
3
|
+
import DefaultValidationError from './validation-errors/default.js';
|
|
4
|
+
import UnevaluatedPropValidationError from './validation-errors/unevaluated-prop.js';
|
|
5
|
+
import RequiredValidationError from './validation-errors/required.js';
|
|
6
|
+
import PatternValidationError from './validation-errors/pattern.js';
|
|
7
|
+
import AdditionalPropValidationError from './validation-errors/additional-prop.js';
|
|
8
|
+
|
|
9
|
+
const JSON_POINTERS_REGEX = /\/[\w_-]+(\/\d+)?/g;
|
|
10
|
+
function makeTree(ajvErrors = []) {
|
|
11
|
+
const root = { children: {} };
|
|
12
|
+
ajvErrors.forEach((ajvError) => {
|
|
13
|
+
const instancePath = typeof ajvError.instancePath !== "undefined" ? ajvError.instancePath : ajvError.dataPath;
|
|
14
|
+
const paths = instancePath === "" ? [""] : instancePath.match(JSON_POINTERS_REGEX);
|
|
15
|
+
if (paths) {
|
|
16
|
+
paths.reduce((obj, path, i) => {
|
|
17
|
+
obj.children[path] = obj.children[path] || { children: {}, errors: [] };
|
|
18
|
+
if (i === paths.length - 1) {
|
|
19
|
+
obj.children[path].errors.push(ajvError);
|
|
20
|
+
}
|
|
21
|
+
return obj.children[path];
|
|
22
|
+
}, root);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return root;
|
|
26
|
+
}
|
|
27
|
+
function filterRedundantErrors(root, parent, key) {
|
|
28
|
+
getErrors(root).forEach((error) => {
|
|
29
|
+
if (isRequiredError(error)) {
|
|
30
|
+
root.errors = [error];
|
|
31
|
+
root.children = {};
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (getErrors(root).some(isAnyOfError)) {
|
|
35
|
+
if (Object.keys(root.children).length > 0) {
|
|
36
|
+
delete root.errors;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (root.errors && root.errors.length && getErrors(root).every(isEnumError)) {
|
|
40
|
+
if (getSiblings(parent)(root).filter(notUndefined).some(getErrors)) {
|
|
41
|
+
delete parent.children[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
Object.entries(root.children).forEach(
|
|
45
|
+
([k, child]) => filterRedundantErrors(child, root, k)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function createErrorInstances(root, options) {
|
|
49
|
+
const errors = getErrors(root);
|
|
50
|
+
if (errors.length && errors.every(isEnumError)) {
|
|
51
|
+
const uniqueValues = new Set(
|
|
52
|
+
concatAll([])(errors.map((e) => e.params.allowedValues))
|
|
53
|
+
);
|
|
54
|
+
const allowedValues = [...uniqueValues];
|
|
55
|
+
const error = errors[0];
|
|
56
|
+
return [
|
|
57
|
+
new EnumValidationError(
|
|
58
|
+
{
|
|
59
|
+
...error,
|
|
60
|
+
params: { allowedValues }
|
|
61
|
+
},
|
|
62
|
+
options
|
|
63
|
+
)
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
return concatAll(
|
|
67
|
+
errors.reduce((ret, error) => {
|
|
68
|
+
switch (error.keyword) {
|
|
69
|
+
case "additionalProperties":
|
|
70
|
+
return ret.concat(new AdditionalPropValidationError(error, options));
|
|
71
|
+
case "pattern":
|
|
72
|
+
return ret.concat(new PatternValidationError(error, options));
|
|
73
|
+
case "required":
|
|
74
|
+
return ret.concat(new RequiredValidationError(error, options));
|
|
75
|
+
case "unevaluatedProperties":
|
|
76
|
+
return ret.concat(new UnevaluatedPropValidationError(error, options));
|
|
77
|
+
default:
|
|
78
|
+
return ret.concat(new DefaultValidationError(error, options));
|
|
79
|
+
}
|
|
80
|
+
}, [])
|
|
81
|
+
)(getChildren(root).map((child) => createErrorInstances(child, options)));
|
|
82
|
+
}
|
|
83
|
+
function prettify(ajvErrors, options) {
|
|
84
|
+
const tree = makeTree(ajvErrors || []);
|
|
85
|
+
filterRedundantErrors(tree);
|
|
86
|
+
return createErrorInstances(tree, options);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { createErrorInstances, prettify as default, filterRedundantErrors, makeTree };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import prettify from './helpers.js';
|
|
2
|
+
|
|
3
|
+
function betterAjvErrors(schema, data, errors, options = {}) {
|
|
4
|
+
const { indent = null, json = null } = options;
|
|
5
|
+
const jsonRaw = json || JSON.stringify(data, null, indent);
|
|
6
|
+
const customErrorToStructure = (error) => error.getError();
|
|
7
|
+
const customErrors = prettify(errors, {
|
|
8
|
+
data,
|
|
9
|
+
schema,
|
|
10
|
+
jsonRaw
|
|
11
|
+
});
|
|
12
|
+
return customErrors.map(customErrorToStructure);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { betterAjvErrors };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const eq = (x) => (y) => x === y;
|
|
2
|
+
const not = (fn) => (x) => !fn(x);
|
|
3
|
+
const getValues = (o) => Object.values(o);
|
|
4
|
+
const notUndefined = (x) => x !== void 0;
|
|
5
|
+
const isXError = (x) => (error) => error.keyword === x;
|
|
6
|
+
const isRequiredError = isXError("required");
|
|
7
|
+
const isAnyOfError = isXError("anyOf");
|
|
8
|
+
const isEnumError = isXError("enum");
|
|
9
|
+
const getErrors = (node) => node && node.errors || [];
|
|
10
|
+
const getChildren = (node) => node && getValues(node.children) || [];
|
|
11
|
+
const getSiblings = (parent) => (node) => getChildren(parent).filter(not(eq(node)));
|
|
12
|
+
const concatAll = (
|
|
13
|
+
/* ::<T> */
|
|
14
|
+
(xs) => (ys) => ys.reduce((zs, z) => zs.concat(z), xs)
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export { concatAll, getChildren, getErrors, getSiblings, isAnyOfError, isEnumError, isRequiredError, notUndefined };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseValidationError from './base.js';
|
|
2
|
+
|
|
3
|
+
class AdditionalPropValidationError extends BaseValidationError {
|
|
4
|
+
constructor(...args) {
|
|
5
|
+
super(...args);
|
|
6
|
+
this.name = "AdditionalPropValidationError";
|
|
7
|
+
this.options.isIdentifierLocation = true;
|
|
8
|
+
}
|
|
9
|
+
getError() {
|
|
10
|
+
const { params } = this.options;
|
|
11
|
+
return {
|
|
12
|
+
message: `Property ${params.additionalProperty} is not expected to be here`,
|
|
13
|
+
path: this.instancePath
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { AdditionalPropValidationError as default };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class BaseValidationError {
|
|
2
|
+
// eslint-disable-next-line default-param-last
|
|
3
|
+
constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {
|
|
4
|
+
this.options = options;
|
|
5
|
+
this.data = data;
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
this.jsonAst = jsonAst;
|
|
8
|
+
this.jsonRaw = jsonRaw;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* @return {string}
|
|
12
|
+
*/
|
|
13
|
+
get instancePath() {
|
|
14
|
+
return typeof this.options.instancePath !== "undefined" ? this.options.instancePath : this.options.dataPath;
|
|
15
|
+
}
|
|
16
|
+
getError() {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Implement the 'getError' method inside ${this.constructor.name}!`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { BaseValidationError as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseValidationError from './base.js';
|
|
2
|
+
|
|
3
|
+
class DefaultValidationError extends BaseValidationError {
|
|
4
|
+
constructor(...args) {
|
|
5
|
+
super(...args);
|
|
6
|
+
this.name = "DefaultValidationError";
|
|
7
|
+
this.options.isSkipEndLocation = true;
|
|
8
|
+
}
|
|
9
|
+
getError() {
|
|
10
|
+
const { keyword, message } = this.options;
|
|
11
|
+
return {
|
|
12
|
+
message: `${keyword} ${message}`,
|
|
13
|
+
path: this.instancePath
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { DefaultValidationError as default };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pointer from 'jsonpointer';
|
|
2
|
+
import leven from 'leven';
|
|
3
|
+
import BaseValidationError from './base.js';
|
|
4
|
+
|
|
5
|
+
class EnumValidationError extends BaseValidationError {
|
|
6
|
+
constructor(...args) {
|
|
7
|
+
super(...args);
|
|
8
|
+
this.name = "EnumValidationError";
|
|
9
|
+
}
|
|
10
|
+
getError() {
|
|
11
|
+
const { message, params } = this.options;
|
|
12
|
+
const bestMatch = this.findBestMatch();
|
|
13
|
+
const allowedValues = params.allowedValues.join(", ");
|
|
14
|
+
const output = {
|
|
15
|
+
message: `${message}: ${allowedValues}`,
|
|
16
|
+
path: this.instancePath
|
|
17
|
+
};
|
|
18
|
+
if (bestMatch !== null) {
|
|
19
|
+
output.suggestion = `Did you mean ${bestMatch}?`;
|
|
20
|
+
}
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
findBestMatch() {
|
|
24
|
+
const {
|
|
25
|
+
params: { allowedValues }
|
|
26
|
+
} = this.options;
|
|
27
|
+
const currentValue = this.instancePath === "" ? this.data : pointer.get(this.data, this.instancePath);
|
|
28
|
+
if (!currentValue) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const bestMatch = allowedValues.map((value) => ({
|
|
32
|
+
value,
|
|
33
|
+
weight: leven(value, currentValue.toString())
|
|
34
|
+
})).sort(
|
|
35
|
+
(x, y) => x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0
|
|
36
|
+
)[0];
|
|
37
|
+
return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length ? bestMatch.value : null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { EnumValidationError as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseValidationError from './base.js';
|
|
2
|
+
|
|
3
|
+
class PatternValidationError extends BaseValidationError {
|
|
4
|
+
constructor(...args) {
|
|
5
|
+
super(...args);
|
|
6
|
+
this.name = "PatternValidationError";
|
|
7
|
+
this.options.isIdentifierLocation = true;
|
|
8
|
+
}
|
|
9
|
+
getError() {
|
|
10
|
+
const { params, propertyName } = this.options;
|
|
11
|
+
return {
|
|
12
|
+
message: `Property "${propertyName}" must match pattern ${params.pattern}`,
|
|
13
|
+
path: this.instancePath
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { PatternValidationError as default };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseValidationError from './base.js';
|
|
2
|
+
|
|
3
|
+
class RequiredValidationError extends BaseValidationError {
|
|
4
|
+
constructor(...args) {
|
|
5
|
+
super(...args);
|
|
6
|
+
this.name = "RequiredValidationError";
|
|
7
|
+
}
|
|
8
|
+
getError() {
|
|
9
|
+
const { message } = this.options;
|
|
10
|
+
return {
|
|
11
|
+
message: `${message}`,
|
|
12
|
+
path: this.instancePath
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { RequiredValidationError as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseValidationError from './base.js';
|
|
2
|
+
|
|
3
|
+
class UnevaluatedPropValidationError extends BaseValidationError {
|
|
4
|
+
constructor(...args) {
|
|
5
|
+
super(...args);
|
|
6
|
+
this.name = "UnevaluatedPropValidationError";
|
|
7
|
+
this.options.isIdentifierLocation = true;
|
|
8
|
+
}
|
|
9
|
+
getError() {
|
|
10
|
+
const { params } = this.options;
|
|
11
|
+
return {
|
|
12
|
+
message: `Property ${params.unevaluatedProperty} is not expected to be here`,
|
|
13
|
+
path: this.instancePath
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { UnevaluatedPropValidationError as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { details } from './details.js';
|
|
2
|
+
import { getEntrypoint } from './getEntrypoint.js';
|
|
3
|
+
import { makeFilesystem } from './makeFilesystem.js';
|
|
4
|
+
import { resolveReferences } from './resolveReferences.js';
|
|
5
|
+
|
|
6
|
+
async function dereference(value) {
|
|
7
|
+
const filesystem = makeFilesystem(value);
|
|
8
|
+
const entrypoint = getEntrypoint(filesystem);
|
|
9
|
+
const result = resolveReferences(filesystem);
|
|
10
|
+
return {
|
|
11
|
+
specification: entrypoint.specification,
|
|
12
|
+
errors: result.errors,
|
|
13
|
+
schema: result.schema,
|
|
14
|
+
...details(entrypoint.specification)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { dereference };
|