@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.
Files changed (139) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/lib/benchmark/benches/lint-with-many-rules.bench.js +2 -2
  3. package/lib/benchmark/benches/lint-with-nested-rule.bench.js +2 -2
  4. package/lib/benchmark/benches/lint-with-no-rules.bench.js +2 -2
  5. package/lib/benchmark/benches/lint-with-top-level-rule-report.bench.js +2 -2
  6. package/lib/benchmark/benches/lint-with-top-level-rule.bench.js +2 -2
  7. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -2
  8. package/lib/benchmark/benches/resolve-with-no-external.bench.js +2 -2
  9. package/lib/benchmark/utils.js +7 -4
  10. package/lib/bundle.d.ts +2 -2
  11. package/lib/bundle.js +127 -120
  12. package/lib/config/all.js +9 -0
  13. package/lib/config/builtIn.js +7 -1
  14. package/lib/config/config-resolvers.js +179 -138
  15. package/lib/config/config.d.ts +2 -2
  16. package/lib/config/config.js +53 -34
  17. package/lib/config/load.js +105 -117
  18. package/lib/config/minimal.js +9 -0
  19. package/lib/config/recommended-strict.js +9 -0
  20. package/lib/config/recommended.js +9 -0
  21. package/lib/config/rules.d.ts +3 -3
  22. package/lib/config/rules.js +1 -2
  23. package/lib/config/types.d.ts +9 -3
  24. package/lib/config/utils.js +70 -49
  25. package/lib/decorators/async3/index.d.ts +1 -0
  26. package/lib/decorators/async3/index.js +4 -0
  27. package/lib/decorators/common/filters/filter-helper.js +2 -3
  28. package/lib/decorators/common/filters/filter-in.js +1 -1
  29. package/lib/decorators/common/filters/filter-out.js +1 -1
  30. package/lib/decorators/common/info-override.js +1 -12
  31. package/lib/decorators/common/media-type-examples-override.js +8 -2
  32. package/lib/decorators/common/remove-x-internal.js +4 -5
  33. package/lib/decorators/oas2/remove-unused-components.js +1 -2
  34. package/lib/decorators/oas3/remove-unused-components.js +1 -2
  35. package/lib/env.d.ts +0 -1
  36. package/lib/env.js +1 -1
  37. package/lib/format/codeframes.js +10 -8
  38. package/lib/format/format.js +23 -15
  39. package/lib/index.d.ts +2 -1
  40. package/lib/index.js +6 -4
  41. package/lib/js-yaml/index.js +1 -1
  42. package/lib/lint.d.ts +2 -0
  43. package/lib/lint.js +92 -99
  44. package/lib/oas-types.d.ts +9 -5
  45. package/lib/oas-types.js +22 -12
  46. package/lib/redocly/domains.js +6 -6
  47. package/lib/redocly/index.js +60 -73
  48. package/lib/redocly/registry-api.js +64 -82
  49. package/lib/ref-utils.js +13 -13
  50. package/lib/resolve.js +186 -205
  51. package/lib/rules/ajv.js +10 -8
  52. package/lib/rules/async3/channels-kebab-case.d.ts +2 -0
  53. package/lib/rules/async3/channels-kebab-case.js +19 -0
  54. package/lib/rules/async3/index.d.ts +3 -0
  55. package/lib/rules/async3/index.js +22 -0
  56. package/lib/rules/async3/no-channel-trailing-slash.d.ts +2 -0
  57. package/lib/rules/async3/no-channel-trailing-slash.js +16 -0
  58. package/lib/rules/common/assertions/asserts.js +5 -5
  59. package/lib/rules/common/assertions/index.d.ts +5 -4
  60. package/lib/rules/common/assertions/utils.js +43 -28
  61. package/lib/rules/common/no-invalid-parameter-examples.js +1 -2
  62. package/lib/rules/common/no-invalid-schema-examples.js +1 -2
  63. package/lib/rules/common/no-required-schema-properties-undefined.js +1 -2
  64. package/lib/rules/common/operation-tag-defined.js +1 -2
  65. package/lib/rules/common/path-http-verbs-order.js +1 -1
  66. package/lib/rules/common/required-string-property-missing-min-length.js +2 -2
  67. package/lib/rules/common/response-contains-header.js +2 -2
  68. package/lib/rules/common/security-defined.js +3 -7
  69. package/lib/rules/common/spec.d.ts +2 -2
  70. package/lib/rules/common/spec.js +6 -7
  71. package/lib/rules/no-unresolved-refs.js +3 -4
  72. package/lib/rules/oas2/response-contains-property.js +1 -2
  73. package/lib/rules/oas3/array-parameter-serialization.js +1 -2
  74. package/lib/rules/oas3/component-name-unique.js +2 -4
  75. package/lib/rules/oas3/no-invalid-media-type-examples.js +1 -2
  76. package/lib/rules/oas3/no-server-variables-empty-enum.js +1 -2
  77. package/lib/rules/oas3/no-undefined-server-variable.js +2 -3
  78. package/lib/rules/oas3/no-unused-components.js +1 -2
  79. package/lib/rules/oas3/response-contains-property.js +1 -2
  80. package/lib/rules/utils.js +14 -12
  81. package/lib/types/asyncapi2.d.ts +17 -0
  82. package/lib/types/{asyncapi.js → asyncapi2.js} +56 -52
  83. package/lib/types/asyncapi3.d.ts +2 -0
  84. package/lib/types/asyncapi3.js +347 -0
  85. package/lib/types/index.js +19 -10
  86. package/lib/types/json-schema-adapter.js +4 -18
  87. package/lib/types/oas2.js +6 -6
  88. package/lib/types/oas3.js +10 -10
  89. package/lib/types/oas3_1.js +14 -8
  90. package/lib/types/redocly-yaml.d.ts +3 -1
  91. package/lib/types/redocly-yaml.js +131 -35
  92. package/lib/typings/asyncapi3.d.ts +53 -0
  93. package/lib/typings/asyncapi3.js +2 -0
  94. package/lib/utils.d.ts +4 -3
  95. package/lib/utils.js +55 -72
  96. package/lib/visitors.d.ts +11 -0
  97. package/lib/visitors.js +21 -8
  98. package/lib/walk.js +30 -23
  99. package/package.json +2 -2
  100. package/src/__tests__/bundle.test.ts +142 -0
  101. package/src/bundle.ts +17 -3
  102. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +22 -0
  103. package/src/config/__tests__/__snapshots__/config.test.ts.snap +24 -0
  104. package/src/config/__tests__/config.test.ts +11 -0
  105. package/src/config/all.ts +9 -0
  106. package/src/config/builtIn.ts +6 -0
  107. package/src/config/config-resolvers.ts +15 -2
  108. package/src/config/config.ts +24 -5
  109. package/src/config/minimal.ts +9 -0
  110. package/src/config/recommended-strict.ts +9 -0
  111. package/src/config/recommended.ts +9 -0
  112. package/src/config/rules.ts +12 -4
  113. package/src/config/types.ts +15 -2
  114. package/src/config/utils.ts +15 -0
  115. package/src/decorators/async3/index.ts +1 -0
  116. package/src/decorators/common/remove-x-internal.ts +2 -2
  117. package/src/index.ts +2 -1
  118. package/src/lint.ts +26 -3
  119. package/src/oas-types.ts +31 -13
  120. package/src/rules/arazzo/index.ts +1 -1
  121. package/src/rules/async2/index.ts +5 -5
  122. package/src/rules/async3/__tests__/channels-kebab-case.test.ts +141 -0
  123. package/src/rules/async3/__tests__/no-channel-trailing-slash.test.ts +96 -0
  124. package/src/rules/async3/channels-kebab-case.ts +19 -0
  125. package/src/rules/async3/index.ts +23 -0
  126. package/src/rules/async3/no-channel-trailing-slash.ts +16 -0
  127. package/src/rules/common/assertions/index.ts +13 -4
  128. package/src/rules/common/spec.ts +2 -2
  129. package/src/rules/oas2/index.ts +4 -4
  130. package/src/rules/oas3/index.ts +39 -37
  131. package/src/types/{asyncapi.ts → asyncapi2.ts} +37 -34
  132. package/src/types/asyncapi3.ts +381 -0
  133. package/src/types/oas3_1.ts +2 -1
  134. package/src/types/redocly-yaml.ts +14 -0
  135. package/src/typings/asyncapi3.ts +61 -0
  136. package/src/utils.ts +5 -3
  137. package/src/visitors.ts +18 -0
  138. package/tsconfig.tsbuildinfo +1 -1
  139. package/lib/types/asyncapi.d.ts +0 -2
