@redocly/openapi-core 1.0.0-beta.109 → 1.0.0-beta.111

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 (88) hide show
  1. package/README.md +2 -2
  2. package/lib/config/config-resolvers.js +44 -25
  3. package/lib/config/config.d.ts +1 -0
  4. package/lib/config/config.js +1 -0
  5. package/lib/config/load.d.ts +8 -2
  6. package/lib/config/load.js +4 -2
  7. package/lib/config/types.d.ts +10 -0
  8. package/lib/config/utils.js +2 -2
  9. package/lib/rules/ajv.d.ts +1 -1
  10. package/lib/rules/ajv.js +5 -5
  11. package/lib/rules/common/assertions/asserts.d.ts +3 -5
  12. package/lib/rules/common/assertions/asserts.js +137 -97
  13. package/lib/rules/common/assertions/index.js +2 -6
  14. package/lib/rules/common/assertions/utils.d.ts +12 -6
  15. package/lib/rules/common/assertions/utils.js +33 -20
  16. package/lib/rules/common/no-ambiguous-paths.js +1 -1
  17. package/lib/rules/common/no-identical-paths.js +4 -4
  18. package/lib/rules/common/operation-2xx-response.js +2 -2
  19. package/lib/rules/common/operation-4xx-response.js +2 -2
  20. package/lib/rules/common/path-not-include-query.js +1 -1
  21. package/lib/rules/common/path-params-defined.js +7 -2
  22. package/lib/rules/common/response-contains-header.js +2 -2
  23. package/lib/rules/common/security-defined.js +10 -5
  24. package/lib/rules/common/spec.js +14 -12
  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 +18 -2
  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 +30 -5
  35. package/lib/utils.d.ts +1 -0
  36. package/lib/utils.js +13 -1
  37. package/lib/visitors.d.ts +7 -6
  38. package/lib/visitors.js +11 -3
  39. package/package.json +3 -5
  40. package/src/__tests__/__snapshots__/bundle.test.ts.snap +1 -1
  41. package/src/__tests__/lint.test.ts +88 -0
  42. package/src/__tests__/utils.test.ts +11 -0
  43. package/src/__tests__/walk.test.ts +2 -2
  44. package/src/config/__tests__/config-resolvers.test.ts +62 -1
  45. package/src/config/__tests__/config.test.ts +5 -0
  46. package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +16 -0
  47. package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +16 -0
  48. package/src/config/__tests__/fixtures/resolve-config/plugin.js +11 -0
  49. package/src/config/__tests__/load.test.ts +1 -1
  50. package/src/config/__tests__/resolve-plugins.test.ts +3 -3
  51. package/src/config/config-resolvers.ts +30 -6
  52. package/src/config/config.ts +2 -0
  53. package/src/config/load.ts +10 -4
  54. package/src/config/types.ts +13 -0
  55. package/src/config/utils.ts +1 -0
  56. package/src/rules/ajv.ts +4 -4
  57. package/src/rules/common/__tests__/operation-2xx-response.test.ts +37 -0
  58. package/src/rules/common/__tests__/operation-4xx-response.test.ts +37 -0
  59. package/src/rules/common/__tests__/path-params-defined.test.ts +69 -0
  60. package/src/rules/common/__tests__/security-defined.test.ts +6 -6
  61. package/src/rules/common/__tests__/spec.test.ts +125 -0
  62. package/src/rules/common/assertions/__tests__/asserts.test.ts +491 -428
  63. package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
  64. package/src/rules/common/assertions/asserts.ts +155 -97
  65. package/src/rules/common/assertions/index.ts +2 -11
  66. package/src/rules/common/assertions/utils.ts +66 -36
  67. package/src/rules/common/no-ambiguous-paths.ts +1 -1
  68. package/src/rules/common/no-identical-paths.ts +4 -4
  69. package/src/rules/common/operation-2xx-response.ts +2 -2
  70. package/src/rules/common/operation-4xx-response.ts +2 -2
  71. package/src/rules/common/path-not-include-query.ts +1 -1
  72. package/src/rules/common/path-params-defined.ts +9 -2
  73. package/src/rules/common/response-contains-header.ts +6 -1
  74. package/src/rules/common/security-defined.ts +10 -5
  75. package/src/rules/common/spec.ts +15 -11
  76. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +51 -2
  77. package/src/rules/oas3/__tests__/response-contains-header.test.ts +116 -0
  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 +24 -1
  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 +30 -4
  86. package/src/utils.ts +13 -0
  87. package/src/visitors.ts +25 -10
  88. package/tsconfig.tsbuildinfo +1 -1
