@redocly/openapi-core 1.0.0-beta.117 → 1.0.0-beta.119

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 (50) hide show
  1. package/lib/bundle.d.ts +1 -1
  2. package/lib/config/config-resolvers.js +13 -8
  3. package/lib/decorators/common/media-type-examples-override.d.ts +2 -0
  4. package/lib/decorators/common/media-type-examples-override.js +53 -0
  5. package/lib/decorators/oas3/index.d.ts +1 -0
  6. package/lib/decorators/oas3/index.js +2 -0
  7. package/lib/index.d.ts +2 -2
  8. package/lib/index.js +3 -1
  9. package/lib/resolve.js +3 -0
  10. package/lib/rules/common/assertions/asserts.js +10 -2
  11. package/lib/rules/common/assertions/utils.js +12 -6
  12. package/lib/rules/common/spec.js +7 -4
  13. package/lib/rules/oas3/spec-components-invalid-map-name.js +26 -5
  14. package/lib/types/index.d.ts +1 -0
  15. package/lib/types/index.js +9 -1
  16. package/lib/types/oas2.js +16 -0
  17. package/lib/types/oas3.js +28 -0
  18. package/lib/types/oas3_1.js +7 -0
  19. package/lib/types/redocly-yaml.js +1 -0
  20. package/lib/utils.d.ts +1 -0
  21. package/lib/utils.js +6 -1
  22. package/lib/visitors.d.ts +3 -1
  23. package/lib/visitors.js +4 -0
  24. package/lib/walk.js +8 -0
  25. package/package.json +1 -1
  26. package/src/__tests__/__snapshots__/bundle.test.ts.snap +10 -0
  27. package/src/config/config-resolvers.ts +12 -8
  28. package/src/decorators/__tests__/media-type-examples-override.test.ts +665 -0
  29. package/src/decorators/__tests__/resources/request.yaml +3 -0
  30. package/src/decorators/__tests__/resources/response.yaml +3 -0
  31. package/src/decorators/common/media-type-examples-override.ts +79 -0
  32. package/src/decorators/oas3/index.ts +2 -0
  33. package/src/index.ts +2 -1
  34. package/src/resolve.ts +4 -1
  35. package/src/rules/common/__tests__/spec.test.ts +119 -0
  36. package/src/rules/common/assertions/__tests__/asserts.test.ts +89 -19
  37. package/src/rules/common/assertions/asserts.ts +10 -1
  38. package/src/rules/common/assertions/utils.ts +15 -6
  39. package/src/rules/common/spec.ts +8 -5
  40. package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +88 -0
  41. package/src/rules/oas3/spec-components-invalid-map-name.ts +26 -5
  42. package/src/types/index.ts +11 -0
  43. package/src/types/oas2.ts +16 -0
  44. package/src/types/oas3.ts +28 -0
  45. package/src/types/oas3_1.ts +7 -0
  46. package/src/types/redocly-yaml.ts +1 -0
  47. package/src/utils.ts +5 -0
  48. package/src/visitors.ts +7 -1
  49. package/src/walk.ts +15 -1
  50. package/tsconfig.tsbuildinfo +1 -1
package/src/types/oas2.ts CHANGED
@@ -24,6 +24,7 @@ const Root: NodeType = {
24
24
  'x-ignoredHeaderParameters': { type: 'array', items: { type: 'string' } },
25
25
  },
26
26
  required: ['swagger', 'paths', 'info'],
27
+ extensionsPrefix: 'x-',
27
28
  };
28
29
 
29
30
  const Info: NodeType = {
@@ -37,6 +38,7 @@ const Info: NodeType = {
37
38
  'x-logo': 'Logo',
38
39
  },
39
40
  required: ['title', 'version'],
41
+ extensionsPrefix: 'x-',
40
42
  };
41
43
 
