From ef4b2e2e381b54d8dc9568df236f330e57e68ada Mon Sep 17 00:00:00 2001 From: Jeremy Fiel <32110157+jeremyfiel@users.noreply.github.com> Date: Mon, 20 Jan 2025 05:20:59 -0500 Subject: [PATCH] fix(typings): update OpenAPI 3.0 and 3.1 typing declarations (#1795) --- .changeset/selfish-pandas-speak.md | 5 + packages/cli/src/commands/join.ts | 18 +- packages/cli/src/commands/split/index.ts | 30 +-- packages/cli/src/commands/split/types.ts | 29 +-- packages/core/src/__tests__/lint.test.ts | 95 +++++++++ .../oas3/remove-unused-components.ts | 20 +- packages/core/src/index.ts | 2 +- .../src/rules/common/operation-tag-defined.ts | 4 +- .../core/src/rules/common/security-defined.ts | 3 +- .../src/rules/common/tags-alphabetical.ts | 7 +- .../oas3/array-parameter-serialization.ts | 15 +- .../src/rules/oas3/component-name-unique.ts | 6 +- packages/core/src/types/oas3_1.ts | 2 + packages/core/src/typings/openapi.ts | 186 +++++++++++------- packages/core/src/utils.ts | 2 + packages/core/src/visitors.ts | 47 +++-- 16 files changed, 315 insertions(+), 156 deletions(-) create mode 100644 .changeset/selfish-pandas-speak.md diff --git a/.changeset/selfish-pandas-speak.md b/.changeset/selfish-pandas-speak.md new file mode 100644 index 0000000000..25ec9fa3ce --- /dev/null +++ b/.changeset/selfish-pandas-speak.md @@ -0,0 +1,5 @@ +--- +"@redocly/openapi-core": patch +--- + +Updated typings for OAS 3.0 and OAS 3.1 Schemas. diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts index 8671e0a35d..916d8e70c0 100644 --- a/packages/cli/src/commands/join.ts +++ b/packages/cli/src/commands/join.ts @@ -23,14 +23,17 @@ import { isObject, isString, keysOf } from '../utils/js-utils'; import { COMPONENTS, OPENAPI3_METHOD } from './split/types'; import { crawl, startsWithComponents } from './split'; -import type { Oas3Definition, Document, Oas3Tag, Referenced } from '@redocly/openapi-core'; +import type { Document, Referenced } from '@redocly/openapi-core'; import type { BundleResult } from '@redocly/openapi-core/lib/bundle'; import type { + Oas3Definition, + Oas3_1Definition, Oas3Parameter, Oas3PathItem, Oas3Server, - Oas3_1Definition, + Oas3Tag, } from '@redocly/openapi-core/lib/typings/openapi'; +import type { StrictObject } from '@redocly/openapi-core/lib/utils'; import type { CommandArgs } from '../wrapper'; import type { VerifyConfigOptions } from '../types'; @@ -311,7 +314,7 @@ export async function handleJoin({ } } - function collectServers(openapi: Oas3Definition) { + function collectServers(openapi: Oas3Definition | Oas3_1Definition) { const { servers } = openapi; if (servers) { if (!joinedDef.hasOwnProperty('servers')) { @@ -325,7 +328,10 @@ export async function handleJoin({ } } - function collectExternalDocs(openapi: Oas3Definition, { api }: JoinDocumentContext) { + function collectExternalDocs( + openapi: Oas3Definition | Oas3_1Definition, + { api }: JoinDocumentContext + ) { const { externalDocs } = openapi; if (externalDocs) { if (joinedDef.hasOwnProperty('externalDocs')) { @@ -339,7 +345,7 @@ export async function handleJoin({ } function collectPaths( - openapi: Oas3Definition, + openapi: Oas3Definition | Oas3_1Definition, { apiFilename, apiTitle, @@ -567,7 +573,7 @@ export async function handleJoin({ function collectWebhooks( oasVersion: SpecVersion, - openapi: Oas3_1Definition, + openapi: StrictObject, { apiFilename, apiTitle, diff --git a/packages/cli/src/commands/split/index.ts b/packages/cli/src/commands/split/index.ts index 0c5881610d..2cde3c2195 100644 --- a/packages/cli/src/commands/split/index.ts +++ b/packages/cli/src/commands/split/index.ts @@ -22,20 +22,18 @@ import { OPENAPI3_COMPONENT_NAMES, } from './types'; -import type { OasRef } from '@redocly/openapi-core'; +import type { Oas3Definition, Oas3_1Definition, Oas2Definition } from '@redocly/openapi-core'; import type { - Definition, - Oas2Definition, Oas3Schema, - Oas3Definition, - Oas3_1Definition, + Oas3_1Schema, Oas3Components, + Oas3_1Components, Oas3ComponentName, - ComponentsFiles, - RefObject, Oas3PathItem, + OasRef, Referenced, -} from './types'; +} from '@redocly/openapi-core/lib/typings/openapi'; +import type { ComponentsFiles, Definition, RefObject } from './types'; import type { CommandArgs } from '../../wrapper'; import type { VerifyConfigOptions } from '../../types'; @@ -239,7 +237,7 @@ function doesFileDiffer(filename: string, componentData: any) { function removeEmptyComponents( openapi: Oas3Definition | Oas3_1Definition, - componentType: Oas3ComponentName + componentType: Oas3ComponentName ) { if (openapi.components && isEmptyObject(openapi.components[componentType])) { delete openapi.components[componentType]; @@ -264,15 +262,17 @@ function getFileNamePath(componentDirPath: string, componentName: string, ext: s } function gatherComponentsFiles( - components: Oas3Components, + components: Oas3Components | Oas3_1Components, componentsFiles: ComponentsFiles, - componentType: Oas3ComponentName, + componentType: Oas3ComponentName, componentName: string, filename: string ) { let inherits: string[] = []; if (componentType === OPENAPI3_COMPONENT.Schemas) { - inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || []) + inherits = ( + (components?.[componentType]?.[componentName] as Oas3Schema | Oas3_1Schema)?.allOf || [] + ) .map(({ $ref }) => $ref) .filter(isTruthy); } @@ -347,7 +347,9 @@ function iterateComponents( componentTypes.forEach(iterateComponentTypes); // eslint-disable-next-line no-inner-declarations - function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) { + function iterateAndGatherComponentsFiles( + componentType: Oas3ComponentName + ) { const componentDirPath = path.join(componentsDir, componentType); for (const componentName of Object.keys(components?.[componentType] || {})) { const filename = getFileNamePath(componentDirPath, componentName, ext); @@ -356,7 +358,7 @@ function iterateComponents( } // eslint-disable-next-line no-inner-declarations - function iterateComponentTypes(componentType: Oas3ComponentName) { + function iterateComponentTypes(componentType: Oas3ComponentName) { const componentDirPath = path.join(componentsDir, componentType); createComponentDir(componentDirPath, componentType); for (const componentName of Object.keys(components?.[componentType] || {})) { diff --git a/packages/cli/src/commands/split/types.ts b/packages/cli/src/commands/split/types.ts index 2b56b24ca5..c77757c032 100644 --- a/packages/cli/src/commands/split/types.ts +++ b/packages/cli/src/commands/split/types.ts @@ -1,29 +1,6 @@ -import { - Oas3Schema, - Oas3_1Schema, - Oas3Definition, - Oas3_1Definition, - Oas3Components, - Oas3PathItem, - Oas3Paths, - Oas3ComponentName, - Oas3_1Webhooks, - Oas2Definition, - Referenced, -} from '@redocly/openapi-core'; -export { - Oas3_1Definition, - Oas3Definition, - Oas2Definition, - Oas3Components, - Oas3Paths, - Oas3PathItem, - Oas3ComponentName, - Oas3_1Schema, - Oas3Schema, - Oas3_1Webhooks, - Referenced, -}; +import type { Oas2Definition } from '@redocly/openapi-core'; +import type { Oas3_1Definition, Oas3Definition } from '@redocly/openapi-core/lib/typings/openapi'; + export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition; export interface ComponentsFiles { [schemas: string]: any; diff --git a/packages/core/src/__tests__/lint.test.ts b/packages/core/src/__tests__/lint.test.ts index 91b71437d6..195f7d5aec 100644 --- a/packages/core/src/__tests__/lint.test.ts +++ b/packages/core/src/__tests__/lint.test.ts @@ -1674,4 +1674,99 @@ describe('lint', () => { expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); }); + + it('should throw an error for $schema not expected here - OAS 3.0.x', async () => { + const document = parseYamlToDocument( + outdent` + openapi: 3.0.4 + info: + title: test json schema validation keyword $schema - should use an OAS Schema, not JSON Schema + version: 1.0.0 + paths: + '/thing': + get: + summary: a sample api + responses: + '200': + description: OK + content: + 'application/json': + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: {} + `, + '' + ); + + const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml'); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await makeConfig({ + rules: { spec: 'error' }, + decorators: undefined, + configPath: configFilePath, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "from": undefined, + "location": [ + { + "pointer": "#/paths/~1thing/get/responses/200/content/application~1json/schema/$schema", + "reportOnKey": true, + "source": "", + }, + ], + "message": "Property \`$schema\` is not expected here.", + "ruleId": "struct", + "severity": "error", + "suggest": [], + }, + ] + `); + }); + + it('should allow for $schema to be defined - OAS 3.1.x', async () => { + const document = parseYamlToDocument( + outdent` + openapi: 3.1.1 + info: + title: test json schema validation keyword $schema - should allow a JSON Schema + version: 1.0.0 + paths: + '/thing': + get: + summary: a sample api + responses: + '200': + description: OK + content: + 'application/json': + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: {} + `, + '' + ); + + const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml'); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await makeConfig({ + rules: { spec: 'error' }, + decorators: undefined, + configPath: configFilePath, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); }); diff --git a/packages/core/src/decorators/oas3/remove-unused-components.ts b/packages/core/src/decorators/oas3/remove-unused-components.ts index 4780961b9c..b7b7369935 100644 --- a/packages/core/src/decorators/oas3/remove-unused-components.ts +++ b/packages/core/src/decorators/oas3/remove-unused-components.ts @@ -2,17 +2,26 @@ import { isEmptyObject } from '../../utils'; import type { Location } from '../../ref-utils'; import type { Oas3Decorator } from '../../visitors'; -import type { Oas3Components, Oas3Definition } from '../../typings/openapi'; +import type { + Oas3Definition, + Oas3_1Definition, + Oas3Components, + Oas3_1Components, +} from '../../typings/openapi'; export const RemoveUnusedComponents: Oas3Decorator = () => { const components = new Map< string, - { usedIn: Location[]; componentType?: keyof Oas3Components; name: string } + { + usedIn: Location[]; + componentType?: keyof (Oas3Components | Oas3_1Components); + name: string; + } >(); function registerComponent( location: Location, - componentType: keyof Oas3Components, + componentType: keyof (Oas3Components | Oas3_1Components), name: string ): void { components.set(location.absolutePointer, { @@ -22,7 +31,10 @@ export const RemoveUnusedComponents: Oas3Decorator = () => { }); } - function removeUnusedComponents(root: Oas3Definition, removedPaths: string[]): number { + function removeUnusedComponents( + root: Oas3Definition | Oas3_1Definition, + removedPaths: string[] + ): number { const removedLengthStart = removedPaths.length; for (const [path, { usedIn, name, componentType }] of components) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d96598fca2..449ab05fbe 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -18,13 +18,13 @@ export type { Oas3Definition, Oas3_1Definition, Oas3Components, + Oas3_1Components, Oas3PathItem, Oas3Paths, Oas3ComponentName, Oas3Schema, Oas3_1Schema, Oas3Tag, - Oas3_1Webhooks, Referenced, OasRef, } from './typings/openapi'; diff --git a/packages/core/src/rules/common/operation-tag-defined.ts b/packages/core/src/rules/common/operation-tag-defined.ts index e492dba7e2..804381aea0 100644 --- a/packages/core/src/rules/common/operation-tag-defined.ts +++ b/packages/core/src/rules/common/operation-tag-defined.ts @@ -1,13 +1,13 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Definition, Oas2Operation } from '../../typings/swagger'; -import type { Oas3Definition, Oas3Operation } from '../../typings/openapi'; +import type { Oas3Definition, Oas3_1Definition, Oas3Operation } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const OperationTagDefined: Oas3Rule | Oas2Rule = () => { let definedTags: Set; return { - Root(root: Oas2Definition | Oas3Definition) { + Root(root: Oas2Definition | Oas3Definition | Oas3_1Definition) { definedTags = new Set((root.tags ?? []).map((t) => t.name)); }, Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { diff --git a/packages/core/src/rules/common/security-defined.ts b/packages/core/src/rules/common/security-defined.ts index 1e1ea0b98e..9ebe205786 100644 --- a/packages/core/src/rules/common/security-defined.ts +++ b/packages/core/src/rules/common/security-defined.ts @@ -9,6 +9,7 @@ import type { } from '../../typings/swagger'; import type { Oas3Definition, + Oas3_1Definition, Oas3Operation, Oas3PathItem, Oas3SecurityScheme, @@ -31,7 +32,7 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = (opts: { return { Root: { - leave(root: Oas2Definition | Oas3Definition, { report }: UserContext) { + leave(root: Oas2Definition | Oas3Definition | Oas3_1Definition, { report }: UserContext) { for (const [name, scheme] of referencedSchemes.entries()) { if (scheme.defined) continue; for (const reportedFromLocation of scheme.from) { diff --git a/packages/core/src/rules/common/tags-alphabetical.ts b/packages/core/src/rules/common/tags-alphabetical.ts index 63f85f2f52..2257d51a51 100644 --- a/packages/core/src/rules/common/tags-alphabetical.ts +++ b/packages/core/src/rules/common/tags-alphabetical.ts @@ -1,11 +1,14 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Definition, Oas2Tag } from '../../typings/swagger'; -import type { Oas3Definition, Oas3Tag } from '../../typings/openapi'; +import type { Oas3Definition, Oas3Tag, Oas3_1Definition } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const TagsAlphabetical: Oas3Rule | Oas2Rule = ({ ignoreCase = false }) => { return { - Root(root: Oas2Definition | Oas3Definition, { report, location }: UserContext) { + Root( + root: Oas2Definition | Oas3Definition | Oas3_1Definition, + { report, location }: UserContext + ) { if (!root.tags) return; for (let i = 0; i < root.tags.length - 1; i++) { if (getTagName(root.tags[i], ignoreCase) > getTagName(root.tags[i + 1], ignoreCase)) { diff --git a/packages/core/src/rules/oas3/array-parameter-serialization.ts b/packages/core/src/rules/oas3/array-parameter-serialization.ts index 2ca6e206e5..fc897c1f35 100644 --- a/packages/core/src/rules/oas3/array-parameter-serialization.ts +++ b/packages/core/src/rules/oas3/array-parameter-serialization.ts @@ -12,15 +12,18 @@ export const ArrayParameterSerialization: Oas3Rule = ( ): Oas3Visitor => { return { Parameter: { - leave(node: Oas3Parameter, ctx) { + leave(node, ctx) { if (!node.schema) { return; } - const schema = isRef(node.schema) - ? ctx.resolve(node.schema).node - : (node.schema as Oas3_1Schema); + const schema = ( + isRef(node.schema) ? ctx.resolve(node.schema).node : node.schema + ) as Oas3_1Schema; - if (schema && shouldReportMissingStyleAndExplode(node, schema, options)) { + if ( + schema && + shouldReportMissingStyleAndExplode(node as Oas3Parameter, schema, options) + ) { ctx.report({ message: `Parameter \`${node.name}\` should have \`style\` and \`explode \` fields`, location: ctx.location, @@ -32,7 +35,7 @@ export const ArrayParameterSerialization: Oas3Rule = ( }; function shouldReportMissingStyleAndExplode( - node: Oas3Parameter, + node: Oas3Parameter, schema: Oas3_1Schema, options: ArrayParameterSerializationOptions ) { diff --git a/packages/core/src/rules/oas3/component-name-unique.ts b/packages/core/src/rules/oas3/component-name-unique.ts index cbf21fd555..b6475345bd 100644 --- a/packages/core/src/rules/oas3/component-name-unique.ts +++ b/packages/core/src/rules/oas3/component-name-unique.ts @@ -3,10 +3,12 @@ import type { Problem, UserContext } from '../../walk'; import type { Oas2Rule, Oas3Rule, Oas3Visitor } from '../../visitors'; import type { Oas3Definition, + Oas3_1Definition, Oas3Parameter, Oas3RequestBody, Oas3Response, Oas3Schema, + Oas3_1Schema, OasRef, } from '../../typings/openapi'; @@ -54,7 +56,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => { }, }, Root: { - leave(root: Oas3Definition, ctx: UserContext) { + leave(root: Oas3Definition | Oas3_1Definition, ctx: UserContext) { components.forEach((value, key, _) => { if (value.absolutePointers.size > 1) { const component = getComponentFromKey(key); @@ -82,7 +84,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => { if (options.schemas != 'off') { rule.NamedSchemas = { - Schema(_: Oas3Schema, { location }: UserContext) { + Schema(_: Oas3Schema | Oas3_1Schema, { location }: UserContext) { addComponentFromAbsoluteLocation(TYPE_NAME_SCHEMA, location); }, }; diff --git a/packages/core/src/types/oas3_1.ts b/packages/core/src/types/oas3_1.ts index c9c5de778d..3f3d74e58a 100755 --- a/packages/core/src/types/oas3_1.ts +++ b/packages/core/src/types/oas3_1.ts @@ -182,6 +182,8 @@ export const Schema: NodeType = { const: null, $comment: { type: 'string' }, 'x-tags': { type: 'array', items: { type: 'string' } }, + $dynamicAnchor: { type: 'string' }, + $dynamicRef: { type: 'string' }, }, extensionsPrefix: 'x-', }; diff --git a/packages/core/src/typings/openapi.ts b/packages/core/src/typings/openapi.ts index 8860358073..c35452b6cb 100644 --- a/packages/core/src/typings/openapi.ts +++ b/packages/core/src/typings/openapi.ts @@ -1,19 +1,26 @@ -export interface Oas3Definition { +// common fields for OAS descriptions v3.x +interface Oas3DefinitionBase { openapi: string; info?: Oas3Info; servers?: Oas3Server[]; - paths?: Oas3Paths; - components?: Oas3Components; + paths?: Oas3Paths; + components?: T extends Oas3_1Schema ? Oas3_1Components : Oas3Components; security?: Oas3SecurityRequirement[]; tags?: Oas3Tag[]; externalDocs?: Oas3ExternalDocs; - 'x-webhooks'?: Oas3_1Webhooks; +} + +export interface Oas3Definition extends Oas3DefinitionBase { + 'x-webhooks'?: Oas3Webhooks; +} + +export interface Oas3_1Definition extends Oas3DefinitionBase { + webhooks?: Oas3Webhooks; } export interface Oas3Info { title: string; version: string; - description?: string; termsOfService?: string; contact?: Oas3Contact; @@ -32,8 +39,8 @@ export interface Oas3ServerVariable { description?: string; } -export interface Oas3Paths { - [path: string]: Referenced; +export interface Oas3Paths { + [path: string]: Referenced>; } export interface OasRef { $ref: string; @@ -41,19 +48,19 @@ export interface OasRef { export type Referenced = OasRef | T; -export interface Oas3PathItem { +export interface Oas3PathItem { summary?: string; description?: string; - get?: Oas3Operation; - put?: Oas3Operation; - post?: Oas3Operation; - delete?: Oas3Operation; - options?: Oas3Operation; - head?: Oas3Operation; - patch?: Oas3Operation; - trace?: Oas3Operation; + get?: Oas3Operation; + put?: Oas3Operation; + post?: Oas3Operation; + delete?: Oas3Operation; + options?: Oas3Operation; + head?: Oas3Operation; + patch?: Oas3Operation; + trace?: Oas3Operation; servers?: Oas3Server[]; - parameters?: Array>; + parameters?: Array>>; } export interface Oas3XCodeSample { @@ -62,16 +69,16 @@ export interface Oas3XCodeSample { source: string; } -export interface Oas3Operation { +export interface Oas3Operation { tags?: string[]; summary?: string; description?: string; externalDocs?: Oas3ExternalDocs; operationId?: string; - parameters?: Array>; - requestBody?: Referenced; - responses: Oas3Responses; - callbacks?: { [name: string]: Referenced }; + parameters?: Array>>; + requestBody?: Referenced>; + responses: Oas3Responses; + callbacks?: { [name: string]: Referenced> }; deprecated?: boolean; security?: Oas3SecurityRequirement[]; servers?: Oas3Server[]; @@ -80,7 +87,7 @@ export interface Oas3Operation { 'x-hideTryItPanel'?: boolean; } -export interface Oas3Parameter { +export interface Oas3Parameter { name: string; in?: Oas3ParameterLocation; description?: string; @@ -90,14 +97,14 @@ export interface Oas3Parameter { style?: Oas3ParameterStyle; explode?: boolean; allowReserved?: boolean; - schema?: Referenced; - example?: any; + schema?: Referenced; + example?: unknown; examples?: { [media: string]: Referenced }; - content?: { [media: string]: Oas3MediaType }; + content?: { [media: string]: Oas3MediaType }; } export interface Oas3Example { - value: any; + value: unknown; summary?: string; description?: string; externalValue?: string; @@ -112,13 +119,12 @@ export interface Oas3Xml { } // common fields for OpenAPI Schema v3.x -interface Oas3XSchemaBase { +interface Oas3XSchemaBase { $ref?: string; - properties?: { [name: string]: T }; + properties?: { [name: string]: Referenced }; additionalProperties?: boolean | T; description?: string; - default?: any; - items?: T; + default?: unknown; required?: string[]; readOnly?: boolean; writeOnly?: boolean; @@ -126,28 +132,25 @@ interface Oas3XSchemaBase { format?: string; externalDocs?: Oas3ExternalDocs; discriminator?: Oas3Discriminator; - nullable?: boolean; oneOf?: T[]; anyOf?: T[]; allOf?: T[]; not?: T; - title?: string; multipleOf?: number; maximum?: number; - exclusiveMaximum?: boolean; minimum?: number; - exclusiveMinimum?: boolean; maxLength?: number; minLength?: number; pattern?: string; + items?: boolean | T; maxItems?: number; minItems?: number; uniqueItems?: boolean; maxProperties?: number; minProperties?: number; - enum?: any[]; - example?: any; + enum?: unknown[]; + example?: unknown; xml?: Oas3Xml; 'x-tags'?: string[]; @@ -155,20 +158,45 @@ interface Oas3XSchemaBase { export interface Oas3Schema extends Oas3XSchemaBase { type?: string; + exclusiveMaximum?: boolean; + exclusiveMinimum?: boolean; + nullable?: boolean; } export interface Oas3_1Schema extends Oas3XSchemaBase { + $id?: string; + $schema?: string; + $anchor?: string; + $dynamicAnchor?: string; + $dynamicRef?: string; + $defs?: { [name: string]: Referenced }; + $vocabulary?: { [uri: string]: boolean }; + $comment?: string; type?: string | string[]; - examples?: any[]; + examples?: unknown[]; prefixItems?: Oas3_1Schema[]; -} - -export interface Oas3_1Definition extends Oas3Definition { - webhooks?: Oas3_1Webhooks; -} - -export interface Oas3_1Webhooks { - [webhook: string]: Referenced; + exclusiveMaximum?: number; + exclusiveMinimum?: number; + const?: unknown; + contains?: Oas3_1Schema; + minContains?: number; + maxContains?: number; + propertyNames?: Oas3_1Schema; + if?: Oas3_1Schema; + then?: Oas3_1Schema; + else?: Oas3_1Schema; + dependentRequired?: { [name: string]: string[] }; + dependentSchemas?: { [name: string]: Referenced }; + patternProperties?: { [name: string]: Referenced }; + unevaluatedItems?: boolean | Oas3_1Schema; + unevaluatedProperties?: boolean | Oas3_1Schema; + contentSchema?: Oas3_1Schema; + contentMediaType?: string; + contentEncoding?: string; +} + +export interface Oas3Webhooks { + [webhook: string]: Referenced>; } export interface Oas3Discriminator { @@ -177,16 +205,16 @@ export interface Oas3Discriminator { 'x-explicitMappingOnly'?: boolean; } -export interface Oas3MediaType { - schema?: Referenced; - example?: any; +export interface Oas3MediaType { + schema?: Referenced; + example?: unknown; examples?: { [name: string]: Referenced }; - encoding?: { [field: string]: Oas3Encoding }; + encoding?: { [field: string]: Oas3Encoding }; } -export interface Oas3Encoding { +export interface Oas3Encoding { contentType: string; - headers?: { [name: string]: Referenced }; + headers?: { [name: string]: Referenced> }; style: Oas3ParameterStyle; explode: boolean; allowReserved: boolean; @@ -202,46 +230,62 @@ export type Oas3ParameterStyle = | 'pipeDelimited' | 'deepObject'; -export interface Oas3RequestBody { +export interface Oas3RequestBody { description?: string; required?: boolean; - content: { [mime: string]: Oas3MediaType }; + content: { [mime: string]: Oas3MediaType }; } -export interface Oas3Responses { - [code: string]: Oas3Response; +export interface Oas3Responses { + [code: string]: Oas3Response; } -export interface Oas3Response { +export interface Oas3Response { description?: string; - headers?: { [name: string]: Referenced }; - content?: { [mime: string]: Oas3MediaType }; + headers?: { [name: string]: Referenced> }; + content?: { [mime: string]: Oas3MediaType }; links?: { [name: string]: Referenced }; } export interface Oas3Link { - $ref?: string; + operationRef?: string; + operationId?: string; + parameters?: { [name: string]: unknown }; + requestBody?: unknown; + description?: string; + server?: Oas3Server; } -export type Oas3Header = Omit; +export type Oas3Header = Omit< + Oas3Parameter, + 'in' | 'name' +>; -export interface Oas3Callback { - [name: string]: Oas3PathItem; +export interface Oas3Callback { + [name: string]: Oas3PathItem; } -export interface Oas3Components { - schemas?: { [name: string]: Referenced }; - responses?: { [name: string]: Referenced }; - parameters?: { [name: string]: Referenced }; +// common fields for OAS components v3.x +export interface Oas3ComponentsBase { + schemas?: { [name: string]: Referenced }; + responses?: { [name: string]: Referenced> }; + parameters?: { [name: string]: Referenced> }; examples?: { [name: string]: Referenced }; - requestBodies?: { [name: string]: Referenced }; - headers?: { [name: string]: Referenced }; + requestBodies?: { [name: string]: Referenced> }; + headers?: { [name: string]: Referenced> }; securitySchemes?: { [name: string]: Referenced }; links?: { [name: string]: Referenced }; - callbacks?: { [name: string]: Referenced }; + callbacks?: { [name: string]: Referenced> }; } -export type Oas3ComponentName = keyof Oas3Components; +export interface Oas3_1Components extends Oas3ComponentsBase { + pathItems?: { [name: string]: Referenced> }; +} + +export interface Oas3Components extends Oas3ComponentsBase {} + +export type Oas3ComponentName = + keyof Oas3ComponentsBase; export interface Oas3SecurityRequirement { [name: string]: string[]; diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 6b6d0476e3..16d5940bb2 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -354,3 +354,5 @@ export function dequal(foo: any, bar: any): boolean { } export type CollectFn = (value: unknown) => void; + +export type StrictObject = T & { [key: string]: undefined }; diff --git a/packages/core/src/visitors.ts b/packages/core/src/visitors.ts index 61b7282ec2..4dece6af4d 100644 --- a/packages/core/src/visitors.ts +++ b/packages/core/src/visitors.ts @@ -6,12 +6,15 @@ import type { UserContext, ResolveResult, ProblemSeverity } from './walk'; import type { Location } from './ref-utils'; import type { Oas3Definition, + Oas3_1Definition, Oas3ExternalDocs, Oas3Info, Oas3Contact, Oas3Components, + Oas3_1Components, Oas3License, Oas3Schema, + Oas3_1Schema, Oas3Header, Oas3Parameter, Oas3Operation, @@ -152,7 +155,7 @@ export type BaseVisitor = { }; type Oas3FlatVisitor = { - Root?: VisitFunctionOrObject; + Root?: VisitFunctionOrObject; Tag?: VisitFunctionOrObject; ExternalDocs?: VisitFunctionOrObject; Server?: VisitFunctionOrObject; @@ -161,36 +164,38 @@ type Oas3FlatVisitor = { Info?: VisitFunctionOrObject; Contact?: VisitFunctionOrObject; License?: VisitFunctionOrObject; - Paths?: VisitFunctionOrObject>; - PathItem?: VisitFunctionOrObject; - Callback?: VisitFunctionOrObject; - CallbacksMap?: VisitFunctionOrObject>; - Parameter?: VisitFunctionOrObject; - Operation?: VisitFunctionOrObject; - RequestBody?: VisitFunctionOrObject; - MediaTypesMap?: VisitFunctionOrObject>; - MediaType?: VisitFunctionOrObject; + Paths?: VisitFunctionOrObject>>; + PathItem?: VisitFunctionOrObject>; + Callback?: VisitFunctionOrObject>; + CallbacksMap?: VisitFunctionOrObject>>; + Parameter?: VisitFunctionOrObject>; + Operation?: VisitFunctionOrObject>; + RequestBody?: VisitFunctionOrObject>; + MediaTypesMap?: VisitFunctionOrObject>>; + MediaType?: VisitFunctionOrObject>; Example?: VisitFunctionOrObject; - Encoding?: VisitFunctionOrObject; - Header?: VisitFunctionOrObject; - Responses?: VisitFunctionOrObject>; - Response?: VisitFunctionOrObject; + Encoding?: VisitFunctionOrObject>; + Header?: VisitFunctionOrObject>; + Responses?: VisitFunctionOrObject>>; + Response?: VisitFunctionOrObject>; Link?: VisitFunctionOrObject; - Schema?: VisitFunctionOrObject; + Schema?: VisitFunctionOrObject; Xml?: VisitFunctionOrObject; SchemaProperties?: VisitFunctionOrObject>; DiscriminatorMapping?: VisitFunctionOrObject>; Discriminator?: VisitFunctionOrObject; - Components?: VisitFunctionOrObject; + Components?: VisitFunctionOrObject; NamedSchemas?: VisitFunctionOrObject>; - NamedResponses?: VisitFunctionOrObject>; - NamedParameters?: VisitFunctionOrObject>; + NamedResponses?: VisitFunctionOrObject>>; + NamedParameters?: VisitFunctionOrObject>>; NamedExamples?: VisitFunctionOrObject>; - NamedRequestBodies?: VisitFunctionOrObject>; - NamedHeaders?: VisitFunctionOrObject>; + NamedRequestBodies?: VisitFunctionOrObject< + Record> + >; + NamedHeaders?: VisitFunctionOrObject>>; NamedSecuritySchemes?: VisitFunctionOrObject>; NamedLinks?: VisitFunctionOrObject>; - NamedCallbacks?: VisitFunctionOrObject>; + NamedCallbacks?: VisitFunctionOrObject>>; ImplicitFlow?: VisitFunctionOrObject; PasswordFlow?: VisitFunctionOrObject; ClientCredentials?: VisitFunctionOrObject;