@@ -13,10 +13,11 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
13
13
  }
14
14
  >();
15
15
 
16
+ const operationsWithoutSecurity: Location[] = [];
16
17
  let eachOperationHasSecurity: boolean = true;
17
18
 
18
19
  return {
19
- DefinitionRoot: {
20
+ Root: {
20
21
  leave(root: Oas2Definition | Oas3Definition, { report }: UserContext) {
21
22
  for (const [name, scheme] of referencedSchemes.entries()) {
22
23
  if (scheme.defined) continue;
@@ -31,9 +32,12 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
31
32
  if (root.security || eachOperationHasSecurity) {
32
33
  return;
33
34
  } else {
34
- report({
35
- message: `Every API should have security defined on the root level or for each operation.`,
36
- });
35
+ for (const operationLocation of operationsWithoutSecurity) {
36
+ report({
37
+ message: `Every operation should have security defined on it or on the root level.`,
38
+ location: operationLocation.key(),
39
+ });
40
+ }
37
41
  }
38
42
  },
39
43
  },
@@ -51,9 +55,10 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
51
55
  }
52
56
  }
53
57
  },
54
- Operation(operation: Oas2Operation | Oas3Operation) {
58
+ Operation(operation: Oas2Operation | Oas3Operation, { location }: UserContext) {
55
59
  if (!operation?.security) {
56
60
  eachOperationHasSecurity = false;
61
+ operationsWithoutSecurity.push(location);
57
62
  }
58
63
  },
59
64
  };
@@ -1,8 +1,9 @@
1
1
  import type { Oas3Rule, Oas2Rule } from '../../visitors';
2
2
  import { isNamedType } from '../../types';
3
- import { oasTypeOf, matchesJsonSchemaType, getSuggest } from '../utils';
3
+ import { oasTypeOf, matchesJsonSchemaType, getSuggest, validateSchemaEnumType } from '../utils';
4
4
  import { isRef } from '../../ref-utils';
5
5
  import { isPlainObject } from '../../utils';
6
+ import { UserContext } from '../../walk';
6
7
 
