@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.
- package/CHANGELOG.md +12 -0
- package/README.md +5 -5
- package/lib/bundle.d.ts +8 -6
- package/lib/bundle.js +48 -14
- package/lib/config/all.js +2 -0
- package/lib/config/config-resolvers.d.ts +9 -1
- package/lib/config/config-resolvers.js +22 -1
- package/lib/config/load.d.ts +11 -3
- package/lib/config/load.js +12 -6
- package/lib/config/minimal.js +2 -0
- package/lib/config/recommended-strict.js +2 -0
- package/lib/config/recommended.js +2 -0
- package/lib/lint.d.ts +6 -3
- package/lib/lint.js +14 -3
- package/lib/rules/oas3/array-parameter-serialization.d.ts +5 -0
- package/lib/rules/oas3/array-parameter-serialization.js +31 -0
- package/lib/rules/oas3/index.js +2 -0
- package/lib/types/portal-config-schema.d.ts +22 -2465
- package/lib/types/portal-config-schema.js +56 -38
- package/lib/types/redocly-yaml.d.ts +1 -1
- package/lib/types/redocly-yaml.js +5 -4
- package/lib/typings/openapi.d.ts +1 -0
- package/lib/visitors.d.ts +5 -5
- package/lib/visitors.js +4 -6
- package/lib/walk.d.ts +3 -3
- package/lib/walk.js +2 -2
- package/package.json +1 -1
- package/src/__tests__/lint.test.ts +2 -2
- package/src/bundle.ts +67 -26
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +4 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/config-with-refs.yaml +8 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/rules.yaml +2 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/seo.yaml +1 -0
- package/src/config/__tests__/load.test.ts +80 -1
- package/src/config/all.ts +2 -0
- package/src/config/config-resolvers.ts +41 -11
- package/src/config/load.ts +36 -19
- package/src/config/minimal.ts +2 -0
- package/src/config/recommended-strict.ts +2 -0
- package/src/config/recommended.ts +2 -0
- package/src/lint.ts +32 -10
- package/src/rules/oas3/__tests__/array-parameter-serialization.test.ts +263 -0
- package/src/rules/oas3/array-parameter-serialization.ts +43 -0
- package/src/rules/oas3/index.ts +2 -0
- package/src/types/portal-config-schema.ts +65 -42
- package/src/types/redocly-yaml.ts +3 -4
- package/src/typings/openapi.ts +1 -0
- package/src/visitors.ts +20 -22
- package/src/walk.ts +8 -8
- 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
|
|
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
|
-
|
|
101
|
-
|
|
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 = {
|
|
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
|
|
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
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
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
|
};
|
package/src/typings/openapi.ts
CHANGED
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<
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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<
|
|
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<
|
|
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
|
|
435
|
+
)) as NestedVisitObject<unknown, T>;
|
|
438
436
|
const normalizedTypeVisitor = normalizedVisitors[typeName];
|
|
439
437
|
|
|
440
438
|
if (!typeVisitor) continue;
|
|
441
439
|
|
|
442
|
-
let visitorEnter: VisitFunction<
|
|
443
|
-
let visitorLeave: VisitFunction<
|
|
444
|
-
let visitorSkip: SkipFunction<
|
|
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
|
|
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
|
|
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
|
},
|