42
44
  const Logo: NodeType = {
@@ -46,6 +48,7 @@ const Logo: NodeType = {
46
48
  backgroundColor: { type: 'string' },
47
49
  href: { type: 'string' },
48
50
  },
51
+ extensionsPrefix: 'x-',
49
52
  };
50
53
 
51
54
  const Contact: NodeType = {
@@ -54,6 +57,7 @@ const Contact: NodeType = {
54
57
  url: { type: 'string' },
55
58
  email: { type: 'string' },
56
59
  },
60
+ extensionsPrefix: 'x-',
57
61
  };
58
62
 
59
63
  const License: NodeType = {
@@ -62,6 +66,7 @@ const License: NodeType = {
62
66
  url: { type: 'string' },
63
67
  },
64
68
  required: ['name'],
69
+ extensionsPrefix: 'x-',
65
70
  };
66
71
 
67
72
  const Paths: NodeType = {
@@ -83,6 +88,7 @@ const PathItem: NodeType = {
83
88
  head: 'Operation',
84
89
  patch: 'Operation',
85
90
  },
91
+ extensionsPrefix: 'x-',
86
92
  };
87
93
 
88
94
  const Operation: NodeType = {
@@ -104,6 +110,7 @@ const Operation: NodeType = {
104
110
  'x-hideTryItPanel': { type: 'boolean' },
105
111
  },
106
112
  required: ['responses'],
113
+ extensionsPrefix: 'x-',
107
114
  };
108
115
 
109
116
  const XCodeSample: NodeType = {
@@ -128,6 +135,7 @@ const ExternalDocs: NodeType = {
128
135
  url: { type: 'string' },
129
136
  },
130
137
  required: ['url'],
138
+ extensionsPrefix: 'x-',
131
139
  };
132
140
 
133
141
  const Parameter: NodeType = {
@@ -172,6 +180,7 @@ const Parameter: NodeType = {
172
180
  }
173
181
  }
174
182
  },
183
+ extensionsPrefix: 'x-',
175
184
  };
176
185
 
177
186
  const ParameterItems: NodeType = {
@@ -201,6 +210,7 @@ const ParameterItems: NodeType = {
201
210
  return ['type'];
202
211
  }
203
212
  },
213
+ extensionsPrefix: 'x-',
204
214
  };
205
215
 
206
216
  const Responses: NodeType = {
@@ -220,6 +230,7 @@ const Response: NodeType = {
220
230
  'x-summary': { type: 'string' },
221
231
  },
222
232
  required: ['description'],
233
+ extensionsPrefix: 'x-',
223
234
  };
224
235
 
225
236
  const Examples: NodeType = {
@@ -255,6 +266,7 @@ const Header: NodeType = {
255
266
  return ['type'];
256
267
  }
257
268
  },
269
+ extensionsPrefix: 'x-',
258
270
  };
259
271
 
260
272
  const Tag: NodeType = {
@@ -266,6 +278,7 @@ const Tag: NodeType = {
266
278
  'x-displayName': { type: 'string' },
267
279
  },
268
280
  required: ['name'],
281
+ extensionsPrefix: 'x-',
269
282
  };
270
283
 
271
284
  const TagGroup: NodeType = {
@@ -328,6 +341,7 @@ const Schema: NodeType = {
328
341
  'x-explicitMappingOnly': { type: 'boolean' },
329
342
  'x-enumDescriptions': 'EnumDescriptions',
330
343
  },
344
+ extensionsPrefix: 'x-',
331
345
  };
332
346
 
333
347
  const EnumDescriptions: NodeType = {
@@ -348,6 +362,7 @@ const Xml: NodeType = {
348
362
  attribute: { type: 'boolean' },
349
363
  wrapped: { type: 'boolean' },
350
364
  },
365
+ extensionsPrefix: 'x-',
351
366
  };
352
367
 
353
368
  const SecurityScheme: NodeType = {
@@ -419,6 +434,7 @@ const Example: NodeType = {
419
434
  description: { type: 'string' },
420
435
  externalValue: { type: 'string' },
421
436
  },
437
+ extensionsPrefix: 'x-',
422
438
  };