7
8
  export const OasSpec: Oas3Rule | Oas2Rule = () => {
8
9
  return {
@@ -114,17 +115,20 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
114
115
  propValue = resolve(propValue).node;
115
116
  }
116
117
 
117
- if (propSchema.enum) {
118
- if (!propSchema.enum.includes(propValue)) {
119
- report({
120
- location: propLocation,
121
- message: `\`${propName}\` can be one of the following only: ${propSchema.enum
122
- .map((i) => `"${i}"`)
123
- .join(', ')}.`,
124
- from: refLocation,
125
- suggest: getSuggest(propValue, propSchema.enum),
126
- });
118
+ if (propSchema.items && propSchema.items?.enum && Array.isArray(propValue)) {
119
+ for (let i = 0; i < propValue.length; i++) {
120
+ validateSchemaEnumType(propSchema.items?.enum, propValue[i], propName, refLocation, {
121
+ report,
122
+ location: location.child([propName, i]),
123
+ } as UserContext);
127
124
  }
125
+ }
126
+
127
+ if (propSchema.enum) {
128
+ validateSchemaEnumType(propSchema.enum, propValue, propName, refLocation, {
129
+ report,
130
+ location: location.child([propName]),
131
+ } as UserContext);
128
132
  } else if (propSchema.type && !matchesJsonSchemaType(propValue, propSchema.type, false)) {
129
133
  report({
130
134
  message: `Expected type \`${propSchema.type}\` but got \`${propValueType}\`.`,
@@ -128,7 +128,7 @@ describe('no-invalid-media-type-examples', () => {
128
128
  "source": "foobar.yaml",
129
129
  },
130
130
  ],
131
- "message": "Example value must conform to the schema: must NOT have additional properties \`c\`.",
131
+ "message": "Example value must conform to the schema: must NOT have unevaluated properties \`c\`.",
132
132
  "ruleId": "no-invalid-media-type-examples",
133
133
  "severity": "error",
134
134
  "suggest": Array [],
@@ -137,7 +137,7 @@ describe('no-invalid-media-type-examples', () => {
137
137
  `);
138
138
  });
139
139
 
140
- it('should not on invalid example with allowAdditionalProperties', async () => {
140
+ it('should not report on valid example with allowAdditionalProperties', async () => {
141
141
  const document = parseYamlToDocument(
142
142
  outdent`
143
143
  openapi: 3.0.0
@@ -177,6 +177,55 @@ describe('no-invalid-media-type-examples', () => {
177
177
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
178
178
  });
179
179
 
180
+ it('should not report on valid example with allowAdditionalProperties and allOf and $ref', async () => {
181
+ const document = parseYamlToDocument(
182
+ outdent`
183
+ openapi: 3.0.0
184
+ components:
185
+ schemas:
186
+ C:
187
+ properties:
188
+ c:
189
+ type: string
190
+ paths:
191
+ /pet:
192
+ get:
193
+ responses:
194
+ 200:
195
+ content:
196
+ application/json:
197
+ example:
198
+ a: "string"
199
+ b: 13
200
+ c: "string"
201
+ schema:
202
+ type: object
203
+ allOf:
204
+ - $ref: '#/components/schemas/C'
205
+ properties:
206
+ a:
207
+ type: string
208
+ b:
209
+ type: number
210
+
211
+ `,
212
+ 'foobar.yaml'
213
+ );
214
+
215
+ const results = await lintDocument({
216
+ externalRefResolver: new BaseResolver(),
217
+ document,
218
+ config: await makeConfig({
219
+ 'no-invalid-media-type-examples': {
220
+ severity: 'error',
221
+ allowAdditionalProperties: false,
222
+ },
223
+ }),
224
+ });
225
+
226
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
227
+ });
228
+
180
229
  it('should not on invalid examples', async () => {
181
230
  const document = parseYamlToDocument(
182
231
  outdent`
@@ -270,4 +270,120 @@ describe('Oas3 response-contains-header', () => {
270
270
  });
271
271
  expect(results).toMatchInlineSnapshot(`Array []`);
272
272
  });
273
+
274
+ it('should not report response object containing header name upper cased', async () => {
275
+ const document = parseYamlToDocument(outdent`
276
+ openapi: 3.0.3
277
+ info:
278
+ version: 3.0.0
279
+ paths:
280
+ /store/subscribe:
281
+ post:
282
+ responses:
283
+ '200':
284
+ description: successful operation
285
+ headers:
286
+ X-Test-Header:
287
+ description: calls per hour allowed by the user
288
+ schema:
289
+ type: integer
290
+ format: int32
291
+ `);
292
+
293
+ const results = await lintDocument({
294
+ externalRefResolver: new BaseResolver(),
295
+ document,
296
+ config: await makeConfig({
297
+ 'response-contains-header': {
298
+ severity: 'error',
299
+ names: { '2XX': ['x-test-header'] },
300
+ },
301
+ }),
302
+ });
303
+ expect(results).toMatchInlineSnapshot(`Array []`);
304
+ });
305
+
306
+ it('should not report response object containing header name in the rule upper cased', async () => {
307
+ const document = parseYamlToDocument(outdent`
308
+ openapi: 3.0.3
309
+ info:
310
+ version: 3.0.0
311
+ paths:
312
+ /store/subscribe:
313
+ post:
314
+ responses:
315
+ '200':
316
+ description: successful operation
317
+ headers:
318
+ x-test-header:
319
+ description: calls per hour allowed by the user
320
+ schema:
321
+ type: integer
322
+ format: int32
323
+ `);
324
+
325
+ const results = await lintDocument({
326
+ externalRefResolver: new BaseResolver(),
327
+ document,
328
+ config: await makeConfig({
329
+ 'response-contains-header': {
330
+ severity: 'error',
331
+ names: { '2XX': ['X-Test-Header'] },
332
+ },
333
+ }),
334
+ });
335
+ expect(results).toMatchInlineSnapshot(`Array []`);
336
+ });
337
+
338
+ it('should report even if the response is null', async () => {
339
+ const document = parseYamlToDocument(
340
+ outdent`
341
+ openapi: 3.0.0
342
+ paths:
343
+ '/test/':
344
+ put:
345
+ responses:
346
+ '200': null
347
+ `,
348
+ 'foobar.yaml'
349
+ );
350
+
351
+ const results = await lintDocument({
352
+ externalRefResolver: new BaseResolver(),
353
+ document,
354
+ config: await makeConfig({
355
+ 'response-contains-header': {
356
+ severity: 'error',
357
+ names: { '2XX': ['X-Test-Header'] },
358
+ },
359
+ }),
360
+ });
361
+
362
+ expect(results).toMatchInlineSnapshot(`
363
+ Array [
364
+ Object {
365
+ "location": Array [
366
+ Object {
367
+ "pointer": "#/paths/~1test~1/put/responses/200/headers",
368
+ "reportOnKey": true,
369
+ "source": Source {
370
+ "absoluteRef": "foobar.yaml",
371
+ "body": "openapi: 3.0.0
372
+ paths:
373
+ '/test/':
374
+ put:
375
+ responses:
376
+ '200': null",
377
+ "mimeType": undefined,
378
+ },
379
+ },
380
+ ],
381
+ "message": "Response object must contain a \\"X-Test-Header\\" header.",
382
+ "ruleId": "response-contains-header",
383
+ "severity": "error",
384
+ "suggest": Array [],
385
+ },
386
+ ]
387
+ `);
388
+ });
273
389
  });
@@ -5,7 +5,7 @@ import { validateMimeTypeOAS3 } from '../../utils';
5
5
 
6
6
  export const RequestMimeType: Oas3Rule = ({ allowedValues }) => {
7
7
  return {
8
- PathsMap: {
8
+ Paths: {
9
9
  RequestBody: {
10
10
  leave(requestBody: Oas3RequestBody, ctx: UserContext) {
11
11
  validateMimeTypeOAS3({ type: 'consumes', value: requestBody }, ctx, allowedValues);
@@ -5,7 +5,7 @@ import { validateMimeTypeOAS3 } from '../../utils';
5
5
 
6
6
  export const ResponseMimeType: Oas3Rule = ({ allowedValues }) => {
7
7
  return {
8
- PathsMap: {
8
+ Paths: {
9
9
  Response: {
10
10
  leave(response: Oas3Response, ctx: UserContext) {
11
11
  validateMimeTypeOAS3({ type: 'produces', value: response }, ctx, allowedValues);
@@ -41,7 +41,7 @@ export const Stats = (statsAccumulator: StatsAccumulator) => {
41
41
  },
42
42
  },
43
43
  },
44
- PathsMap: {
44
+ Paths: {
45
45
  PathItem: {
46
46
  leave() {
47
47
  statsAccumulator.pathItems.total++;
@@ -109,7 +109,8 @@ export function validateExample(
109
109
  message: `Example value must conform to the schema: ${error.message}.`,
110
110
  location: {
111
111
  ...new Location(dataLoc.source, error.instancePath),
112
- reportOnKey: error.keyword === 'additionalProperties',
112
+ reportOnKey:
113
+ error.keyword === 'unevaluatedProperties' || error.keyword === 'additionalProperties',
113
114
  },
114
115
  from: location,
115
116
  suggest: error.suggest,
@@ -137,3 +138,25 @@ export function getAdditionalPropertiesOption(opts: Record<string, any>): boolea
137
138
  showWarningForDeprecatedField('disallowAdditionalProperties', 'allowAdditionalProperties');
138
139
  return !opts.disallowAdditionalProperties;
139
140
  }
141
+
142
+ export function validateSchemaEnumType(
143
+ schemaEnum: string[],
144
+ propertyValue: string,
145
+ propName: string,
146
+ refLocation: Location | undefined,
147
+ { report, location }: UserContext
148
+ ) {
149
+ if (!schemaEnum) {
150
+ return;
151
+ }
152
+ if (!schemaEnum.includes(propertyValue === null ? 'null' : propertyValue)) {
153
+ report({
154
+ location,
155
+ message: `\`${propName}\` can be one of the following only: ${schemaEnum
156
+ .map((type) => `"${type}"`)
157
+ .join(', ')}.`,
158
+ from: refLocation,
159
+ suggest: getSuggest(propertyValue, schemaEnum),
160
+ });
161
+ }
162
+ }
package/src/types/oas2.ts CHANGED
@@ -11,7 +11,7 @@ const Root: NodeType = {
11
11
  schemes: { type: 'array', items: { type: 'string' } },
12
12
  consumes: { type: 'array', items: { type: 'string' } },
13
13
  produces: { type: 'array', items: { type: 'string' } },
14
- paths: 'PathsMap',
14
+ paths: 'Paths',
15
15
  definitions: 'NamedSchemas',
16
16
  parameters: 'NamedParameters',
17
17
  responses: 'NamedResponses',
@@ -51,7 +51,7 @@ const License: NodeType = {
51
51
  required: ['name'],
52
52
  };
53
53
 
54
- const PathsMap: NodeType = {
54
+ const Paths: NodeType = {
55
55
  properties: {},
56
56
  additionalProperties: (_value: any, key: string) =>
57
57
  key.startsWith('/') ? 'PathItem' : undefined,
@@ -82,7 +82,7 @@ const Operation: NodeType = {
82
82
  consumes: { type: 'array', items: { type: 'string' } },
83
83
  produces: { type: 'array', items: { type: 'string' } },
84
84
  parameters: listOf('Parameter'),
85
- responses: 'ResponsesMap',
85
+ responses: 'Responses',
86
86
  schemes: { type: 'array', items: { type: 'string' } },
87
87
  deprecated: { type: 'boolean' },
88
88
  security: listOf('SecurityRequirement'),
@@ -180,7 +180,7 @@ const ParameterItems: NodeType = {
180
180
  },
181
181
  };
182
182
 
183
- const ResponsesMap: NodeType = {
183
+ const Responses: NodeType = {
184
184
  properties: {
185
185
  default: 'Response',
186
186
  },
@@ -376,14 +376,14 @@ export const Oas2Types: Record<string, NodeType> = {
376
376
  Info,
377
377
  Contact,
378
378
  License,
379
- PathsMap,
379
+ Paths,
380
380
  PathItem,
381
381
  Parameter,
382
382
  ParameterItems,
383
383
  Operation,
384
384
  Examples,
385
385
  Header,
386
- ResponsesMap,
386
+ Responses,
387
387
  Response,
388
388
  Schema,
389
389
  Xml,
package/src/types/oas3.ts CHANGED
@@ -10,7 +10,7 @@ const Root: NodeType = {
10
10
  security: listOf('SecurityRequirement'),
11
11
  tags: listOf('Tag'),
12
12
  externalDocs: 'ExternalDocs',
13
- paths: 'PathsMap',
13
+ paths: 'Paths',
14
14
  components: 'Components',
15
15
  'x-webhooks': 'WebhooksMap',
16
16
  },
@@ -88,7 +88,7 @@ const License: NodeType = {
88
88
  required: ['name'],
89
89
  };
90
90
 
91
- const PathsMap: NodeType = {
91
+ const Paths: NodeType = {
92
92
  properties: {},
93
93
  additionalProperties: (_value: any, key: string) =>
94
94
  key.startsWith('/') ? 'PathItem' : undefined,
@@ -153,7 +153,7 @@ const Operation: NodeType = {
153
153
  security: listOf('SecurityRequirement'),
154
154
  servers: listOf('Server'),
155
155
  requestBody: 'RequestBody',
156
- responses: 'ResponsesMap',
156
+ responses: 'Responses',
157
157
  deprecated: { type: 'boolean' },
158
158
  callbacks: 'CallbacksMap',
159
159
  'x-codeSamples': listOf('XCodeSample'),
@@ -190,7 +190,7 @@ const MediaType: NodeType = {
190
190
  schema: 'Schema',
191
191
  example: { isExample: true },
192
192
  examples: 'ExamplesMap',
193
- encoding: 'EncodingsMap',
193
+ encoding: 'EncodingMap',
194
194
  },
195
195
  };
196
196
 
@@ -234,7 +234,7 @@ const Header: NodeType = {
234
234
  requiredOneOf: ['schema', 'content'],
235
235
  };
236
236
 
237
- const ResponsesMap: NodeType = {
237
+ const Responses: NodeType = {
238
238
  properties: { default: 'Response' },
239
239
  additionalProperties: (_v: any, key: string) =>
240
240
  responseCodeRegexp.test(key) ? 'Response' : undefined,
@@ -408,7 +408,7 @@ const AuthorizationCode: NodeType = {
408
408
  required: ['authorizationUrl', 'tokenUrl', 'scopes'],
409
409
  };
410
410
 
411
- const SecuritySchemeFlows: NodeType = {
411
+ const OAuth2Flows: NodeType = {
412
412
  properties: {
413
413
  implicit: 'ImplicitFlow',
414
414
  password: 'PasswordFlow',
@@ -425,7 +425,7 @@ const SecurityScheme: NodeType = {
425
425
  in: { type: 'string', enum: ['query', 'header', 'cookie'] },
426
426
  scheme: { type: 'string' },
427
427
  bearerFormat: { type: 'string' },
428
- flows: 'SecuritySchemeFlows',
428
+ flows: 'OAuth2Flows',
429
429
  openIdConnectUrl: { type: 'string' },
430
430
  },
431
431
  required(value) {
@@ -470,7 +470,7 @@ export const Oas3Types: Record<string, NodeType> = {
470
470
  Info,
471
471
  Contact,
472
472
  License,
473
- PathsMap,
473
+ Paths,
474
474
  PathItem,
475
475
  Parameter,
476
476
  Operation,
@@ -482,10 +482,10 @@ export const Oas3Types: Record<string, NodeType> = {
482
482
  Example,
483
483
  ExamplesMap: mapOf('Example'),
484
484
  Encoding,
485
- EncodingsMap: mapOf('Encoding'),
485
+ EncodingMap: mapOf('Encoding'),
486
486
  Header,
487
487
  HeadersMap: mapOf('Header'),
488
- ResponsesMap,
488
+ Responses,
489
489
  Response,
490
490
  Link,
491
491
  Schema,
@@ -508,7 +508,7 @@ export const Oas3Types: Record<string, NodeType> = {
508
508
  PasswordFlow,
509
509
  ClientCredentials,
510
510
  AuthorizationCode,
511
- SecuritySchemeFlows,
511
+ OAuth2Flows,
512
512
  SecurityScheme,
513
513
  XCodeSample,
514
514
  WebhooksMap,
@@ -9,7 +9,7 @@ const Root: NodeType = {
9
9
  security: listOf('SecurityRequirement'),
10
10
  tags: listOf('Tag'),
11
11
  externalDocs: 'ExternalDocs',
12
- paths: 'PathsMap',
12
+ paths: 'Paths',
13
13
  webhooks: 'WebhooksMap',
14
14
  components: 'Components',
15
15
  jsonSchemaDialect: { type: 'string' },
@@ -69,7 +69,7 @@ const Operation: NodeType = {
69
69
  security: listOf('SecurityRequirement'),
70
70
  servers: listOf('Server'),
71
71
  requestBody: 'RequestBody',
72
- responses: 'ResponsesMap',
72
+ responses: 'Responses',
73
73
  deprecated: { type: 'boolean' },
74
74
  callbacks: mapOf('Callback'),
75
75
  'x-codeSamples': listOf('XCodeSample'),
@@ -176,7 +176,7 @@ const SecurityScheme: NodeType = {
176
176
  in: { type: 'string', enum: ['query', 'header', 'cookie'] },
177
177
  scheme: { type: 'string' },
178
178
  bearerFormat: { type: 'string' },
179
- flows: 'SecuritySchemeFlows',
179
+ flows: 'OAuth2Flows',
180
180
  openIdConnectUrl: { type: 'string' },
181
181
  },
182
182
  required(value) {
@@ -66,7 +66,7 @@ const nodeTypesList = [
66
66
  'Info',
67
67
  'Contact',
68
68
  'License',
69
- 'PathsMap',
69
+ 'Paths',
70
70
  'PathItem',
71
71
  'Parameter',
72
72
  'Operation',
@@ -78,10 +78,10 @@ const nodeTypesList = [
78
78
  'Example',
79
79
  'ExamplesMap',
80
80
  'Encoding',
81
- 'EncodingsMap',
81
+ 'EncodingMap',
82
82
  'Header',
83
83
  'HeadersMap',
84
- 'ResponsesMap',
84
+ 'Responses',
85
85
  'Response',
86
86
  'Link',
87
87
  'LinksMap',
@@ -104,7 +104,7 @@ const nodeTypesList = [
104
104
  'PasswordFlow',
105
105
  'ClientCredentials',
106
106
  'AuthorizationCode',
107
- 'SecuritySchemeFlows',
107
+ 'OAuth2Flows',
108
108
  'SecurityScheme',
109
109
  'XCodeSample',
110
110
  'WebhooksMap',
@@ -165,6 +165,12 @@ const ConfigRoot: NodeType = {
165
165
  doNotResolveExamples: { type: 'boolean' },
166
166
  },
167
167
  },
168
+ files: {
169
+ type: 'array',
170
+ items: {
171
+ type: 'string',
172
+ },
173
+ },
168
174
  },
169
175
  };
170
176
 
@@ -187,6 +193,12 @@ const ConfigApisProperties: NodeType = {
187
193
  ...ConfigStyleguide.properties,
188
194
  'features.openapi': 'ConfigReferenceDocs',
189
195
  'features.mockServer': 'ConfigMockServer',
196
+ files: {
197
+ type: 'array',
198
+ items: {
199
+ type: 'string',
200
+ },
201
+ },
190
202
  },
191
203
  required: ['root'],
192
204
  };
@@ -275,6 +287,10 @@ const Assert: NodeType = {
275
287
  ref: (value: string | boolean) =>
276
288
  typeof value === 'string' ? { type: 'string' } : { type: 'boolean' },
277
289
  },
290
+ additionalProperties: (_value: unknown, key: string) => {
291
+ if (/^\w+\/\w+$/.test(key)) return { type: 'object' };
292
+ return;
293
+ },
278
294
  required: ['subject'],
279
295
  };
280
296
 
@@ -874,6 +890,16 @@ const ConfigReferenceDocs: NodeType = {
874
890
  unstable_externalDescription: { type: 'boolean' }, // deprecated
875
891
  unstable_ignoreMimeParameters: { type: 'boolean' },
876
892
  untrustedDefinition: { type: 'boolean' },
893
+ mockServer: {
894
+ properties: {
895
+ url: { type: 'string' },
896
+ position: { enum: ['first', 'last', 'replace', 'off'] },
897
+ description: { type: 'string' },
898
+ },
899
+ },
900
+ showAccessMode: { type: 'boolean' },
901
+ preserveOriginalExtensionsName: { type: 'boolean' },
902
+ markdownHeadingsAnchorLevel: { type: 'number' },
877
903
  },
878
904
  additionalProperties: { type: 'string' },
879
905
  };
package/src/utils.ts CHANGED
@@ -231,3 +231,16 @@ export function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy {
231
231
  export function identity<T>(value: T): T {
232
232
  return value;
233
233
  }
234
+
235
+ export function pickDefined<T extends Record<string, unknown>>(
236
+ obj?: T
237
+ ): Record<string, unknown> | undefined {
238
+ if (!obj) return undefined;
239
+ const res: Record<string, unknown> = {};
240
+ for (const key in obj) {
241
+ if (obj[key] !== undefined) {
242
+ res[key] = obj[key];
243
+ }
244
+ }
245
+ return res;
246
+ }