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

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 (42) hide show
  1. package/lib/config/config-resolvers.js +3 -6
  2. package/lib/config/config.d.ts +4 -10
  3. package/lib/config/config.js +1 -1
  4. package/lib/config/load.js +6 -6
  5. package/lib/config/rules.d.ts +6 -3
  6. package/lib/config/rules.js +3 -2
  7. package/lib/config/types.d.ts +3 -0
  8. package/lib/ref-utils.d.ts +1 -0
  9. package/lib/ref-utils.js +5 -1
  10. package/lib/resolve.js +19 -0
  11. package/lib/rules/common/spec.js +6 -0
  12. package/lib/rules/utils.js +3 -0
  13. package/lib/types/oas2.js +11 -7
  14. package/lib/types/oas3.js +15 -10
  15. package/lib/types/oas3_1.js +1 -0
  16. package/lib/types/redocly-yaml.js +5 -0
  17. package/lib/utils.d.ts +1 -0
  18. package/lib/utils.js +7 -1
  19. package/package.json +1 -1
  20. package/src/__tests__/bundle.test.ts +46 -0
  21. package/src/benchmark/benches/rebilly.yaml +36 -28
  22. package/src/config/__tests__/config-resolvers.test.ts +1 -2
  23. package/src/config/__tests__/fixtures/load-redocly.yaml +0 -2
  24. package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +0 -1
  25. package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +0 -1
  26. package/src/config/config-resolvers.ts +2 -12
  27. package/src/config/config.ts +6 -5
  28. package/src/config/load.ts +12 -5
  29. package/src/config/rules.ts +11 -3
  30. package/src/config/types.ts +2 -0
  31. package/src/ref-utils.ts +4 -0
  32. package/src/resolve.ts +25 -3
  33. package/src/rules/common/__tests__/spec.test.ts +170 -0
  34. package/src/rules/common/spec.ts +7 -0
  35. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +32 -0
  36. package/src/rules/utils.ts +4 -0
  37. package/src/types/oas2.ts +11 -7
  38. package/src/types/oas3.ts +15 -10
  39. package/src/types/oas3_1.ts +1 -0
  40. package/src/types/redocly-yaml.ts +5 -0
  41. package/src/utils.ts +6 -0
  42. package/tsconfig.tsbuildinfo +1 -1
@@ -342,7 +342,7 @@ describe('resolveApis', () => {
342
342
  });
343
343
 
