@redocly/openapi-core 1.0.0-beta.116 → 1.0.0-beta.118

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 (49) 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 +7 -1
  16. package/lib/types/oas2.js +16 -11
  17. package/lib/types/oas3.js +28 -11
  18. package/lib/types/oas3_1.js +6 -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/assertions/__tests__/asserts.test.ts +89 -19
  36. package/src/rules/common/assertions/asserts.ts +10 -1
  37. package/src/rules/common/assertions/utils.ts +15 -6
  38. package/src/rules/common/spec.ts +8 -5
  39. package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +88 -0
  40. package/src/rules/oas3/spec-components-invalid-map-name.ts +26 -5
  41. package/src/types/index.ts +9 -0
  42. package/src/types/oas2.ts +16 -12
  43. package/src/types/oas3.ts +28 -12
  44. package/src/types/oas3_1.ts +6 -0
  45. package/src/types/redocly-yaml.ts +1 -0
  46. package/src/utils.ts +5 -0
  47. package/src/visitors.ts +7 -1
  48. package/src/walk.ts +15 -1
  49. package/tsconfig.tsbuildinfo +1 -1
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 = {
@@ -180,9 +191,9 @@ const Operation: NodeType = {
180
191
  'x-codeSamples': 'XCodeSampleList',
181
192
  'x-code-samples': 'XCodeSampleList', // deprecated
182
193
  'x-hideTryItPanel': { type: 'boolean' },
183
- 'x-meta': 'XMetaList',
184
194
  },
185
195
  required: ['responses'],
196
+ extensionsPrefix: 'x-',
186
197
  };
187
198
 
188
199
  const XCodeSample: NodeType = {
@@ -200,6 +211,7 @@ const RequestBody: NodeType = {
200
211
  content: 'MediaTypesMap',
201
212
  },
202
213
  required: ['content'],
214
+ extensionsPrefix: 'x-',
203
215
  };
204
216
 
205
217
  const MediaTypesMap: NodeType = {
@@ -214,6 +226,7 @@ const MediaType: NodeType = {
214
226
  examples: 'ExamplesMap',
215
227
  encoding: 'EncodingMap',
216
228
  },
229
+ extensionsPrefix: 'x-',
217
230
  };
218
231
 
219
232
  const Example: NodeType = {
@@ -223,6 +236,7 @@ const Example: NodeType = {
223
236
  description: { type: 'string' },
224
237
  externalValue: { type: 'string' },
225
238
  },
239
+ extensionsPrefix: 'x-',
226
240
  };
227
241
 
228
242
  const Encoding: NodeType = {
@@ -235,6 +249,7 @@ const Encoding: NodeType = {
235
249
  explode: { type: 'boolean' },
236
250
  allowReserved: { type: 'boolean' },
237
251
  },
252
+ extensionsPrefix: 'x-',
238
253
  };
239
254
 
240
255
  const EnumDescriptions: NodeType = {
@@ -259,6 +274,7 @@ const Header: NodeType = {
259
274
  content: 'MediaTypesMap',
260
275
  },
261
276
  requiredOneOf: ['schema', 'content'],
277
+ extensionsPrefix: 'x-',
262
278
  };
263
279
 
264
280
  const Responses: NodeType = {
@@ -276,6 +292,7 @@ const Response: NodeType = {
276
292
  'x-summary': { type: 'string' },
277
293
  },
278
294
  required: ['description'],
295
+ extensionsPrefix: 'x-',
279
296
  };
280
297
 
281
298
  const Link: NodeType = {
@@ -287,6 +304,7 @@ const Link: NodeType = {
287
304
  description: { type: 'string' },
288
305
  server: 'Server',
289
306
  },
307
+ extensionsPrefix: 'x-',
290
308
  };
291
309
 
292
310
  const Schema: NodeType = {
@@ -351,6 +369,7 @@ const Schema: NodeType = {
351
369
  'x-additionalPropertiesName': { type: 'string' },
352
370
  'x-explicitMappingOnly': { type: 'boolean' },
353
371
  },
372
+ extensionsPrefix: 'x-',
354
373
  };
355
374
 
356
375
  const Xml: NodeType = {
@@ -361,6 +380,7 @@ const Xml: NodeType = {
361
380
  attribute: { type: 'boolean' },
362
381
  wrapped: { type: 'boolean' },
363
382
  },
383
+ extensionsPrefix: 'x-',
364
384
  };
365
385
 
