@redocly/openapi-core 1.0.0-beta.110 → 1.0.0-beta.112

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 (93) hide show
  1. package/lib/config/all.js +0 -1
  2. package/lib/config/config-resolvers.js +42 -34
  3. package/lib/config/load.d.ts +1 -1
  4. package/lib/config/load.js +5 -5
  5. package/lib/config/minimal.js +0 -1
  6. package/lib/config/recommended.js +0 -1
  7. package/lib/rules/common/assertions/asserts.d.ts +22 -5
  8. package/lib/rules/common/assertions/asserts.js +25 -0
  9. package/lib/rules/common/assertions/index.d.ts +27 -2
  10. package/lib/rules/common/assertions/index.js +6 -29
  11. package/lib/rules/common/assertions/utils.d.ts +7 -14
  12. package/lib/rules/common/assertions/utils.js +129 -97
  13. package/lib/rules/common/no-ambiguous-paths.js +1 -1
  14. package/lib/rules/common/no-identical-paths.js +4 -4
  15. package/lib/rules/common/operation-2xx-response.js +2 -2
  16. package/lib/rules/common/operation-4xx-response.js +2 -2
  17. package/lib/rules/common/path-not-include-query.js +1 -1
  18. package/lib/rules/common/path-params-defined.js +7 -2
  19. package/lib/rules/common/response-contains-header.js +2 -2
  20. package/lib/rules/common/security-defined.js +10 -5
  21. package/lib/rules/common/spec.js +14 -12
  22. package/lib/rules/oas2/index.d.ts +0 -1
  23. package/lib/rules/oas2/index.js +0 -2
  24. package/lib/rules/oas3/index.js +0 -2
  25. package/lib/rules/oas3/request-mime-type.js +1 -1
  26. package/lib/rules/oas3/response-mime-type.js +1 -1
  27. package/lib/rules/other/stats.d.ts +1 -1
  28. package/lib/rules/other/stats.js +1 -1
  29. package/lib/rules/utils.d.ts +1 -0
  30. package/lib/rules/utils.js +17 -1
  31. package/lib/types/oas2.js +6 -6
  32. package/lib/types/oas3.js +11 -11
  33. package/lib/types/oas3_1.js +3 -3
  34. package/lib/types/redocly-yaml.js +58 -31
  35. package/lib/utils.d.ts +2 -0
  36. package/lib/utils.js +19 -1
  37. package/lib/visitors.d.ts +9 -7
  38. package/lib/visitors.js +12 -3
  39. package/lib/walk.js +7 -1
  40. package/package.json +1 -1
  41. package/src/__tests__/__snapshots__/bundle.test.ts.snap +1 -1
  42. package/src/__tests__/lint.test.ts +24 -5
  43. package/src/__tests__/utils.test.ts +11 -0
  44. package/src/__tests__/walk.test.ts +2 -2
  45. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +1 -3
  46. package/src/config/__tests__/config-resolvers.test.ts +30 -5
  47. package/src/config/__tests__/fixtures/load-redocly.yaml +4 -0
  48. package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +6 -4
  49. package/src/config/__tests__/load.test.ts +4 -1
  50. package/src/config/all.ts +0 -1
  51. package/src/config/config-resolvers.ts +44 -20
  52. package/src/config/load.ts +8 -5
  53. package/src/config/minimal.ts +0 -1
  54. package/src/config/recommended.ts +0 -1
  55. package/src/rules/common/__tests__/operation-2xx-response.test.ts +37 -0
  56. package/src/rules/common/__tests__/operation-4xx-response.test.ts +37 -0
  57. package/src/rules/common/__tests__/path-params-defined.test.ts +69 -0
  58. package/src/rules/common/__tests__/security-defined.test.ts +6 -6
  59. package/src/rules/common/__tests__/spec.test.ts +125 -0
  60. package/src/rules/common/assertions/__tests__/asserts.test.ts +7 -3
  61. package/src/rules/common/assertions/__tests__/index.test.ts +41 -20
  62. package/src/rules/common/assertions/__tests__/utils.test.ts +44 -18
  63. package/src/rules/common/assertions/asserts.ts +60 -8
  64. package/src/rules/common/assertions/index.ts +36 -46
  65. package/src/rules/common/assertions/utils.ts +204 -127
  66. package/src/rules/common/no-ambiguous-paths.ts +1 -1
  67. package/src/rules/common/no-identical-paths.ts +4 -4
  68. package/src/rules/common/operation-2xx-response.ts +2 -2
  69. package/src/rules/common/operation-4xx-response.ts +2 -2
  70. package/src/rules/common/path-not-include-query.ts +1 -1
  71. package/src/rules/common/path-params-defined.ts +9 -2
  72. package/src/rules/common/response-contains-header.ts +6 -1
  73. package/src/rules/common/security-defined.ts +10 -5
  74. package/src/rules/common/spec.ts +15 -11
  75. package/src/rules/oas2/index.ts +0 -2
  76. package/src/rules/oas3/__tests__/response-contains-header.test.ts +116 -0
  77. package/src/rules/oas3/index.ts +0 -2
  78. package/src/rules/oas3/request-mime-type.ts +1 -1
  79. package/src/rules/oas3/response-mime-type.ts +1 -1
  80. package/src/rules/other/stats.ts +1 -1
  81. package/src/rules/utils.ts +22 -0
  82. package/src/types/oas2.ts +6 -6
  83. package/src/types/oas3.ts +11 -11
  84. package/src/types/oas3_1.ts +3 -3
  85. package/src/types/redocly-yaml.ts +58 -33
  86. package/src/utils.ts +18 -0
  87. package/src/visitors.ts +32 -11
  88. package/src/walk.ts +8 -1
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/lib/rules/common/info-description.d.ts +0 -2
  91. package/lib/rules/common/info-description.js +0 -12
  92. package/src/rules/common/__tests__/info-description.test.ts +0 -102
  93. package/src/rules/common/info-description.ts +0 -10
