@redocly/openapi-core 1.18.0 → 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 +20 -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 +3 -3
- package/lib/bundle.js +128 -122
- 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.d.ts +1 -2
- 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/path-segment-plural.js +2 -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/arazzo.d.ts +1299 -73
- package/lib/types/arazzo.js +41 -36
- package/lib/types/asyncapi2.d.ts +17 -0
- package/lib/types/{asyncapi.js → asyncapi2.js} +71 -93
- 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 +15 -9
- package/lib/types/redocly-yaml.d.ts +3 -1
- package/lib/types/redocly-yaml.js +131 -35
- package/lib/typings/arazzo.d.ts +28 -1
- package/lib/typings/asyncapi3.d.ts +53 -0
- package/lib/typings/asyncapi3.js +2 -0
- package/lib/utils.d.ts +12 -7
- package/lib/utils.js +91 -77
- package/lib/visitors.d.ts +11 -0
- package/lib/visitors.js +21 -8
- package/lib/walk.js +30 -23
- package/package.json +3 -3
- package/src/__tests__/bundle.test.ts +142 -0
- package/src/__tests__/lint.test.ts +0 -50
- package/src/bundle.ts +19 -6
- 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/load.ts +1 -2
- 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/__tests__/spec.test.ts +47 -0
- package/src/rules/common/assertions/index.ts +13 -4
- package/src/rules/common/no-invalid-schema-examples.ts +3 -2
- package/src/rules/common/path-segment-plural.ts +3 -2
- 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/arazzo.ts +28 -23
- package/src/types/{asyncapi.ts → asyncapi2.ts} +58 -76
- package/src/types/asyncapi3.ts +381 -0
- package/src/types/oas3_1.ts +3 -2
- package/src/types/redocly-yaml.ts +14 -0
- package/src/typings/arazzo.ts +41 -1
- package/src/typings/asyncapi3.ts +61 -0
- package/src/utils.ts +46 -11
- package/src/visitors.ts +18 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/types/asyncapi.d.ts +0 -2
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getAssertsToApply = getAssertsToApply;
|
|
4
|
+
exports.buildVisitorObject = buildVisitorObject;
|
|
5
|
+
exports.buildSubjectVisitor = buildSubjectVisitor;
|
|
6
|
+
exports.getIntersectionLength = getIntersectionLength;
|
|
7
|
+
exports.isOrdered = isOrdered;
|
|
8
|
+
exports.runAssertion = runAssertion;
|
|
9
|
+
exports.regexFromString = regexFromString;
|
|
4
10
|
const asserts_1 = require("./asserts");
|
|
5
11
|
const logger_1 = require("../../../logger");
|
|
6
12
|
const ref_utils_1 = require("../../../ref-utils");
|
|
@@ -39,9 +45,8 @@ function getAssertsToApply(assertion) {
|
|
|
39
45
|
}
|
|
40
46
|
return assertsToApply;
|
|
41
47
|
}
|
|
42
|
-
exports.getAssertsToApply = getAssertsToApply;
|
|
43
48
|
function getAssertionProperties({ subject }) {
|
|
44
|
-
return (Array.isArray(subject.property) ? subject.property : [subject
|
|
49
|
+
return (Array.isArray(subject.property) ? subject.property : [subject?.property]).filter(Boolean);
|
|
45
50
|
}
|
|
46
51
|
function applyAssertions(assertionDefinition, asserts, ctx) {
|
|
47
52
|
const properties = getAssertionProperties(assertionDefinition);
|
|
@@ -66,13 +71,15 @@ function applyAssertions(assertionDefinition, asserts, ctx) {
|
|
|
66
71
|
return assertResults.flat();
|
|
67
72
|
}
|
|
68
73
|
function buildVisitorObject(assertion, subjectVisitor) {
|
|
69
|
-
var _a, _b;
|
|
70
74
|
const targetVisitorLocatorPredicates = getPredicatesFromLocators(assertion.subject);
|
|
71
75
|
const targetVisitorSkipFunction = targetVisitorLocatorPredicates.length
|
|
72
76
|
? (_, key) => !targetVisitorLocatorPredicates.every((predicate) => predicate(key))
|
|
73
77
|
: undefined;
|
|
74
78
|
const targetVisitor = {
|
|
75
|
-
[assertion.subject.type]:
|
|
79
|
+
[assertion.subject.type]: {
|
|
80
|
+
enter: subjectVisitor,
|
|
81
|
+
...(targetVisitorSkipFunction && { skip: targetVisitorSkipFunction }),
|
|
82
|
+
},
|
|
76
83
|
};
|
|
77
84
|
if (!Array.isArray(assertion.where)) {
|
|
78
85
|
return targetVisitor;
|
|
@@ -82,37 +89,44 @@ function buildVisitorObject(assertion, subjectVisitor) {
|
|
|
82
89
|
const context = assertion.where;
|
|
83
90
|
for (let index = 0; index < context.length; index++) {
|
|
84
91
|
const assertionDefinitionNode = context[index];
|
|
85
|
-
if (!(0, utils_1.isString)(
|
|
92
|
+
if (!(0, utils_1.isString)(assertionDefinitionNode.subject?.type)) {
|
|
86
93
|
throw new Error(`${assertion.assertionId} -> where -> [${index}]: 'type' (String) is required`);
|
|
87
94
|
}
|
|
88
95
|
const locatorPredicates = getPredicatesFromLocators(assertionDefinitionNode.subject);
|
|
89
96
|
const assertsToApply = getAssertsToApply(assertionDefinitionNode);
|
|
90
97
|
const skipFunction = (node, key, ctx) => !locatorPredicates.every((predicate) => predicate(key)) ||
|
|
91
|
-
!!applyAssertions(assertionDefinitionNode, assertsToApply,
|
|
92
|
-
const nodeVisitor =
|
|
98
|
+
!!applyAssertions(assertionDefinitionNode, assertsToApply, { ...ctx, node }).length;
|
|
99
|
+
const nodeVisitor = {
|
|
100
|
+
...((locatorPredicates.length || assertsToApply.length) && { skip: skipFunction }),
|
|
101
|
+
};
|
|
93
102
|
if (assertionDefinitionNode.subject.type === assertion.subject.type &&
|
|
94
103
|
index === context.length - 1) {
|
|
95
104
|
// We have to merge the visitors if the last node inside the `where` is the same as the subject.
|
|
96
|
-
targetVisitor[assertion.subject.type] =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
targetVisitor[assertion.subject.type] = {
|
|
106
|
+
enter: subjectVisitor,
|
|
107
|
+
...((nodeVisitor.skip && { skip: nodeVisitor.skip }) ||
|
|
108
|
+
(targetVisitorSkipFunction && {
|
|
109
|
+
skip: (node, key, ctx // We may have locators defined on assertion level and on where level for the same node type
|
|
110
|
+
) => !!(nodeVisitor.skip?.(node, key, ctx) || targetVisitorSkipFunction?.(node, key)),
|
|
111
|
+
})),
|
|
112
|
+
};
|
|
101
113
|
}
|
|
102
114
|
else {
|
|
103
|
-
currentVisitorLevel = currentVisitorLevel[
|
|
115
|
+
currentVisitorLevel = currentVisitorLevel[assertionDefinitionNode.subject?.type] =
|
|
104
116
|
nodeVisitor;
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
currentVisitorLevel[assertion.subject.type] = targetVisitor[assertion.subject.type];
|
|
108
120
|
return visitor;
|
|
109
121
|
}
|
|
110
|
-
exports.buildVisitorObject = buildVisitorObject;
|
|
111
122
|
function buildSubjectVisitor(assertId, assertion) {
|
|
112
123
|
return (node, ctx) => {
|
|
113
124
|
const properties = getAssertionProperties(assertion);
|
|
114
125
|
const defaultMessage = `${logger_1.colorize.blue(assertId)} failed because the ${logger_1.colorize.blue(assertion.subject.type)} ${logger_1.colorize.blue(properties.join(', '))} didn't meet the assertions: ${assertionMessageTemplates.problems}`.replace(/ +/g, ' ');
|
|
115
|
-
const problems = applyAssertions(assertion, getAssertsToApply(assertion),
|
|
126
|
+
const problems = applyAssertions(assertion, getAssertsToApply(assertion), {
|
|
127
|
+
...ctx,
|
|
128
|
+
node,
|
|
129
|
+
});
|
|
116
130
|
if (problems.length) {
|
|
117
131
|
for (const problemGroup of groupProblemsByPointer(problems)) {
|
|
118
132
|
const message = assertion.message || defaultMessage;
|
|
@@ -128,7 +142,6 @@ function buildSubjectVisitor(assertId, assertion) {
|
|
|
128
142
|
}
|
|
129
143
|
};
|
|
130
144
|
}
|
|
131
|
-
exports.buildSubjectVisitor = buildSubjectVisitor;
|
|
132
145
|
function groupProblemsByPointer(problems) {
|
|
133
146
|
const groups = {};
|
|
134
147
|
for (const problem of problems) {
|
|
@@ -144,10 +157,9 @@ function getProblemsLocation(problems) {
|
|
|
144
157
|
return problems.length ? problems[0].location : undefined;
|
|
145
158
|
}
|
|
146
159
|
function getProblemsMessage(problems) {
|
|
147
|
-
var _a;
|
|
148
160
|
return problems.length === 1
|
|
149
|
-
?
|
|
150
|
-
: problems.map((problem) =>
|
|
161
|
+
? problems[0].message ?? ''
|
|
162
|
+
: problems.map((problem) => `\n- ${problem.message ?? ''}`).join('');
|
|
151
163
|
}
|
|
152
164
|
function getIntersectionLength(keys, properties) {
|
|
153
165
|
const props = new Set(properties);
|
|
@@ -159,7 +171,6 @@ function getIntersectionLength(keys, properties) {
|
|
|
159
171
|
}
|
|
160
172
|
return count;
|
|
161
173
|
}
|
|
162
|
-
exports.getIntersectionLength = getIntersectionLength;
|
|
163
174
|
function isOrdered(value, options) {
|
|
164
175
|
const direction = options.direction || options;
|
|
165
176
|
const property = options.property;
|
|
@@ -186,26 +197,30 @@ function isOrdered(value, options) {
|
|
|
186
197
|
}
|
|
187
198
|
return true;
|
|
188
199
|
}
|
|
189
|
-
exports.isOrdered = isOrdered;
|
|
190
200
|
function runAssertion({ assert, ctx, assertionProperty, }) {
|
|
191
|
-
var _a;
|
|
192
201
|
const currentLocation = assert.name === 'ref' ? ctx.rawLocation : ctx.location;
|
|
193
202
|
if (assertionProperty) {
|
|
194
203
|
const values = (0, ref_utils_1.isRef)(ctx.node[assertionProperty])
|
|
195
|
-
?
|
|
204
|
+
? ctx.resolve(ctx.node[assertionProperty])?.node
|
|
196
205
|
: ctx.node[assertionProperty];
|
|
197
206
|
const rawValues = ctx.rawNode[assertionProperty];
|
|
198
207
|
const location = currentLocation.child(assertionProperty);
|
|
199
|
-
return asserts_1.asserts[assert.name](values, assert.conditions,
|
|
208
|
+
return asserts_1.asserts[assert.name](values, assert.conditions, {
|
|
209
|
+
...ctx,
|
|
210
|
+
baseLocation: location,
|
|
211
|
+
rawValue: rawValues,
|
|
212
|
+
});
|
|
200
213
|
}
|
|
201
214
|
else {
|
|
202
215
|
const value = Array.isArray(ctx.node) ? ctx.node : Object.keys(ctx.node);
|
|
203
|
-
return asserts_1.asserts[assert.name](value, assert.conditions,
|
|
216
|
+
return asserts_1.asserts[assert.name](value, assert.conditions, {
|
|
217
|
+
...ctx,
|
|
218
|
+
rawValue: ctx.rawNode,
|
|
219
|
+
baseLocation: currentLocation,
|
|
220
|
+
});
|
|
204
221
|
}
|
|
205
222
|
}
|
|
206
|
-
exports.runAssertion = runAssertion;
|
|
207
223
|
function regexFromString(input) {
|
|
208
224
|
const matches = input.match(/^\/(.*)\/(.*)|(.*)/);
|
|
209
225
|
return matches && new RegExp(matches[1] || matches[3], matches[2]);
|
|
210
226
|
}
|
|
211
|
-
exports.regexFromString = regexFromString;
|
|
@@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NoInvalidParameterExamples = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const NoInvalidParameterExamples = (opts) => {
|
|
6
|
-
|
|
7
|
-
const allowAdditionalProperties = (_a = (0, utils_1.getAdditionalPropertiesOption)(opts)) !== null && _a !== void 0 ? _a : false;
|
|
6
|
+
const allowAdditionalProperties = (0, utils_1.getAdditionalPropertiesOption)(opts) ?? false;
|
|
8
7
|
return {
|
|
9
8
|
Parameter: {
|
|
10
9
|
leave(parameter, ctx) {
|
|
@@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NoInvalidSchemaExamples = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const NoInvalidSchemaExamples = (opts) => {
|
|
6
|
-
|
|
7
|
-
const allowAdditionalProperties = (_a = (0, utils_1.getAdditionalPropertiesOption)(opts)) !== null && _a !== void 0 ? _a : false;
|
|
6
|
+
const allowAdditionalProperties = (0, utils_1.getAdditionalPropertiesOption)(opts) ?? false;
|
|
8
7
|
return {
|
|
9
8
|
Schema: {
|
|
10
9
|
leave(schema, ctx) {
|
|
@@ -10,7 +10,6 @@ const NoRequiredSchemaPropertiesUndefined = () => {
|
|
|
10
10
|
return;
|
|
11
11
|
const visitedSchemas = new Set();
|
|
12
12
|
const elevateProperties = (schema) => {
|
|
13
|
-
var _a, _b, _c, _d;
|
|
14
13
|
// Check if the schema has been visited before processing it
|
|
15
14
|
if (visitedSchemas.has(schema)) {
|
|
16
15
|
return {};
|
|
@@ -19,7 +18,7 @@ const NoRequiredSchemaPropertiesUndefined = () => {
|
|
|
19
18
|
if ((0, ref_utils_1.isRef)(schema)) {
|
|
20
19
|
return elevateProperties(resolve(schema).node);
|
|
21
20
|
}
|
|
22
|
-
return Object.assign({}, schema.properties, ...(
|
|
21
|
+
return Object.assign({}, schema.properties, ...(schema.allOf?.map(elevateProperties) ?? []), ...(schema.anyOf?.map(elevateProperties) ?? []));
|
|
23
22
|
};
|
|
24
23
|
const allProperties = elevateProperties(schema);
|
|
25
24
|
for (const [i, requiredProperty] of schema.required.entries()) {
|
|
@@ -5,8 +5,7 @@ const OperationTagDefined = () => {
|
|
|
5
5
|
let definedTags;
|
|
6
6
|
return {
|
|
7
7
|
Root(root) {
|
|
8
|
-
|
|
9
|
-
definedTags = new Set(((_a = root.tags) !== null && _a !== void 0 ? _a : []).map((t) => t.name));
|
|
8
|
+
definedTags = new Set((root.tags ?? []).map((t) => t.name));
|
|
10
9
|
},
|
|
11
10
|
Operation(operation, { report, location }) {
|
|
12
11
|
if (operation.tags) {
|
|
@@ -16,7 +16,7 @@ const PathHttpVerbsOrder = (opts) => {
|
|
|
16
16
|
if (bIdx < aIdx) {
|
|
17
17
|
report({
|
|
18
18
|
message: 'Operation http verbs must be ordered.',
|
|
19
|
-
location:
|
|
19
|
+
location: { reportOnKey: true, ...location.child(httpVerbs[i + 1]) },
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PathSegmentPlural = void 0;
|
|
4
|
+
const pluralize = require("pluralize");
|
|
4
5
|
const utils_1 = require("../../utils");
|
|
5
6
|
const PathSegmentPlural = (opts) => {
|
|
6
7
|
const { ignoreLastPathSegment, exceptions } = opts;
|
|
@@ -17,7 +18,7 @@ const PathSegmentPlural = (opts) => {
|
|
|
17
18
|
for (const pathSegment of pathSegments) {
|
|
18
19
|
if (exceptions && exceptions.includes(pathSegment))
|
|
19
20
|
continue;
|
|
20
|
-
if (!(0, utils_1.isPathParameter)(pathSegment) &&
|
|
21
|
+
if (!(0, utils_1.isPathParameter)(pathSegment) && pluralize.isSingular(pathSegment)) {
|
|
21
22
|
report({
|
|
22
23
|
message: `path segment \`${pathSegment}\` should be plural.`,
|
|
23
24
|
location: location.key(),
|
|
@@ -7,7 +7,7 @@ const RequiredStringPropertyMissingMinLength = () => {
|
|
|
7
7
|
return {
|
|
8
8
|
Schema: {
|
|
9
9
|
enter(schema) {
|
|
10
|
-
if (!
|
|
10
|
+
if (!schema?.required) {
|
|
11
11
|
skipSchemaProperties = true;
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
@@ -21,7 +21,7 @@ const RequiredStringPropertyMissingMinLength = () => {
|
|
|
21
21
|
Schema: {
|
|
22
22
|
enter(schema, { key, location, report }) {
|
|
23
23
|
if (requiredPropertiesSet.has(key) && schema.type === 'string') {
|
|
24
|
-
if (!
|
|
24
|
+
if (!schema?.minLength) {
|
|
25
25
|
report({
|
|
26
26
|
message: 'Property minLength is required.',
|
|
27
27
|
location: location.key(),
|
|
@@ -13,8 +13,8 @@ const ResponseContainsHeader = (options) => {
|
|
|
13
13
|
names[(0, utils_1.getMatchingStatusCodeRange)(key).toLowerCase()] ||
|
|
14
14
|
[];
|
|
15
15
|
for (const expectedHeader of expectedHeaders) {
|
|
16
|
-
if (!
|
|
17
|
-
!Object.keys(response
|
|
16
|
+
if (!response?.headers ||
|
|
17
|
+
!Object.keys(response?.headers).some((header) => header.toLowerCase() === expectedHeader.toLowerCase())) {
|
|
18
18
|
report({
|
|
19
19
|
message: `Response object must contain a "${expectedHeader}" header.`,
|
|
20
20
|
location: location.child('headers').key(),
|
|
@@ -52,13 +52,9 @@ const SecurityDefined = (opts) => {
|
|
|
52
52
|
path = key;
|
|
53
53
|
},
|
|
54
54
|
Operation(operation, { location, key }) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return item.path === path &&
|
|
59
|
-
(!item.methods || ((_a = item.methods) === null || _a === void 0 ? void 0 : _a.some((method) => method.toLowerCase() === key)));
|
|
60
|
-
});
|
|
61
|
-
if (!(operation === null || operation === void 0 ? void 0 : operation.security) && !isException) {
|
|
55
|
+
const isException = opts.exceptions?.some((item) => item.path === path &&
|
|
56
|
+
(!item.methods || item.methods?.some((method) => method.toLowerCase() === key)));
|
|
57
|
+
if (!operation?.security && !isException) {
|
|
62
58
|
eachOperationHasSecurity = false;
|
|
63
59
|
operationsWithoutSecurity.push(location);
|
|
64
60
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Oas3Rule, Oas2Rule, Async2Rule, ArazzoRule } from '../../visitors';
|
|
2
|
-
export declare const Spec: Oas3Rule | Oas2Rule | Async2Rule | ArazzoRule;
|
|
1
|
+
import type { Oas3Rule, Oas2Rule, Async2Rule, Async3Rule, ArazzoRule } from '../../visitors';
|
|
2
|
+
export declare const Spec: Oas3Rule | Oas2Rule | Async2Rule | Async3Rule | ArazzoRule;
|
package/lib/rules/common/spec.js
CHANGED
|
@@ -8,7 +8,6 @@ const utils_2 = require("../../utils");
|
|
|
8
8
|
const Spec = () => {
|
|
9
9
|
return {
|
|
10
10
|
any(node, { report, type, location, rawLocation, key, resolve, ignoreNextVisitorsOnNode }) {
|
|
11
|
-
var _a, _b, _c, _d, _e, _f;
|
|
12
11
|
const nodeType = (0, utils_1.oasTypeOf)(node);
|
|
13
12
|
const refLocation = rawLocation !== location ? rawLocation : undefined;
|
|
14
13
|
if (type.items) {
|
|
@@ -42,7 +41,7 @@ const Spec = () => {
|
|
|
42
41
|
});
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
|
-
const allowed =
|
|
44
|
+
const allowed = type.allowed?.(node);
|
|
46
45
|
if (allowed && (0, utils_2.isPlainObject)(node)) {
|
|
47
46
|
for (const propName in node) {
|
|
48
47
|
if (allowed.includes(propName) ||
|
|
@@ -67,7 +66,7 @@ const Spec = () => {
|
|
|
67
66
|
}
|
|
68
67
|
if (!hasProperty)
|
|
69
68
|
report({
|
|
70
|
-
message: `Must contain at least one of the following fields: ${
|
|
69
|
+
message: `Must contain at least one of the following fields: ${type.requiredOneOf?.join(', ')}.`,
|
|
71
70
|
from: refLocation,
|
|
72
71
|
location: [{ reportOnKey: true }],
|
|
73
72
|
});
|
|
@@ -102,9 +101,9 @@ const Spec = () => {
|
|
|
102
101
|
if (propSchema.resolvable !== false && (0, ref_utils_1.isRef)(propValue)) {
|
|
103
102
|
propValue = resolve(propValue).node;
|
|
104
103
|
}
|
|
105
|
-
if (propSchema.items &&
|
|
104
|
+
if (propSchema.items && propSchema.items?.enum && Array.isArray(propValue)) {
|
|
106
105
|
for (let i = 0; i < propValue.length; i++) {
|
|
107
|
-
(0, utils_1.validateSchemaEnumType)(
|
|
106
|
+
(0, utils_1.validateSchemaEnumType)(propSchema.items?.enum, propValue[i], propName, refLocation, {
|
|
108
107
|
report,
|
|
109
108
|
location: location.child([propName, i]),
|
|
110
109
|
});
|
|
@@ -124,8 +123,8 @@ const Spec = () => {
|
|
|
124
123
|
});
|
|
125
124
|
ignoreNextVisitorsOnNode();
|
|
126
125
|
}
|
|
127
|
-
else if (propValueType === 'array' &&
|
|
128
|
-
const itemsType =
|
|
126
|
+
else if (propValueType === 'array' && propSchema.items?.type) {
|
|
127
|
+
const itemsType = propSchema.items?.type;
|
|
129
128
|
for (let i = 0; i < propValue.length; i++) {
|
|
130
129
|
const item = propValue[i];
|
|
131
130
|
if (!(0, utils_1.matchesJsonSchemaType)(item, itemsType, false)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.NoUnresolvedRefs = void 0;
|
|
4
|
+
exports.reportUnresolvedRef = reportUnresolvedRef;
|
|
4
5
|
const resolve_1 = require("../resolve");
|
|
5
6
|
const NoUnresolvedRefs = () => {
|
|
6
7
|
return {
|
|
@@ -23,7 +24,6 @@ const NoUnresolvedRefs = () => {
|
|
|
23
24
|
};
|
|
24
25
|
exports.NoUnresolvedRefs = NoUnresolvedRefs;
|
|
25
26
|
function reportUnresolvedRef(resolved, report, location) {
|
|
26
|
-
var _a;
|
|
27
27
|
const error = resolved.error;
|
|
28
28
|
if (error instanceof resolve_1.YamlParseError) {
|
|
29
29
|
report({
|
|
@@ -38,10 +38,9 @@ function reportUnresolvedRef(resolved, report, location) {
|
|
|
38
38
|
},
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
const message =
|
|
41
|
+
const message = resolved.error?.message;
|
|
42
42
|
report({
|
|
43
43
|
location,
|
|
44
44
|
message: `Can't resolve $ref${message ? ': ' + message : ''}`,
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
-
exports.reportUnresolvedRef = reportUnresolvedRef;
|
|
@@ -15,7 +15,6 @@ const ResponseContainsProperty = (options) => {
|
|
|
15
15
|
key = ctx.key;
|
|
16
16
|
},
|
|
17
17
|
Schema(schema, { report, location }) {
|
|
18
|
-
var _a;
|
|
19
18
|
if (schema.type !== 'object')
|
|
20
19
|
return;
|
|
21
20
|
const expectedProperties = names[key] ||
|
|
@@ -23,7 +22,7 @@ const ResponseContainsProperty = (options) => {
|
|
|
23
22
|
names[(0, utils_1.getMatchingStatusCodeRange)(key).toLowerCase()] ||
|
|
24
23
|
[];
|
|
25
24
|
for (const expectedProperty of expectedProperties) {
|
|
26
|
-
if (!
|
|
25
|
+
if (!schema.properties?.[expectedProperty]) {
|
|
27
26
|
report({
|
|
28
27
|
message: `Response object must contain a top-level "${expectedProperty}" property.`,
|
|
29
28
|
location: location.child('properties').key(),
|
|
@@ -24,8 +24,7 @@ const ArrayParameterSerialization = (options) => {
|
|
|
24
24
|
};
|
|
25
25
|
exports.ArrayParameterSerialization = ArrayParameterSerialization;
|
|
26
26
|
function shouldReportMissingStyleAndExplode(node, schema, options) {
|
|
27
|
-
var _a;
|
|
28
27
|
return ((schema.type === 'array' || schema.items || schema.prefixItems) &&
|
|
29
28
|
(node.style === undefined || node.explode === undefined) &&
|
|
30
|
-
(!options.in || (node.in &&
|
|
29
|
+
(!options.in || (node.in && options.in?.includes(node.in))));
|
|
31
30
|
}
|
|
@@ -99,9 +99,8 @@ const ComponentNameUnique = (options) => {
|
|
|
99
99
|
return componentName;
|
|
100
100
|
}
|
|
101
101
|
function addFoundComponent(typeName, componentName, absoluteLocation) {
|
|
102
|
-
var _a;
|
|
103
102
|
const key = getKeyForComponent(typeName, componentName);
|
|
104
|
-
const locations =
|
|
103
|
+
const locations = components.get(key) ?? new Set();
|
|
105
104
|
locations.add(absoluteLocation);
|
|
106
105
|
components.set(key, locations);
|
|
107
106
|
}
|
|
@@ -112,8 +111,7 @@ const ComponentNameUnique = (options) => {
|
|
|
112
111
|
};
|
|
113
112
|
exports.ComponentNameUnique = ComponentNameUnique;
|
|
114
113
|
function getOptionComponentNameForTypeName(typeName) {
|
|
115
|
-
|
|
116
|
-
return (_a = TYPE_NAME_TO_OPTION_COMPONENT_NAME[typeName]) !== null && _a !== void 0 ? _a : null;
|
|
114
|
+
return TYPE_NAME_TO_OPTION_COMPONENT_NAME[typeName] ?? null;
|
|
117
115
|
}
|
|
118
116
|
function getKeyForComponent(typeName, componentName) {
|
|
119
117
|
return `${typeName}/${componentName}`;
|
|
@@ -4,8 +4,7 @@ exports.ValidContentExamples = void 0;
|
|
|
4
4
|
const ref_utils_1 = require("../../ref-utils");
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
const ValidContentExamples = (opts) => {
|
|
7
|
-
|
|
8
|
-
const allowAdditionalProperties = (_a = (0, utils_1.getAdditionalPropertiesOption)(opts)) !== null && _a !== void 0 ? _a : false;
|
|
7
|
+
const allowAdditionalProperties = (0, utils_1.getAdditionalPropertiesOption)(opts) ?? false;
|
|
9
8
|
return {
|
|
10
9
|
MediaType: {
|
|
11
10
|
leave(mediaType, ctx) {
|
|
@@ -45,7 +45,6 @@ const NoServerVariablesEmptyEnum = () => {
|
|
|
45
45
|
};
|
|
46
46
|
exports.NoServerVariablesEmptyEnum = NoServerVariablesEmptyEnum;
|
|
47
47
|
function checkEnumVariables(server) {
|
|
48
|
-
var _a;
|
|
49
48
|
if (server.variables && Object.keys(server.variables).length === 0)
|
|
50
49
|
return;
|
|
51
50
|
const errors = [];
|
|
@@ -53,7 +52,7 @@ function checkEnumVariables(server) {
|
|
|
53
52
|
const serverVariable = server.variables[variable];
|
|
54
53
|
if (!serverVariable.enum)
|
|
55
54
|
continue;
|
|
56
|
-
if (Array.isArray(serverVariable.enum) &&
|
|
55
|
+
if (Array.isArray(serverVariable.enum) && serverVariable.enum?.length === 0)
|
|
57
56
|
errors.push(enumError.empty);
|
|
58
57
|
if (!serverVariable.default)
|
|
59
58
|
continue;
|
|
@@ -4,11 +4,10 @@ exports.NoUndefinedServerVariable = void 0;
|
|
|
4
4
|
const NoUndefinedServerVariable = () => {
|
|
5
5
|
return {
|
|
6
6
|
Server(server, { report, location }) {
|
|
7
|
-
var _a;
|
|
8
7
|
if (!server.url)
|
|
9
8
|
return;
|
|
10
|
-
const urlVariables =
|
|
11
|
-
const definedVariables = (
|
|
9
|
+
const urlVariables = server.url.match(/{[^}]+}/g)?.map((e) => e.slice(1, e.length - 1)) || [];
|
|
10
|
+
const definedVariables = (server?.variables && Object.keys(server.variables)) || [];
|
|
12
11
|
for (const serverVar of urlVariables) {
|
|
13
12
|
if (!definedVariables.includes(serverVar)) {
|
|
14
13
|
report({
|
|
@@ -4,9 +4,8 @@ exports.NoUnusedComponents = void 0;
|
|
|
4
4
|
const NoUnusedComponents = () => {
|
|
5
5
|
const components = new Map();
|
|
6
6
|
function registerComponent(location, name) {
|
|
7
|
-
var _a;
|
|
8
7
|
components.set(location.absolutePointer, {
|
|
9
|
-
used:
|
|
8
|
+
used: components.get(location.absolutePointer)?.used || false,
|
|
10
9
|
location,
|
|
11
10
|
name,
|
|
12
11
|
});
|
|
@@ -16,7 +16,6 @@ const ResponseContainsProperty = (options) => {
|
|
|
16
16
|
},
|
|
17
17
|
MediaType: {
|
|
18
18
|
Schema(schema, { report, location }) {
|
|
19
|
-
var _a;
|
|
20
19
|
if (schema.type !== 'object')
|
|
21
20
|
return;
|
|
22
21
|
const expectedProperties = names[key] ||
|
|
@@ -24,7 +23,7 @@ const ResponseContainsProperty = (options) => {
|
|
|
24
23
|
names[(0, utils_1.getMatchingStatusCodeRange)(key).toLowerCase()] ||
|
|
25
24
|
[];
|
|
26
25
|
for (const expectedProperty of expectedProperties) {
|
|
27
|
-
if (!
|
|
26
|
+
if (!schema.properties?.[expectedProperty]) {
|
|
28
27
|
report({
|
|
29
28
|
message: `Response object must contain a top-level "${expectedProperty}" property.`,
|
|
30
29
|
location: location.child('properties').key(),
|
package/lib/rules/utils.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.oasTypeOf = oasTypeOf;
|
|
4
|
+
exports.matchesJsonSchemaType = matchesJsonSchemaType;
|
|
5
|
+
exports.missingRequiredField = missingRequiredField;
|
|
6
|
+
exports.fieldNonEmpty = fieldNonEmpty;
|
|
7
|
+
exports.validateDefinedAndNonEmpty = validateDefinedAndNonEmpty;
|
|
8
|
+
exports.getSuggest = getSuggest;
|
|
9
|
+
exports.validateExample = validateExample;
|
|
10
|
+
exports.getAdditionalPropertiesOption = getAdditionalPropertiesOption;
|
|
11
|
+
exports.validateSchemaEnumType = validateSchemaEnumType;
|
|
12
|
+
exports.validateResponseCodes = validateResponseCodes;
|
|
4
13
|
const levenshtein = require("js-levenshtein");
|
|
5
14
|
const ref_utils_1 = require("../ref-utils");
|
|
6
15
|
const ajv_1 = require("./ajv");
|
|
@@ -19,7 +28,6 @@ function oasTypeOf(value) {
|
|
|
19
28
|
return typeof value;
|
|
20
29
|
}
|
|
21
30
|
}
|
|
22
|
-
exports.oasTypeOf = oasTypeOf;
|
|
23
31
|
/**
|
|
24
32
|
* Checks if value matches specified JSON schema type
|
|
25
33
|
*
|
|
@@ -44,15 +52,12 @@ function matchesJsonSchemaType(value, type, nullable) {
|
|
|
44
52
|
return typeof value === type;
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
|
-
exports.matchesJsonSchemaType = matchesJsonSchemaType;
|
|
48
55
|
function missingRequiredField(type, field) {
|
|
49
56
|
return `${type} object should contain \`${field}\` field.`;
|
|
50
57
|
}
|
|
51
|
-
exports.missingRequiredField = missingRequiredField;
|
|
52
58
|
function fieldNonEmpty(type, field) {
|
|
53
59
|
return `${type} object \`${field}\` must be non-empty string.`;
|
|
54
60
|
}
|
|
55
|
-
exports.fieldNonEmpty = fieldNonEmpty;
|
|
56
61
|
function validateDefinedAndNonEmpty(fieldName, value, ctx) {
|
|
57
62
|
if (typeof value !== 'object') {
|
|
58
63
|
return;
|
|
@@ -70,7 +75,6 @@ function validateDefinedAndNonEmpty(fieldName, value, ctx) {
|
|
|
70
75
|
});
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
|
-
exports.validateDefinedAndNonEmpty = validateDefinedAndNonEmpty;
|
|
74
78
|
function getSuggest(given, variants) {
|
|
75
79
|
if (given === null)
|
|
76
80
|
return variants;
|
|
@@ -87,7 +91,6 @@ function getSuggest(given, variants) {
|
|
|
87
91
|
// if (bestMatch.distance <= 4) return bestMatch.string;
|
|
88
92
|
return distances.map((d) => d.variant);
|
|
89
93
|
}
|
|
90
|
-
exports.getSuggest = getSuggest;
|
|
91
94
|
function validateExample(example, schema, dataLoc, { resolve, location, report }, allowAdditionalProperties) {
|
|
92
95
|
try {
|
|
93
96
|
const { valid, errors } = (0, ajv_1.validateJsonSchema)(example, schema, location.child('schema'), dataLoc.pointer, resolve, allowAdditionalProperties);
|
|
@@ -95,7 +98,10 @@ function validateExample(example, schema, dataLoc, { resolve, location, report }
|
|
|
95
98
|
for (const error of errors) {
|
|
96
99
|
report({
|
|
97
100
|
message: `Example value must conform to the schema: ${error.message}.`,
|
|
98
|
-
location:
|
|
101
|
+
location: {
|
|
102
|
+
...new ref_utils_1.Location(dataLoc.source, error.instancePath),
|
|
103
|
+
reportOnKey: error.keyword === 'unevaluatedProperties' || error.keyword === 'additionalProperties',
|
|
104
|
+
},
|
|
99
105
|
from: location,
|
|
100
106
|
suggest: error.suggest,
|
|
101
107
|
});
|
|
@@ -113,7 +119,6 @@ function validateExample(example, schema, dataLoc, { resolve, location, report }
|
|
|
113
119
|
});
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
|
-
exports.validateExample = validateExample;
|
|
117
122
|
function getAdditionalPropertiesOption(opts) {
|
|
118
123
|
if (opts.disallowAdditionalProperties === undefined) {
|
|
119
124
|
return opts.allowAdditionalProperties;
|
|
@@ -124,7 +129,6 @@ function getAdditionalPropertiesOption(opts) {
|
|
|
124
129
|
(0, utils_1.showWarningForDeprecatedField)('disallowAdditionalProperties', 'allowAdditionalProperties');
|
|
125
130
|
return !opts.disallowAdditionalProperties;
|
|
126
131
|
}
|
|
127
|
-
exports.getAdditionalPropertiesOption = getAdditionalPropertiesOption;
|
|
128
132
|
function validateSchemaEnumType(schemaEnum, propertyValue, propName, refLocation, { report, location }) {
|
|
129
133
|
if (!schemaEnum) {
|
|
130
134
|
return;
|
|
@@ -140,7 +144,6 @@ function validateSchemaEnumType(schemaEnum, propertyValue, propName, refLocation
|
|
|
140
144
|
});
|
|
141
145
|
}
|
|
142
146
|
}
|
|
143
|
-
exports.validateSchemaEnumType = validateSchemaEnumType;
|
|
144
147
|
function validateResponseCodes(responseCodes, codeRange, { report }) {
|
|
145
148
|
const responseCodeRegexp = new RegExp(`^${codeRange[0]}[0-9Xx]{2}$`);
|
|
146
149
|
const containsNeededCode = responseCodes.some((code) => (codeRange === '2XX' && code === 'default') || // It's OK to replace 2xx codes with the default
|
|
@@ -152,4 +155,3 @@ function validateResponseCodes(responseCodes, codeRange, { report }) {
|
|
|
152
155
|
});
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
|
-
exports.validateResponseCodes = validateResponseCodes;
|