423
439
 
424
440
  export const Oas2Types: Record<string, NodeType> = {
package/src/types/oas3.ts CHANGED
@@ -17,6 +17,7 @@ const Root: NodeType = {
17
17
  'x-ignoredHeaderParameters': { type: 'array', items: { type: 'string' } },
18
18
  },
19
19
  required: ['openapi', 'paths', 'info'],
20
+ extensionsPrefix: 'x-',
20
21
  };
21
22
 
22
23
  const Tag: NodeType = {
@@ -28,6 +29,7 @@ const Tag: NodeType = {
28
29
  'x-displayName': { type: 'string' },
29
30
  },
30
31
  required: ['name'],
32
+ extensionsPrefix: 'x-',
31
33
  };
32
34
 
33
35
  const TagGroup: NodeType = {
@@ -35,6 +37,7 @@ const TagGroup: NodeType = {
35
37
  name: { type: 'string' },
36
38
  tags: { type: 'array', items: { type: 'string' } },
37
39
  },
40
+ extensionsPrefix: 'x-',
38
41
  };
39
42
 
40
43
  const ExternalDocs: NodeType = {
@@ -43,6 +46,7 @@ const ExternalDocs: NodeType = {
43
46
  url: { type: 'string' },
44
47
  },
45
48
  required: ['url'],
49
+ extensionsPrefix: 'x-',
46
50
  };
47
51
 
48
52
  const Server: NodeType = {
@@ -52,6 +56,7 @@ const Server: NodeType = {
52
56
  variables: 'ServerVariablesMap',
53
57
  },
54
58
  required: ['url'],
59
+ extensionsPrefix: 'x-',
55
60
  };
56
61
 
57
62
  const ServerVariable: NodeType = {
@@ -64,6 +69,7 @@ const ServerVariable: NodeType = {
64
69
  description: null,
65
70
  },
66
71
  required: ['default'],
72
+ extensionsPrefix: 'x-',
67
73
  };
68
74
 
69
75
  const SecurityRequirement: NodeType = {
@@ -82,6 +88,7 @@ const Info: NodeType = {
82
88
  'x-logo': 'Logo',
83
89
  },
84
90
  required: ['title', 'version'],
91
+ extensionsPrefix: 'x-',
85
92
  };
86
93
 
87
94
  const Logo: NodeType = {
@@ -99,6 +106,7 @@ const Contact: NodeType = {
99
106
  url: { type: 'string' },
100
107
  email: { type: 'string' },
101
108
  },
109
+ extensionsPrefix: 'x-',
102
110
  };
103
111
 
104
112
  const License: NodeType = {
@@ -107,6 +115,7 @@ const License: NodeType = {
107
115
  url: { type: 'string' },
108
116
  },
109
117
  required: ['name'],
118
+ extensionsPrefix: 'x-',
110
119
  };
111
120
 
112
121
  const Paths: NodeType = {
@@ -136,6 +145,7 @@ const PathItem: NodeType = {
136
145
  patch: 'Operation',
137
146
  trace: 'Operation',
138
147
  },
148
+ extensionsPrefix: 'x-',
139
149
  };
140
150
 
141
151
  const Parameter: NodeType = {
@@ -158,6 +168,7 @@ const Parameter: NodeType = {
158
168
  },
159
169
  required: ['name', 'in'],
160
170
  requiredOneOf: ['schema', 'content'],
171
+ extensionsPrefix: 'x-',
161
172
  };
162
173
 
163
174
  const Operation: NodeType = {
@@ -182,6 +193,7 @@ const Operation: NodeType = {
182
193
  'x-hideTryItPanel': { type: 'boolean' },
183
194
  },
184
195
  required: ['responses'],
196
+ extensionsPrefix: 'x-',
185
197
  };
186
198
 
