@redocly/openapi-core 1.4.1 → 1.6.0

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/CHANGELOG.md +12 -0
  2. package/README.md +5 -5
  3. package/lib/bundle.d.ts +8 -6
  4. package/lib/bundle.js +48 -14
  5. package/lib/config/all.js +2 -0
  6. package/lib/config/config-resolvers.d.ts +9 -1
  7. package/lib/config/config-resolvers.js +22 -1
  8. package/lib/config/load.d.ts +11 -3
  9. package/lib/config/load.js +12 -6
  10. package/lib/config/minimal.js +2 -0
  11. package/lib/config/recommended-strict.js +2 -0
  12. package/lib/config/recommended.js +2 -0
  13. package/lib/lint.d.ts +6 -3
  14. package/lib/lint.js +14 -3
  15. package/lib/rules/oas3/array-parameter-serialization.d.ts +5 -0
  16. package/lib/rules/oas3/array-parameter-serialization.js +31 -0
  17. package/lib/rules/oas3/index.js +2 -0
  18. package/lib/types/portal-config-schema.d.ts +22 -2465
  19. package/lib/types/portal-config-schema.js +56 -38
  20. package/lib/types/redocly-yaml.d.ts +1 -1
  21. package/lib/types/redocly-yaml.js +5 -4
  22. package/lib/typings/openapi.d.ts +1 -0
  23. package/lib/visitors.d.ts +5 -5
  24. package/lib/visitors.js +4 -6
  25. package/lib/walk.d.ts +3 -3
  26. package/lib/walk.js +2 -2
  27. package/package.json +1 -1
  28. package/src/__tests__/lint.test.ts +2 -2
  29. package/src/bundle.ts +67 -26
  30. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +4 -0
  31. package/src/config/__tests__/fixtures/resolve-refs-in-config/config-with-refs.yaml +8 -0
  32. package/src/config/__tests__/fixtures/resolve-refs-in-config/rules.yaml +2 -0
  33. package/src/config/__tests__/fixtures/resolve-refs-in-config/seo.yaml +1 -0
  34. package/src/config/__tests__/load.test.ts +80 -1
  35. package/src/config/all.ts +2 -0
  36. package/src/config/config-resolvers.ts +41 -11
  37. package/src/config/load.ts +36 -19
  38. package/src/config/minimal.ts +2 -0
  39. package/src/config/recommended-strict.ts +2 -0
  40. package/src/config/recommended.ts +2 -0
  41. package/src/lint.ts +32 -10
  42. package/src/rules/oas3/__tests__/array-parameter-serialization.test.ts +263 -0
  43. package/src/rules/oas3/array-parameter-serialization.ts +43 -0
  44. package/src/rules/oas3/index.ts +2 -0
  45. package/src/types/portal-config-schema.ts +65 -42
  46. package/src/types/redocly-yaml.ts +3 -4
  47. package/src/typings/openapi.ts +1 -0
  48. package/src/visitors.ts +20 -22
  49. package/src/walk.ts +8 -8
  50. package/tsconfig.tsbuildinfo +1 -1
@@ -4,6 +4,8 @@ import {
4
4
  DEFAULT_TEAM_CLAIM_NAME,
5
5
  } from '../config';
6
6
 
7
+ import type { NodeType } from '.';
8
+
7
9
  const oidcIssuerMetadataSchema = {
8
10
  type: 'object',
9
11
  properties: {
@@ -87,8 +89,9 @@ const authProviderConfigSchema = {
87
89
 
88
90
  export const ssoConfigSchema = {
89
91
  type: 'object',
92
+ properties: {},
90
93
  additionalProperties: authProviderConfigSchema,
91
- } as const;
94
+ } as NodeType;
92
95
 
93
96
  const redirectConfigSchema = {
94
97
  type: 'object',
@@ -97,8 +100,14 @@ const redirectConfigSchema = {
97
100
  type: { type: 'number', default: 301 },
98
101
  },
99
102
  required: ['to'],
100
- additionalProperties: false,
101
- } as const;
103
+ } as NodeType;
104
+
105
+ const redirectsConfigSchema = {
106
+ type: 'object',
107
+ properties: {},
108
+ additionalProperties: 'redirectConfigSchema',
109
+ default: {},
110
+ } as NodeType;
102
111
 
103
112
  export const apiConfigSchema = {
104
113
  type: 'object',
@@ -147,18 +156,21 @@ const seoConfigSchema = {
147
156
  },
148
157
  },
149
158
  },
150
- additionalProperties: false,
151
159
  } as const;
152
160
 
153
- const rbacScopeItemsSchema = { type: 'object', additionalProperties: { type: 'string' } } as const;
161
+ const rbacScopeItemsSchema = {
162
+ type: 'object',
163
+ properties: {},
164
+ additionalProperties: { type: 'string' },
165
+ } as NodeType;
154
166
 