@@ -11,6 +11,9 @@ import type {
11
11
  Async2PreprocessorsSet,
12
12
  Async2DecoratorsSet,
13
13
  Async2RuleSet,
14
+ Async3PreprocessorsSet,
15
+ Async3DecoratorsSet,
16
+ Async3RuleSet,
14
17
  ArazzoRuleSet,
15
18
  ArazzoPreprocessorsSet,
16
19
  ArazzoDecoratorsSet,
@@ -22,6 +25,7 @@ import { Location } from '../ref-utils';
22
25
  import type { SkipFunctionContext } from '../visitors';
23
26
  import {
24
27
  BuiltInAsync2RuleId,
28
+ BuiltInAsync3RuleId,
25
29
  BuiltInCommonOASRuleId,
26
30
  BuiltInOAS2RuleId,
27
31
  BuiltInOAS3RuleId,
@@ -59,6 +63,7 @@ export type StyleguideRawConfig<T = undefined> = {
59
63
  oas3_0Rules?: RuleMap<BuiltInOAS3RuleId, RuleConfig, T>;
60
64
  oas3_1Rules?: RuleMap<BuiltInOAS3RuleId, RuleConfig, T>;
61
65
  async2Rules?: RuleMap<BuiltInAsync2RuleId, RuleConfig, T>;
66
+ async3Rules?: RuleMap<BuiltInAsync3RuleId, RuleConfig, T>;
62
67
  arazzoRules?: RuleMap<BuiltInArazzoRuleId, RuleConfig, T>;
63
68
 
64
69
  preprocessors?: Record<string, PreprocessorConfig>;
@@ -66,6 +71,7 @@ export type StyleguideRawConfig<T = undefined> = {
66
71
  oas3_0Preprocessors?: Record<string, PreprocessorConfig>;
67
72
  oas3_1Preprocessors?: Record<string, PreprocessorConfig>;
68
73
  async2Preprocessors?: Record<string, PreprocessorConfig>;
74
+ async3Preprocessors?: Record<string, PreprocessorConfig>;
69
75
  arazzoPreprocessors?: Record<string, PreprocessorConfig>;
70
76
 
71
77
  decorators?: Record<string, DecoratorConfig>;
@@ -73,6 +79,7 @@ export type StyleguideRawConfig<T = undefined> = {
73
79
  oas3_0Decorators?: Record<string, DecoratorConfig>;
74
80
  oas3_1Decorators?: Record<string, DecoratorConfig>;
75
81
  async2Decorators?: Record<string, DecoratorConfig>;
82
+ async3Decorators?: Record<string, DecoratorConfig>;
76
83
  arazzoDecorators?: Record<string, DecoratorConfig>;
77
84
  };
78
85
 
@@ -90,6 +97,7 @@ export type PreprocessorsConfig = {
90
97
  oas3?: Oas3PreprocessorsSet;
91
98
  oas2?: Oas2PreprocessorsSet;
92
99
  async2?: Async2PreprocessorsSet;
100
+ async3?: Async3PreprocessorsSet;
93
101
  arazzo?: ArazzoPreprocessorsSet;
94
102
  };
95
103
 
@@ -97,6 +105,7 @@ export type DecoratorsConfig = {
97
105
  oas3?: Oas3DecoratorsSet;
98
106
  oas2?: Oas2DecoratorsSet;
99
107
  async2?: Async2DecoratorsSet;
108
+ async3?: Async3DecoratorsSet;
100
109
  arazzo?: ArazzoDecoratorsSet;
101
110
  };
102
111
 
@@ -111,6 +120,7 @@ export type CustomRulesConfig = {
111
120
  oas3?: Oas3RuleSet;
112
121
  oas2?: Oas2RuleSet;
113
122
  async2?: Async2RuleSet;
123
+ async3?: Async3RuleSet;
114
124
  arazzo?: ArazzoRuleSet;
115
125
  };
116
126
 
@@ -243,16 +253,19 @@ export type RulesFields =
243
253
  | 'oas3_0Rules'
244
254
  | 'oas3_1Rules'
245
255
  | 'async2Rules'
256
+ | 'async3Rules'
246
257
  | 'arazzoRules'
247
258
  | 'preprocessors'
248
259
  | 'oas2Preprocessors'
249
260
  | 'oas3_0Preprocessors'
250
261
  | 'oas3_1Preprocessors'
251
262
  | 'async2Preprocessors'
263
+ | 'async3Preprocessors'
252
264
  | 'arazzoPreprocessors'
253
265
  | 'decorators'
254
266
  | 'oas2Decorators'
255
267
  | 'oas3_0Decorators'
256
268
  | 'oas3_1Decorators'
257
- | 'arazzoDecorators'
258
- | 'async2Decorators';
269
+ | 'async2Decorators'
270
+ | 'async3Decorators'
271
+ | 'arazzoDecorators';
@@ -56,6 +56,7 @@ function extractFlatConfig<
56
56
  oas3_0Rules,
57
57
  oas3_1Rules,
58
58
  async2Rules,
59
+ async3Rules,
59
60
  arazzoRules,
60
61
 
61
62
  preprocessors,
@@ -63,6 +64,7 @@ function extractFlatConfig<
63
64
  oas3_0Preprocessors,
64
65
  oas3_1Preprocessors,
65
66
  async2Preprocessors,
67
+ async3Preprocessors,
66
68
  arazzoPreprocessors,
67
69
 
68
70
  decorators,
@@ -70,6 +72,7 @@ function extractFlatConfig<
70
72
  oas3_0Decorators,
71
73
  oas3_1Decorators,
72
74
  async2Decorators,
75
+ async3Decorators,
73
76
  arazzoDecorators,
74
77
 
75
78
  ...rawConfigRest
@@ -86,6 +89,7 @@ function extractFlatConfig<
86
89
  oas3_0Rules,
87
90
  oas3_1Rules,
88
91
  async2Rules,
92
+ async3Rules,
89
93
  arazzoRules,
90
94
 
91
95
  preprocessors,
@@ -93,6 +97,7 @@ function extractFlatConfig<
93
97
  oas3_0Preprocessors,
94
98
  oas3_1Preprocessors,
95
99
  async2Preprocessors,
100
+ async3Preprocessors,
96
101
  arazzoPreprocessors,
97
102
 
98
103
  decorators,
@@ -100,6 +105,7 @@ function extractFlatConfig<
100
105
  oas3_0Decorators,
101
106
  oas3_1Decorators,
102
107
  async2Decorators,
108
+ async3Decorators,
103
109
  arazzoDecorators,
104
110
 
105
111
  doNotResolveExamples: rawConfigRest.resolve?.doNotResolveExamples,
@@ -158,6 +164,7 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
158
164
  oas3_0Rules: {},
159
165
  oas3_1Rules: {},
160
166
  async2Rules: {},
167
+ async3Rules: {},
161
168
  arazzoRules: {},
162
169
 
163
170
  preprocessors: {},
@@ -165,6 +172,7 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
165
172
  oas3_0Preprocessors: {},
166
173
  oas3_1Preprocessors: {},
167
174
  async2Preprocessors: {},
175
+ async3Preprocessors: {},
168
176
  arazzoPreprocessors: {},
169
177
 
170
178
  decorators: {},
@@ -172,6 +180,7 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
172
180
  oas3_0Decorators: {},
173
181
  oas3_1Decorators: {},
174
182
  async2Decorators: {},
183
+ async3Decorators: {},
175
184
  arazzoDecorators: {},
176
185
 
177
186
  plugins: [],
@@ -195,6 +204,8 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
195
204
  assignExisting(result.oas3_1Rules, rulesConf.rules || {});
196
205
  Object.assign(result.async2Rules, rulesConf.async2Rules);
197
206
  assignExisting(result.async2Rules, rulesConf.rules || {});
207
+ Object.assign(result.async3Rules, rulesConf.async3Rules);
208
+ assignExisting(result.async3Rules, rulesConf.rules || {});
198
209
  Object.assign(result.arazzoRules, rulesConf.arazzoRules);
199
210
  assignExisting(result.arazzoRules, rulesConf.rules || {});
200
211
 
@@ -207,6 +218,8 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
207
218
  assignExisting(result.oas3_1Preprocessors, rulesConf.preprocessors || {});
208
219
  Object.assign(result.async2Preprocessors, rulesConf.async2Preprocessors);
209
220
  assignExisting(result.async2Preprocessors, rulesConf.preprocessors || {});
221
+ Object.assign(result.async3Preprocessors, rulesConf.async3Preprocessors);
222
+ assignExisting(result.async3Preprocessors, rulesConf.preprocessors || {});
210
223
  Object.assign(result.arazzoPreprocessors, rulesConf.arazzoPreprocessors);
211
224
  assignExisting(result.arazzoPreprocessors, rulesConf.preprocessors || {});
212
225
 
@@ -219,6 +232,8 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
219
232
  assignExisting(result.oas3_1Decorators, rulesConf.decorators || {});
220
233
  Object.assign(result.async2Decorators, rulesConf.async2Decorators);
221
234
  assignExisting(result.async2Decorators, rulesConf.decorators || {});
235
+ Object.assign(result.async3Decorators, rulesConf.async3Decorators);
236
+ assignExisting(result.async3Decorators, rulesConf.decorators || {});
222
237
  Object.assign(result.arazzoDecorators, rulesConf.arazzoDecorators);
223
238
  assignExisting(result.arazzoDecorators, rulesConf.decorators || {});
224
239
 
@@ -0,0 +1 @@
1
+ export const decorators = {};
@@ -6,7 +6,7 @@ import { isRef } from '../../ref-utils';
6
6
  const DEFAULT_INTERNAL_PROPERTY_NAME = 'x-internal';
7
7
 
8
8
  export const RemoveXInternal: Oas3Decorator | Oas2Decorator = ({ internalFlagProperty }) => {
9
- const hiddenTag = internalFlagProperty || DEFAULT_INTERNAL_PROPERTY_NAME;
9
+ const hiddenTag: string = internalFlagProperty || DEFAULT_INTERNAL_PROPERTY_NAME;
10
10
 
11
11
  function removeInternal(node: any, ctx: UserContext) {
12
12
  const { parent, key } = ctx;
@@ -31,7 +31,7 @@ export const RemoveXInternal: Oas3Decorator | Oas2Decorator = ({ internalFlagPro
31
31
  for (const key of Object.keys(node)) {
32
32
  node = node as any;
33
33
  if (isRef(node[key])) {
34
- const resolved = ctx.resolve(node[key]);
34
+ const resolved = ctx.resolve<any>(node[key]);
35
35
  if (resolved.node?.[hiddenTag]) {
36
36
  delete node[key];
37
37
  didDelete = true;
package/src/index.ts CHANGED
@@ -11,7 +11,8 @@ export { Oas3_1Types } from './types/oas3_1';
11
11
  export { ArazzoTypes } from './types/arazzo';
12
12
  export { Oas3Types } from './types/oas3';
13
13
  export { Oas2Types } from './types/oas2';
14
- export { AsyncApi2Types } from './types/asyncapi';
14
+ export { AsyncApi2Types } from './types/asyncapi2';
15
+ export { AsyncApi3Types } from './types/asyncapi3';
15
16
  export { ConfigTypes } from './types/redocly-yaml';
16
17
  export type {
17
18
  Oas3Definition,
package/src/lint.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { rootRedoclyConfigSchema } from '@redocly/config';
1
2
  import { BaseResolver, resolveDocument, makeDocumentFromString } from './resolve';
2
3
  import { normalizeVisitors } from './visitors';
3
4
  import { walkDocument } from './walk';
@@ -12,16 +13,26 @@ import { NoUnresolvedRefs } from './rules/no-unresolved-refs';
12
13
  import type { Document, ResolvedRefMap } from './resolve';
13
14
  import type { ProblemSeverity, WalkContext } from './walk';
14
15
  import type { NodeType } from './types';
15
- import type { NestedVisitObject, Oas3Visitor, RuleInstanceConfig } from './visitors';
16
- import { rootRedoclyConfigSchema } from '@redocly/config';
16
+ import type {
17
+ ArazzoVisitor,
18
+ Async2Visitor,
19
+ Async3Visitor,
20
+ NestedVisitObject,
21
+ Oas2Visitor,
22
+ Oas3Visitor,
23
+ RuleInstanceConfig,
24
+ } from './visitors';
25
+ import type { CollectFn } from './utils';
17
26
 
18
27
  export async function lint(opts: {
19
28
  ref: string;
20
29
  config: Config;
21
30
  externalRefResolver?: BaseResolver;
31
+ collectSpecData?: CollectFn;
22
32
  }) {
23
33
  const { ref, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts;
24
34
  const document = (await externalRefResolver.resolveDocument(null, ref, true)) as Document;
35
+ opts.collectSpecData?.(document.parsed);
25
36
 
26
37
  return lintDocument({
27
38
  document,
@@ -130,7 +141,19 @@ export async function lintConfig(opts: {
130
141
  );
131
142
 
132
143
  const rules: (RuleInstanceConfig & {
133
- visitor: NestedVisitObject<unknown, Oas3Visitor | Oas3Visitor[]>;
144
+ visitor: NestedVisitObject<
145
+ unknown,
146
+ | Oas3Visitor
147
+ | Oas3Visitor[]
148
+ | Oas2Visitor
149
+ | Oas2Visitor[]
150
+ | Async2Visitor
151
+ | Async2Visitor[]
152
+ | Async3Visitor
153
+ | Async3Visitor[]
154
+ | ArazzoVisitor
155
+ | ArazzoVisitor[]
156
+ >;
134
157
  })[] = [
135
158
  {
136
159
  severity: severity || 'error',
package/src/oas-types.ts CHANGED
@@ -1,33 +1,37 @@
1
- import {
1
+ import type {
2
2
  Oas3Rule,
3
3
  Oas3Preprocessor,
4
4
  Oas2Rule,
5
5
  Oas2Preprocessor,
6
6
  Async2Preprocessor,
7
7
  Async2Rule,
8
+ Async3Preprocessor,
9
+ Async3Rule,
8
10
  ArazzoPreprocessor,
9
11
  ArazzoRule,
10
12
  } from './visitors';
11
13
  import { Oas2Types } from './types/oas2';
12
14
  import { Oas3Types } from './types/oas3';
13
15
  import { Oas3_1Types } from './types/oas3_1';
14
- import { AsyncApi2Types } from './types/asyncapi';
16
+ import { AsyncApi2Types } from './types/asyncapi2';
17
+ import { AsyncApi3Types } from './types/asyncapi3';
15
18
  import { ArazzoTypes } from './types/arazzo';
16
- import {
19
+ import type {
17
20
  BuiltInAsync2RuleId,
21
+ BuiltInAsync3RuleId,
18
22
  BuiltInCommonOASRuleId,
19
23
  BuiltInArazzoRuleId,
20
24
  BuiltInOAS2RuleId,
21
25
  BuiltInOAS3RuleId,
22
26
  } from './types/redocly-yaml';
23
-
24
- export type RuleSet<T> = Record<string, T>;
27
+ import { isPlainObject } from './utils';
25
28
 
26
29
  export enum SpecVersion {
27
30
  OAS2 = 'oas2',
28
31
  OAS3_0 = 'oas3_0',
29
32
  OAS3_1 = 'oas3_1',
30
- Async2 = 'async2', // todo split into 2.x maybe?
33
+ Async2 = 'async2',
34
+ Async3 = 'async3',
31
35
  Arazzo = 'arazzo',
32
36
  }
33
37
 
@@ -35,6 +39,7 @@ export enum SpecMajorVersion {
35
39
  OAS2 = 'oas2',
36
40
  OAS3 = 'oas3',
37
41
  Async2 = 'async2',
42
+ Async3 = 'async3',
38
43
  Arazzo = 'arazzo',
39
44
  }
40
45
 
@@ -43,6 +48,7 @@ const typesMap = {
43
48
  [SpecVersion.OAS3_0]: Oas3Types,
44
49
  [SpecVersion.OAS3_1]: Oas3_1Types,
45
50
  [SpecVersion.Async2]: AsyncApi2Types,
51
+ [SpecVersion.Async3]: AsyncApi3Types,
46
52
  [SpecVersion.Arazzo]: ArazzoTypes,
47
53
  };
48
54
 
@@ -65,6 +71,11 @@ export type Async2RuleSet<T = undefined> = RuleMap<
65
71
  Async2Rule,
66
72
  T
67
73
  >;
74
+ export type Async3RuleSet<T = undefined> = RuleMap<
75
+ BuiltInAsync3RuleId | 'assertions',
76
+ Async3Rule,
77
+ T
78
+ >;
68
79
  export type ArazzoRuleSet<T = undefined> = RuleMap<
69
80
  BuiltInArazzoRuleId | 'assertions',
70
81
  ArazzoRule,
@@ -74,15 +85,17 @@ export type ArazzoRuleSet<T = undefined> = RuleMap<
74
85
  export type Oas3PreprocessorsSet = Record<string, Oas3Preprocessor>;
75
86
  export type Oas2PreprocessorsSet = Record<string, Oas2Preprocessor>;
76
87
  export type Async2PreprocessorsSet = Record<string, Async2Preprocessor>;
88
+ export type Async3PreprocessorsSet = Record<string, Async3Preprocessor>;
77
89
  export type ArazzoPreprocessorsSet = Record<string, ArazzoPreprocessor>;
78
90
 
79
91
  export type Oas3DecoratorsSet = Record<string, Oas3Preprocessor>;
80
92
  export type Oas2DecoratorsSet = Record<string, Oas2Preprocessor>;
81
93
  export type Async2DecoratorsSet = Record<string, Async2Preprocessor>;
94
+ export type Async3DecoratorsSet = Record<string, Async3Preprocessor>;
82
95
  export type ArazzoDecoratorsSet = Record<string, ArazzoPreprocessor>;
83
96
 
84
- export function detectSpec(root: any): SpecVersion {
85
- if (typeof root !== 'object') {
97
+ export function detectSpec(root: unknown): SpecVersion {
98
+ if (!isPlainObject(root)) {
86
99
  throw new Error(`Document must be JSON object, got ${typeof root}`);
87
100
  }
88
101
 
@@ -90,11 +103,11 @@ export function detectSpec(root: any): SpecVersion {
90
103
  throw new Error(`Invalid OpenAPI version: should be a string but got "${typeof root.openapi}"`);
91
104
  }
92
105
 
93
- if (root.openapi && root.openapi.startsWith('3.0')) {
106
+ if (typeof root.openapi === 'string' && root.openapi.startsWith('3.0')) {
94
107
  return SpecVersion.OAS3_0;
95
108
  }
96
109
 
97
- if (root.openapi && root.openapi.startsWith('3.1')) {
110
+ if (typeof root.openapi === 'string' && root.openapi.startsWith('3.1')) {
98
111
  return SpecVersion.OAS3_1;
99
112
  }
100
113
 
@@ -102,20 +115,23 @@ export function detectSpec(root: any): SpecVersion {
102
115
  return SpecVersion.OAS2;
103
116
  }
104
117
 
105
- // if not detected yet
106
118
  if (root.openapi || root.swagger) {
107
119
  throw new Error(`Unsupported OpenAPI version: ${root.openapi || root.swagger}`);
108
120
  }
109
121
 
110
- if (root.asyncapi && root.asyncapi.startsWith('2.')) {
122
+ if (typeof root.asyncapi === 'string' && root.asyncapi.startsWith('2.')) {
111
123
  return SpecVersion.Async2;
112
124
  }
113
125
 
126
+ if (typeof root.asyncapi === 'string' && root.asyncapi.startsWith('3.')) {
127
+ return SpecVersion.Async3;
128
+ }
129
+
114
130
  if (root.asyncapi) {
115
131
  throw new Error(`Unsupported AsyncAPI version: ${root.asyncapi}`);
116
132
  }
117
133
 
118
- if (root.arazzo && root.arazzo.startsWith('1.')) {
134
+ if (typeof root.arazzo === 'string' && root.arazzo.startsWith('1.')) {
119
135
  return SpecVersion.Arazzo;
120
136
  }
121
137
 
@@ -127,6 +143,8 @@ export function getMajorSpecVersion(version: SpecVersion): SpecMajorVersion {
127
143
  return SpecMajorVersion.OAS2;
128
144
  } else if (version === SpecVersion.Async2) {
129
145
  return SpecMajorVersion.Async2;
146
+ } else if (version === SpecVersion.Async3) {
147
+ return SpecMajorVersion.Async3;
130
148
  } else if (version === SpecVersion.Arazzo) {
131
149
  return SpecMajorVersion.Arazzo;
132
150
  } else {
@@ -5,7 +5,7 @@ import { Assertions } from '../common/assertions';
5
5
 
6
6
  export const rules: ArazzoRuleSet<'built-in'> = {
7
7
  spec: Spec as ArazzoRule,
8
- assertions: Assertions,
8
+ assertions: Assertions as ArazzoRule,
9
9
  };
10
10
 
11
11
  export const preprocessors = {};
@@ -11,13 +11,13 @@ import type { Async2RuleSet } from '../../oas-types';
11
11
 
12
12
  export const rules: Async2RuleSet<'built-in'> = {
13
13
  spec: Spec as Async2Rule,
14
- assertions: Assertions,
15
- 'info-contact': InfoContact,
16
- 'operation-operationId': OperationOperationId,
14
+ assertions: Assertions as Async2Rule,
15
+ 'info-contact': InfoContact as Async2Rule,
16
+ 'operation-operationId': OperationOperationId as Async2Rule,
17
17
  'channels-kebab-case': ChannelsKebabCase,
18
18
  'no-channel-trailing-slash': NoChannelTrailingSlash,
19
- 'tag-description': TagDescription,
20
- 'tags-alphabetical': TagsAlphabetical,
19
+ 'tag-description': TagDescription as Async2Rule,
20
+ 'tags-alphabetical': TagsAlphabetical as Async2Rule,
21
21
  };
22
22
 
23
23
  export const preprocessors = {};
@@ -0,0 +1,141 @@
1
+ import { outdent } from 'outdent';
2
+ import { lintDocument } from '../../../lint';
3
+ import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
4
+ import { BaseResolver } from '../../../resolve';
5
+
6
+ describe('Async2 channels-kebab-case', () => {
7
+ it('should report on no kebab-case channel path', async () => {
8
+ const document = parseYamlToDocument(
9
+ outdent`
10
+ asyncapi: '3.0.0'
11
+ info:
12
+ title: Cool API
13
+ version: 1.0.0
14
+ channels:
15
+ channel1:
16
+ address: /NOT_A_KEBAB/
17
+ payload:
18
+ type: object
19
+ `,
20
+ 'asyncapi.yaml'
21
+ );
22
+
23
+ const results = await lintDocument({
24
+ externalRefResolver: new BaseResolver(),
25
+ document,
26
+ config: await makeConfig({ 'channels-kebab-case': 'error' }),
27
+ });
28
+
29
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
30
+ [
31
+ {
32
+ "location": [
33
+ {
34
+ "pointer": "#/channels/channel1",
35
+ "reportOnKey": true,
36
+ "source": "asyncapi.yaml",
37
+ },
38
+ ],
39
+ "message": "\`/NOT_A_KEBAB/\` does not use kebab-case.",
40
+ "ruleId": "channels-kebab-case",
41
+ "severity": "error",
42
+ "suggest": [],
43
+ },
44
+ ]
45
+ `);
46
+ });
47
+
48
+ it('should report on snake_case in channel path', async () => {
49
+ const document = parseYamlToDocument(
50
+ outdent`
51
+ asyncapi: '3.0.0'
52
+ info:
53
+ title: Cool API
54
+ version: 1.0.0
55
+ channels:
56
+ channel1:
57
+ address: snake_kebab
58
+ payload:
59
+ type: object
60
+ `,
61
+ 'asyncapi.yaml'
62
+ );
63
+
64
+ const results = await lintDocument({
65
+ externalRefResolver: new BaseResolver(),
66
+ document,
67
+ config: await makeConfig({ 'channels-kebab-case': 'error' }),
68
+ });
69
+
70
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
71
+ [
72
+ {
73
+ "location": [
74
+ {
75
+ "pointer": "#/channels/channel1",
76
+ "reportOnKey": true,
77
+ "source": "asyncapi.yaml",
78
+ },
79
+ ],
80
+ "message": "\`snake_kebab\` does not use kebab-case.",
81
+ "ruleId": "channels-kebab-case",
82
+ "severity": "error",
83
+ "suggest": [],
84
+ },
85
+ ]
86
+ `);
87
+ });
88
+
89
+ it('should allow trailing slash in channel path with "channels-kebab-case" rule', async () => {
90
+ const document = parseYamlToDocument(
91
+ outdent`
92
+ asyncapi: '3.0.0'
93
+ info:
94
+ title: Cool API
95
+ version: 1.0.0
96
+ channels:
97
+ channel1:
98
+ address: kebab/
99
+ payload:
100
+ type: object
101
+ `,
102
+ 'asyncapi.yaml'
103
+ );
104
+
105
+ const results = await lintDocument({
106
+ externalRefResolver: new BaseResolver(),
107
+ document,
108
+ config: await makeConfig({
109
+ 'paths-kebab-case': 'error',
110
+ 'no-path-trailing-slash': 'off',
111
+ }),
112
+ });
113
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
114
+ });
115
+
116
+ it('words with hyphens are allowed with "channels-kebab-case" rule', async () => {
117
+ const document = parseYamlToDocument(
118
+ outdent`
119
+ asyncapi: '3.0.0'
120
+ info:
121
+ title: Cool API
122
+ version: 1.0.0
123
+ channels:
124
+ channel1:
125
+ address: kebab-with-longer-channel-path:/
126
+ payload:
127
+ type: object
128
+ `,
129
+ 'asyncapi.yaml'
130
+ );
131
+
132
+ const results = await lintDocument({
133
+ externalRefResolver: new BaseResolver(),
134
+ document,
135
+ config: await makeConfig({
136
+ 'paths-kebab-case': 'error',
137
+ }),
138
+ });
139
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
140
+ });
141
+ });
@@ -0,0 +1,96 @@
1
+ import { outdent } from 'outdent';
2
+ import { lintDocument } from '../../../lint';
3
+ import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
4
+ import { BaseResolver } from '../../../resolve';
5
+
6
+ describe('no-channel-trailing-slash', () => {
7
+ it.only('should report on trailing slash in a channel path', async () => {
8
+ const document = parseYamlToDocument(
9
+ outdent`
10
+ asyncapi: '3.0.0'
11
+ info:
12
+ title: Excellent API
13
+ version: 1.0.0
14
+ channels:
15
+ channel1:
16
+ address: /trailing/
17
+ payload:
18
+ type: object
19
+ `,
20
+ 'asyncapi.yaml'
21
+ );
22
+
23
+ const results = await lintDocument({
24
+ externalRefResolver: new BaseResolver(),
25
+ document,
26
+ config: await makeConfig({ 'no-channel-trailing-slash': 'error' }),
27
+ });
28
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
29
+ [
30
+ {
31
+ "location": [
32
+ {
33
+ "pointer": "#/channels/channel1",
34
+ "reportOnKey": true,
35
+ "source": "asyncapi.yaml",
36
+ },
37
+ ],
38
+ "message": "\`/trailing/\` should not have a trailing slash.",
39
+ "ruleId": "no-channel-trailing-slash",
40
+ "severity": "error",
41
+ "suggest": [],
42
+ },
43
+ ]
44
+ `);
45
+ });
46
+
47
+ it('should not report on if no trailing slash in path', async () => {
48
+ const document = parseYamlToDocument(
49
+ outdent`
50
+ asyncapi: '3.0.0'
51
+ info:
52
+ title: Excellent API
53
+ version: 1.0.0
54
+ channels:
55
+ channel1:
56
+ address: /expected
57
+ payload:
58
+ type: object
59
+ `,
60
+ 'asyncapi.yaml'
61
+ );
62
+
63
+ const results = await lintDocument({
64
+ externalRefResolver: new BaseResolver(),
65
+ document,
66
+ config: await makeConfig({ 'no-channel-trailing-slash': 'error' }),
67
+ });
68
+
69
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
70
+ });
71
+
72
+ it('should not report on trailing slash in path if the path is root', async () => {
73
+ const document = parseYamlToDocument(
74
+ outdent`
75
+ asyncapi: '3.0.0'
76
+ info:
77
+ title: Excellent API
78
+ version: 1.0.0
79
+ channels:
80
+ channel1:
81
+ address: /
82
+ payload:
83
+ type: object
84
+ `,
85
+ 'foobar.yaml'
86
+ );
87
+
88
+ const results = await lintDocument({
89
+ externalRefResolver: new BaseResolver(),
90
+ document,
91
+ config: await makeConfig({ 'no-channel-trailing-slash': 'error' }),
92
+ });
93
+
94
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
95
+ });
96
+ });