187
199
  const XCodeSample: NodeType = {
@@ -199,6 +211,7 @@ const RequestBody: NodeType = {
199
211
  content: 'MediaTypesMap',
200
212
  },
201
213
  required: ['content'],
214
+ extensionsPrefix: 'x-',
202
215
  };
203
216
 
204
217
  const MediaTypesMap: NodeType = {
@@ -213,6 +226,7 @@ const MediaType: NodeType = {
213
226
  examples: 'ExamplesMap',
214
227
  encoding: 'EncodingMap',
215
228
  },
229
+ extensionsPrefix: 'x-',
216
230
  };
217
231
 
218
232
  const Example: NodeType = {
@@ -222,6 +236,7 @@ const Example: NodeType = {
222
236
  description: { type: 'string' },
223
237
  externalValue: { type: 'string' },
224
238
  },
239
+ extensionsPrefix: 'x-',
225
240
  };
226
241
 
227
242
  const Encoding: NodeType = {
@@ -234,6 +249,7 @@ const Encoding: NodeType = {
234
249
  explode: { type: 'boolean' },
235
250
  allowReserved: { type: 'boolean' },
236
251
  },
252
+ extensionsPrefix: 'x-',
237
253
  };
238
254
 
239
255
  const EnumDescriptions: NodeType = {
@@ -258,6 +274,7 @@ const Header: NodeType = {
258
274
  content: 'MediaTypesMap',
259
275
  },
260
276
  requiredOneOf: ['schema', 'content'],
277
+ extensionsPrefix: 'x-',
261
278
  };
262
279
 
263
280
  const Responses: NodeType = {
@@ -275,6 +292,7 @@ const Response: NodeType = {
275
292
  'x-summary': { type: 'string' },
276
293
  },
277
294
  required: ['description'],
295
+ extensionsPrefix: 'x-',
278
296
  };
279
297
 
280
298
  const Link: NodeType = {
@@ -286,6 +304,7 @@ const Link: NodeType = {
286
304
  description: { type: 'string' },
287
305
  server: 'Server',
288
306
  },
307
+ extensionsPrefix: 'x-',
289
308
  };
290
309
 
291
310
  const Schema: NodeType = {
@@ -350,6 +369,7 @@ const Schema: NodeType = {
350
369
  'x-additionalPropertiesName': { type: 'string' },
351
370
  'x-explicitMappingOnly': { type: 'boolean' },
352
371
  },
372
+ extensionsPrefix: 'x-',
353
373
  };
354
374
 
355
375
  const Xml: NodeType = {
@@ -360,6 +380,7 @@ const Xml: NodeType = {
360
380
  attribute: { type: 'boolean' },
361
381
  wrapped: { type: 'boolean' },
362
382
  },
383
+ extensionsPrefix: 'x-',
363
384
  };
364
385
 
365
386
  const SchemaProperties: NodeType = {
@@ -384,6 +405,7 @@ const Discriminator: NodeType = {
384
405
  mapping: 'DiscriminatorMapping',
385
406
  },
386
407
  required: ['propertyName'],
408
+ extensionsPrefix: 'x-',
387
409
  };
388
410
 
389
411
  const Components: NodeType = {
@@ -398,6 +420,7 @@ const Components: NodeType = {
398
420
  links: 'NamedLinks',
399
421
  callbacks: 'NamedCallbacks',
400
422
  },
423
+ extensionsPrefix: 'x-',
401
424
  };
402
425
 
403
426
  const ImplicitFlow: NodeType = {
@@ -407,6 +430,7 @@ const ImplicitFlow: NodeType = {
407
430
  authorizationUrl: { type: 'string' },
408
431
  },
409
432
  required: ['authorizationUrl', 'scopes'],
433
+ extensionsPrefix: 'x-',
410
434
  };
411
435
 
