@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
package/lib/resolve.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
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.BaseResolver = exports.YamlParseError = exports.ResolveError = exports.Source = void 0;
|
|
4
|
+
exports.makeRefId = makeRefId;
|
|
5
|
+
exports.makeDocumentFromString = makeDocumentFromString;
|
|
6
|
+
exports.resolveDocument = resolveDocument;
|
|
13
7
|
const fs = require("fs");
|
|
14
8
|
const path = require("path");
|
|
15
9
|
const ref_utils_1 = require("./ref-utils");
|
|
@@ -23,9 +17,8 @@ class Source {
|
|
|
23
17
|
}
|
|
24
18
|
// pass safeLoad as argument to separate it from browser bundle
|
|
25
19
|
getAst(safeLoad) {
|
|
26
|
-
var _a;
|
|
27
20
|
if (this._ast === undefined) {
|
|
28
|
-
this._ast =
|
|
21
|
+
this._ast = safeLoad(this.body, { filename: this.absoluteRef }) ?? undefined;
|
|
29
22
|
// fix ast representation of file with newlines only
|
|
30
23
|
if (this._ast &&
|
|
31
24
|
this._ast.kind === 0 && // KIND.scalar = 0
|
|
@@ -71,7 +64,6 @@ exports.YamlParseError = YamlParseError;
|
|
|
71
64
|
function makeRefId(absoluteRef, pointer) {
|
|
72
65
|
return absoluteRef + '::' + pointer;
|
|
73
66
|
}
|
|
74
|
-
exports.makeRefId = makeRefId;
|
|
75
67
|
function makeDocumentFromString(sourceString, absoluteRef) {
|
|
76
68
|
const source = new Source(absoluteRef, sourceString);
|
|
77
69
|
try {
|
|
@@ -84,7 +76,6 @@ function makeDocumentFromString(sourceString, absoluteRef) {
|
|
|
84
76
|
throw new YamlParseError(e, source);
|
|
85
77
|
}
|
|
86
78
|
}
|
|
87
|
-
exports.makeDocumentFromString = makeDocumentFromString;
|
|
88
79
|
class BaseResolver {
|
|
89
80
|
constructor(config = { http: { headers: [] } }) {
|
|
90
81
|
this.config = config;
|
|
@@ -102,33 +93,30 @@ class BaseResolver {
|
|
|
102
93
|
}
|
|
103
94
|
return path.resolve(base ? path.dirname(base) : process.cwd(), ref);
|
|
104
95
|
}
|
|
105
|
-
loadExternalRef(absoluteRef) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return new Source(absoluteRef, body, mimeType);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
if (fs.lstatSync(absoluteRef).isDirectory()) {
|
|
114
|
-
throw new Error(`Expected a file but received a folder at ${absoluteRef}`);
|
|
115
|
-
}
|
|
116
|
-
const content = yield fs.promises.readFile(absoluteRef, 'utf-8');
|
|
117
|
-
// In some cases file have \r\n line delimeters like on windows, we should skip it.
|
|
118
|
-
return new Source(absoluteRef, content.replace(/\r\n/g, '\n'));
|
|
119
|
-
}
|
|
96
|
+
async loadExternalRef(absoluteRef) {
|
|
97
|
+
try {
|
|
98
|
+
if ((0, ref_utils_1.isAbsoluteUrl)(absoluteRef)) {
|
|
99
|
+
const { body, mimeType } = await (0, utils_1.readFileFromUrl)(absoluteRef, this.config.http);
|
|
100
|
+
return new Source(absoluteRef, body, mimeType);
|
|
120
101
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
102
|
+
else {
|
|
103
|
+
if (fs.lstatSync(absoluteRef).isDirectory()) {
|
|
104
|
+
throw new Error(`Expected a file but received a folder at ${absoluteRef}`);
|
|
105
|
+
}
|
|
106
|
+
const content = await fs.promises.readFile(absoluteRef, 'utf-8');
|
|
107
|
+
// In some cases file have \r\n line delimeters like on windows, we should skip it.
|
|
108
|
+
return new Source(absoluteRef, content.replace(/\r\n/g, '\n'));
|
|
124
109
|
}
|
|
125
|
-
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
error.message = error.message.replace(', lstat', '');
|
|
113
|
+
throw new ResolveError(error);
|
|
114
|
+
}
|
|
126
115
|
}
|
|
127
116
|
parseDocument(source, isRoot = false) {
|
|
128
|
-
var _a;
|
|
129
117
|
const ext = source.absoluteRef.substr(source.absoluteRef.lastIndexOf('.'));
|
|
130
118
|
if (!['.json', '.json', '.yml', '.yaml'].includes(ext) &&
|
|
131
|
-
!
|
|
119
|
+
!source.mimeType?.match(/(json|yaml|openapi)/) &&
|
|
132
120
|
!isRoot // always parse root
|
|
133
121
|
) {
|
|
134
122
|
return { source, parsed: source.body };
|
|
@@ -143,19 +131,17 @@ class BaseResolver {
|
|
|
143
131
|
throw new YamlParseError(e, source);
|
|
144
132
|
}
|
|
145
133
|
}
|
|
146
|
-
resolveDocument(base, ref, isRoot = false) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return this.parseDocument(source, isRoot);
|
|
155
|
-
});
|
|
156
|
-
this.cache.set(absoluteRef, doc);
|
|
157
|
-
return doc;
|
|
134
|
+
async resolveDocument(base, ref, isRoot = false) {
|
|
135
|
+
const absoluteRef = this.resolveExternalRef(base, ref);
|
|
136
|
+
const cachedDocument = this.cache.get(absoluteRef);
|
|
137
|
+
if (cachedDocument) {
|
|
138
|
+
return cachedDocument;
|
|
139
|
+
}
|
|
140
|
+
const doc = this.loadExternalRef(absoluteRef).then((source) => {
|
|
141
|
+
return this.parseDocument(source, isRoot);
|
|
158
142
|
});
|
|
143
|
+
this.cache.set(absoluteRef, doc);
|
|
144
|
+
return doc;
|
|
159
145
|
}
|
|
160
146
|
}
|
|
161
147
|
exports.BaseResolver = BaseResolver;
|
|
@@ -176,174 +162,169 @@ function hasRef(head, node) {
|
|
|
176
162
|
}
|
|
177
163
|
const unknownType = { name: 'unknown', properties: {} };
|
|
178
164
|
const resolvableScalarType = { name: 'scalar', properties: {} };
|
|
179
|
-
function resolveDocument(opts) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
165
|
+
async function resolveDocument(opts) {
|
|
166
|
+
const { rootDocument, externalRefResolver, rootType } = opts;
|
|
167
|
+
const resolvedRefMap = new Map();
|
|
168
|
+
const seedNodes = new Set(); // format "${type}::${absoluteRef}${pointer}"
|
|
169
|
+
const resolvePromises = [];
|
|
170
|
+
resolveRefsInParallel(rootDocument.parsed, rootDocument, '#/', rootType);
|
|
171
|
+
let resolved;
|
|
172
|
+
do {
|
|
173
|
+
resolved = await Promise.all(resolvePromises);
|
|
174
|
+
} while (resolvePromises.length !== resolved.length);
|
|
175
|
+
return resolvedRefMap;
|
|
176
|
+
function resolveRefsInParallel(rootNode, rootNodeDocument, rootNodePointer, type) {
|
|
177
|
+
const rootNodeDocAbsoluteRef = rootNodeDocument.source.absoluteRef;
|
|
178
|
+
const anchorRefsMap = new Map();
|
|
179
|
+
walk(rootNode, type, rootNodeDocAbsoluteRef + rootNodePointer);
|
|
180
|
+
function walk(node, type, nodeAbsoluteRef) {
|
|
181
|
+
if (typeof node !== 'object' || node === null) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const nodeId = `${type.name}::${nodeAbsoluteRef}`;
|
|
185
|
+
if (seedNodes.has(nodeId)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
seedNodes.add(nodeId);
|
|
189
|
+
const [_, anchor] = Object.entries(node).find(([key]) => key === '$anchor') || [];
|
|
190
|
+
if (anchor) {
|
|
191
|
+
anchorRefsMap.set(`#${anchor}`, node);
|
|
192
|
+
}
|
|
193
|
+
if (Array.isArray(node)) {
|
|
194
|
+
const itemsType = type.items;
|
|
195
|
+
// we continue resolving unknown types, but stop early on known scalars
|
|
196
|
+
if (itemsType === undefined && type !== unknownType && type !== types_1.SpecExtension) {
|
|
201
197
|
return;
|
|
202
198
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (Array.isArray(node)) {
|
|
209
|
-
const itemsType = type.items;
|
|
199
|
+
const isTypeAFunction = typeof itemsType === 'function';
|
|
200
|
+
for (let i = 0; i < node.length; i++) {
|
|
201
|
+
const itemType = isTypeAFunction
|
|
202
|
+
? itemsType(node[i], (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i))
|
|
203
|
+
: itemsType;
|
|
210
204
|
// we continue resolving unknown types, but stop early on known scalars
|
|
211
|
-
if (
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const isTypeAFunction = typeof itemsType === 'function';
|
|
215
|
-
for (let i = 0; i < node.length; i++) {
|
|
216
|
-
const itemType = isTypeAFunction
|
|
217
|
-
? itemsType(node[i], (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i))
|
|
218
|
-
: itemsType;
|
|
219
|
-
// we continue resolving unknown types, but stop early on known scalars
|
|
220
|
-
if (itemType === undefined && type !== unknownType && type !== types_1.SpecExtension) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
walk(node[i], (0, types_1.isNamedType)(itemType) ? itemType : unknownType, (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i));
|
|
224
|
-
}
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
for (const propName of Object.keys(node)) {
|
|
228
|
-
let propValue = node[propName];
|
|
229
|
-
let propType = type.properties[propName];
|
|
230
|
-
if (propType === undefined)
|
|
231
|
-
propType = type.additionalProperties;
|
|
232
|
-
if (typeof propType === 'function')
|
|
233
|
-
propType = propType(propValue, propName);
|
|
234
|
-
if (propType === undefined)
|
|
235
|
-
propType = unknownType;
|
|
236
|
-
if (type.extensionsPrefix &&
|
|
237
|
-
propName.startsWith(type.extensionsPrefix) &&
|
|
238
|
-
propType === unknownType) {
|
|
239
|
-
propType = types_1.SpecExtension;
|
|
240
|
-
}
|
|
241
|
-
if (!(0, types_1.isNamedType)(propType) && (propType === null || propType === void 0 ? void 0 : propType.directResolveAs)) {
|
|
242
|
-
propType = propType.directResolveAs;
|
|
243
|
-
propValue = { $ref: propValue };
|
|
244
|
-
}
|
|
245
|
-
if (propType && propType.name === undefined && propType.resolvable !== false) {
|
|
246
|
-
propType = resolvableScalarType;
|
|
247
|
-
}
|
|
248
|
-
if (!(0, types_1.isNamedType)(propType) || typeof propValue !== 'object') {
|
|
205
|
+
if (itemType === undefined && type !== unknownType && type !== types_1.SpecExtension) {
|
|
249
206
|
continue;
|
|
250
207
|
}
|
|
251
|
-
walk(
|
|
208
|
+
walk(node[i], (0, types_1.isNamedType)(itemType) ? itemType : unknownType, (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i));
|
|
209
|
+
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
for (const propName of Object.keys(node)) {
|
|
213
|
+
let propValue = node[propName];
|
|
214
|
+
let propType = type.properties[propName];
|
|
215
|
+
if (propType === undefined)
|
|
216
|
+
propType = type.additionalProperties;
|
|
217
|
+
if (typeof propType === 'function')
|
|
218
|
+
propType = propType(propValue, propName);
|
|
219
|
+
if (propType === undefined)
|
|
220
|
+
propType = unknownType;
|
|
221
|
+
if (type.extensionsPrefix &&
|
|
222
|
+
propName.startsWith(type.extensionsPrefix) &&
|
|
223
|
+
propType === unknownType) {
|
|
224
|
+
propType = types_1.SpecExtension;
|
|
252
225
|
}
|
|
253
|
-
if ((0,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
node,
|
|
257
|
-
}).then((resolvedRef) => {
|
|
258
|
-
if (resolvedRef.resolved) {
|
|
259
|
-
resolveRefsInParallel(resolvedRef.node, resolvedRef.document, resolvedRef.nodePointer, type);
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
resolvePromises.push(promise);
|
|
226
|
+
if (!(0, types_1.isNamedType)(propType) && propType?.directResolveAs) {
|
|
227
|
+
propType = propType.directResolveAs;
|
|
228
|
+
propValue = { $ref: propValue };
|
|
263
229
|
}
|
|
230
|
+
if (propType && propType.name === undefined && propType.resolvable !== false) {
|
|
231
|
+
propType = resolvableScalarType;
|
|
232
|
+
}
|
|
233
|
+
if (!(0, types_1.isNamedType)(propType) || typeof propValue !== 'object') {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
walk(propValue, propType, (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, (0, ref_utils_1.escapePointer)(propName)));
|
|
264
237
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
yield (0, utils_1.nextTick)();
|
|
273
|
-
const resolvedRef = {
|
|
274
|
-
resolved: true,
|
|
275
|
-
isRemote: false,
|
|
276
|
-
node: anchorRefsMap.get(ref.$ref),
|
|
277
|
-
document,
|
|
278
|
-
nodePointer: ref.$ref,
|
|
279
|
-
};
|
|
280
|
-
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
281
|
-
resolvedRefMap.set(refId, resolvedRef);
|
|
282
|
-
return resolvedRef;
|
|
283
|
-
}
|
|
284
|
-
const { uri, pointer } = (0, ref_utils_1.parseRef)(ref.$ref);
|
|
285
|
-
const isRemote = uri !== null;
|
|
286
|
-
let targetDoc;
|
|
287
|
-
try {
|
|
288
|
-
targetDoc = isRemote
|
|
289
|
-
? (yield externalRefResolver.resolveDocument(document.source.absoluteRef, uri))
|
|
290
|
-
: document;
|
|
238
|
+
if ((0, ref_utils_1.isRef)(node)) {
|
|
239
|
+
const promise = followRef(rootNodeDocument, node, {
|
|
240
|
+
prev: null,
|
|
241
|
+
node,
|
|
242
|
+
}).then((resolvedRef) => {
|
|
243
|
+
if (resolvedRef.resolved) {
|
|
244
|
+
resolveRefsInParallel(resolvedRef.node, resolvedRef.document, resolvedRef.nodePointer, type);
|
|
291
245
|
}
|
|
292
|
-
catch (error) {
|
|
293
|
-
const resolvedRef = {
|
|
294
|
-
resolved: false,
|
|
295
|
-
isRemote,
|
|
296
|
-
document: undefined,
|
|
297
|
-
error: error,
|
|
298
|
-
};
|
|
299
|
-
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
300
|
-
resolvedRefMap.set(refId, resolvedRef);
|
|
301
|
-
return resolvedRef;
|
|
302
|
-
}
|
|
303
|
-
let resolvedRef = {
|
|
304
|
-
resolved: true,
|
|
305
|
-
document: targetDoc,
|
|
306
|
-
isRemote,
|
|
307
|
-
node: document.parsed,
|
|
308
|
-
nodePointer: '#/',
|
|
309
|
-
};
|
|
310
|
-
let target = targetDoc.parsed;
|
|
311
|
-
const segments = pointer;
|
|
312
|
-
for (const segment of segments) {
|
|
313
|
-
if (typeof target !== 'object') {
|
|
314
|
-
target = undefined;
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
317
|
-
else if (target[segment] !== undefined) {
|
|
318
|
-
target = target[segment];
|
|
319
|
-
resolvedRef.nodePointer = (0, ref_utils_1.joinPointer)(resolvedRef.nodePointer, (0, ref_utils_1.escapePointer)(segment));
|
|
320
|
-
}
|
|
321
|
-
else if ((0, ref_utils_1.isRef)(target)) {
|
|
322
|
-
resolvedRef = yield followRef(targetDoc, target, pushRef(refStack, target));
|
|
323
|
-
targetDoc = resolvedRef.document || targetDoc;
|
|
324
|
-
if (typeof resolvedRef.node !== 'object') {
|
|
325
|
-
target = undefined;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
target = resolvedRef.node[segment];
|
|
329
|
-
resolvedRef.nodePointer = (0, ref_utils_1.joinPointer)(resolvedRef.nodePointer, (0, ref_utils_1.escapePointer)(segment));
|
|
330
|
-
}
|
|
331
|
-
else {
|
|
332
|
-
target = undefined;
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
resolvedRef.node = target;
|
|
337
|
-
resolvedRef.document = targetDoc;
|
|
338
|
-
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
339
|
-
if (resolvedRef.document && (0, ref_utils_1.isRef)(target)) {
|
|
340
|
-
resolvedRef = yield followRef(resolvedRef.document, target, pushRef(refStack, target));
|
|
341
|
-
}
|
|
342
|
-
resolvedRefMap.set(refId, resolvedRef);
|
|
343
|
-
return Object.assign({}, resolvedRef);
|
|
344
246
|
});
|
|
247
|
+
resolvePromises.push(promise);
|
|
345
248
|
}
|
|
346
249
|
}
|
|
347
|
-
|
|
250
|
+
async function followRef(document, ref, refStack) {
|
|
251
|
+
if (hasRef(refStack.prev, ref)) {
|
|
252
|
+
throw new Error('Self-referencing circular pointer');
|
|
253
|
+
}
|
|
254
|
+
if ((0, ref_utils_1.isAnchor)(ref.$ref)) {
|
|
255
|
+
// Wait for all anchors in the document to be collected firstly.
|
|
256
|
+
await (0, utils_1.nextTick)();
|
|
257
|
+
const resolvedRef = {
|
|
258
|
+
resolved: true,
|
|
259
|
+
isRemote: false,
|
|
260
|
+
node: anchorRefsMap.get(ref.$ref),
|
|
261
|
+
document,
|
|
262
|
+
nodePointer: ref.$ref,
|
|
263
|
+
};
|
|
264
|
+
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
265
|
+
resolvedRefMap.set(refId, resolvedRef);
|
|
266
|
+
return resolvedRef;
|
|
267
|
+
}
|
|
268
|
+
const { uri, pointer } = (0, ref_utils_1.parseRef)(ref.$ref);
|
|
269
|
+
const isRemote = uri !== null;
|
|
270
|
+
let targetDoc;
|
|
271
|
+
try {
|
|
272
|
+
targetDoc = isRemote
|
|
273
|
+
? (await externalRefResolver.resolveDocument(document.source.absoluteRef, uri))
|
|
274
|
+
: document;
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const resolvedRef = {
|
|
278
|
+
resolved: false,
|
|
279
|
+
isRemote,
|
|
280
|
+
document: undefined,
|
|
281
|
+
error: error,
|
|
282
|
+
};
|
|
283
|
+
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
284
|
+
resolvedRefMap.set(refId, resolvedRef);
|
|
285
|
+
return resolvedRef;
|
|
286
|
+
}
|
|
287
|
+
let resolvedRef = {
|
|
288
|
+
resolved: true,
|
|
289
|
+
document: targetDoc,
|
|
290
|
+
isRemote,
|
|
291
|
+
node: document.parsed,
|
|
292
|
+
nodePointer: '#/',
|
|
293
|
+
};
|
|
294
|
+
let target = targetDoc.parsed;
|
|
295
|
+
const segments = pointer;
|
|
296
|
+
for (const segment of segments) {
|
|
297
|
+
if (typeof target !== 'object') {
|
|
298
|
+
target = undefined;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
else if (target[segment] !== undefined) {
|
|
302
|
+
target = target[segment];
|
|
303
|
+
resolvedRef.nodePointer = (0, ref_utils_1.joinPointer)(resolvedRef.nodePointer, (0, ref_utils_1.escapePointer)(segment));
|
|
304
|
+
}
|
|
305
|
+
else if ((0, ref_utils_1.isRef)(target)) {
|
|
306
|
+
resolvedRef = await followRef(targetDoc, target, pushRef(refStack, target));
|
|
307
|
+
targetDoc = resolvedRef.document || targetDoc;
|
|
308
|
+
if (typeof resolvedRef.node !== 'object') {
|
|
309
|
+
target = undefined;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
target = resolvedRef.node[segment];
|
|
313
|
+
resolvedRef.nodePointer = (0, ref_utils_1.joinPointer)(resolvedRef.nodePointer, (0, ref_utils_1.escapePointer)(segment));
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
target = undefined;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
resolvedRef.node = target;
|
|
321
|
+
resolvedRef.document = targetDoc;
|
|
322
|
+
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
323
|
+
if (resolvedRef.document && (0, ref_utils_1.isRef)(target)) {
|
|
324
|
+
resolvedRef = await followRef(resolvedRef.document, target, pushRef(refStack, target));
|
|
325
|
+
}
|
|
326
|
+
resolvedRefMap.set(refId, resolvedRef);
|
|
327
|
+
return { ...resolvedRef };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
348
330
|
}
|
|
349
|
-
exports.resolveDocument = resolveDocument;
|
package/lib/rules/ajv.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.releaseAjvInstance = releaseAjvInstance;
|
|
4
|
+
exports.validateJsonSchema = validateJsonSchema;
|
|
4
5
|
const _2020_1 = require("@redocly/ajv/dist/2020");
|
|
5
6
|
const ref_utils_1 = require("../ref-utils");
|
|
6
7
|
let ajvInstance = null;
|
|
7
8
|
function releaseAjvInstance() {
|
|
8
9
|
ajvInstance = null;
|
|
9
10
|
}
|
|
10
|
-
exports.releaseAjvInstance = releaseAjvInstance;
|
|
11
11
|
function getAjv(resolve, allowAdditionalProperties) {
|
|
12
12
|
if (!ajvInstance) {
|
|
13
13
|
ajvInstance = new _2020_1.default({
|
|
@@ -19,13 +19,13 @@ function getAjv(resolve, allowAdditionalProperties) {
|
|
|
19
19
|
validateSchema: false,
|
|
20
20
|
discriminator: true,
|
|
21
21
|
allowUnionTypes: true,
|
|
22
|
-
validateFormats: false,
|
|
22
|
+
validateFormats: false, // TODO: fix it
|
|
23
23
|
defaultUnevaluatedProperties: allowAdditionalProperties,
|
|
24
24
|
loadSchemaSync(base, $ref, $id) {
|
|
25
25
|
const resolvedRef = resolve({ $ref }, base.split('#')[0]);
|
|
26
26
|
if (!resolvedRef || !resolvedRef.location)
|
|
27
27
|
return false;
|
|
28
|
-
return
|
|
28
|
+
return { $id: resolvedRef.location.source.absoluteRef + '#' + $id, ...resolvedRef.node };
|
|
29
29
|
},
|
|
30
30
|
logger: false,
|
|
31
31
|
});
|
|
@@ -35,7 +35,7 @@ function getAjv(resolve, allowAdditionalProperties) {
|
|
|
35
35
|
function getAjvValidator(schema, loc, resolve, allowAdditionalProperties) {
|
|
36
36
|
const ajv = getAjv(resolve, allowAdditionalProperties);
|
|
37
37
|
if (!ajv.getSchema(loc.absolutePointer)) {
|
|
38
|
-
ajv.addSchema(
|
|
38
|
+
ajv.addSchema({ $id: loc.absolutePointer, ...schema }, loc.absolutePointer);
|
|
39
39
|
}
|
|
40
40
|
return ajv.getSchema(loc.absolutePointer);
|
|
41
41
|
}
|
|
@@ -73,8 +73,10 @@ function validateJsonSchema(data, schema, schemaLoc, instancePath, resolve, allo
|
|
|
73
73
|
message = `${message} \`${property}\``;
|
|
74
74
|
error.instancePath += '/' + (0, ref_utils_1.escapePointer)(property);
|
|
75
75
|
}
|
|
76
|
-
return
|
|
77
|
-
|
|
76
|
+
return {
|
|
77
|
+
...error,
|
|
78
|
+
message,
|
|
79
|
+
suggest,
|
|
80
|
+
};
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
|
-
exports.validateJsonSchema = validateJsonSchema;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChannelsKebabCase = void 0;
|
|
4
|
+
const ChannelsKebabCase = () => {
|
|
5
|
+
return {
|
|
6
|
+
Channel(channel, { report }) {
|
|
7
|
+
const segments = (channel.address || '')
|
|
8
|
+
.split(/[/.:]/) // split on / or : as likely channel namespacers
|
|
9
|
+
.filter((s) => s !== ''); // filter out empty segments
|
|
10
|
+
if (!segments.every((segment) => /^{.+}$/.test(segment) || /^[a-z0-9-.]+$/.test(segment))) {
|
|
11
|
+
report({
|
|
12
|
+
message: `\`${channel.address}\` does not use kebab-case.`,
|
|
13
|
+
location: { reportOnKey: true },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
exports.ChannelsKebabCase = ChannelsKebabCase;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preprocessors = exports.rules = void 0;
|
|
4
|
+
const assertions_1 = require("../common/assertions");
|
|
5
|
+
const spec_1 = require("../common/spec");
|
|
6
|
+
const info_contact_1 = require("../common/info-contact");
|
|
7
|
+
const operation_operationId_1 = require("../common/operation-operationId");
|
|
8
|
+
const tag_description_1 = require("../common/tag-description");
|
|
9
|
+
const tags_alphabetical_1 = require("../common/tags-alphabetical");
|
|
10
|
+
const channels_kebab_case_1 = require("./channels-kebab-case");
|
|
11
|
+
const no_channel_trailing_slash_1 = require("./no-channel-trailing-slash");
|
|
12
|
+
exports.rules = {
|
|
13
|
+
spec: spec_1.Spec,
|
|
14
|
+
assertions: assertions_1.Assertions,
|
|
15
|
+
'info-contact': info_contact_1.InfoContact,
|
|
16
|
+
'operation-operationId': operation_operationId_1.OperationOperationId,
|
|
17
|
+
'channels-kebab-case': channels_kebab_case_1.ChannelsKebabCase,
|
|
18
|
+
'no-channel-trailing-slash': no_channel_trailing_slash_1.NoChannelTrailingSlash,
|
|
19
|
+
'tag-description': tag_description_1.TagDescription,
|
|
20
|
+
'tags-alphabetical': tags_alphabetical_1.TagsAlphabetical,
|
|
21
|
+
};
|
|
22
|
+
exports.preprocessors = {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoChannelTrailingSlash = void 0;
|
|
4
|
+
const NoChannelTrailingSlash = () => {
|
|
5
|
+
return {
|
|
6
|
+
Channel(channel, { report, location }) {
|
|
7
|
+
if (channel.address.endsWith('/') && channel.address !== '/') {
|
|
8
|
+
report({
|
|
9
|
+
message: `\`${channel.address}\` should not have a trailing slash.`,
|
|
10
|
+
location: location.key(),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.NoChannelTrailingSlash = NoChannelTrailingSlash;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.asserts = exports.runOnValuesSet = exports.runOnKeysSet = void 0;
|
|
4
|
+
exports.buildAssertCustomFunction = buildAssertCustomFunction;
|
|
4
5
|
const utils_1 = require("../../../utils");
|
|
5
6
|
const utils_2 = require("./utils");
|
|
6
7
|
exports.runOnKeysSet = new Set([
|
|
@@ -41,7 +42,7 @@ exports.asserts = {
|
|
|
41
42
|
const values = Array.isArray(value) ? value : [value];
|
|
42
43
|
const regex = (0, utils_2.regexFromString)(condition);
|
|
43
44
|
return values
|
|
44
|
-
.map((_val) => !
|
|
45
|
+
.map((_val) => !regex?.test(_val) && {
|
|
45
46
|
message: `"${_val}" should match a regex ${condition}`,
|
|
46
47
|
location: (0, utils_1.isString)(value)
|
|
47
48
|
? baseLocation
|
|
@@ -57,7 +58,7 @@ exports.asserts = {
|
|
|
57
58
|
const values = Array.isArray(value) ? value : [value];
|
|
58
59
|
const regex = (0, utils_2.regexFromString)(condition);
|
|
59
60
|
return values
|
|
60
|
-
.map((_val) =>
|
|
61
|
+
.map((_val) => regex?.test(_val) && {
|
|
61
62
|
message: `"${_val}" should not match a regex ${condition}`,
|
|
62
63
|
location: (0, utils_1.isString)(value)
|
|
63
64
|
? baseLocation
|
|
@@ -264,7 +265,7 @@ exports.asserts = {
|
|
|
264
265
|
];
|
|
265
266
|
}
|
|
266
267
|
const regex = (0, utils_2.regexFromString)(condition);
|
|
267
|
-
const isValid = hasRef &&
|
|
268
|
+
const isValid = hasRef && regex?.test(rawValue['$ref']);
|
|
268
269
|
return isValid
|
|
269
270
|
? []
|
|
270
271
|
: [
|
|
@@ -278,4 +279,3 @@ exports.asserts = {
|
|
|
278
279
|
function buildAssertCustomFunction(fn) {
|
|
279
280
|
return (value, options, ctx) => fn.call(null, value, options, ctx);
|
|
280
281
|
}
|
|
281
|
-
exports.buildAssertCustomFunction = buildAssertCustomFunction;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { asserts
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { asserts } from './asserts';
|
|
2
|
+
import type { AssertionFn } from './asserts';
|
|
3
|
+
import type { ArazzoVisitor, Async2Visitor, Async3Visitor, Oas2Visitor, Oas3Visitor } from '../../../visitors';
|
|
4
|
+
import type { RuleSeverity } from '../../../config';
|
|
4
5
|
export type AssertionLocators = {
|
|
5
6
|
filterInParentKeys?: (string | number)[];
|
|
6
7
|
filterOutParentKeys?: (string | number)[];
|
|
@@ -24,4 +25,4 @@ export type RawAssertion = AssertionDefinition & {
|
|
|
24
25
|
export type Assertion = RawAssertion & {
|
|
25
26
|
assertionId: string;
|
|
26
27
|
};
|
|
27
|
-
export declare const Assertions: (opts: Record<string, Assertion>) => (Oas3Visitor | Oas2Visitor)[];
|
|
28
|
+
export declare const Assertions: (opts: Record<string, Assertion>) => (Oas3Visitor | Oas2Visitor | Async2Visitor | Async3Visitor | ArazzoVisitor)[];
|