155
167
  const rbacConfigSchema = {
156
168
  type: 'object',
157
169
  properties: {
158
- defaults: rbacScopeItemsSchema,
170
+ defaults: 'rbacScopeItemsSchema',
159
171
  },
160
172
  additionalProperties: rbacScopeItemsSchema,
161
- } as const;
173
+ } as NodeType;
162
174
 
163
175
  const graviteeAdapterConfigSchema = {
164
176
  type: 'object',
@@ -234,14 +246,38 @@ const devOnboardingAdapterConfigSchema = {
234
246
  const devOnboardingConfigSchema = {
235
247
  type: 'object',
236
248
  required: ['adapters'],
237
- additionalProperties: false,
238
249
  properties: {
239
250
  adapters: {
240
251
  type: 'array',
241
252
  items: devOnboardingAdapterConfigSchema,
242
253
  },
243
254
  },
244
- } as const;
255
+ } as NodeType;
256
+
257
+ const i18ConfigSchema = {
258
+ type: 'object',
259
+ properties: {
260
+ defaultLocale: {
261
+ type: 'string',
262
+ },
263
+ locales: {
264
+ type: 'array',
265
+ items: {
266
+ type: 'object',
267
+ properties: {
268
+ code: {
269
+ type: 'string',
270
+ },
271
+ name: {
272
+ type: 'string',
273
+ },
274
+ },
275
+ required: ['code'],
276
+ },
277
+ },
278
+ },
279
+ required: ['defaultLocale', 'locales'],
280
+ } as NodeType;
245
281
 
246
282
  const responseHeaderSchema = {
247
283
  type: 'object',
@@ -253,14 +289,25 @@ const responseHeaderSchema = {
253
289
  required: ['name', 'value'],
254
290
  } as const;
255
291
 
292
+ export const PortalConfigNodeTypes: Record<string, NodeType> = {
293
+ seoConfigSchema,
294
+ rbacConfigSchema,
295
+ rbacScopeItemsSchema,
296
+ ssoConfigSchema,
297
+ devOnboardingConfigSchema,
298
+ i18ConfigSchema,
299
+ redirectsConfigSchema,
300
+ redirectConfigSchema,
301
+ // TODO: Extract other types that need to be linted in the config
302
+ };
303
+
256
304
  export const redoclyConfigSchema = {
257
305
  type: 'object',
258
306
  properties: {
259
307
  licenseKey: { type: 'string' },
260
- theme: { type: 'object', default: {} }, // ThemeConfig
261
- redirects: { type: 'object', additionalProperties: redirectConfigSchema, default: {} },
262
- seo: seoConfigSchema,
263
- rbac: rbacConfigSchema,
308
+ redirects: 'redirectsConfigSchema',
309
+ seo: 'seoConfigSchema',
310
+ rbac: 'rbacConfigSchema',
264
311
  responseHeaders: {
265
312
  type: 'object',
266
313
  additionalProperties: {
@@ -282,38 +329,13 @@ export const redoclyConfigSchema = {
282
329
  type: 'object',
283
330
  additionalProperties: apiConfigSchema,
284
331
  },
285
- sso: ssoConfigSchema,
286
- developerOnboarding: devOnboardingConfigSchema,
287
- i18n: {
288
- type: 'object',
289
- properties: {
290
- defaultLocale: {
291
- type: 'string',
292
- },
293
- locales: {
294
- type: 'array',
295
- items: {
296
- type: 'object',
297
- properties: {
298
- code: {
299
- type: 'string',
300
- },
301
- name: {
302
- type: 'string',
303
- },
304
- },
305
- required: ['code'],
306
- },
307
- },
308
- },
309
- additionalProperties: false,
310
- required: ['defaultLocale', 'locales'],
311
- },
332
+ sso: 'ssoConfigSchema',
333
+ developerOnboarding: 'devOnboardingConfigSchema',
334
+ i18n: 'i18ConfigSchema',
312
335
  metadata: metadataConfigSchema,
313
336
  },
314
337
  default: {},
315
- additionalProperties: true,
316
- } as const;
338
+ } as NodeType;
317
339
 
318
340
  export const environmentSchema = {
319
341
  oneOf: [
@@ -335,6 +357,7 @@ export const rootRedoclyConfigSchema = {
335
357
  ...redoclyConfigSchema.properties,
336
358
  env: {
337
359
  type: 'object',
360
+ properties: {},
338
361
  additionalProperties: environmentSchema,
339
362
  },
340
363
  },
@@ -1,8 +1,8 @@
1
1
  import { rootRedoclyConfigSchema, apiConfigSchema } from './portal-config-schema';
2
2
  import { themeConfigSchema } from './theme-config';
3
3
  import { NodeType, listOf } from '.';
4
- import { Oas3_1Types } from './oas3_1';
5
4
  import { omitObjectProps, pickObjectProps, isCustomRuleId } from '../utils';
5
+ import { PortalConfigNodeTypes } from './portal-config-schema';
6
6
 
7
7
  const builtInCommonRules = [
8
8
  'spec',
@@ -77,6 +77,7 @@ const builtInOAS3Rules = [
77
77
  'response-contains-property',
78
78
  'response-mime-type',
79
79
  'spec-components-invalid-map-name',
80
+ 'array-parameter-serialization',
80
81
  ] as const;
81
82
 
82
83
  export type BuiltInOAS3RuleId = typeof builtInOAS3Rules[number];
@@ -235,8 +236,6 @@ const ConfigApisProperties: NodeType = {
235
236
  type: 'string',
236
237
  },
237
238
  },
238
- lint: 'ConfigStyleguide', // deprecated
239
- styleguide: 'ConfigStyleguide', // deprecated
240
239
  ...ConfigStyleguide.properties,
241
240
  'features.openapi': 'ConfigReferenceDocs', // deprecated
242
241
  'features.mockServer': 'ConfigMockServer', // deprecated
@@ -996,7 +995,6 @@ const ConfigMockServer: NodeType = {
996
995
  };
997
996
 
998
997
  export const ConfigTypes: Record<string, NodeType> = {
999
- ...Oas3_1Types,
1000
998
  Assert,
1001
999
  ConfigRoot,
1002
1000
  ConfigApis,
@@ -1061,4 +1059,5 @@ export const ConfigTypes: Record<string, NodeType> = {
1061
1059
  Typography,
1062
1060
  AssertionDefinitionAssertions,
1063
1061
  AssertionDefinitionSubject,
1062
+ ...PortalConfigNodeTypes,
1064
1063
  };
@@ -156,6 +156,7 @@ export interface Oas3Schema {
156
156
  export type Oas3_1Schema = Oas3Schema & {
157
157
  type?: string | string[];
158
158
  examples?: any[];
159
+ prefixItems?: Oas3_1Schema[];
159
160
  };
160
161
 
161
162
  export interface Oas3_1Definition extends Oas3Definition {
package/src/visitors.ts CHANGED
@@ -1,3 +1,9 @@
1
+ import { SpecExtension } from './types';
2
+
3
+ import type { NormalizedNodeType } from './types';
4
+ import type { Stack } from './utils';
5
+ import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
6
+ import type { Location } from './ref-utils';
1
7
  import type {
2
8
  Oas3Definition,
3
9
  Oas3ExternalDocs,
@@ -26,7 +32,6 @@ import type {
26
32
  Oas3Discriminator,
27
33
  Oas3Callback,
28
34
  } from './typings/openapi';
29
-
30
35
  import type {
31
36
  Oas2Definition,
32
37
  Oas2Tag,
@@ -44,12 +49,7 @@ import type {
44
49
  Oas2Parameter,
45
50
  Oas2SecurityScheme,
46
51
  } from './typings/swagger';
47
-
48
- import { NormalizedNodeType, SpecExtension } from './types';
49
- import type { Stack } from './utils';
50
- import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
51
- import type { Location } from './ref-utils';
52
- import { Async2Definition } from './typings/asyncapi';
52
+ import type { Async2Definition } from './typings/asyncapi';
53
53
 
54
54
  export type SkipFunctionContext = Pick<
55
55
  UserContext,
@@ -297,7 +297,7 @@ export type RuleInstanceConfig = {
297
297
  };
298
298
 
299
299
  export function normalizeVisitors<T extends BaseVisitor>(
300
- visitorsConfig: (RuleInstanceConfig & { visitor: NestedVisitObject<any, T> })[],
300
+ visitorsConfig: (RuleInstanceConfig & { visitor: NestedVisitObject<unknown, T> })[],
301
301
  types: Record<keyof T, NormalizedNodeType>
302
302
  ): NormalizedOasVisitors<T> {
303
303
  const normalizedVisitors: NormalizedOasVisitors<T> = {} as any;
@@ -377,12 +377,10 @@ export function normalizeVisitors<T extends BaseVisitor>(
377
377
 
378
378
  function addWeakFromStack(ruleConf: RuleInstanceConfig, stack: NormalizedNodeType[]) {
379
379
  for (const interType of stack.slice(1)) {
380
- (normalizedVisitors as any)[interType.name] =
381
- normalizedVisitors[interType.name] ||
382
- ({
383
- enter: [],
384
- leave: [],
385
- } as any);
380
+ (normalizedVisitors as any)[interType.name] = normalizedVisitors[interType.name] || {
381
+ enter: [],
382
+ leave: [],
383
+ };
386
384
  normalizedVisitors[interType.name].enter.push({
387
385
  ...ruleConf,
388
386
  visit: () => undefined,
@@ -398,7 +396,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
398
396
  }
399
397
 
400
398
  function findLegacyVisitorNode<T>(
401
- visitor: NestedVisitObject<any, T>,
399
+ visitor: NestedVisitObject<unknown, T>,
402
400
  typeName: keyof T | Array<keyof T>
403
401
  ) {
404
402
  if (Array.isArray(typeName)) {
@@ -411,7 +409,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
411
409
 
412
410
  function normalizeVisitorLevel(
413
411
  ruleConf: RuleInstanceConfig,
414
- visitor: NestedVisitObject<any, T>,
412
+ visitor: NestedVisitObject<unknown, T>,
415
413
  parentContext: VisitorLevelContext | null,
416
414
  depth = 0
417
415
  ) {
@@ -434,14 +432,14 @@ export function normalizeVisitors<T extends BaseVisitor>(
434
432
  findLegacyVisitorNode(
435
433
  visitor,
436
434
  legacyTypesMap[typeName as keyof typeof legacyTypesMap] as keyof T
437
- )) as any as NestedVisitObject<any, T>;
435
+ )) as NestedVisitObject<unknown, T>;
438
436
  const normalizedTypeVisitor = normalizedVisitors[typeName];
439
437
 
440
438
  if (!typeVisitor) continue;
441
439
 
442
- let visitorEnter: VisitFunction<any> | undefined;
443
- let visitorLeave: VisitFunction<any> | undefined;
444
- let visitorSkip: SkipFunction<any> | undefined;
440
+ let visitorEnter: VisitFunction<unknown> | undefined;
441
+ let visitorLeave: VisitFunction<unknown> | undefined;
442
+ let visitorSkip: SkipFunction<unknown> | undefined;
445
443
 
446
444
  const isObjectVisitor = typeof typeVisitor === 'object';
447
445
 
@@ -450,7 +448,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
450
448
  }
451
449
 
452
450
  if (typeof typeVisitor === 'function') {
453
- visitorEnter = typeVisitor as any;
451
+ visitorEnter = typeVisitor;
454
452
  } else if (isObjectVisitor) {
455
453
  visitorEnter = typeVisitor.enter;
456
454
  visitorLeave = typeVisitor.leave;
@@ -465,7 +463,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
465
463
  };
466
464
 
467
465
  if (typeof typeVisitor === 'object') {
468
- normalizeVisitorLevel(ruleConf, typeVisitor as any, context, depth + 1);
466
+ normalizeVisitorLevel(ruleConf, typeVisitor, context, depth + 1);
469
467
  }
470
468
 
471
469
  if (parentContext) {
package/src/walk.ts CHANGED
@@ -1,3 +1,9 @@
1
+ import { Location, isRef } from './ref-utils';
2
+ import { pushStack, popStack } from './utils';
3
+ import { ResolveError, YamlParseError, Source, makeRefId } from './resolve';
4
+ import { SpecVersion } from './oas-types';
5
+ import { isNamedType, SpecExtension } from './types';
6
+
1
7
  import type { Referenced } from './typings/openapi';
2
8
  import type {
3
9
  VisitorLevelContext,
@@ -12,12 +18,6 @@ import type { ResolvedRefMap, Document } from './resolve';
12
18
  import type { NormalizedNodeType } from './types';
13
19
  import type { RuleSeverity } from './config';
14
20
 
15
- import { Location, isRef } from './ref-utils';
16
- import { pushStack, popStack } from './utils';
17
- import { ResolveError, YamlParseError, Source, makeRefId } from './resolve';
18
- import { SpecVersion } from './oas-types';
19
- import { isNamedType, SpecExtension } from './types';
20
-
21
21
  export type NonUndefined =
22
22
  | string
23
23
  | number
@@ -204,7 +204,7 @@ export function walkDocument<T extends BaseVisitor>(opts: {
204
204
  const activatedContexts: Array<VisitorSkippedLevelContext | VisitorLevelContext> = [];
205
205
 
206
206
  for (const { context, visit, skip, ruleId, severity } of currentEnterVisitors) {
207
- if (ignoredNodes.has(currentLocation.pointer)) break;
207
+ if (ignoredNodes.has(`${currentLocation.absolutePointer}${currentLocation.pointer}`)) break;
208
208
 
209
209
  if (context.isSkippedLevel) {
210
210
  if (
@@ -413,7 +413,7 @@ export function walkDocument<T extends BaseVisitor>(opts: {
413
413
  parentLocations: collectParentsLocations(context),
414
414
  oasVersion: ctx.oasVersion,
415
415
  ignoreNextVisitorsOnNode: () => {
416
- ignoredNodes.add(currentLocation.pointer);
416
+ ignoredNodes.add(`${currentLocation.absolutePointer}${currentLocation.pointer}`);
417
417
  },
418
418
  getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
419
419
  },