366
386
  const SchemaProperties: NodeType = {
@@ -385,6 +405,7 @@ const Discriminator: NodeType = {
385
405
  mapping: 'DiscriminatorMapping',
386
406
  },
387
407
  required: ['propertyName'],
408
+ extensionsPrefix: 'x-',
388
409
  };
389
410
 
390
411
  const Components: NodeType = {
@@ -399,6 +420,7 @@ const Components: NodeType = {
399
420
  links: 'NamedLinks',
400
421
  callbacks: 'NamedCallbacks',
401
422
  },
423
+ extensionsPrefix: 'x-',
402
424
  };
403
425
 
404
426
  const ImplicitFlow: NodeType = {
@@ -408,6 +430,7 @@ const ImplicitFlow: NodeType = {
408
430
  authorizationUrl: { type: 'string' },
409
431
  },
410
432
  required: ['authorizationUrl', 'scopes'],
433
+ extensionsPrefix: 'x-',
411
434
  };
412
435
 
413
436
  const PasswordFlow: NodeType = {
@@ -417,6 +440,7 @@ const PasswordFlow: NodeType = {
417
440
  tokenUrl: { type: 'string' },
418
441
  },
419
442
  required: ['tokenUrl', 'scopes'],
443
+ extensionsPrefix: 'x-',
420
444
  };
421
445
 
422
446
  const ClientCredentials: NodeType = {
@@ -426,6 +450,7 @@ const ClientCredentials: NodeType = {
426
450
  tokenUrl: { type: 'string' },
427
451
  },
428
452
  required: ['tokenUrl', 'scopes'],
453
+ extensionsPrefix: 'x-',
429
454
  };
430
455
 
431
456
  const AuthorizationCode: NodeType = {
@@ -443,6 +468,7 @@ const AuthorizationCode: NodeType = {
443
468
  },
444
469
  },
445
470
  required: ['authorizationUrl', 'tokenUrl', 'scopes'],
471
+ extensionsPrefix: 'x-',
446
472
  };
447
473
 
448
474
  const OAuth2Flows: NodeType = {
@@ -452,6 +478,7 @@ const OAuth2Flows: NodeType = {
452
478
  clientCredentials: 'ClientCredentials',
453
479
  authorizationCode: 'AuthorizationCode',
454
480
  },
481
+ extensionsPrefix: 'x-',
455
482
  };
456
483
 
457
484
  const SecurityScheme: NodeType = {
@@ -497,15 +524,6 @@ const SecurityScheme: NodeType = {
497
524
  extensionsPrefix: 'x-',
498
525
  };
499
526
 
500
- const XMeta: NodeType = {
501
- properties: {
502
- title: { type: 'string' },
503
- description: { type: 'string' },
504
- keywords: { type: 'string' },
505
- image: { type: 'string' },
506
- },
507
- };
508
-
509
527
  const XUsePkce: NodeType = {
510
528
  properties: {
511
529
  disableManualConfiguration: { type: 'boolean' },
@@ -574,8 +592,6 @@ export const Oas3Types: Record<string, NodeType> = {
574
592
  SecurityScheme,
575
593
  XCodeSample,
576
594
  XCodeSampleList: listOf('XCodeSample'),
577
- XMeta,
578
- XMetaList: listOf('XMeta'),
579
595
  XUsePkce,
580
596
  WebhooksMap,
581
597
  };
@@ -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 = {
@@ -38,6 +40,7 @@ const Info: NodeType = {
38
40
  license: 'License',
39
41
  },
40
42
  required: ['title', 'version'],
43
+ extensionsPrefix: 'x-',
41
44
  };
42
45
 
43
46
  const Components: NodeType = {
@@ -53,6 +56,7 @@ const Components: NodeType = {
53
56
  callbacks: 'NamedCallbacks',
54
57
  pathItems: 'NamedPathItems',
55
58
  },
59
+ extensionsPrefix: 'x-',
56
60
  };
57
61
 
58
62
  const Operation: NodeType = {
@@ -76,6 +80,7 @@ const Operation: NodeType = {
76
80
  'x-code-samples': listOf('XCodeSample'), // deprecated
77
81
  'x-hideTryItPanel': { type: 'boolean' },
78
82
  },
83
+ extensionsPrefix: 'x-',
79
84
  };
80
85
 
81
86
  const Schema: NodeType = {
@@ -167,6 +172,7 @@ const Schema: NodeType = {
167
172
  $comment: { type: 'string' },
168
173
  'x-tags': { type: 'array', items: { type: 'string' } },
169
174
  },
175
+ extensionsPrefix: 'x-',
170
176
  };
171
177
 
172
178
  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 };