412
436
  const PasswordFlow: NodeType = {
@@ -416,6 +440,7 @@ const PasswordFlow: NodeType = {
416
440
  tokenUrl: { type: 'string' },
417
441
  },
418
442
  required: ['tokenUrl', 'scopes'],
443
+ extensionsPrefix: 'x-',
419
444
  };
420
445
 
421
446
  const ClientCredentials: NodeType = {
@@ -425,6 +450,7 @@ const ClientCredentials: NodeType = {
425
450
  tokenUrl: { type: 'string' },
426
451
  },
427
452
  required: ['tokenUrl', 'scopes'],
453
+ extensionsPrefix: 'x-',
428
454
  };
429
455
 
430
456
  const AuthorizationCode: NodeType = {
@@ -442,6 +468,7 @@ const AuthorizationCode: NodeType = {
442
468
  },
443
469
  },
444
470
  required: ['authorizationUrl', 'tokenUrl', 'scopes'],
471
+ extensionsPrefix: 'x-',
445
472
  };
446
473
 
447
474
  const OAuth2Flows: NodeType = {
@@ -451,6 +478,7 @@ const OAuth2Flows: NodeType = {
451
478
  clientCredentials: 'ClientCredentials',
452
479
  authorizationCode: 'AuthorizationCode',
453
480
  },
481
+ extensionsPrefix: 'x-',
454
482
  };
455
483
 
456
484
  const SecurityScheme: NodeType = {
@@ -16,6 +16,7 @@ const Root: NodeType = {
16
16
  },
17
17
  required: ['openapi', 'info'],
18
18
  requiredOneOf: ['paths', 'components', 'webhooks'],
19
+ extensionsPrefix: 'x-',
19
20
  };
20
21
 
21
22
  const License: NodeType = {
@@ -25,6 +26,7 @@ const License: NodeType = {
25
26
  identifier: { type: 'string' },
26
27
  },
27
28
  required: ['name'],
29
+ extensionsPrefix: 'x-',
28
30
  };
29
31
 
30
32
  const Info: NodeType = {
@@ -36,8 +38,10 @@ const Info: NodeType = {
36
38
  summary: { type: 'string' },
37
39
  contact: 'Contact',
38
40
  license: 'License',
41
+ 'x-logo': 'Logo',
39
42
  },
40
43
  required: ['title', 'version'],
44
+ extensionsPrefix: 'x-',
41
45
  };
42
46
 
43
47
  const Components: NodeType = {
@@ -53,6 +57,7 @@ const Components: NodeType = {
53
57
  callbacks: 'NamedCallbacks',
54
58
  pathItems: 'NamedPathItems',
55
59
  },
60
+ extensionsPrefix: 'x-',
56
61
  };
57
62
 
58
63
  const Operation: NodeType = {
@@ -76,6 +81,7 @@ const Operation: NodeType = {
76
81
  'x-code-samples': listOf('XCodeSample'), // deprecated
77
82
  'x-hideTryItPanel': { type: 'boolean' },
78
83
  },
84
+ extensionsPrefix: 'x-',
79
85
  };
80
86
 
81
87
  const Schema: NodeType = {
@@ -167,6 +173,7 @@ const Schema: NodeType = {
167
173
  $comment: { type: 'string' },
168
174
  'x-tags': { type: 'array', items: { type: 'string' } },
169
175
  },
176
+ extensionsPrefix: 'x-',
170
177
  };
171
178
 