package/lib/types/oas3.js CHANGED
@@ -12,7 +12,7 @@ const Root = {
12
12
  security: _1.listOf('SecurityRequirement'),
13
13
  tags: _1.listOf('Tag'),
14
14
  externalDocs: 'ExternalDocs',
15
- paths: 'PathsMap',
15
+ paths: 'Paths',
16
16
  components: 'Components',
17
17
  'x-webhooks': 'WebhooksMap',
18
18
  },
@@ -81,7 +81,7 @@ const License = {
81
81
  },
82
82
  required: ['name'],
83
83
  };
84
- const PathsMap = {
84
+ const Paths = {
85
85
  properties: {},
86
86
  additionalProperties: (_value, key) => key.startsWith('/') ? 'PathItem' : undefined,
87
87
  };
@@ -141,7 +141,7 @@ const Operation = {
141
141
  security: _1.listOf('SecurityRequirement'),
142
142
  servers: _1.listOf('Server'),
143
143
  requestBody: 'RequestBody',
144
- responses: 'ResponsesMap',
144
+ responses: 'Responses',
145
145
  deprecated: { type: 'boolean' },
146
146
  callbacks: 'CallbacksMap',
147
147
  'x-codeSamples': _1.listOf('XCodeSample'),
@@ -174,7 +174,7 @@ const MediaType = {
174
174
  schema: 'Schema',
175
175
  example: { isExample: true },
176
176
  examples: 'ExamplesMap',
177
- encoding: 'EncodingsMap',
177
+ encoding: 'EncodingMap',
178
178
  },
179
179
  };
180
180
  const Example = {
@@ -214,7 +214,7 @@ const Header = {
214
214
  },
215
215
  requiredOneOf: ['schema', 'content'],
216
216
  };
217
- const ResponsesMap = {
217
+ const Responses = {
218
218
  properties: { default: 'Response' },
219
219
  additionalProperties: (_v, key) => responseCodeRegexp.test(key) ? 'Response' : undefined,
220
220
  };
@@ -378,7 +378,7 @@ const AuthorizationCode = {
378
378
  },
379
379
  required: ['authorizationUrl', 'tokenUrl', 'scopes'],
380
380
  };
381
- const SecuritySchemeFlows = {
381
+ const OAuth2Flows = {
382
382
  properties: {
383
383
  implicit: 'ImplicitFlow',
384
384
  password: 'PasswordFlow',
@@ -394,7 +394,7 @@ const SecurityScheme = {
394
394
  in: { type: 'string', enum: ['query', 'header', 'cookie'] },
395
395
  scheme: { type: 'string' },
396
396
  bearerFormat: { type: 'string' },
397
- flows: 'SecuritySchemeFlows',
397
+ flows: 'OAuth2Flows',
398
398
  openIdConnectUrl: { type: 'string' },
399
399
  },
400
400
  required(value) {
@@ -438,7 +438,7 @@ exports.Oas3Types = {
438
438
  Info,
439
439
  Contact,
440
440
  License,
441
- PathsMap,
441
+ Paths,
442
442
  PathItem,
443
443
  Parameter,
444
444
  Operation,
@@ -450,10 +450,10 @@ exports.Oas3Types = {
450
450
  Example,
451
451
  ExamplesMap: _1.mapOf('Example'),
452
452
  Encoding,
453
- EncodingsMap: _1.mapOf('Encoding'),
453
+ EncodingMap: _1.mapOf('Encoding'),
454
454
  Header,
455
455
  HeadersMap: _1.mapOf('Header'),
456
- ResponsesMap,
456
+ Responses,
457
457
  Response,
458
458
  Link,
459
459
  Schema,
@@ -476,7 +476,7 @@ exports.Oas3Types = {
476
476
  PasswordFlow,
477
477
  ClientCredentials,
478
478
  AuthorizationCode,
479
- SecuritySchemeFlows,
479
+ OAuth2Flows,
480
480
  SecurityScheme,
481
481
  XCodeSample,
482
482
  WebhooksMap,
@@ -11,7 +11,7 @@ const Root = {
11
11
  security: _1.listOf('SecurityRequirement'),
12
12
  tags: _1.listOf('Tag'),
13
13
  externalDocs: 'ExternalDocs',
14
- paths: 'PathsMap',
14
+ paths: 'Paths',
15
15
  webhooks: 'WebhooksMap',
16
16
  components: 'Components',
17
17
  jsonSchemaDialect: { type: 'string' },
@@ -67,7 +67,7 @@ const Operation = {
67
67
  security: _1.listOf('SecurityRequirement'),
68
68
  servers: _1.listOf('Server'),
69
69
  requestBody: 'RequestBody',
70
- responses: 'ResponsesMap',
70
+ responses: 'Responses',
71
71
  deprecated: { type: 'boolean' },
72
72
  callbacks: _1.mapOf('Callback'),
73
73
  'x-codeSamples': _1.listOf('XCodeSample'),
@@ -175,7 +175,7 @@ const SecurityScheme = {
175
175
  in: { type: 'string', enum: ['query', 'header', 'cookie'] },
176
176
  scheme: { type: 'string' },
177
177
  bearerFormat: { type: 'string' },
178
- flows: 'SecuritySchemeFlows',
178
+ flows: 'OAuth2Flows',
179
179
  openIdConnectUrl: { type: 'string' },
180
180
  },
181
181
  required(value) {
@@ -5,7 +5,6 @@ const _1 = require(".");
5
5
  const utils_1 = require("../utils");
6
6
  const builtInRulesList = [
7
7
  'spec',
8
- 'info-description',
9
8
  'info-contact',
10
9
  'info-license',
11
10
  'info-license-url',
@@ -68,7 +67,7 @@ const nodeTypesList = [
68
67
  'Info',
69
68
  'Contact',
70
69
  'License',
71
- 'PathsMap',
70
+ 'Paths',
72
71
  'PathItem',
73
72
  'Parameter',
74
73
  'Operation',
@@ -80,10 +79,10 @@ const nodeTypesList = [
80
79
  'Example',
81
80
  'ExamplesMap',
82
81
  'Encoding',
83
- 'EncodingsMap',
82
+ 'EncodingMap',
84
83
  'Header',
85
84
  'HeadersMap',
86
- 'ResponsesMap',
85
+ 'Responses',
87
86
  'Response',
88
87
  'Link',
89
88
  'LinksMap',
@@ -106,7 +105,7 @@ const nodeTypesList = [
106
105
  'PasswordFlow',
107
106
  'ClientCredentials',
108
107
  'AuthorizationCode',
109
- 'SecuritySchemeFlows',
108
+ 'OAuth2Flows',
110
109
  'SecurityScheme',
111
110
  'XCodeSample',
112
111
  'WebhooksMap',
@@ -140,11 +139,7 @@ const RootConfigStyleguide = {
140
139
  } }, ConfigStyleguide.properties),
141
140
  };
142
141
  const ConfigRoot = {
143
- properties: Object.assign(Object.assign({ organization: { type: 'string' }, apis: 'ConfigApis', apiDefinitions: {
144
- type: 'object',
145
- properties: {},
146
- additionalProperties: { properties: { type: 'string' } },
147
- } }, RootConfigStyleguide.properties), { styleguide: 'RootConfigStyleguide', lint: 'RootConfigStyleguide', 'features.openapi': 'ConfigReferenceDocs', referenceDocs: 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', region: { enum: ['us', 'eu'] }, resolve: {
142
+ properties: Object.assign(Object.assign({ organization: { type: 'string' }, apis: 'ConfigApis' }, RootConfigStyleguide.properties), { 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', region: { enum: ['us', 'eu'] }, resolve: {
148
143
  properties: {
149
144
  http: 'ConfigHTTP',
150
145
  doNotResolveExamples: { type: 'boolean' },
@@ -209,16 +204,9 @@ const ObjectRule = {
209
204
  additionalProperties: {},
210
205
  required: ['severity'],
211
206
  };
212
- const Assert = {
207
+ const AssertionDefinitionSubject = {
213
208
  properties: {
214
- subject: (value) => {
215
- if (Array.isArray(value)) {
216
- return { type: 'array', items: { enum: nodeTypesList } };
217
- }
218
- else {
219
- return { enum: nodeTypesList };
220
- }
221
- },
209
+ type: { enum: nodeTypesList },
222
210
  property: (value) => {
223
211
  if (Array.isArray(value)) {
224
212
  return { type: 'array', items: { type: 'string' } };
@@ -230,10 +218,14 @@ const Assert = {
230
218
  return { type: 'string' };
231
219
  }
232
220
  },
233
- context: _1.listOf('Context'),
234
- message: { type: 'string' },
235
- suggest: { type: 'array', items: { type: 'string' } },
236
- severity: { enum: ['error', 'warn', 'off'] },
221
+ filterInParentKeys: { type: 'array', items: { type: 'string' } },
222
+ filterOutParentKeys: { type: 'array', items: { type: 'string' } },
223
+ matchParentKeys: { type: 'string' },
224
+ },
225
+ required: ['type'],
226
+ };
227
+ const AssertionDefinitionAssertions = {
228
+ properties: {
237
229
  enum: { type: 'array', items: { type: 'string' } },
238
230
  pattern: { type: 'string' },
239
231
  casing: {
@@ -253,26 +245,49 @@ const Assert = {
253
245
  requireAny: { type: 'array', items: { type: 'string' } },
254
246
  disallowed: { type: 'array', items: { type: 'string' } },
255
247
  defined: { type: 'boolean' },
256
- undefined: { type: 'boolean' },
248
+ // undefined: { type: 'boolean' }, // TODO: Remove `undefined` assertion from codebase overall
257
249
  nonEmpty: { type: 'boolean' },
258
250
  minLength: { type: 'integer' },
259
251
  maxLength: { type: 'integer' },
260
252
  ref: (value) => typeof value === 'string' ? { type: 'string' } : { type: 'boolean' },
253
+ const: (value) => {
254
+ if (typeof value === 'string') {
255
+ return { type: 'string' };
256
+ }
257
+ if (typeof value === 'number') {
258
+ return { type: 'number' };
259
+ }
260
+ if (typeof value === 'boolean') {
261
+ return { type: 'boolean' };
262
+ }
263
+ else {
264
+ return;
265
+ }
266
+ },
261
267
  },
262
268
  additionalProperties: (_value, key) => {
263
269
  if (/^\w+\/\w+$/.test(key))
264
270
  return { type: 'object' };
265
271
  return;
266
272
  },
267
- required: ['subject'],
268
273
  };
269
- const Context = {
274
+ const AssertDefinition = {
270
275
  properties: {
271
- type: { enum: nodeTypesList },
272
- matchParentKeys: { type: 'array', items: { type: 'string' } },
273
- excludeParentKeys: { type: 'array', items: { type: 'string' } },
276
+ subject: 'AssertionDefinitionSubject',
277
+ assertions: 'AssertionDefinitionAssertions',
274
278
  },
275
- required: ['type'],
279
+ required: ['subject', 'assertions'],
280
+ };
281
+ const Assert = {
282
+ properties: {
283
+ subject: 'AssertionDefinitionSubject',
284
+ assertions: 'AssertionDefinitionAssertions',
285
+ where: _1.listOf('AssertDefinition'),
286
+ message: { type: 'string' },
287
+ suggest: { type: 'array', items: { type: 'string' } },
288
+ severity: { enum: ['error', 'warn', 'off'] },
289
+ },
290
+ required: ['subject', 'assertions'],
276
291
  };
277
292
  const ConfigLanguage = {
278
293
  properties: {
@@ -758,6 +773,16 @@ const ConfigReferenceDocs = {
758
773
  unstable_externalDescription: { type: 'boolean' },
759
774
  unstable_ignoreMimeParameters: { type: 'boolean' },
760
775
  untrustedDefinition: { type: 'boolean' },
776
+ mockServer: {
777
+ properties: {
778
+ url: { type: 'string' },
779
+ position: { enum: ['first', 'last', 'replace', 'off'] },
780
+ description: { type: 'string' },
781
+ },
782
+ },
783
+ showAccessMode: { type: 'boolean' },
784
+ preserveOriginalExtensionsName: { type: 'boolean' },
785
+ markdownHeadingsAnchorLevel: { type: 'number' },
761
786
  },
762
787
  additionalProperties: { type: 'string' },
763
788
  };
@@ -782,7 +807,7 @@ exports.ConfigTypes = {
782
807
  ConfigSidebarLinks,
783
808
  CommonConfigSidebarLinks,
784
809
  ConfigTheme,
785
- Context,
810
+ AssertDefinition,
786
811
  ThemeColors,
787
812
  CommonThemeColors,
788
813
  BorderThemeColors,
@@ -829,4 +854,6 @@ exports.ConfigTypes = {
829
854
  Sidebar,
830
855
  Heading,
831
856
  Typography,
857
+ AssertionDefinitionAssertions,
858
+ AssertionDefinitionSubject,
832
859
  };
package/lib/utils.d.ts CHANGED
@@ -46,3 +46,5 @@ export declare function showErrorForDeprecatedField(deprecatedField: string, upd
46
46
  export declare type Falsy = undefined | null | false | '' | 0;
47
47
  export declare function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy;
48
48
  export declare function identity<T>(value: T): T;
49
+ export declare function keysOf<T>(obj: T): (keyof T)[];
50
+ export declare function pickDefined<T extends Record<string, unknown>>(obj?: T): Record<string, unknown> | undefined;
package/lib/utils.js CHANGED
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
12
+ exports.pickDefined = exports.keysOf = exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
13
13
  const fs = require("fs");
14
14
  const path_1 = require("path");
15
15
  const minimatch = require("minimatch");
@@ -205,3 +205,21 @@ function identity(value) {
205
205
  return value;
206
206
  }
207
207
  exports.identity = identity;
208
+ function keysOf(obj) {
209
+ if (!obj)
210
+ return [];
211
+ return Object.keys(obj);
212
+ }
213
+ exports.keysOf = keysOf;
214
+ function pickDefined(obj) {
215
+ if (!obj)
216
+ return undefined;
217
+ const res = {};
218
+ for (const key in obj) {
219
+ if (obj[key] !== undefined) {
220
+ res[key] = obj[key];
221
+ }
222
+ }
223
+ return res;
224
+ }
225
+ exports.pickDefined = pickDefined;
package/lib/visitors.d.ts CHANGED
@@ -4,11 +4,12 @@ import type { NormalizedNodeType } from './types';
4
4
  import type { Stack } from './utils';
5
5
  import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
6
6
  import type { Location } from './ref-utils';
7
+ export declare type SkipFunctionContext = Pick<UserContext, 'location' | 'rawNode' | 'resolve' | 'rawLocation'>;
7
8
  export declare type VisitFunction<T> = (node: T, ctx: UserContext & {
8
9
  ignoreNextVisitorsOnNode: () => void;
9
10
  }, parents?: any, context?: any) => void;
10
11
  declare type VisitRefFunction = (node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) => void;
11
- declare type SkipFunction<T> = (node: T, key: string | number) => boolean;
12
+ declare type SkipFunction<T> = (node: T, key: string | number, ctx: SkipFunctionContext) => boolean;
12
13
  declare type VisitObject<T> = {
13
14
  enter?: VisitFunction<T>;
14
15
  leave?: VisitFunction<T>;
@@ -70,9 +71,10 @@ declare type Oas3FlatVisitor = {
70
71
  Info?: VisitFunctionOrObject<Oas3Info>;
71
72
  Contact?: VisitFunctionOrObject<Oas3Contact>;
72
73
  License?: VisitFunctionOrObject<Oas3License>;
73
- PathsMap?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
74
+ Paths?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
74
75
  PathItem?: VisitFunctionOrObject<Oas3PathItem>;
75
- Callback?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
76
+ Callback?: VisitFunctionOrObject<Oas3Callback>;
77
+ CallbacksMap?: VisitFunctionOrObject<Record<string, Oas3Callback>>;
76
78
  Parameter?: VisitFunctionOrObject<Oas3Parameter>;
77
79
  Operation?: VisitFunctionOrObject<Oas3Operation>;
78
80
  RequestBody?: VisitFunctionOrObject<Oas3RequestBody>;
@@ -81,7 +83,7 @@ declare type Oas3FlatVisitor = {
81
83
  Example?: VisitFunctionOrObject<Oas3Example>;
82
84
  Encoding?: VisitFunctionOrObject<Oas3Encoding>;
83
85
  Header?: VisitFunctionOrObject<Oas3Header>;
84
- ResponsesMap?: VisitFunctionOrObject<Record<string, Oas3Response>>;
86
+ Responses?: VisitFunctionOrObject<Record<string, Oas3Response>>;
85
87
  Response?: VisitFunctionOrObject<Oas3Response>;
86
88
  Link?: VisitFunctionOrObject<Oas3Link>;
87
89
  Schema?: VisitFunctionOrObject<Oas3Schema>;
@@ -103,7 +105,7 @@ declare type Oas3FlatVisitor = {
103
105
  PasswordFlow?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['password']>;
104
106
  ClientCredentials?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['clientCredentials']>;
105
107
  AuthorizationCode?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['authorizationCode']>;
106
- SecuritySchemeFlows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
108
+ OAuth2Flows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
107
109
  SecurityScheme?: VisitFunctionOrObject<Oas3SecurityScheme>;
108
110
  };
109
111
  declare type Oas2FlatVisitor = {
@@ -114,13 +116,13 @@ declare type Oas2FlatVisitor = {
114
116
  Info?: VisitFunctionOrObject<Oas2Info>;
115
117
  Contact?: VisitFunctionOrObject<Oas2Contact>;
116
118
  License?: VisitFunctionOrObject<Oas2License>;
117
- PathsMap?: VisitFunctionOrObject<Record<string, Oas2PathItem>>;
119
+ Paths?: VisitFunctionOrObject<Record<string, Oas2PathItem>>;
118
120
  PathItem?: VisitFunctionOrObject<Oas2PathItem>;
119
121
  Parameter?: VisitFunctionOrObject<any>;
120
122
  Operation?: VisitFunctionOrObject<Oas2Operation>;
121
123
  Examples?: VisitFunctionOrObject<Record<string, any>>;
122
124
  Header?: VisitFunctionOrObject<Oas2Header>;
123
- ResponsesMap?: VisitFunctionOrObject<Record<string, Oas2Response>>;
125
+ Responses?: VisitFunctionOrObject<Record<string, Oas2Response>>;
124
126
  Response?: VisitFunctionOrObject<Oas2Response>;
125
127
  Schema?: VisitFunctionOrObject<Oas2Schema>;
126
128
  Xml?: VisitFunctionOrObject<Oas2Xml>;
package/lib/visitors.js CHANGED
@@ -4,13 +4,15 @@ exports.normalizeVisitors = void 0;
4
4
  const legacyTypesMap = {
5
5
  Root: 'DefinitionRoot',
6
6
  ServerVariablesMap: 'ServerVariableMap',
7
- PathsMap: 'PathMap',
7
+ Paths: ['PathMap', 'PathsMap'],
8
8
  CallbacksMap: 'CallbackMap',
9
9
  MediaTypesMap: 'MediaTypeMap',
10
10
  ExamplesMap: 'ExampleMap',
11
- EncodingsMap: 'EncodingMap',
11
+ EncodingMap: 'EncodingsMap',
12
12
  HeadersMap: 'HeaderMap',
13
13
  LinksMap: 'LinkMap',
14
+ OAuth2Flows: 'SecuritySchemeFlows',
15
+ Responses: 'ResponsesMap',
14
16
  };
15
17
  function normalizeVisitors(visitorsConfig, types) {
16
18
  const normalizedVisitors = {};
@@ -85,6 +87,13 @@ function normalizeVisitors(visitorsConfig, types) {
85
87
  }
86
88
  }
87
89
  }
90
+ function findLegacyVisitorNode(visitor, typeName) {
91
+ if (Array.isArray(typeName)) {
92
+ const name = typeName.find((name) => visitor[name]) || undefined;
93
+ return name && visitor[name];
94
+ }
95
+ return visitor[typeName];
96
+ }
88
97
  function normalizeVisitorLevel(ruleConf, visitor, parentContext, depth = 0) {
89
98
  const visitorKeys = Object.keys(types);
90
99
  if (depth === 0) {
@@ -101,7 +110,7 @@ function normalizeVisitors(visitorsConfig, types) {
101
110
  }
102
111
  for (const typeName of visitorKeys) {
103
112
  const typeVisitor = (visitor[typeName] ||
104
- visitor[legacyTypesMap[typeName]]);
113
+ findLegacyVisitorNode(visitor, legacyTypesMap[typeName]));
105
114
  const normalizedTypeVisitor = normalizedVisitors[typeName];
106
115
  if (!typeVisitor)
107
116
  continue;
package/lib/walk.js CHANGED
@@ -112,7 +112,13 @@ function walkDocument(opts) {
112
112
  location: resolvedLocation,
113
113
  nextLevelTypeActivated: null,
114
114
  withParentNode: (_g = (_f = context.parent) === null || _f === void 0 ? void 0 : _f.activatedOn) === null || _g === void 0 ? void 0 : _g.value.node,
115
- skipped: (_k = (((_j = (_h = context.parent) === null || _h === void 0 ? void 0 : _h.activatedOn) === null || _j === void 0 ? void 0 : _j.value.skipped) || (skip === null || skip === void 0 ? void 0 : skip(resolvedNode, key)))) !== null && _k !== void 0 ? _k : false,
115
+ skipped: (_k = (((_j = (_h = context.parent) === null || _h === void 0 ? void 0 : _h.activatedOn) === null || _j === void 0 ? void 0 : _j.value.skipped) ||
116
+ (skip === null || skip === void 0 ? void 0 : skip(resolvedNode, key, {
117
+ location,
118
+ rawLocation,
119
+ resolve,
120
+ rawNode: node,
121
+ })))) !== null && _k !== void 0 ? _k : false,
116
122
  };
117
123
  context.activatedOn = utils_1.pushStack(context.activatedOn, activatedOn);
118
124
  let ctx = context.parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.110",
3
+ "version": "1.0.0-beta.112",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -94,7 +94,7 @@ rootType:
94
94
  name: ExternalDocs
95
95
  paths:
96
96
  properties: {}
97
- name: PathsMap
97
+ name: Paths
98
98
  components:
99
99
  properties:
100
100
  parameters:
@@ -61,11 +61,13 @@ describe('lint', () => {
61
61
  path-http-verbs-order: error
62
62
  boolean-parameter-prefixes: off
63
63
  assert/operation-summary-length:
64
- subject: Operation
65
- property: summary
64
+ subject:
65
+ type: Operation
66
+ property: summary
66
67
  message: Operation summary should start with an active verb
67
- local/checkWordsCount:
68
- min: 3
68
+ assertions:
69
+ local/checkWordsCount:
70
+ min: 3
69
71
  features.openapi:
70
72
  showConsole: true
71
73
  layout:
@@ -247,7 +249,24 @@ describe('lint', () => {
247
249
  );
248
250
  const results = await lintConfig({ document });
249
251
 
250
- expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
252
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
253
+ Array [
254
+ Object {
255
+ "from": undefined,
256
+ "location": Array [
257
+ Object {
258
+ "pointer": "#/referenceDocs",
259
+ "reportOnKey": true,
260
+ "source": "",
261
+ },
262
+ ],
263
+ "message": "Property \`referenceDocs\` is not expected here.",
264
+ "ruleId": "configuration spec",
265
+ "severity": "error",
266
+ "suggest": Array [],
267
+ },
268
+ ]
269
+ `);
251
270
  });
252
271
 
253
272
  it("'plugins' shouldn't be allowed in 'apis'", async () => {
@@ -4,6 +4,7 @@ import {
4
4
  slash,
5
5
  getMatchingStatusCodeRange,
6
6
  doesYamlFileExist,
7
+ pickDefined,
7
8
  } from '../utils';
8
9
  import { isBrowser } from '../env';
9
10
  import * as fs from 'fs';
@@ -81,6 +82,16 @@ describe('utils', () => {
81
82
  });
82
83
  });
83
84
 
85
+ describe('pickDefined', () => {
86
+ it('returns undefined for undefined', () => {
87
+ expect(pickDefined(undefined)).toBeUndefined();
88
+ });
89
+
90
+ it('picks only defined values', () => {
91
+ expect(pickDefined({ a: 1, b: undefined, c: 3 })).toStrictEqual({ a: 1, c: 3 });
92
+ });
93
+ });
94
+
84
95
  describe('getMatchingStatusCodeRange', () => {
85
96
  it('should get the generalized form of status codes', () => {
86
97
  expect(getMatchingStatusCodeRange('202')).toEqual('2XX');
@@ -1115,7 +1115,7 @@ describe('walk order', () => {
1115
1115
  expect(calls).toMatchInlineSnapshot(`
1116
1116
  Array [
1117
1117
  "enter Root",
1118
- "enter PathsMap",
1118
+ "enter Paths",
1119
1119
  "enter PathItem",
1120
1120
  "enter ParameterList",
1121
1121
  "enter Parameter",
@@ -1134,7 +1134,7 @@ describe('walk order', () => {
1134
1134
  "leave ParameterList",
1135
1135
  "leave Operation",
1136
1136
  "leave PathItem",
1137
- "leave PathsMap",
1137
+ "leave Paths",
1138
1138
  "enter Components",
1139
1139
  "enter NamedParameters",
1140
1140
  "leave NamedParameters",
@@ -39,7 +39,6 @@ Object {
39
39
  "assertions": "warn",
40
40
  "boolean-parameter-prefixes": "error",
41
41
  "info-contact": "off",
42
- "info-description": "warn",
43
42
  "info-license": "warn",
44
43
  "info-license-url": "warn",
45
44
  "local/operation-id-not-test": "error",
@@ -73,7 +72,7 @@ Object {
73
72
  }
74
73
  `;
75
74
 
76
- exports[`resolveStyleguideConfig should resolve extends with local file config witch contains path to nested config 1`] = `
75
+ exports[`resolveStyleguideConfig should resolve extends with local file config which contains path to nested config 1`] = `
77
76
  Object {
78
77
  "decorators": Object {},
79
78
  "doNotResolveExamples": undefined,
@@ -129,7 +128,6 @@ Object {
129
128
  ],
130
129
  "boolean-parameter-prefixes": "error",
131
130
  "info-contact": "off",
132
- "info-description": "warn",
133
131
  "info-license": "warn",
134
132
  "info-license-url": "warn",
135
133
  "local/operation-id-not-test": "error",
@@ -1,5 +1,5 @@
1
1
  import { colorize } from '../../logger';
2
- import { asserts } from '../../rules/common/assertions/asserts';
2
+ import { Asserts, asserts } from '../../rules/common/assertions/asserts';
3
3
  import { resolveStyleguideConfig, resolveApis, resolveConfig } from '../config-resolvers';
4
4
  const path = require('path');
5
5
 
@@ -100,7 +100,7 @@ describe('resolveStyleguideConfig', () => {
100
100
  }).toThrow('Circular dependency in config file');
101
101
  });
102
102
 
103
- it('should resolve extends with local file config witch contains path to nested config', async () => {
103
+ it('should resolve extends with local file config which contains path to nested config', async () => {
104
104
  const styleguideConfig = {
105
105
  extends: ['local-config-with-file.yaml'],
106
106
  };
@@ -143,7 +143,7 @@ describe('resolveStyleguideConfig', () => {
143
143
 
144
144
  expect(plugins).toBeDefined();
145
145
  expect(plugins?.length).toBe(2);
146
- expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
146
+ expect(asserts['test-plugin/checkWordsCount' as keyof Asserts]).toBeDefined();
147
147
  });
148
148
 
149
149
  it('should throw error when custom assertion load not exist plugin', async () => {
@@ -163,7 +163,7 @@ describe('resolveStyleguideConfig', () => {
163
163
  );
164
164
  }
165
165
 
166
- expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
166
+ expect(asserts['test-plugin/checkWordsCount' as keyof Asserts]).toBeDefined();
167
167
  });
168
168
 
169
169
  it('should correctly merge assertions from nested config', async () => {
@@ -197,7 +197,7 @@ describe('resolveStyleguideConfig', () => {
197
197
  ]);
198
198
  });
199
199
 
200
- it('should resolve extends with url file config witch contains path to nested config', async () => {
200
+ it('should resolve extends with url file config which contains path to nested config', async () => {
201
201
  const styleguideConfig = {
202
202
  // This points to ./fixtures/resolve-remote-configs/remote-config.yaml
203
203
  extends: [
@@ -464,4 +464,29 @@ describe('resolveConfig', () => {
464
464
  delete apis['petstore'].styleguide.pluginPaths;
465
465
  expect(apis['petstore'].styleguide).toMatchSnapshot();
466
466
  });
467
+
468
+ it('should default to the extends from the main config if no extends defined', async () => {
469
+ const rawConfig: RawConfig = {
470
+ apis: {
471
+ petstore: {
472
+ root: 'some/path',
473
+ styleguide: {
474
+ rules: {
475
+ 'operation-4xx-response': 'error',
476
+ },
477
+ },
478
+ },
479
+ },
480
+ styleguide: {
481
+ extends: ['minimal'],
482
+ rules: {
483
+ 'operation-2xx-response': 'warn',
484
+ },
485
+ },
486
+ };
487
+
488
+ const { apis } = await resolveConfig(rawConfig, configPath);
489
+ expect(apis['petstore'].styleguide.rules).toBeDefined();
490
+ expect(apis['petstore'].styleguide.rules?.['operation-2xx-response']).toEqual('warn'); // from minimal ruleset
491
+ });
467
492
  });
@@ -0,0 +1,4 @@
1
+ lint:
2
+ rules:
3
+ info-contact: warn
4
+ extends: []