diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts index 8671e0a35..916d8e70c 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 0c5881610..5b3e0225b 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, - Oas3Components, + Oas3_1Schema, + Oas3ComponentsBase, + 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: Oas3ComponentsBase| 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,7 @@ 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 +356,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 2b56b24ca..bf46fa30e 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 'core/src/typings/openapi'; + export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition; export interface ComponentsFiles { [schemas: string]: any; diff --git a/packages/core/src/decorators/common/media-type-examples-override.ts b/packages/core/src/decorators/common/media-type-examples-override.ts index b2a064943..8b8037fe3 100644 --- a/packages/core/src/decorators/common/media-type-examples-override.ts +++ b/packages/core/src/decorators/common/media-type-examples-override.ts @@ -2,13 +2,13 @@ import { yamlAndJsonSyncReader } from '../../utils'; import { isRef } from '../../ref-utils'; import type { Oas3Decorator } from '../../visitors'; -import type { Oas3_1Schema, Oas3Operation, Oas3RequestBody, Oas3Response, Oas3Schema } from '../../typings/openapi'; +import type { Oas3Operation, Oas3RequestBody, Oas3Response } from '../../typings/openapi'; import type { NonUndefined, ResolveFn, UserContext } from '../../walk'; export const MediaTypeExamplesOverride: Oas3Decorator = ({ operationIds }) => { return { Operation: { - enter(operation: Oas3Operation, ctx: UserContext) { + enter(operation: Oas3Operation, ctx: UserContext) { const operationId = operation.operationId; if (!operationId) { @@ -23,7 +23,7 @@ export const MediaTypeExamplesOverride: Oas3Decorator = ({ operationIds }) => { if (properties.responses && operation.responses) { for (const responseCode of Object.keys(properties.responses)) { - const resolvedResponse = checkAndResolveRef>( + const resolvedResponse = checkAndResolveRef( operation.responses[responseCode], ctx.resolve ); @@ -46,7 +46,7 @@ export const MediaTypeExamplesOverride: Oas3Decorator = ({ operationIds }) => { } if (properties.request && operation.requestBody) { - const resolvedRequest = checkAndResolveRef>( + const resolvedRequest = checkAndResolveRef( operation.requestBody, ctx.resolve ); diff --git a/packages/core/src/decorators/common/operation-description-override.ts b/packages/core/src/decorators/common/operation-description-override.ts index 46df16160..c583fb4ff 100644 --- a/packages/core/src/decorators/common/operation-description-override.ts +++ b/packages/core/src/decorators/common/operation-description-override.ts @@ -2,13 +2,13 @@ import { readFileAsStringSync } from '../../utils'; import type { Oas3Decorator, Oas2Decorator } from '../../visitors'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Schema, Oas3_1Schema, Oas3Operation } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const OperationDescriptionOverride: Oas3Decorator | Oas2Decorator = ({ operationIds }) => { return { Operation: { - leave(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { + leave(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { if (!operation.operationId) return; if (!operationIds) throw new Error( diff --git a/packages/core/src/decorators/oas3/remove-unused-components.ts b/packages/core/src/decorators/oas3/remove-unused-components.ts index f65ae6d99..87ec90cdd 100644 --- a/packages/core/src/decorators/oas3/remove-unused-components.ts +++ b/packages/core/src/decorators/oas3/remove-unused-components.ts @@ -2,17 +2,28 @@ import { isEmptyObject } from '../../utils'; import type { Location } from '../../ref-utils'; import type { Oas3Decorator } from '../../visitors'; -import type { Oas3Schema, Oas3_1Schema, Oas3Components, Oas3Definition } from '../../typings/openapi'; +import type { + Oas3Definition, + Oas3_1Definition, + Oas3ComponentsBase, + Oas3_1Components, + Oas3Schema, + Oas3_1Schema, +} from '../../typings/openapi'; export const RemoveUnusedComponents: Oas3Decorator = () => { const components = new Map< string, - { usedIn: Location[]; componentType?: keyof Oas3Components; name: string } + { + usedIn: Location[]; + componentType?: keyof (Oas3ComponentsBase| Oas3_1Components); + name: string; + } >(); function registerComponent( location: Location, - componentType: keyof Oas3Components, + componentType: keyof (Oas3ComponentsBase | Oas3_1Components), name: string ): void { components.set(location.absolutePointer, { @@ -22,7 +33,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 d96598fca..c7758b939 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,14 +17,14 @@ export { ConfigTypes } from './types/redocly-yaml'; export type { Oas3Definition, Oas3_1Definition, - Oas3Components, + Oas3ComponentsBase, + Oas3_1Components, Oas3PathItem, Oas3Paths, Oas3ComponentName, Oas3Schema, Oas3_1Schema, Oas3Tag, - Oas3_1Webhooks, Referenced, OasRef, } from './typings/openapi'; diff --git a/packages/core/src/rules/common/no-ambiguous-paths.ts b/packages/core/src/rules/common/no-ambiguous-paths.ts index 3fcf2c130..a8d2af5b2 100644 --- a/packages/core/src/rules/common/no-ambiguous-paths.ts +++ b/packages/core/src/rules/common/no-ambiguous-paths.ts @@ -1,11 +1,11 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { UserContext } from '../../walk'; -import type { Oas3_1Schema, Oas3Paths, Oas3Schema } from '../../typings/openapi'; +import type { Oas3Paths } from '../../typings/openapi'; import type { Oas2Paths } from '../../typings/swagger'; export const NoAmbiguousPaths: Oas3Rule | Oas2Rule = () => { return { - Paths(pathMap: Oas3Paths | Oas2Paths, { report, location }: UserContext) { + Paths(pathMap: Oas3Paths | Oas2Paths, { report, location }: UserContext) { const seenPaths: string[] = []; for (const currentPath of Object.keys(pathMap)) { diff --git a/packages/core/src/rules/common/no-http-verbs-in-paths.ts b/packages/core/src/rules/common/no-http-verbs-in-paths.ts index dce002b90..59560154f 100644 --- a/packages/core/src/rules/common/no-http-verbs-in-paths.ts +++ b/packages/core/src/rules/common/no-http-verbs-in-paths.ts @@ -2,14 +2,14 @@ import { isPathParameter, splitCamelCaseIntoWords } from '../../utils'; import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2PathItem } from '../../typings/swagger'; -import type { Oas3Schema, Oas3_1Schema, Oas3PathItem } from '../../typings/openapi'; +import type { Oas3PathItem } from '../../typings/openapi'; import type { UserContext } from '../../walk'; const httpMethods = ['get', 'head', 'post', 'put', 'patch', 'delete', 'options', 'trace']; export const NoHttpVerbsInPaths: Oas3Rule | Oas2Rule = ({ splitIntoWords }) => { return { - PathItem(_path: Oas2PathItem | Oas3PathItem, { key, report, location }: UserContext) { + PathItem(_path: Oas2PathItem | Oas3PathItem, { key, report, location }: UserContext) { const pathKey = key.toString(); if (!pathKey.startsWith('/')) return; const pathSegments = pathKey.split('/'); diff --git a/packages/core/src/rules/common/no-identical-paths.ts b/packages/core/src/rules/common/no-identical-paths.ts index d5da26d80..988dec3e5 100644 --- a/packages/core/src/rules/common/no-identical-paths.ts +++ b/packages/core/src/rules/common/no-identical-paths.ts @@ -1,11 +1,11 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { UserContext } from '../../walk'; -import type { Oas3Schema, Oas3_1Schema, Oas3Paths } from '../../typings/openapi'; +import type { Oas3Paths } from '../../typings/openapi'; import type { Oas2Paths } from '../../typings/swagger'; export const NoIdenticalPaths: Oas3Rule | Oas2Rule = () => { return { - Paths(pathMap: Oas3Paths | Oas2Paths, { report, location }: UserContext) { + Paths(pathMap: Oas3Paths | Oas2Paths, { report, location }: UserContext) { const Paths = new Map(); for (const pathName of Object.keys(pathMap)) { const id = pathName.replace(/{.+?}/g, '{VARIABLE}'); diff --git a/packages/core/src/rules/common/no-invalid-parameter-examples.ts b/packages/core/src/rules/common/no-invalid-parameter-examples.ts index a20094a65..740335dfe 100644 --- a/packages/core/src/rules/common/no-invalid-parameter-examples.ts +++ b/packages/core/src/rules/common/no-invalid-parameter-examples.ts @@ -1,13 +1,13 @@ import { getAdditionalPropertiesOption, validateExample } from '../utils'; import type { UserContext } from '../../walk'; -import type { Oas3Schema, Oas3_1Schema, Oas3Parameter } from '../../typings/openapi'; +import type { Oas3Parameter } from '../../typings/openapi'; export const NoInvalidParameterExamples: any = (opts: any) => { const allowAdditionalProperties = getAdditionalPropertiesOption(opts) ?? false; return { Parameter: { - leave(parameter: Oas3Parameter, ctx: UserContext) { + leave(parameter: Oas3Parameter, ctx: UserContext) { if (parameter.example !== undefined) { validateExample( parameter.example, diff --git a/packages/core/src/rules/common/operation-description.ts b/packages/core/src/rules/common/operation-description.ts index b5cd7999b..6f2bb45c0 100644 --- a/packages/core/src/rules/common/operation-description.ts +++ b/packages/core/src/rules/common/operation-description.ts @@ -3,11 +3,11 @@ import { validateDefinedAndNonEmpty } from '../utils'; import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { UserContext } from '../../walk'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Operation, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; export const OperationDescription: Oas3Rule | Oas2Rule = () => { return { - Operation(operation: Oas2Operation | Oas3Operation, ctx: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, ctx: UserContext) { validateDefinedAndNonEmpty('description', operation, ctx); }, }; diff --git a/packages/core/src/rules/common/operation-operationId-unique.ts b/packages/core/src/rules/common/operation-operationId-unique.ts index 7819ca2eb..54aa597ab 100644 --- a/packages/core/src/rules/common/operation-operationId-unique.ts +++ b/packages/core/src/rules/common/operation-operationId-unique.ts @@ -1,13 +1,13 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Operation, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const OperationIdUnique: Oas3Rule | Oas2Rule = () => { const seenOperations = new Set(); return { - Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { if (!operation.operationId) return; if (seenOperations.has(operation.operationId)) { report({ diff --git a/packages/core/src/rules/common/operation-operationId-url-safe.ts b/packages/core/src/rules/common/operation-operationId-url-safe.ts index 7a3ffb920..5f1c41080 100644 --- a/packages/core/src/rules/common/operation-operationId-url-safe.ts +++ b/packages/core/src/rules/common/operation-operationId-url-safe.ts @@ -1,6 +1,6 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Operation, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; import type { UserContext } from '../../walk'; // eslint-disable-next-line no-useless-escape @@ -8,7 +8,7 @@ const validUrlSymbols = /^[A-Za-z0-9-._~:/?#\[\]@!\$&'()*+,;=]*$/; export const OperationIdUrlSafe: Oas3Rule | Oas2Rule = () => { return { - Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { if (operation.operationId && !validUrlSymbols.test(operation.operationId)) { report({ message: 'Operation `operationId` should not have URL invalid characters.', diff --git a/packages/core/src/rules/common/operation-operationId.ts b/packages/core/src/rules/common/operation-operationId.ts index c4323802f..b759a2ab0 100644 --- a/packages/core/src/rules/common/operation-operationId.ts +++ b/packages/core/src/rules/common/operation-operationId.ts @@ -3,13 +3,13 @@ import { validateDefinedAndNonEmpty } from '../utils'; import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { UserContext } from '../../walk'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Operation, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; export const OperationOperationId: Oas3Rule | Oas2Rule = () => { return { Root: { PathItem: { - Operation(operation: Oas2Operation | Oas3Operation, ctx: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, ctx: UserContext) { validateDefinedAndNonEmpty('operationId', operation, ctx); }, }, diff --git a/packages/core/src/rules/common/operation-parameters-unique.ts b/packages/core/src/rules/common/operation-parameters-unique.ts index dc4f30a6c..901dfb26d 100644 --- a/packages/core/src/rules/common/operation-parameters-unique.ts +++ b/packages/core/src/rules/common/operation-parameters-unique.ts @@ -1,6 +1,6 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Parameter } from '../../typings/swagger'; -import type { Oas3Schema, Oas3_1Schema, Oas3Parameter } from '../../typings/openapi'; +import type { Oas3Parameter } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const OperationParametersUnique: Oas3Rule | Oas2Rule = () => { @@ -13,7 +13,7 @@ export const OperationParametersUnique: Oas3Rule | Oas2Rule = () => { seenPathParams = new Set(); }, Parameter( - parameter: Oas2Parameter | Oas3Parameter, + parameter: Oas2Parameter | Oas3Parameter, { report, key, parentLocations }: UserContext ) { const paramId = `${parameter.in}___${parameter.name}`; @@ -30,7 +30,7 @@ export const OperationParametersUnique: Oas3Rule | Oas2Rule = () => { seenOperationParams = new Set(); }, Parameter( - parameter: Oas2Parameter | Oas3Parameter, + parameter: Oas2Parameter | Oas3Parameter, { report, key, parentLocations }: UserContext ) { const paramId = `${parameter.in}___${parameter.name}`; diff --git a/packages/core/src/rules/common/operation-singular-tag.ts b/packages/core/src/rules/common/operation-singular-tag.ts index df3e914e5..6e149f9ca 100644 --- a/packages/core/src/rules/common/operation-singular-tag.ts +++ b/packages/core/src/rules/common/operation-singular-tag.ts @@ -1,11 +1,11 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Operation, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const OperationSingularTag: Oas3Rule | Oas2Rule = () => { return { - Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { if (operation.tags && operation.tags.length > 1) { report({ message: 'Operation `tags` object should have only one tag.', diff --git a/packages/core/src/rules/common/operation-summary.ts b/packages/core/src/rules/common/operation-summary.ts index 171b1b747..18743ad9e 100644 --- a/packages/core/src/rules/common/operation-summary.ts +++ b/packages/core/src/rules/common/operation-summary.ts @@ -3,11 +3,11 @@ import { validateDefinedAndNonEmpty } from '../utils'; import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { UserContext } from '../../walk'; import type { Oas2Operation } from '../../typings/swagger'; -import type { Oas3Operation, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Operation } from '../../typings/openapi'; export const OperationSummary: Oas3Rule | Oas2Rule = () => { return { - Operation(operation: Oas2Operation | Oas3Operation, ctx: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, ctx: UserContext) { validateDefinedAndNonEmpty('summary', operation, ctx); }, }; diff --git a/packages/core/src/rules/common/operation-tag-defined.ts b/packages/core/src/rules/common/operation-tag-defined.ts index ea70225e1..804381aea 100644 --- a/packages/core/src/rules/common/operation-tag-defined.ts +++ b/packages/core/src/rules/common/operation-tag-defined.ts @@ -1,16 +1,16 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Definition, Oas2Operation } from '../../typings/swagger'; -import type { Oas3Definition, Oas3Operation, Oas3Schema, Oas3_1Schema } 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) { + Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) { if (operation.tags) { for (let i = 0; i < operation.tags.length; i++) { if (!definedTags.has(operation.tags[i])) { diff --git a/packages/core/src/rules/common/parameter-description.ts b/packages/core/src/rules/common/parameter-description.ts index 2580307b7..f1495b29e 100644 --- a/packages/core/src/rules/common/parameter-description.ts +++ b/packages/core/src/rules/common/parameter-description.ts @@ -1,11 +1,11 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Parameter } from '../../typings/swagger'; -import type { Oas3Schema, Oas3_1Schema, Oas3Parameter } from '../../typings/openapi'; +import type { Oas3Parameter } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const ParameterDescription: Oas3Rule | Oas2Rule = () => { return { - Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) { + Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) { if (parameter.description === undefined) { report({ message: 'Parameter object description must be present.', diff --git a/packages/core/src/rules/common/path-excludes-patterns.ts b/packages/core/src/rules/common/path-excludes-patterns.ts index 3304c5314..14988cbff 100644 --- a/packages/core/src/rules/common/path-excludes-patterns.ts +++ b/packages/core/src/rules/common/path-excludes-patterns.ts @@ -1,11 +1,11 @@ import type { Oas2Rule, Oas3Rule } from '../../visitors'; import type { Oas2PathItem } from '../../typings/swagger'; -import type { Oas3PathItem, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3PathItem } from '../../typings/openapi'; import type { UserContext } from '../../walk'; export const PathExcludesPatterns: Oas3Rule | Oas2Rule = ({ patterns }) => { return { - PathItem(_path: Oas2PathItem | Oas3PathItem, { report, key, location }: UserContext) { + PathItem(_path: Oas2PathItem | Oas3PathItem, { report, key, location }: UserContext) { if (!patterns) throw new Error(`Parameter "patterns" is not provided for "path-excludes-patterns" rule`); const pathKey = key.toString(); diff --git a/packages/core/src/rules/common/path-http-verbs-order.ts b/packages/core/src/rules/common/path-http-verbs-order.ts index 4f1f2615b..3bfe0bcdd 100644 --- a/packages/core/src/rules/common/path-http-verbs-order.ts +++ b/packages/core/src/rules/common/path-http-verbs-order.ts @@ -1,6 +1,6 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2PathItem } from '../../typings/swagger'; -import type { Oas3PathItem, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3PathItem } from '../../typings/openapi'; import type { UserContext } from '../../walk'; const defaultOrder = ['get', 'head', 'post', 'put', 'patch', 'delete', 'options', 'trace']; @@ -12,7 +12,7 @@ export const PathHttpVerbsOrder: Oas3Rule | Oas2Rule = (opts: any) => { } return { - PathItem(path: Oas2PathItem | Oas3PathItem, { report, location }: UserContext) { + PathItem(path: Oas2PathItem | Oas3PathItem, { report, location }: UserContext) { const httpVerbs = Object.keys(path).filter((k) => order.includes(k)); for (let i = 0; i < httpVerbs.length - 1; i++) { diff --git a/packages/core/src/rules/common/path-params-defined.ts b/packages/core/src/rules/common/path-params-defined.ts index 88c5ed50b..bbb696b86 100644 --- a/packages/core/src/rules/common/path-params-defined.ts +++ b/packages/core/src/rules/common/path-params-defined.ts @@ -1,6 +1,6 @@ import type { Oas3Rule, Oas2Rule } from '../../visitors'; import type { Oas2Parameter } from '../../typings/swagger'; -import type { Oas3Schema, Oas3_1Schema, Oas3Parameter } from '../../typings/openapi'; +import type { Oas3Parameter } from '../../typings/openapi'; import type { UserContext } from '../../walk'; const pathRegex = /\{([a-zA-Z0-9_.-]+)\}+/g; @@ -20,7 +20,7 @@ export const PathParamsDefined: Oas3Rule | Oas2Rule = () => { Array.from(key!.toString().matchAll(pathRegex)).map((m) => m[1]) ); }, - Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) { + Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) { if (parameter.in === 'path' && parameter.name) { definedPathParams.add(parameter.name); if (!pathTemplateParams.has(parameter.name)) { @@ -48,7 +48,7 @@ export const PathParamsDefined: Oas3Rule | Oas2Rule = () => { } } }, - Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) { + Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) { if (parameter.in === 'path' && parameter.name) { definedOperationParams.add(parameter.name); if (!pathTemplateParams.has(parameter.name)) { diff --git a/packages/core/src/rules/common/response-contains-header.ts b/packages/core/src/rules/common/response-contains-header.ts index ba875329c..38ad9d6f7 100644 --- a/packages/core/src/rules/common/response-contains-header.ts +++ b/packages/core/src/rules/common/response-contains-header.ts @@ -2,7 +2,7 @@ import { getMatchingStatusCodeRange } from '../../utils'; import type { Oas2Rule, Oas3Rule } from '../../visitors'; import type { UserContext } from '../../walk'; -import type { Oas3Response, Oas3Schema, Oas3_1Schema } from '../../typings/openapi'; +import type { Oas3Response } from '../../typings/openapi'; import type { Oas2Response } from '../../typings/swagger'; export const ResponseContainsHeader: Oas3Rule | Oas2Rule = (options) => { @@ -10,7 +10,7 @@ export const ResponseContainsHeader: Oas3Rule | Oas2Rule = (options) => { return { Operation: { Response: { - enter: (response: Oas2Response | Oas3Response, { report, location, key }: UserContext) => { + enter: (response: Oas2Response | Oas3Response, { report, location, key }: UserContext) => { const expectedHeaders = names[key] || names[getMatchingStatusCodeRange(key)] || diff --git a/packages/core/src/rules/common/security-defined.ts b/packages/core/src/rules/common/security-defined.ts index b27dfed6f..9ebe20578 100644 --- a/packages/core/src/rules/common/security-defined.ts +++ b/packages/core/src/rules/common/security-defined.ts @@ -8,9 +8,8 @@ import type { Oas2SecurityScheme, } from '../../typings/swagger'; import type { - Oas3Schema, - Oas3_1Schema, Oas3Definition, + Oas3_1Definition, Oas3Operation, Oas3PathItem, Oas3SecurityScheme, @@ -33,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) { @@ -71,10 +70,10 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = (opts: { } }, PathItem: { - enter(pathItem: Oas2PathItem | Oas3PathItem, { key }: UserContext) { + enter(pathItem: Oas2PathItem | Oas3PathItem, { key }: UserContext) { path = key as string; }, - Operation(operation: Oas2Operation | Oas3Operation, { location, key }: UserContext) { + Operation(operation: Oas2Operation | Oas3Operation, { location, key }: UserContext) { const isException = opts.exceptions?.some( (item) => item.path === path && diff --git a/packages/core/src/rules/common/tags-alphabetical.ts b/packages/core/src/rules/common/tags-alphabetical.ts index 0411406c4..2257d51a5 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 { Oas3Schema, Oas3_1Schema, 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 8f48a28c2..33430b3ac 100644 --- a/packages/core/src/rules/oas3/array-parameter-serialization.ts +++ b/packages/core/src/rules/oas3/array-parameter-serialization.ts @@ -12,15 +12,15 @@ 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, diff --git a/packages/core/src/rules/oas3/component-name-unique.ts b/packages/core/src/rules/oas3/component-name-unique.ts index 8a83365e8..b6475345b 100644 --- a/packages/core/src/rules/oas3/component-name-unique.ts +++ b/packages/core/src/rules/oas3/component-name-unique.ts @@ -3,6 +3,7 @@ import type { Problem, UserContext } from '../../walk'; import type { Oas2Rule, Oas3Rule, Oas3Visitor } from '../../visitors'; import type { Oas3Definition, + Oas3_1Definition, Oas3Parameter, Oas3RequestBody, Oas3Response, @@ -55,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); @@ -83,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); }, }; @@ -91,7 +92,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => { if (options.responses != 'off') { rule.NamedResponses = { - Response(_: Oas3Response, { location }: UserContext) { + Response(_: Oas3Response, { location }: UserContext) { addComponentFromAbsoluteLocation(TYPE_NAME_RESPONSE, location); }, }; @@ -99,7 +100,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => { if (options.parameters != 'off') { rule.NamedParameters = { - Parameter(_: Oas3Parameter, { location }: UserContext) { + Parameter(_: Oas3Parameter, { location }: UserContext) { addComponentFromAbsoluteLocation(TYPE_NAME_PARAMETER, location); }, }; @@ -107,7 +108,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => { if (options.requestBodies != 'off') { rule.NamedRequestBodies = { - RequestBody(_: Oas3RequestBody, { location }: UserContext) { + RequestBody(_: Oas3RequestBody, { location }: UserContext) { addComponentFromAbsoluteLocation(TYPE_NAME_REQUEST_BODY, location); }, }; diff --git a/packages/core/src/rules/other/stats.ts b/packages/core/src/rules/other/stats.ts index faf180819..3ba9a152c 100644 --- a/packages/core/src/rules/other/stats.ts +++ b/packages/core/src/rules/other/stats.ts @@ -1,4 +1,4 @@ -import type { Oas3Schema, Oas3_1Schema, Oas3Parameter, OasRef, Oas3Tag } from '../../typings/openapi'; +import type { Oas3Parameter, OasRef, Oas3Tag } from '../../typings/openapi'; import type { Oas2Parameter } from '../../typings/swagger'; import type { StatsAccumulator } from '../../typings/common'; @@ -58,7 +58,7 @@ export const Stats = (statsAccumulator: StatsAccumulator) => { }, }, Parameter: { - leave(parameter: Oas2Parameter | Oas3Parameter) { + leave(parameter: Oas2Parameter | Oas3Parameter) { statsAccumulator.parameters.items!.add(parameter.name); }, }, diff --git a/packages/core/src/typings/openapi.ts b/packages/core/src/typings/openapi.ts index 0a7023052..03b8ff9fe 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; + components?: T extends Oas3_1Schema ? Oas3_1Components : Oas3ComponentsBase; 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,7 +39,7 @@ export interface Oas3ServerVariable { description?: string; } -export interface Oas3Paths { +export interface Oas3Paths { [path: string]: Referenced>; } export interface OasRef { @@ -41,7 +48,7 @@ export interface OasRef { export type Referenced = OasRef | T; -export interface Oas3PathItem { +export interface Oas3PathItem { summary?: string; description?: string; get?: Oas3Operation; @@ -54,7 +61,6 @@ export interface Oas3PathItem { trace?: Oas3Operation; servers?: Oas3Server[]; parameters?: Array>>; - $ref?: Referenced; } export interface Oas3XCodeSample { @@ -63,7 +69,7 @@ export interface Oas3XCodeSample { source: string; } -export interface Oas3Operation { +export interface Oas3Operation { tags?: string[]; summary?: string; description?: string; @@ -81,7 +87,7 @@ export interface Oas3Operation { 'x-hideTryItPanel'?: boolean; } -export interface Oas3Parameter { +export interface Oas3Parameter { name: string; in?: Oas3ParameterLocation; description?: string; @@ -115,6 +121,8 @@ export interface Oas3Xml { // common fields for OpenAPI Schema v3.x interface Oas3XSchemaBase { $ref?: string; + properties?: { [name: string]: Referenced }; + additionalProperties?: boolean | { [name: string]: Referenced }; description?: string; default?: unknown; required?: string[]; @@ -139,7 +147,6 @@ interface Oas3XSchemaBase { maxItems?: number; minItems?: number; uniqueItems?: boolean; - additionalProperties?: boolean | T; maxProperties?: number; minProperties?: number; enum?: unknown[]; @@ -151,7 +158,6 @@ interface Oas3XSchemaBase { export interface Oas3Schema extends Oas3XSchemaBase { type?: string; - properties?: { [name: string]: Referenced }; exclusiveMaximum?: boolean; exclusiveMinimum?: boolean; nullable?: boolean; @@ -167,7 +173,6 @@ export interface Oas3_1Schema extends Oas3XSchemaBase { $vocabulary?: { [uri: string]: boolean }; $comment?: string; type?: string | string[]; - properties?: { [name: string]: Referenced }; examples?: unknown[]; prefixItems?: Oas3_1Schema[]; exclusiveMaximum?: number; @@ -190,11 +195,7 @@ export interface Oas3_1Schema extends Oas3XSchemaBase { contentEncoding?: string; } -export interface Oas3_1Definition extends Oas3Definition { - webhooks?: Oas3_1Webhooks; -} - -export interface Oas3_1Webhooks { +export interface Oas3Webhooks { [webhook: string]: Referenced>; } @@ -204,14 +205,14 @@ export interface Oas3Discriminator { 'x-explicitMappingOnly'?: boolean; } -export interface Oas3MediaType { +export interface Oas3MediaType { schema?: Referenced; example?: unknown; examples?: { [name: string]: Referenced }; encoding?: { [field: string]: Oas3Encoding }; } -export interface Oas3Encoding { +export interface Oas3Encoding { contentType: string; headers?: { [name: string]: Referenced> }; style: Oas3ParameterStyle; @@ -229,7 +230,7 @@ export type Oas3ParameterStyle = | 'pipeDelimited' | 'deepObject'; -export interface Oas3RequestBody { +export interface Oas3RequestBody { description?: string; required?: boolean; content: { [mime: string]: Oas3MediaType }; @@ -239,7 +240,7 @@ export interface Oas3Responses { [code: string]: Oas3Response; } -export interface Oas3Response { +export interface Oas3Response { description?: string; headers?: { [name: string]: Referenced> }; content?: { [mime: string]: Oas3MediaType }; @@ -249,19 +250,23 @@ export interface Oas3Response { export interface Oas3Link { operationRef?: string; operationId?: string; - parameters?: { [ name: string]: unknown }; + parameters?: { [name: string]: unknown }; requestBody?: unknown; description?: string; server?: Oas3Server; } -export type Oas3Header = Omit, 'in' | 'name'>; +export type Oas3Header = Omit< + Oas3Parameter, + 'in' | 'name' +>; -export interface Oas3Callback { +export interface Oas3Callback { [name: string]: Oas3PathItem; } -export interface Oas3Components { +// common fields for OAS components v3.x +export interface Oas3ComponentsBase { schemas?: { [name: string]: Referenced }; responses?: { [name: string]: Referenced> }; parameters?: { [name: string]: Referenced> }; @@ -273,11 +278,11 @@ export interface Oas3Components { callbacks?: { [name: string]: Referenced> }; } -export interface Oas3_1Components>{ - pathItems?: { [name: string]: Referenced> }; +export interface Oas3_1Components extends Oas3ComponentsBase { + pathItems?: { [name: string]: Referenced> }; } -export type Oas3ComponentName = keyof Oas3Components; +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 6b6d0476e..16d5940bb 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 96e6faa6f..0d56fc75f 100644 --- a/packages/core/src/visitors.ts +++ b/packages/core/src/visitors.ts @@ -6,10 +6,11 @@ import type { UserContext, ResolveResult, ProblemSeverity } from './walk'; import type { Location } from './ref-utils'; import type { Oas3Definition, + Oas3_1Definition, Oas3ExternalDocs, Oas3Info, Oas3Contact, - Oas3Components, + Oas3ComponentsBase, Oas3_1Components, Oas3License, Oas3Schema, @@ -152,7 +153,7 @@ export type BaseVisitor = { }; type Oas3FlatVisitor = { - Root?: VisitFunctionOrObject>; + Root?: VisitFunctionOrObject; Tag?: VisitFunctionOrObject; ExternalDocs?: VisitFunctionOrObject; Server?: VisitFunctionOrObject; @@ -176,12 +177,12 @@ type Oas3FlatVisitor = { Responses?: VisitFunctionOrObject>>; Response?: VisitFunctionOrObject>; Link?: VisitFunctionOrObject; - Schema?: VisitFunctionOrObject; + Schema?: VisitFunctionOrObject; Xml?: VisitFunctionOrObject; SchemaProperties?: VisitFunctionOrObject>; DiscriminatorMapping?: VisitFunctionOrObject>; Discriminator?: VisitFunctionOrObject; - Components?: VisitFunctionOrObject | Oas3_1Components>; + Components?: VisitFunctionOrObject | Oas3_1Components>; NamedSchemas?: VisitFunctionOrObject>; NamedResponses?: VisitFunctionOrObject>>; NamedParameters?: VisitFunctionOrObject>>;