172
179
  const SecurityScheme: NodeType = {
@@ -113,6 +113,7 @@ const nodeTypesList = [
113
113
  'XCodeSample',
114
114
  'XCodeSampleList',
115
115
  'WebhooksMap',
116
+ 'SpecExtension',
116
117
  ];
117
118
 
118
119
  const ConfigStyleguide: NodeType = {
package/src/utils.ts CHANGED
@@ -153,6 +153,11 @@ export function readFileAsStringSync(filePath: string) {
153
153
  return fs.readFileSync(filePath, 'utf-8');
154
154
  }
155
155
 
156
+ export function yamlAndJsonSyncReader<T>(filePath: string): T {
157
+ const content = fs.readFileSync(filePath, 'utf-8');
158
+ return parseYaml(content) as T;
159
+ }
160
+
156
161
  export function isPathParameter(pathSegment: string) {
157
162
  return pathSegment.startsWith('{') && pathSegment.endsWith('}');
158
163
  }
package/src/visitors.ts CHANGED
@@ -45,7 +45,7 @@ import type {
45
45
  Oas2SecurityScheme,
46
46
  } from './typings/swagger';
47
47
 
48
- import type { NormalizedNodeType } from './types';
48
+ import { NormalizedNodeType, SpecExtension } from './types';
49
49
  import type { Stack } from './utils';
50
50
  import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
51
51
  import type { Location } from './ref-utils';
@@ -177,6 +177,7 @@ type Oas3FlatVisitor = {
177
177
  AuthorizationCode?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['authorizationCode']>;
178
178
  OAuth2Flows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
179
179
  SecurityScheme?: VisitFunctionOrObject<Oas3SecurityScheme>;
180
+ SpecExtension?: VisitFunctionOrObject<unknown>;
180
181
  };
181
182
 
182
183
  type Oas2FlatVisitor = {
@@ -202,6 +203,7 @@ type Oas2FlatVisitor = {
202
203
  NamedResponses?: VisitFunctionOrObject<Record<string, Oas2Response>>;
203
204
  NamedParameters?: VisitFunctionOrObject<Record<string, Oas2Parameter>>;
204
205
  SecurityScheme?: VisitFunctionOrObject<Oas2SecurityScheme>;
206
+ SpecExtension?: VisitFunctionOrObject<unknown>;
205
207
  };
206
208
 
207
209
  const legacyTypesMap = {
@@ -354,6 +356,10 @@ export function normalizeVisitors<T extends BaseVisitor>(
354
356
  }
355
357
  }
356
358
 
359
+ if (from.extensionsPrefix) {
360
+ possibleChildren.add(SpecExtension);
361
+ }
362
+
357
363
  for (const fromType of Array.from(possibleChildren.values())) {
358
364
  addWeakNodes(ruleConf, fromType, to, parentContext, stack);
359
365
  }
package/src/walk.ts CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  } from './resolve';
18
18
  import { pushStack, popStack } from './utils';
19
19
  import { OasVersion } from './oas-types';
20
- import { NormalizedNodeType, isNamedType } from './types';
20
+ import { NormalizedNodeType, isNamedType, SpecExtension } from './types';
21
21
  import type { RuleSeverity } from './config';
22
22
 
23
23
  type NonUndefined = string | number | boolean | symbol | bigint | object | Record<string, any>;
@@ -280,6 +280,12 @@ export function walkDocument<T>(opts: {
280
280
  const props = Object.keys(type.properties);
281
281
  if (type.additionalProperties) {
282
282
  props.push(...Object.keys(resolvedNode).filter((k) => !props.includes(k)));
283
+ } else if (type.extensionsPrefix) {
284
+ props.push(
285
+ ...Object.keys(resolvedNode).filter((k) =>
286
+ k.startsWith(type.extensionsPrefix as string)
287
+ )
288
+ );
283
289
  }
284
290
 
285
291
  if (isRef(node)) {
@@ -300,6 +306,14 @@ export function walkDocument<T>(opts: {
300
306
  if (propType === undefined) propType = type.additionalProperties;
301
307
  if (typeof propType === 'function') propType = propType(value, propName);
302
308
 
309
+ if (
310
+ propType === undefined &&
311
+ type.extensionsPrefix &&
312
+ propName.startsWith(type.extensionsPrefix)
313
+ ) {
314
+ propType = SpecExtension;
315
+ }
316
+
303
317
  if (!isNamedType(propType) && propType?.directResolveAs) {
304
318
  propType = propType.directResolveAs;
305
319
  value = { $ref: value };