344
344
  describe('resolveConfig', () => {
345
- it('should add recommended to top level by default', async () => {
345
+ it('should NOT add recommended to top level by default IF there is a config file', async () => {
346
346
  const rawConfig: RawConfig = {
347
347
  apis: {
348
348
  petstore: {
@@ -373,7 +373,6 @@ describe('resolveConfig', () => {
373
373
  expect(apis['petstore'].styleguide.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
374
374
 
375
375
  expect(apis['petstore'].styleguide.rules).toEqual({
376
- ...(await recommendedStyleguidePreset).rules,
377
376
  'operation-2xx-response': 'warn',
378
377
  'operation-4xx-response': 'error',
379
378
  });
@@ -1,4 +1,2 @@
1
- lint:
2
1
  rules:
3
2
  info-contact: warn
4
- extends: []
@@ -1,4 +1,3 @@
1
- lint:
2
1
  rules:
3
2
  no-invalid-media-type-examples: warn
4
3
  operation-4xx-response: off
@@ -1,4 +1,3 @@
1
- lint:
2
1
  rules:
3
2
  no-invalid-media-type-examples: warn
4
3
  operation-4xx-response: off
@@ -41,25 +41,15 @@ export async function resolveConfig(rawConfig: RawConfig, configPath?: string):
41
41
  }
42
42
 
43
43
  const resolver = new BaseResolver(getResolveConfig(rawConfig.resolve));
44
- const configExtends = rawConfig?.styleguide?.extends ?? ['recommended'];
45
- const recommendedFallback = !rawConfig?.styleguide?.extends;
46
- const styleguideConfig = {
47
- ...rawConfig?.styleguide,
48
- extends: configExtends,
49
- recommendedFallback,
50
- };
51
44
 
52
45
  const apis = await resolveApis({
53
- rawConfig: {
54
- ...rawConfig,
55
- styleguide: styleguideConfig,
56
- },
46
+ rawConfig,
57
47
  configPath,
58
48
  resolver,
59
49
  });
60
50
 
61
51
  const styleguide = await resolveStyleguideConfig({
62
- styleguideConfig,
52
+ styleguideConfig: rawConfig.styleguide,
63
53
  configPath,
64
54
  resolver,
65
55
  });
@@ -4,7 +4,7 @@ import { parseYaml, stringifyYaml } from '../js-yaml';
4
4
  import { slash, doesYamlFileExist } from '../utils';
5
5
  import { NormalizedProblem } from '../walk';
6
6
  import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
7
- import { env } from '../env';
7
+ import { isBrowser, env } from '../env';
8
8
 
9
9
  import type { NodeType } from '../types';
10
10
  import type {
@@ -17,6 +17,7 @@ import type {
17
17
  ResolvedConfig,
18
18
  ResolvedStyleguideConfig,
19
19
  RuleConfig,
20
+ RuleSettings,
20
21
  } from './types';
21
22
  import { getResolveConfig } from './utils';
22
23
 
@@ -50,7 +51,7 @@ function getIgnoreFilePath(configFile?: string): string | undefined {
50
51
  ? path.join(path.dirname(configFile), IGNORE_FILE)
51
52
  : path.join(configFile, IGNORE_FILE);
52
53
  } else {
53
- return typeof process === 'undefined' ? undefined : path.join(process.cwd(), IGNORE_FILE);
54
+ return isBrowser ? undefined : path.join(process.cwd(), IGNORE_FILE);
54
55
  }
55
56
  }
56
57
 
@@ -183,7 +184,7 @@ export class StyleguideConfig {
183
184
  return extendedTypes;
184
185
  }
185
186
 
186
- getRuleSettings(ruleId: string, oasVersion: OasVersion) {
187
+ getRuleSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
187
188
  this._usedRules.add(ruleId);
188
189
  this._usedVersions.add(oasVersion);
189
190
  const settings = this.rules[oasVersion][ruleId] || 'off';
@@ -196,7 +197,7 @@ export class StyleguideConfig {
196
197
  }
197
198
  }
198
199
 
199
- getPreprocessorSettings(ruleId: string, oasVersion: OasVersion) {
200
+ getPreprocessorSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
200
201
  this._usedRules.add(ruleId);
201
202
  this._usedVersions.add(oasVersion);
202
203
 
@@ -210,7 +211,7 @@ export class StyleguideConfig {
210
211
  }
211
212
  }
212
213
 
213
- getDecoratorSettings(ruleId: string, oasVersion: OasVersion) {
214
+ getDecoratorSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
214
215
  this._usedRules.add(ruleId);
215
216
  this._usedVersions.add(oasVersion);
216
217
  const settings = this.decorators[oasVersion][ruleId] || 'off';
@@ -15,18 +15,21 @@ async function addConfigMetadata({
15
15
  customExtends,
16
16
  configPath,
17
17
  tokens,
18
+ files,
19
+ region,
18
20
  }: {
19
21
  rawConfig: RawConfig;
20
22
  customExtends?: string[];
21
23
  configPath?: string;
22
24
  tokens?: RegionalTokenWithValidity[];
25
+ files?: string[];
26
+ region?: Region;
23
27
  }): Promise<Config> {
24
28
  if (customExtends !== undefined) {
25
29
  rawConfig.styleguide = rawConfig.styleguide || {};
26
30
  rawConfig.styleguide.extends = customExtends;
27
31
  } else if (isEmptyObject(rawConfig)) {
28
- // TODO: check if we can add recommended here. add message here?
29
- // rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
32
+ rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
30
33
  }
31
34
 
32
35
  if (tokens?.length) {
@@ -58,7 +61,10 @@ async function addConfigMetadata({
58
61
  }
59
62
  }
60
63
 
61
- return resolveConfig(rawConfig, configPath);
64
+ return resolveConfig(
65
+ { ...rawConfig, files: files ?? rawConfig.files, region: region ?? rawConfig.region },
66
+ configPath
67
+ );
62
68
  }
63
69
 
64
70
  export async function loadConfig(
@@ -71,8 +77,7 @@ export async function loadConfig(
71
77
  } = {}
72
78
  ): Promise<Config> {
73
79
  const { configPath = findConfig(), customExtends, processRawConfig, files, region } = options;
74
- const config = await getConfig(configPath, processRawConfig);
75
- const rawConfig = { ...config, files: files ?? config.files, region: region ?? config.region };
80
+ const rawConfig = await getConfig(configPath, processRawConfig);
76
81
 
77
82
  const redoclyClient = new RedoclyClient();
78
83
  const tokens = await redoclyClient.getTokens();
@@ -82,6 +87,8 @@ export async function loadConfig(
82
87
  customExtends,
83
88
  configPath,
84
89
  tokens,
90
+ files,
91
+ region,
85
92
  });
86
93
  }
87
94
 
@@ -1,13 +1,20 @@
1
1
  import { RuleSet, OasVersion } from '../oas-types';
2
2
  import { StyleguideConfig } from './config';
3
3
  import { isDefined } from '../utils';
4
+ import type { ProblemSeverity } from '../walk';
5
+
6
+ type InitializedRule = {
7
+ severity: ProblemSeverity;
8
+ ruleId: string;
9
+ visitor: any;
10
+ };
4
11
 
5
12
  export function initRules<T extends Function, P extends RuleSet<T>>(
6
13
  rules: P[],
7
14
  config: StyleguideConfig,
8
15
  type: 'rules' | 'preprocessors' | 'decorators',
9
16
  oasVersion: OasVersion
10
- ) {
17
+ ): InitializedRule[] {
11
18
  return rules
12
19
  .flatMap((ruleset) =>
13
20
  Object.keys(ruleset).map((ruleId) => {
@@ -23,19 +30,20 @@ export function initRules<T extends Function, P extends RuleSet<T>>(
23
30
  if (ruleSettings.severity === 'off') {
24
31
  return undefined;
25
32
  }
33
+ const severity: ProblemSeverity = ruleSettings.severity;
26
34
 
27
35
  const visitors = rule(ruleSettings);
28
36
 
29
37
  if (Array.isArray(visitors)) {
30
38
  return visitors.map((visitor: any) => ({
31
- severity: ruleSettings.severity,
39
+ severity,
32
40
  ruleId,
33
41
  visitor: visitor,
34
42
  }));
35
43
  }
36
44
 
37
45
  return {
38
- severity: ruleSettings.severity,
46
+ severity,
39
47
  ruleId,
40
48
  visitor: visitors, // note: actually it is only one visitor object
41
49
  };
@@ -14,6 +14,8 @@ import { Location } from '../ref-utils';
14
14
 
15
15
  export type RuleSeverity = ProblemSeverity | 'off';
16
16
 
17
+ export type RuleSettings = { severity: RuleSeverity };
18
+
17
19
  export type PreprocessorSeverity = RuleSeverity | 'on';
18
20
 
19
21
  export type RuleConfig =
package/src/ref-utils.ts CHANGED
@@ -79,3 +79,7 @@ export function isMappingRef(mapping: string) {
79
79
  mapping.indexOf('/') > -1
80
80
  );
81
81
  }
82
+
83
+ export function isAnchor(ref: string) {
84
+ return /^#[A-Za-z][A-Za-z0-9\-_:.]*$/.test(ref);
85
+ }
package/src/resolve.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { OasRef } from './typings/openapi';
4
- import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl } from './ref-utils';
4
+ import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl, isAnchor } from './ref-utils';
5
5
  import type { YAMLNode, LoadOptions } from 'yaml-ast-parser';
6
6
  import { NormalizedNodeType, isNamedType } from './types';
7
- import { readFileFromUrl, parseYaml } from './utils';
7
+ import { readFileFromUrl, parseYaml, nextTick } from './utils';
8
8
  import { ResolveConfig } from './config/types';
9
9
 
10
10
  export type CollectedRefs = Map<string /* absoluteFilePath */, Document>;
@@ -237,6 +237,7 @@ export async function resolveDocument(opts: {
237
237
  type: any
238
238
  ) {
239
239
  const rootNodeDocAbsoluteRef = rootNodeDocument.source.absoluteRef;
240
+ const anchorRefsMap: Map<string, any> = new Map();
240
241
 
241
242
  walk(rootNode, type, rootNodeDocAbsoluteRef + rootNodePointer);
242
243
 
@@ -252,6 +253,11 @@ export async function resolveDocument(opts: {
252
253
 
253
254
  seedNodes.add(nodeId);
254
255
 
256
+ const [_, anchor] = Object.entries(node).find(([key]) => key === '$anchor') || [];
257
+ if (anchor) {
258
+ anchorRefsMap.set(`#${anchor}`, node);
259
+ }
260
+
255
261
  if (Array.isArray(node)) {
256
262
  const itemsType = type.items;
257
263
  // we continue resolving unknown types, but stop early on known scalars
@@ -313,6 +319,22 @@ export async function resolveDocument(opts: {
313
319
  if (hasRef(refStack.prev, ref)) {
314
320
  throw new Error('Self-referencing circular pointer');
315
321
  }
322
+
323
+ if (isAnchor(ref.$ref)) {
324
+ // Wait for all anchors in the document to be collected firstly.
325
+ await nextTick();
326
+ const resolvedRef: ResolvedRef = {
327
+ resolved: true,
328
+ isRemote: false,
329
+ node: anchorRefsMap.get(ref.$ref),
330
+ document,
331
+ nodePointer: ref.$ref,
332
+ };
333
+ const refId = makeRefId(document.source.absoluteRef, ref.$ref);
334
+ resolvedRefMap.set(refId, resolvedRef);
335
+ return resolvedRef;
336
+ }
337
+
316
338
  const { uri, pointer } = parseRef(ref.$ref);
317
339
  const isRemote = uri !== null;
318
340
  let targetDoc: Document;
@@ -336,7 +358,7 @@ export async function resolveDocument(opts: {
336
358
  }
337
359
 
338
360
  let resolvedRef: ResolvedRef = {
339
- resolved: true as const,
361
+ resolved: true,
340
362
  document: targetDoc,
341
363
  isRemote,
342
364
  node: document.parsed,
@@ -138,6 +138,176 @@ describe('Oas3 spec', () => {
138
138
  ]
139
139
  `);
140
140
  });
141
+
142
+ it('should report on nullable without type', async () => {
143
+ const document = parseYamlToDocument(
144
+ outdent`
145
+ openapi: 3.0.0
146
+ components:
147
+ requestBodies:
148
+ TestRequestBody:
149
+ content:
150
+ application/json:
151
+ schema:
152
+ nullable: true
153
+ `,
154
+ 'foobar.yaml'
155
+ );
156
+
157
+ const results = await lintDocument({
158
+ externalRefResolver: new BaseResolver(),
159
+ document,
160
+ config: await makeConfig({ spec: 'error' }),
161
+ });
162
+
163
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
164
+ Array [
165
+ Object {
166
+ "from": undefined,
167
+ "location": Array [
168
+ Object {
169
+ "pointer": "#/",
170
+ "reportOnKey": true,
171
+ "source": "foobar.yaml",
172
+ },
173
+ ],
174
+ "message": "The field \`paths\` must be present on this level.",
175
+ "ruleId": "spec",
176
+ "severity": "error",
177
+ "suggest": Array [],
178
+ },
179
+ Object {
180
+ "from": undefined,
181
+ "location": Array [
182
+ Object {
183
+ "pointer": "#/",
184
+ "reportOnKey": true,
185
+ "source": "foobar.yaml",
186
+ },
187
+ ],
188
+ "message": "The field \`info\` must be present on this level.",
189
+ "ruleId": "spec",
190
+ "severity": "error",
191
+ "suggest": Array [],
192
+ },
193
+ Object {
194
+ "location": Array [
195
+ Object {
196
+ "pointer": "#/components/requestBodies/TestRequestBody/content/application~1json/schema/nullable",
197
+ "reportOnKey": false,
198
+ "source": "foobar.yaml",
199
+ },
200
+ ],
201
+ "message": "The \`type\` field must be defined when the \`nullable\` field is used.",
202
+ "ruleId": "spec",
203
+ "severity": "error",
204
+ "suggest": Array [],
205
+ },
206
+ ]
207
+ `);
208
+ });
209
+
210
+ it('should report on nullable with type defined in allOf', async () => {
211
+ const document = parseYamlToDocument(
212
+ outdent`
213
+ openapi: 3.0.0
214
+ components:
215
+ requestBodies:
216
+ TestRequestBody:
217
+ content:
218
+ application/json:
219
+ schema:
220
+ nullable: true
221
+ allOf:
222
+ - $ref: "#/components/requestBodies/TestSchema"
223
+ schemas:
224
+ TestSchema:
225
+ title: TestSchema
226
+ type: object
227
+ `,
228
+ 'foobar.yaml'
229
+ );
230
+
231
+ const results = await lintDocument({
232
+ externalRefResolver: new BaseResolver(),
233
+ document,
234
+ config: await makeConfig({ spec: 'error' }),
235
+ });
236
+
237
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
238
+ Array [
239
+ Object {
240
+ "from": undefined,
241
+ "location": Array [
242
+ Object {
243
+ "pointer": "#/",
244
+ "reportOnKey": true,
245
+ "source": "foobar.yaml",
246
+ },
247
+ ],
248
+ "message": "The field \`paths\` must be present on this level.",
249
+ "ruleId": "spec",
250
+ "severity": "error",
251
+ "suggest": Array [],
252
+ },
253
+ Object {
254
+ "from": undefined,
255
+ "location": Array [
256
+ Object {
257
+ "pointer": "#/",
258
+ "reportOnKey": true,
259
+ "source": "foobar.yaml",
260
+ },
261
+ ],
262
+ "message": "The field \`info\` must be present on this level.",
263
+ "ruleId": "spec",
264
+ "severity": "error",
265
+ "suggest": Array [],
266
+ },
267
+ Object {
268
+ "location": Array [
269
+ Object {
270
+ "pointer": "#/components/requestBodies/TestRequestBody/content/application~1json/schema/nullable",
271
+ "reportOnKey": false,
272
+ "source": "foobar.yaml",
273
+ },
274
+ ],
275
+ "message": "The \`type\` field must be defined when the \`nullable\` field is used.",
276
+ "ruleId": "spec",
277
+ "severity": "error",
278
+ "suggest": Array [],
279
+ },
280
+ Object {
281
+ "from": undefined,
282
+ "location": Array [
283
+ Object {
284
+ "pointer": "#/components/requestBodies/schemas",
285
+ "reportOnKey": true,
286
+ "source": "foobar.yaml",
287
+ },
288
+ ],
289
+ "message": "The field \`content\` must be present on this level.",
290
+ "ruleId": "spec",
291
+ "severity": "error",
292
+ "suggest": Array [],
293
+ },
294
+ Object {
295
+ "from": undefined,
296
+ "location": Array [
297
+ Object {
298
+ "pointer": "#/components/requestBodies/schemas/TestSchema",
299
+ "reportOnKey": true,
300
+ "source": "foobar.yaml",
301
+ },
302
+ ],
303
+ "message": "Property \`TestSchema\` is not expected here.",
304
+ "ruleId": "spec",
305
+ "severity": "error",
306
+ "suggest": Array [],
307
+ },
308
+ ]
309
+ `);
310
+ });
141
311
  });
142
312
 
143
313
  describe('Oas3.1 spec', () => {
@@ -158,6 +158,13 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
158
158
  });
159
159
  }
160
160
  }
161
+
162
+ if (propName === 'nullable' && !node.type) {
163
+ report({
164
+ message: 'The `type` field must be defined when the `nullable` field is used.',
165
+ location: location.child([propName]),
166
+ });
167
+ }
161
168
  }
162
169
  },
163
170
  };
@@ -438,4 +438,36 @@ describe('no-invalid-media-type-examples', () => {
438
438
  ]
439
439
  `);
440
440
  });
441
+
442
+ it('should not report if allOf used with discriminator', async () => {
443
+ const document = parseYamlToDocument(
444
+ outdent`
445
+ openapi: 3.0.0
446
+ paths:
447
+ /pet:
448
+ get:
449
+ responses:
450
+ '200':
451
+ content:
452
+ application/json:
453
+ schema:
454
+ discriminator:
455
+ propertyName: powerSource
456
+ mapping: {}
457
+ allOf: []
458
+ examples:
459
+ first:
460
+ value: {}
461
+ `,
462
+ 'foobar.yaml'
463
+ );
464
+
465
+ const results = await lintDocument({
466
+ externalRefResolver: new BaseResolver(),
467
+ document,
468
+ config: await makeConfig({ 'no-invalid-media-type-examples': 'error' }),
469
+ });
470
+
471
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
472
+ });
441
473
  });
@@ -118,6 +118,10 @@ export function validateExample(
118
118
  }
119
119
  }
120
120
  } catch (e) {
121
+ if (e.message === 'discriminator: requires oneOf or anyOf composite keyword') {
122
+ return;
123
+ }
124
+
121
125
  report({
122
126
  message: `Example validation errored: ${e.message}.`,
123
127
  location: location.child('schema'),
package/src/types/oas2.ts CHANGED
@@ -16,8 +16,8 @@ const Root: NodeType = {
16
16
  parameters: 'NamedParameters',
17
17
  responses: 'NamedResponses',
18
18
  securityDefinitions: 'NamedSecuritySchemes',
19
- security: listOf('SecurityRequirement'),
20
- tags: listOf('Tag'),
19
+ security: 'SecurityRequirementList',
20
+ tags: 'TagList',
21
21
  externalDocs: 'ExternalDocs',
22
22
  },
23
23
  required: ['swagger', 'paths', 'info'],
@@ -60,7 +60,7 @@ const Paths: NodeType = {
60
60
  const PathItem: NodeType = {
61
61
  properties: {
62
62
  $ref: { type: 'string' }, // TODO: verify special $ref handling for Path Item
63
- parameters: listOf('Parameter'),
63
+ parameters: 'ParameterList',
64
64
 
65
65
  get: 'Operation',
66
66
  put: 'Operation',
@@ -81,13 +81,13 @@ const Operation: NodeType = {
81
81
  operationId: { type: 'string' },
82
82
  consumes: { type: 'array', items: { type: 'string' } },
83
83
  produces: { type: 'array', items: { type: 'string' } },
84
- parameters: listOf('Parameter'),
84
+ parameters: 'ParameterList',
85
85
  responses: 'Responses',
86
86
  schemes: { type: 'array', items: { type: 'string' } },
87
87
  deprecated: { type: 'boolean' },
88
- security: listOf('SecurityRequirement'),
89
- 'x-codeSamples': listOf('XCodeSample'),
90
- 'x-code-samples': listOf('XCodeSample'), // deprecated
88
+ security: 'SecurityRequirementList',
89
+ 'x-codeSamples': 'XCodeSampleList',
90
+ 'x-code-samples': 'XCodeSampleList', // deprecated
91
91
  'x-hideTryItPanel': { type: 'boolean' },
92
92
  },
93
93
  required: ['responses'],
@@ -371,8 +371,10 @@ const SecurityRequirement: NodeType = {
371
371
  export const Oas2Types: Record<string, NodeType> = {
372
372
  Root,
373
373
  Tag,
374
+ TagList: listOf('Tag'),
374
375
  ExternalDocs,
375
376
  SecurityRequirement,
377
+ SecurityRequirementList: listOf('SecurityRequirement'),
376
378
  Info,
377
379
  Contact,
378
380
  License,
@@ -380,6 +382,7 @@ export const Oas2Types: Record<string, NodeType> = {
380
382
  PathItem,
381
383
  Parameter,
382
384
  ParameterItems,
385
+ ParameterList: listOf('Parameter'),
383
386
  Operation,
384
387
  Examples,
385
388
  Header,
@@ -394,4 +397,5 @@ export const Oas2Types: Record<string, NodeType> = {
394
397
  NamedSecuritySchemes: mapOf('SecurityScheme'),
395
398
  SecurityScheme,
396
399
  XCodeSample,
400
+ XCodeSampleList: listOf('XCodeSample'),
397
401
  };
package/src/types/oas3.ts CHANGED
@@ -6,9 +6,9 @@ const Root: NodeType = {
6
6
  properties: {
7
7
  openapi: null,
8
8
  info: 'Info',
9
- servers: listOf('Server'),
10
- security: listOf('SecurityRequirement'),
11
- tags: listOf('Tag'),
9
+ servers: 'ServerList',
10
+ security: 'SecurityRequirementList',
11
+ tags: 'TagList',
12
12
  externalDocs: 'ExternalDocs',
13
13
  paths: 'Paths',
14
14
  components: 'Components',
@@ -102,8 +102,8 @@ const WebhooksMap: NodeType = {
102
102
  const PathItem: NodeType = {
103
103
  properties: {
104
104
  $ref: { type: 'string' }, // TODO: verify special $ref handling for Path Item
105
- servers: listOf('Server'),
106
- parameters: listOf('Parameter'),
105
+ servers: 'ServerList',
106
+ parameters: 'ParameterList',
107
107
  summary: { type: 'string' },
108
108
  description: { type: 'string' },
109
109
  get: 'Operation',
@@ -149,15 +149,15 @@ const Operation: NodeType = {
149
149
  description: { type: 'string' },
150
150
  externalDocs: 'ExternalDocs',
151
151
  operationId: { type: 'string' },
152
- parameters: listOf('Parameter'),
153
- security: listOf('SecurityRequirement'),
154
- servers: listOf('Server'),
152
+ parameters: 'ParameterList',
153
+ security: 'SecurityRequirementList',
154
+ servers: 'ServerList',
155
155
  requestBody: 'RequestBody',
156
156
  responses: 'Responses',
157
157
  deprecated: { type: 'boolean' },
158
158
  callbacks: 'CallbacksMap',
159
- 'x-codeSamples': listOf('XCodeSample'),
160
- 'x-code-samples': listOf('XCodeSample'), // deprecated
159
+ 'x-codeSamples': 'XCodeSampleList',
160
+ 'x-code-samples': 'XCodeSampleList', // deprecated
161
161
  'x-hideTryItPanel': { type: 'boolean' },
162
162
  },
163
163
  required: ['responses'],
@@ -462,17 +462,21 @@ const SecurityScheme: NodeType = {
462
462
  export const Oas3Types: Record<string, NodeType> = {
463
463
  Root,
464
464
  Tag,
465
+ TagList: listOf('Tag'),
465
466
  ExternalDocs,
466
467
  Server,
468
+ ServerList: listOf('Server'),
467
469
  ServerVariable,
468
470
  ServerVariablesMap: mapOf('ServerVariable'),
469
471
  SecurityRequirement,
472
+ SecurityRequirementList: listOf('SecurityRequirement'),
470
473
  Info,
471
474
  Contact,
472
475
  License,
473
476
  Paths,
474
477
  PathItem,
475
478
  Parameter,
479
+ ParameterList: listOf('Parameter'),
476
480
  Operation,
477
481
  Callback: mapOf('PathItem'),
478
482
  CallbacksMap: mapOf('Callback'),
@@ -511,5 +515,6 @@ export const Oas3Types: Record<string, NodeType> = {
511
515
  OAuth2Flows,
512
516
  SecurityScheme,
513
517
  XCodeSample,
518
+ XCodeSampleList: listOf('XCodeSample'),
514
519
  WebhooksMap,
515
520
  };
@@ -81,6 +81,7 @@ const Operation: NodeType = {
81
81
  const Schema: NodeType = {
82
82
  properties: {
83
83
  $id: { type: 'string' },
84
+ $anchor: { type: 'string' },
84
85
  id: { type: 'string' },
85
86
  $schema: { type: 'string' },
86
87
  definitions: 'NamedSchemas',