@redocly/openapi-core 1.18.1 → 1.19.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 +10 -0
- package/lib/benchmark/benches/lint-with-many-rules.bench.js +2 -2
- package/lib/benchmark/benches/lint-with-nested-rule.bench.js +2 -2
- package/lib/benchmark/benches/lint-with-no-rules.bench.js +2 -2
- package/lib/benchmark/benches/lint-with-top-level-rule-report.bench.js +2 -2
- package/lib/benchmark/benches/lint-with-top-level-rule.bench.js +2 -2
- package/lib/benchmark/benches/recommended-oas3.bench.js +2 -2
- package/lib/benchmark/benches/resolve-with-no-external.bench.js +2 -2
- package/lib/benchmark/utils.js +7 -4
- package/lib/bundle.d.ts +2 -2
- package/lib/bundle.js +127 -120
- package/lib/config/all.js +9 -0
- package/lib/config/builtIn.js +7 -1
- package/lib/config/config-resolvers.js +179 -138
- package/lib/config/config.d.ts +2 -2
- package/lib/config/config.js +53 -34
- package/lib/config/load.js +105 -117
- package/lib/config/minimal.js +9 -0
- package/lib/config/recommended-strict.js +9 -0
- package/lib/config/recommended.js +9 -0
- package/lib/config/rules.d.ts +3 -3
- package/lib/config/rules.js +1 -2
- package/lib/config/types.d.ts +9 -3
- package/lib/config/utils.js +70 -49
- package/lib/decorators/async3/index.d.ts +1 -0
- package/lib/decorators/async3/index.js +4 -0
- package/lib/decorators/common/filters/filter-helper.js +2 -3
- package/lib/decorators/common/filters/filter-in.js +1 -1
- package/lib/decorators/common/filters/filter-out.js +1 -1
- package/lib/decorators/common/info-override.js +1 -12
- package/lib/decorators/common/media-type-examples-override.js +8 -2
- package/lib/decorators/common/remove-x-internal.js +4 -5
- package/lib/decorators/oas2/remove-unused-components.js +1 -2
- package/lib/decorators/oas3/remove-unused-components.js +1 -2
- package/lib/env.d.ts +0 -1
- package/lib/env.js +1 -1
- package/lib/format/codeframes.js +10 -8
- package/lib/format/format.js +23 -15
- package/lib/index.d.ts +2 -1
- package/lib/index.js +6 -4
- package/lib/js-yaml/index.js +1 -1
- package/lib/lint.d.ts +2 -0
- package/lib/lint.js +92 -99
- package/lib/oas-types.d.ts +9 -5
- package/lib/oas-types.js +22 -12
- package/lib/redocly/domains.js +6 -6
- package/lib/redocly/index.js +60 -73
- package/lib/redocly/registry-api.js +64 -82
- package/lib/ref-utils.js +13 -13
- package/lib/resolve.js +186 -205
- package/lib/rules/ajv.js +10 -8
- package/lib/rules/async3/channels-kebab-case.d.ts +2 -0
- package/lib/rules/async3/channels-kebab-case.js +19 -0
- package/lib/rules/async3/index.d.ts +3 -0
- package/lib/rules/async3/index.js +22 -0
- package/lib/rules/async3/no-channel-trailing-slash.d.ts +2 -0
- package/lib/rules/async3/no-channel-trailing-slash.js +16 -0
- package/lib/rules/common/assertions/asserts.js +5 -5
- package/lib/rules/common/assertions/index.d.ts +5 -4
- package/lib/rules/common/assertions/utils.js +43 -28
- package/lib/rules/common/no-invalid-parameter-examples.js +1 -2
- package/lib/rules/common/no-invalid-schema-examples.js +1 -2
- package/lib/rules/common/no-required-schema-properties-undefined.js +1 -2
- package/lib/rules/common/operation-tag-defined.js +1 -2
- package/lib/rules/common/path-http-verbs-order.js +1 -1
- package/lib/rules/common/required-string-property-missing-min-length.js +2 -2
- package/lib/rules/common/response-contains-header.js +2 -2
- package/lib/rules/common/security-defined.js +3 -7
- package/lib/rules/common/spec.d.ts +2 -2
- package/lib/rules/common/spec.js +6 -7
- package/lib/rules/no-unresolved-refs.js +3 -4
- package/lib/rules/oas2/response-contains-property.js +1 -2
- package/lib/rules/oas3/array-parameter-serialization.js +1 -2
- package/lib/rules/oas3/component-name-unique.js +2 -4
- package/lib/rules/oas3/no-invalid-media-type-examples.js +1 -2
- package/lib/rules/oas3/no-server-variables-empty-enum.js +1 -2
- package/lib/rules/oas3/no-undefined-server-variable.js +2 -3
- package/lib/rules/oas3/no-unused-components.js +1 -2
- package/lib/rules/oas3/response-contains-property.js +1 -2
- package/lib/rules/utils.js +14 -12
- package/lib/types/asyncapi2.d.ts +17 -0
- package/lib/types/{asyncapi.js → asyncapi2.js} +56 -52
- package/lib/types/asyncapi3.d.ts +2 -0
- package/lib/types/asyncapi3.js +347 -0
- package/lib/types/index.js +19 -10
- package/lib/types/json-schema-adapter.js +4 -18
- package/lib/types/oas2.js +6 -6
- package/lib/types/oas3.js +10 -10
- package/lib/types/oas3_1.js +14 -8
- package/lib/types/redocly-yaml.d.ts +3 -1
- package/lib/types/redocly-yaml.js +131 -35
- package/lib/typings/asyncapi3.d.ts +53 -0
- package/lib/typings/asyncapi3.js +2 -0
- package/lib/utils.d.ts +4 -3
- package/lib/utils.js +55 -72
- package/lib/visitors.d.ts +11 -0
- package/lib/visitors.js +21 -8
- package/lib/walk.js +30 -23
- package/package.json +2 -2
- package/src/__tests__/bundle.test.ts +142 -0
- package/src/bundle.ts +17 -3
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +22 -0
- package/src/config/__tests__/__snapshots__/config.test.ts.snap +24 -0
- package/src/config/__tests__/config.test.ts +11 -0
- package/src/config/all.ts +9 -0
- package/src/config/builtIn.ts +6 -0
- package/src/config/config-resolvers.ts +15 -2
- package/src/config/config.ts +24 -5
- package/src/config/minimal.ts +9 -0
- package/src/config/recommended-strict.ts +9 -0
- package/src/config/recommended.ts +9 -0
- package/src/config/rules.ts +12 -4
- package/src/config/types.ts +15 -2
- package/src/config/utils.ts +15 -0
- package/src/decorators/async3/index.ts +1 -0
- package/src/decorators/common/remove-x-internal.ts +2 -2
- package/src/index.ts +2 -1
- package/src/lint.ts +26 -3
- package/src/oas-types.ts +31 -13
- package/src/rules/arazzo/index.ts +1 -1
- package/src/rules/async2/index.ts +5 -5
- package/src/rules/async3/__tests__/channels-kebab-case.test.ts +141 -0
- package/src/rules/async3/__tests__/no-channel-trailing-slash.test.ts +96 -0
- package/src/rules/async3/channels-kebab-case.ts +19 -0
- package/src/rules/async3/index.ts +23 -0
- package/src/rules/async3/no-channel-trailing-slash.ts +16 -0
- package/src/rules/common/assertions/index.ts +13 -4
- package/src/rules/common/spec.ts +2 -2
- package/src/rules/oas2/index.ts +4 -4
- package/src/rules/oas3/index.ts +39 -37
- package/src/types/{asyncapi.ts → asyncapi2.ts} +37 -34
- package/src/types/asyncapi3.ts +381 -0
- package/src/types/oas3_1.ts +2 -1
- package/src/types/redocly-yaml.ts +14 -0
- package/src/typings/asyncapi3.ts +61 -0
- package/src/utils.ts +5 -3
- package/src/visitors.ts +18 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/types/asyncapi.d.ts +0 -2
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface Async3Definition {
|
|
2
|
+
asyncapi: string;
|
|
3
|
+
servers?: Record<string, any>;
|
|
4
|
+
info: Async3Info;
|
|
5
|
+
channels?: Record<string, Channel>;
|
|
6
|
+
components?: Record<string, any>;
|
|
7
|
+
operations?: Record<string, any>;
|
|
8
|
+
defaultContentType?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Async3Info {
|
|
11
|
+
title: string;
|
|
12
|
+
version: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
termsOfService?: string;
|
|
15
|
+
contact?: Async3Contact;
|
|
16
|
+
license?: Async3License;
|
|
17
|
+
tags?: Tag[];
|
|
18
|
+
externalDocs?: ExternalDoc;
|
|
19
|
+
}
|
|
20
|
+
export interface Async3Contact {
|
|
21
|
+
name?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
email?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface Async3License {
|
|
26
|
+
name: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface Tag {
|
|
30
|
+
name: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
externalDocs?: ExternalDoc;
|
|
33
|
+
}
|
|
34
|
+
export interface ExternalDoc {
|
|
35
|
+
url: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface Channel {
|
|
39
|
+
address?: string | null;
|
|
40
|
+
messages?: Record<string, any>;
|
|
41
|
+
title?: string;
|
|
42
|
+
summary?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
servers?: Record<string, any>[];
|
|
45
|
+
parameters?: Record<string, any>;
|
|
46
|
+
tags?: Record<string, any>;
|
|
47
|
+
externalDocs?: ExternalDocumentation;
|
|
48
|
+
bindings?: Record<string, any>;
|
|
49
|
+
}
|
|
50
|
+
export interface ExternalDocumentation {
|
|
51
|
+
url: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
}
|
package/lib/utils.d.ts
CHANGED
|
@@ -16,9 +16,9 @@ export declare function popStack<T, P extends Stack<T>>(head: P): StackFrame<T>
|
|
|
16
16
|
export type BundleOutputFormat = 'json' | 'yml' | 'yaml';
|
|
17
17
|
export declare function loadYaml<T>(filename: string): Promise<T>;
|
|
18
18
|
export declare function isDefined<T>(x: T | undefined): x is T;
|
|
19
|
-
export declare function isPlainObject(value:
|
|
20
|
-
export declare function isEmptyObject(value:
|
|
21
|
-
export declare function isEmptyArray(value:
|
|
19
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
20
|
+
export declare function isEmptyObject(value: unknown): value is Record<string, unknown>;
|
|
21
|
+
export declare function isEmptyArray(value: unknown): boolean;
|
|
22
22
|
export declare function readFileFromUrl(url: string, config: HttpResolveConfig): Promise<{
|
|
23
23
|
body: any;
|
|
24
24
|
mimeType: any;
|
|
@@ -57,3 +57,4 @@ export declare function getProxyAgent(): HttpsProxyAgent<string> | undefined;
|
|
|
57
57
|
* Borrowed the source code from https://github.com/lukeed/dequal.
|
|
58
58
|
*/
|
|
59
59
|
export declare function dequal(foo: any, bar: any): boolean;
|
|
60
|
+
export type CollectFn = (value: unknown) => void;
|
package/lib/utils.js
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
3
|
+
exports.stringifyYaml = exports.parseYaml = void 0;
|
|
4
|
+
exports.pushStack = pushStack;
|
|
5
|
+
exports.popStack = popStack;
|
|
6
|
+
exports.loadYaml = loadYaml;
|
|
7
|
+
exports.isDefined = isDefined;
|
|
8
|
+
exports.isPlainObject = isPlainObject;
|
|
9
|
+
exports.isEmptyObject = isEmptyObject;
|
|
10
|
+
exports.isEmptyArray = isEmptyArray;
|
|
11
|
+
exports.readFileFromUrl = readFileFromUrl;
|
|
12
|
+
exports.pickObjectProps = pickObjectProps;
|
|
13
|
+
exports.omitObjectProps = omitObjectProps;
|
|
14
|
+
exports.splitCamelCaseIntoWords = splitCamelCaseIntoWords;
|
|
15
|
+
exports.validateMimeType = validateMimeType;
|
|
16
|
+
exports.validateMimeTypeOAS3 = validateMimeTypeOAS3;
|
|
17
|
+
exports.readFileAsStringSync = readFileAsStringSync;
|
|
18
|
+
exports.yamlAndJsonSyncReader = yamlAndJsonSyncReader;
|
|
19
|
+
exports.isPathParameter = isPathParameter;
|
|
20
|
+
exports.slash = slash;
|
|
21
|
+
exports.isNotEmptyObject = isNotEmptyObject;
|
|
22
|
+
exports.isString = isString;
|
|
23
|
+
exports.isNotString = isNotString;
|
|
24
|
+
exports.assignExisting = assignExisting;
|
|
25
|
+
exports.getMatchingStatusCodeRange = getMatchingStatusCodeRange;
|
|
26
|
+
exports.isCustomRuleId = isCustomRuleId;
|
|
27
|
+
exports.doesYamlFileExist = doesYamlFileExist;
|
|
28
|
+
exports.showWarningForDeprecatedField = showWarningForDeprecatedField;
|
|
29
|
+
exports.showErrorForDeprecatedField = showErrorForDeprecatedField;
|
|
30
|
+
exports.isTruthy = isTruthy;
|
|
31
|
+
exports.identity = identity;
|
|
32
|
+
exports.keysOf = keysOf;
|
|
33
|
+
exports.pickDefined = pickDefined;
|
|
34
|
+
exports.nextTick = nextTick;
|
|
35
|
+
exports.pause = pause;
|
|
36
|
+
exports.getProxyAgent = getProxyAgent;
|
|
37
|
+
exports.dequal = dequal;
|
|
13
38
|
const fs = require("fs");
|
|
14
39
|
const path_1 = require("path");
|
|
15
40
|
const minimatch = require("minimatch");
|
|
@@ -24,54 +49,41 @@ Object.defineProperty(exports, "stringifyYaml", { enumerable: true, get: functio
|
|
|
24
49
|
function pushStack(head, value) {
|
|
25
50
|
return { prev: head, value };
|
|
26
51
|
}
|
|
27
|
-
exports.pushStack = pushStack;
|
|
28
52
|
function popStack(head) {
|
|
29
|
-
|
|
30
|
-
return (_a = head === null || head === void 0 ? void 0 : head.prev) !== null && _a !== void 0 ? _a : null;
|
|
53
|
+
return head?.prev ?? null;
|
|
31
54
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
-
const contents = yield fs.promises.readFile(filename, 'utf-8');
|
|
36
|
-
return (0, js_yaml_1.parseYaml)(contents);
|
|
37
|
-
});
|
|
55
|
+
async function loadYaml(filename) {
|
|
56
|
+
const contents = await fs.promises.readFile(filename, 'utf-8');
|
|
57
|
+
return (0, js_yaml_1.parseYaml)(contents);
|
|
38
58
|
}
|
|
39
|
-
exports.loadYaml = loadYaml;
|
|
40
59
|
function isDefined(x) {
|
|
41
60
|
return x !== undefined;
|
|
42
61
|
}
|
|
43
|
-
exports.isDefined = isDefined;
|
|
44
62
|
function isPlainObject(value) {
|
|
45
63
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
46
64
|
}
|
|
47
|
-
exports.isPlainObject = isPlainObject;
|
|
48
65
|
function isEmptyObject(value) {
|
|
49
66
|
return isPlainObject(value) && Object.keys(value).length === 0;
|
|
50
67
|
}
|
|
51
|
-
exports.isEmptyObject = isEmptyObject;
|
|
52
68
|
function isEmptyArray(value) {
|
|
53
69
|
return Array.isArray(value) && value.length === 0;
|
|
54
70
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
headers[header.name] =
|
|
62
|
-
header.envVariable !== undefined ? env_1.env[header.envVariable] || '' : header.value;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
const req = yield (config.customFetch || node_fetch_1.default)(url, {
|
|
66
|
-
headers: headers,
|
|
67
|
-
});
|
|
68
|
-
if (!req.ok) {
|
|
69
|
-
throw new Error(`Failed to load ${url}: ${req.status} ${req.statusText}`);
|
|
71
|
+
async function readFileFromUrl(url, config) {
|
|
72
|
+
const headers = {};
|
|
73
|
+
for (const header of config.headers) {
|
|
74
|
+
if (match(url, header.matches)) {
|
|
75
|
+
headers[header.name] =
|
|
76
|
+
header.envVariable !== undefined ? env_1.env[header.envVariable] || '' : header.value;
|
|
70
77
|
}
|
|
71
|
-
|
|
78
|
+
}
|
|
79
|
+
const req = await (config.customFetch || node_fetch_1.default)(url, {
|
|
80
|
+
headers: headers,
|
|
72
81
|
});
|
|
82
|
+
if (!req.ok) {
|
|
83
|
+
throw new Error(`Failed to load ${url}: ${req.status} ${req.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
return { body: await req.text(), mimeType: req.headers.get('content-type') };
|
|
73
86
|
}
|
|
74
|
-
exports.readFileFromUrl = readFileFromUrl;
|
|
75
87
|
function match(url, pattern) {
|
|
76
88
|
if (!pattern.match(/^https?:\/\//)) {
|
|
77
89
|
// if pattern doesn't specify protocol directly, do not match against it
|
|
@@ -82,11 +94,9 @@ function match(url, pattern) {
|
|
|
82
94
|
function pickObjectProps(object, keys) {
|
|
83
95
|
return Object.fromEntries(keys.filter((key) => key in object).map((key) => [key, object[key]]));
|
|
84
96
|
}
|
|
85
|
-
exports.pickObjectProps = pickObjectProps;
|
|
86
97
|
function omitObjectProps(object, keys) {
|
|
87
98
|
return Object.fromEntries(Object.entries(object).filter(([key]) => !keys.includes(key)));
|
|
88
99
|
}
|
|
89
|
-
exports.omitObjectProps = omitObjectProps;
|
|
90
100
|
function splitCamelCaseIntoWords(str) {
|
|
91
101
|
const camel = str
|
|
92
102
|
.split(/(?:[-._])|([A-Z][a-z]+)/)
|
|
@@ -98,7 +108,6 @@ function splitCamelCaseIntoWords(str) {
|
|
|
98
108
|
.map((item) => item.toLocaleLowerCase());
|
|
99
109
|
return new Set([...camel, ...caps]);
|
|
100
110
|
}
|
|
101
|
-
exports.splitCamelCaseIntoWords = splitCamelCaseIntoWords;
|
|
102
111
|
function validateMimeType({ type, value }, { report, location }, allowedValues) {
|
|
103
112
|
const ruleType = type === 'consumes' ? 'request' : 'response';
|
|
104
113
|
if (!allowedValues)
|
|
@@ -114,7 +123,6 @@ function validateMimeType({ type, value }, { report, location }, allowedValues)
|
|
|
114
123
|
}
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
|
-
exports.validateMimeType = validateMimeType;
|
|
118
126
|
function validateMimeTypeOAS3({ type, value }, { report, location }, allowedValues) {
|
|
119
127
|
const ruleType = type === 'consumes' ? 'request' : 'response';
|
|
120
128
|
if (!allowedValues)
|
|
@@ -130,20 +138,16 @@ function validateMimeTypeOAS3({ type, value }, { report, location }, allowedValu
|
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
}
|
|
133
|
-
exports.validateMimeTypeOAS3 = validateMimeTypeOAS3;
|
|
134
141
|
function readFileAsStringSync(filePath) {
|
|
135
142
|
return fs.readFileSync(filePath, 'utf-8');
|
|
136
143
|
}
|
|
137
|
-
exports.readFileAsStringSync = readFileAsStringSync;
|
|
138
144
|
function yamlAndJsonSyncReader(filePath) {
|
|
139
145
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
140
146
|
return (0, js_yaml_1.parseYaml)(content);
|
|
141
147
|
}
|
|
142
|
-
exports.yamlAndJsonSyncReader = yamlAndJsonSyncReader;
|
|
143
148
|
function isPathParameter(pathSegment) {
|
|
144
149
|
return pathSegment.startsWith('{') && pathSegment.endsWith('}');
|
|
145
150
|
}
|
|
146
|
-
exports.isPathParameter = isPathParameter;
|
|
147
151
|
/**
|
|
148
152
|
* Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
|
|
149
153
|
*/
|
|
@@ -154,20 +158,16 @@ function slash(path) {
|
|
|
154
158
|
}
|
|
155
159
|
return path.replace(/\\/g, '/');
|
|
156
160
|
}
|
|
157
|
-
exports.slash = slash;
|
|
158
161
|
function isNotEmptyObject(obj) {
|
|
159
162
|
return !!obj && Object.keys(obj).length > 0;
|
|
160
163
|
}
|
|
161
|
-
exports.isNotEmptyObject = isNotEmptyObject;
|
|
162
164
|
// TODO: use it everywhere
|
|
163
165
|
function isString(value) {
|
|
164
166
|
return typeof value === 'string';
|
|
165
167
|
}
|
|
166
|
-
exports.isString = isString;
|
|
167
168
|
function isNotString(value) {
|
|
168
169
|
return !isString(value);
|
|
169
170
|
}
|
|
170
|
-
exports.isNotString = isNotString;
|
|
171
171
|
function assignExisting(target, obj) {
|
|
172
172
|
for (const k of Object.keys(obj)) {
|
|
173
173
|
if (target.hasOwnProperty(k)) {
|
|
@@ -175,46 +175,36 @@ function assignExisting(target, obj) {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
exports.assignExisting = assignExisting;
|
|
179
178
|
function getMatchingStatusCodeRange(code) {
|
|
180
179
|
return `${code}`.replace(/^(\d)\d\d$/, (_, firstDigit) => `${firstDigit}XX`);
|
|
181
180
|
}
|
|
182
|
-
exports.getMatchingStatusCodeRange = getMatchingStatusCodeRange;
|
|
183
181
|
function isCustomRuleId(id) {
|
|
184
182
|
return id.includes('/');
|
|
185
183
|
}
|
|
186
|
-
exports.isCustomRuleId = isCustomRuleId;
|
|
187
184
|
function doesYamlFileExist(filePath) {
|
|
188
|
-
var _a;
|
|
189
185
|
return (((0, path_1.extname)(filePath) === '.yaml' || (0, path_1.extname)(filePath) === '.yml') &&
|
|
190
|
-
|
|
186
|
+
fs?.hasOwnProperty?.('existsSync') &&
|
|
191
187
|
fs.existsSync(filePath));
|
|
192
188
|
}
|
|
193
|
-
exports.doesYamlFileExist = doesYamlFileExist;
|
|
194
189
|
function showWarningForDeprecatedField(deprecatedField, updatedField, updatedObject) {
|
|
195
190
|
logger_1.logger.warn(`The '${logger_1.colorize.red(deprecatedField)}' field is deprecated. ${updatedField
|
|
196
191
|
? `Use ${logger_1.colorize.green(getUpdatedFieldName(updatedField, updatedObject))} instead. `
|
|
197
192
|
: ''}Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`);
|
|
198
193
|
}
|
|
199
|
-
exports.showWarningForDeprecatedField = showWarningForDeprecatedField;
|
|
200
194
|
function showErrorForDeprecatedField(deprecatedField, updatedField, updatedObject) {
|
|
201
195
|
throw new Error(`Do not use '${deprecatedField}' field. ${updatedField ? `Use '${getUpdatedFieldName(updatedField, updatedObject)}' instead. ` : ''}\n`);
|
|
202
196
|
}
|
|
203
|
-
exports.showErrorForDeprecatedField = showErrorForDeprecatedField;
|
|
204
197
|
function isTruthy(value) {
|
|
205
198
|
return !!value;
|
|
206
199
|
}
|
|
207
|
-
exports.isTruthy = isTruthy;
|
|
208
200
|
function identity(value) {
|
|
209
201
|
return value;
|
|
210
202
|
}
|
|
211
|
-
exports.identity = identity;
|
|
212
203
|
function keysOf(obj) {
|
|
213
204
|
if (!obj)
|
|
214
205
|
return [];
|
|
215
206
|
return Object.keys(obj);
|
|
216
207
|
}
|
|
217
|
-
exports.keysOf = keysOf;
|
|
218
208
|
function pickDefined(obj) {
|
|
219
209
|
if (!obj)
|
|
220
210
|
return undefined;
|
|
@@ -226,19 +216,14 @@ function pickDefined(obj) {
|
|
|
226
216
|
}
|
|
227
217
|
return res;
|
|
228
218
|
}
|
|
229
|
-
exports.pickDefined = pickDefined;
|
|
230
219
|
function nextTick() {
|
|
231
220
|
return new Promise((resolve) => {
|
|
232
221
|
setTimeout(resolve);
|
|
233
222
|
});
|
|
234
223
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
238
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
239
|
-
});
|
|
224
|
+
async function pause(ms) {
|
|
225
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
240
226
|
}
|
|
241
|
-
exports.pause = pause;
|
|
242
227
|
function getUpdatedFieldName(updatedField, updatedObject) {
|
|
243
228
|
return `${typeof updatedObject !== 'undefined' ? `${updatedObject}.` : ''}${updatedField}`;
|
|
244
229
|
}
|
|
@@ -246,7 +231,6 @@ function getProxyAgent() {
|
|
|
246
231
|
const proxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
|
247
232
|
return proxy ? new https_proxy_agent_1.HttpsProxyAgent(proxy) : undefined;
|
|
248
233
|
}
|
|
249
|
-
exports.getProxyAgent = getProxyAgent;
|
|
250
234
|
/**
|
|
251
235
|
* Checks if two objects are deeply equal.
|
|
252
236
|
* Borrowed the source code from https://github.com/lukeed/dequal.
|
|
@@ -282,4 +266,3 @@ function dequal(foo, bar) {
|
|
|
282
266
|
}
|
|
283
267
|
return foo !== foo && bar !== bar;
|
|
284
268
|
}
|
|
285
|
-
exports.dequal = dequal;
|
package/lib/visitors.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Location } from './ref-utils';
|
|
|
5
5
|
import type { Oas3Definition, Oas3ExternalDocs, Oas3Info, Oas3Contact, Oas3Components, Oas3License, Oas3Schema, Oas3Header, Oas3Parameter, Oas3Operation, Oas3PathItem, Oas3ServerVariable, Oas3Server, Oas3MediaType, Oas3Response, Oas3Example, Oas3RequestBody, Oas3Tag, OasRef, Oas3SecurityScheme, Oas3SecurityRequirement, Oas3Encoding, Oas3Link, Oas3Xml, Oas3Discriminator, Oas3Callback } from './typings/openapi';
|
|
6
6
|
import type { Oas2Definition, Oas2Tag, Oas2ExternalDocs, Oas2SecurityRequirement, Oas2Info, Oas2Contact, Oas2License, Oas2PathItem, Oas2Operation, Oas2Header, Oas2Response, Oas2Schema, Oas2Xml, Oas2Parameter, Oas2SecurityScheme } from './typings/swagger';
|
|
7
7
|
import type { Async2Definition } from './typings/asyncapi';
|
|
8
|
+
import type { Async3Definition } from './typings/asyncapi3';
|
|
8
9
|
import type { ArazzoDefinition } from './typings/arazzo';
|
|
9
10
|
export type SkipFunctionContext = Pick<UserContext, 'location' | 'rawNode' | 'resolve' | 'rawLocation'>;
|
|
10
11
|
export type VisitFunction<T> = (node: T, ctx: UserContext & {
|
|
@@ -140,6 +141,9 @@ type Oas2FlatVisitor = {
|
|
|
140
141
|
type Async2FlatVisitor = {
|
|
141
142
|
Root?: VisitFunctionOrObject<Async2Definition>;
|
|
142
143
|
};
|
|
144
|
+
type Async3FlatVisitor = {
|
|
145
|
+
Root?: VisitFunctionOrObject<Async3Definition>;
|
|
146
|
+
};
|
|
143
147
|
type ArazzoFlatVisitor = {
|
|
144
148
|
Root?: VisitFunctionOrObject<ArazzoDefinition>;
|
|
145
149
|
};
|
|
@@ -152,12 +156,16 @@ type Oas2NestedVisitor = {
|
|
|
152
156
|
type Async2NestedVisitor = {
|
|
153
157
|
[T in keyof Async2FlatVisitor]: Async2FlatVisitor[T] extends Function ? Async2FlatVisitor[T] : Async2FlatVisitor[T] & NestedVisitor<Async2NestedVisitor>;
|
|
154
158
|
};
|
|
159
|
+
type Async3NestedVisitor = {
|
|
160
|
+
[T in keyof Async3FlatVisitor]: Async3FlatVisitor[T] extends Function ? Async3FlatVisitor[T] : Async3FlatVisitor[T] & NestedVisitor<Async3NestedVisitor>;
|
|
161
|
+
};
|
|
155
162
|
type ArazzoNestedVisitor = {
|
|
156
163
|
[T in keyof ArazzoFlatVisitor]: ArazzoFlatVisitor[T] extends Function ? ArazzoFlatVisitor[T] : ArazzoFlatVisitor[T] & NestedVisitor<ArazzoNestedVisitor>;
|
|
157
164
|
};
|
|
158
165
|
export type Oas3Visitor = BaseVisitor & Oas3NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Oas3NestedVisitor>>;
|
|
159
166
|
export type Oas2Visitor = BaseVisitor & Oas2NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Oas2NestedVisitor>>;
|
|
160
167
|
export type Async2Visitor = BaseVisitor & Async2NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Async2NestedVisitor>>;
|
|
168
|
+
export type Async3Visitor = BaseVisitor & Async3NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Async3NestedVisitor>>;
|
|
161
169
|
export type ArazzoVisitor = BaseVisitor & ArazzoNestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, ArazzoNestedVisitor>>;
|
|
162
170
|
export type NestedVisitor<T> = Exclude<T, 'any' | 'ref' | 'Root'>;
|
|
163
171
|
export type NormalizedOasVisitors<T extends BaseVisitor> = {
|
|
@@ -178,14 +186,17 @@ export type NormalizedOasVisitors<T extends BaseVisitor> = {
|
|
|
178
186
|
export type Oas3Rule = (options: Record<string, any>) => Oas3Visitor | Oas3Visitor[];
|
|
179
187
|
export type Oas2Rule = (options: Record<string, any>) => Oas2Visitor | Oas2Visitor[];
|
|
180
188
|
export type Async2Rule = (options: Record<string, any>) => Async2Visitor | Async2Visitor[];
|
|
189
|
+
export type Async3Rule = (options: Record<string, any>) => Async3Visitor | Async3Visitor[];
|
|
181
190
|
export type ArazzoRule = (options: Record<string, any>) => ArazzoVisitor | ArazzoVisitor[];
|
|
182
191
|
export type Oas3Preprocessor = (options: Record<string, any>) => Oas3Visitor;
|
|
183
192
|
export type Oas2Preprocessor = (options: Record<string, any>) => Oas2Visitor;
|
|
184
193
|
export type Async2Preprocessor = (options: Record<string, any>) => Async2Visitor;
|
|
194
|
+
export type Async3Preprocessor = (options: Record<string, any>) => Async3Visitor;
|
|
185
195
|
export type ArazzoPreprocessor = (options: Record<string, any>) => ArazzoVisitor;
|
|
186
196
|
export type Oas3Decorator = (options: Record<string, any>) => Oas3Visitor;
|
|
187
197
|
export type Oas2Decorator = (options: Record<string, any>) => Oas2Visitor;
|
|
188
198
|
export type Async2Decorator = (options: Record<string, any>) => Async2Visitor;
|
|
199
|
+
export type Async3Decorator = (options: Record<string, any>) => Async3Visitor;
|
|
189
200
|
export type ArazzoDecorator = (options: Record<string, any>) => ArazzoVisitor;
|
|
190
201
|
export type OasRule = Oas3Rule;
|
|
191
202
|
export type OasPreprocessor = Oas3Preprocessor;
|
package/lib/visitors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeVisitors =
|
|
3
|
+
exports.normalizeVisitors = normalizeVisitors;
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const legacyTypesMap = {
|
|
6
6
|
Root: 'DefinitionRoot',
|
|
@@ -81,11 +81,16 @@ function normalizeVisitors(visitorsConfig, types) {
|
|
|
81
81
|
enter: [],
|
|
82
82
|
leave: [],
|
|
83
83
|
};
|
|
84
|
-
normalizedVisitors[interType.name].enter.push(
|
|
84
|
+
normalizedVisitors[interType.name].enter.push({
|
|
85
|
+
...ruleConf,
|
|
86
|
+
visit: () => undefined,
|
|
87
|
+
depth: 0,
|
|
88
|
+
context: {
|
|
85
89
|
isSkippedLevel: true,
|
|
86
90
|
seen: new Set(),
|
|
87
91
|
parent: parentContext,
|
|
88
|
-
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
89
94
|
}
|
|
90
95
|
}
|
|
91
96
|
}
|
|
@@ -147,17 +152,25 @@ function normalizeVisitors(visitorsConfig, types) {
|
|
|
147
152
|
if (visitorEnter && typeof visitorEnter !== 'function') {
|
|
148
153
|
throw new Error('DEV: should be function');
|
|
149
154
|
}
|
|
150
|
-
normalizedTypeVisitor.enter.push(
|
|
151
|
-
|
|
155
|
+
normalizedTypeVisitor.enter.push({
|
|
156
|
+
...ruleConf,
|
|
157
|
+
visit: visitorEnter || (() => undefined),
|
|
158
|
+
skip: visitorSkip,
|
|
159
|
+
depth,
|
|
160
|
+
context,
|
|
161
|
+
});
|
|
152
162
|
}
|
|
153
163
|
if (visitorLeave) {
|
|
154
164
|
if (typeof visitorLeave !== 'function') {
|
|
155
165
|
throw new Error('DEV: should be function');
|
|
156
166
|
}
|
|
157
|
-
normalizedTypeVisitor.leave.push(
|
|
158
|
-
|
|
167
|
+
normalizedTypeVisitor.leave.push({
|
|
168
|
+
...ruleConf,
|
|
169
|
+
visit: visitorLeave,
|
|
170
|
+
depth,
|
|
171
|
+
context,
|
|
172
|
+
});
|
|
159
173
|
}
|
|
160
174
|
}
|
|
161
175
|
}
|
|
162
176
|
}
|
|
163
|
-
exports.normalizeVisitors = normalizeVisitors;
|
package/lib/walk.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.walkDocument =
|
|
3
|
+
exports.walkDocument = walkDocument;
|
|
4
4
|
const ref_utils_1 = require("./ref-utils");
|
|
5
5
|
const utils_1 = require("./utils");
|
|
6
6
|
const resolve_1 = require("./resolve");
|
|
7
7
|
const types_1 = require("./types");
|
|
8
8
|
function collectParents(ctx) {
|
|
9
|
-
var _a;
|
|
10
9
|
const parents = {};
|
|
11
10
|
while (ctx.parent) {
|
|
12
|
-
parents[ctx.parent.type.name] =
|
|
11
|
+
parents[ctx.parent.type.name] = ctx.parent.activatedOn?.value.node;
|
|
13
12
|
ctx = ctx.parent;
|
|
14
13
|
}
|
|
15
14
|
return parents;
|
|
16
15
|
}
|
|
17
16
|
function collectParentsLocations(ctx) {
|
|
18
|
-
var _a, _b;
|
|
19
17
|
const locations = {};
|
|
20
18
|
while (ctx.parent) {
|
|
21
|
-
if (
|
|
22
|
-
locations[ctx.parent.type.name] =
|
|
19
|
+
if (ctx.parent.activatedOn?.value.location) {
|
|
20
|
+
locations[ctx.parent.type.name] = ctx.parent.activatedOn?.value.location;
|
|
23
21
|
}
|
|
24
22
|
ctx = ctx.parent;
|
|
25
23
|
}
|
|
@@ -31,7 +29,6 @@ function walkDocument(opts) {
|
|
|
31
29
|
const ignoredNodes = new Set();
|
|
32
30
|
walkNode(document.parsed, rootType, new ref_utils_1.Location(document.source, '#/'), undefined, '');
|
|
33
31
|
function walkNode(node, type, location, parent, key) {
|
|
34
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
35
32
|
const resolve = (ref, from = currentLocation.source.absoluteRef) => {
|
|
36
33
|
if (!(0, ref_utils_1.isRef)(ref))
|
|
37
34
|
return { location, node: ref };
|
|
@@ -73,17 +70,17 @@ function walkDocument(opts) {
|
|
|
73
70
|
oasVersion: ctx.oasVersion,
|
|
74
71
|
getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
|
|
75
72
|
}, { node: resolvedNode, location: resolvedLocation, error });
|
|
76
|
-
if (
|
|
77
|
-
ctx.refTypes.set(resolvedLocation
|
|
73
|
+
if (resolvedLocation?.source.absoluteRef && ctx.refTypes) {
|
|
74
|
+
ctx.refTypes.set(resolvedLocation?.source.absoluteRef, type);
|
|
78
75
|
}
|
|
79
76
|
}
|
|
80
77
|
}
|
|
81
78
|
if (resolvedNode !== undefined && resolvedLocation && type.name !== 'scalar') {
|
|
82
79
|
currentLocation = resolvedLocation;
|
|
83
|
-
const isNodeSeen =
|
|
80
|
+
const isNodeSeen = seenNodesPerType[type.name]?.has?.(resolvedNode);
|
|
84
81
|
let visitedBySome = false;
|
|
85
82
|
const anyEnterVisitors = normalizedVisitors.any.enter;
|
|
86
|
-
const currentEnterVisitors = anyEnterVisitors.concat(
|
|
83
|
+
const currentEnterVisitors = anyEnterVisitors.concat(normalizedVisitors[type.name]?.enter || []);
|
|
87
84
|
const activatedContexts = [];
|
|
88
85
|
for (const { context, visit, skip, ruleId, severity } of currentEnterVisitors) {
|
|
89
86
|
if (ignoredNodes.has(`${currentLocation.absolutePointer}${currentLocation.pointer}`))
|
|
@@ -101,9 +98,9 @@ function walkDocument(opts) {
|
|
|
101
98
|
else {
|
|
102
99
|
if ((context.parent && // if nested
|
|
103
100
|
context.parent.activatedOn &&
|
|
104
|
-
|
|
101
|
+
context.activatedOn?.value.withParentNode !== context.parent.activatedOn.value.node &&
|
|
105
102
|
// do not enter if visited by parent children (it works thanks because deeper visitors are sorted before)
|
|
106
|
-
|
|
103
|
+
context.parent.activatedOn.value.nextLevelTypeActivated?.value !== type) ||
|
|
107
104
|
(!context.parent && !isNodeSeen) // if top-level visit each node just once
|
|
108
105
|
) {
|
|
109
106
|
activatedContexts.push(context);
|
|
@@ -111,14 +108,15 @@ function walkDocument(opts) {
|
|
|
111
108
|
node: resolvedNode,
|
|
112
109
|
location: resolvedLocation,
|
|
113
110
|
nextLevelTypeActivated: null,
|
|
114
|
-
withParentNode:
|
|
115
|
-
skipped: (
|
|
116
|
-
|
|
111
|
+
withParentNode: context.parent?.activatedOn?.value.node,
|
|
112
|
+
skipped: (context.parent?.activatedOn?.value.skipped ||
|
|
113
|
+
skip?.(resolvedNode, key, {
|
|
117
114
|
location,
|
|
118
115
|
rawLocation,
|
|
119
116
|
resolve,
|
|
120
117
|
rawNode: node,
|
|
121
|
-
}))
|
|
118
|
+
})) ??
|
|
119
|
+
false,
|
|
122
120
|
};
|
|
123
121
|
context.activatedOn = (0, utils_1.pushStack)(context.activatedOn, activatedOn);
|
|
124
122
|
let ctx = context.parent;
|
|
@@ -180,7 +178,7 @@ function walkDocument(opts) {
|
|
|
180
178
|
propName.startsWith(type.extensionsPrefix)) {
|
|
181
179
|
propType = types_1.SpecExtension;
|
|
182
180
|
}
|
|
183
|
-
if (!(0, types_1.isNamedType)(propType) &&
|
|
181
|
+
if (!(0, types_1.isNamedType)(propType) && propType?.directResolveAs) {
|
|
184
182
|
propType = propType.directResolveAs;
|
|
185
183
|
value = { $ref: value };
|
|
186
184
|
}
|
|
@@ -195,7 +193,7 @@ function walkDocument(opts) {
|
|
|
195
193
|
}
|
|
196
194
|
}
|
|
197
195
|
const anyLeaveVisitors = normalizedVisitors.any.leave;
|
|
198
|
-
const currentLeaveVisitors = (
|
|
196
|
+
const currentLeaveVisitors = (normalizedVisitors[type.name]?.leave || []).concat(anyLeaveVisitors);
|
|
199
197
|
for (const context of activatedContexts.reverse()) {
|
|
200
198
|
if (context.isSkippedLevel) {
|
|
201
199
|
context.seen.delete(resolvedNode);
|
|
@@ -264,11 +262,21 @@ function walkDocument(opts) {
|
|
|
264
262
|
? Array.isArray(opts.location)
|
|
265
263
|
? opts.location
|
|
266
264
|
: [opts.location]
|
|
267
|
-
: [
|
|
268
|
-
const location = normalizedLocation.map((l) => (
|
|
265
|
+
: [{ ...currentLocation, reportOnKey: false }];
|
|
266
|
+
const location = normalizedLocation.map((l) => ({
|
|
267
|
+
...currentLocation,
|
|
268
|
+
reportOnKey: false,
|
|
269
|
+
...l,
|
|
270
|
+
}));
|
|
269
271
|
const ruleSeverity = opts.forceSeverity || severity;
|
|
270
272
|
if (ruleSeverity !== 'off') {
|
|
271
|
-
ctx.problems.push(
|
|
273
|
+
ctx.problems.push({
|
|
274
|
+
ruleId: opts.ruleId || ruleId,
|
|
275
|
+
severity: ruleSeverity,
|
|
276
|
+
...opts,
|
|
277
|
+
suggest: opts.suggest || [],
|
|
278
|
+
location,
|
|
279
|
+
});
|
|
272
280
|
}
|
|
273
281
|
}
|
|
274
282
|
function getVisitorDataFn(ruleId) {
|
|
@@ -277,4 +285,3 @@ function walkDocument(opts) {
|
|
|
277
285
|
}
|
|
278
286
|
}
|
|
279
287
|
}
|
|
280
|
-
exports.walkDocument = walkDocument;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/openapi-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"@types/node-fetch": "^2.5.7",
|
|
57
57
|
"@types/pluralize": "^0.0.29",
|
|
58
58
|
"json-schema-to-ts": "^3.1.0",
|
|
59
|
-
"typescript": "
|
|
59
|
+
"typescript": "5.5.3"
|
|
60
60
|
}
|
|
61
61
|
}
|