From 083815dd80e30c9d828b45932eacfb1b8266f737 Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Sun, 6 Sep 2026 21:01:11 +0700 Subject: [PATCH 1/2] fix(types): define 3.1 LinkObject locally, add prepack hook, extend type tests - Define LinkObject in v3.1 against the 3.1 ServerObject instead of re-exporting the 3.0 type, per the module's own re-export rule. - Add a prepack build script so publishing always ships a fresh dist, matching the downgrader package. - Add @ts-expect-error tests for fields that belong to a later spec version (3.1-only fields in 3.0, 3.2-only fields in 3.1), header style restriction, and positive tests for referenced media types in 3.2 content maps. --- packages/types/package.json | 2 +- packages/types/src/v3.0.test-d.ts | 61 ++++++++++++++ packages/types/src/v3.1.test-d.ts | 136 +++++++++++++++++++++++++++++- packages/types/src/v3.1.ts | 52 +++++++++++- packages/types/src/v3.2.test-d.ts | 34 +++++++- 5 files changed, 280 insertions(+), 5 deletions(-) diff --git a/packages/types/package.json b/packages/types/package.json index aed68d1..f18b5e9 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -64,7 +64,7 @@ } }, "scripts": { - "build": "unbuild", + "prepack": "unbuild", "type:check": "tsc -b" } } diff --git a/packages/types/src/v3.0.test-d.ts b/packages/types/src/v3.0.test-d.ts index 8230ffe..ccd02f1 100644 --- a/packages/types/src/v3.0.test-d.ts +++ b/packages/types/src/v3.0.test-d.ts @@ -1,4 +1,5 @@ import type { + HeaderObject, OpenAPIObject, ReferenceObject, ResponsesObject, @@ -427,3 +428,63 @@ export const typedSchemaMismatch = { default: 1, type: 'string', } satisfies SchemaObject + +export const noInfoSummary = { + info: { + // @ts-expect-error: The Info Object's `summary` field was added in OpenAPI 3.1. + summary: 'A short summary', + title: 'API', + version: '1.0.0', + }, + openapi: '3.0.4', + paths: {}, +} satisfies OpenAPIObject + +export const noLicenseIdentifier = { + info: { + license: { + // @ts-expect-error: The License Object's `identifier` field was added in OpenAPI 3.1. + identifier: 'MIT', + name: 'MIT', + }, + title: 'API', + version: '1.0.0', + }, + openapi: '3.0.4', + paths: {}, +} satisfies OpenAPIObject + +export const noJsonSchemaDialect = { + info: { title: 'API', version: '1.0.0' }, + // @ts-expect-error: The `jsonSchemaDialect` field was added in OpenAPI 3.1. + jsonSchemaDialect: 'https://spec.openapis.org/oas/3.1/dialect/base', + openapi: '3.0.4', + paths: {}, +} satisfies OpenAPIObject + +export const noPathItemsComponent = { + components: { + // @ts-expect-error: The Components Object's `pathItems` field was added in OpenAPI 3.1. + pathItems: {}, + }, + info: { title: 'API', version: '1.0.0' }, + openapi: '3.0.4', + paths: {}, +} satisfies OpenAPIObject + +export const noNullType = { + // @ts-expect-error: `"null"` is not a valid `type` in OpenAPI 3.0; use `nullable` instead. + type: 'null', +} satisfies SchemaObject + +export const noConstKeyword = { + // @ts-expect-error: The JSON Schema `const` keyword is not part of the OpenAPI 3.0 Schema Object. + const: 'fixed', + type: 'string', +} satisfies SchemaObject + +export const headerStyleMustBeSimple = { + schema: { type: 'string' }, + // @ts-expect-error: Header Objects only allow the `simple` style. + style: 'form', +} satisfies HeaderObject diff --git a/packages/types/src/v3.1.test-d.ts b/packages/types/src/v3.1.test-d.ts index 412777c..74d7929 100644 --- a/packages/types/src/v3.1.test-d.ts +++ b/packages/types/src/v3.1.test-d.ts @@ -1,4 +1,12 @@ -import type { OpenAPIObject, PathItemObject, SchemaObject } from './v3.1' +import type { + ExampleObject, + MediaTypeObject, + OpenAPIObject, + ParameterObject, + PathItemObject, + ResponseObject, + SchemaObject, +} from './v3.1' export const webhooksOnly = { components: { @@ -172,3 +180,129 @@ export const typedSchemaMismatch = { examples: [1], type: 'string', } satisfies SchemaObject + +export const noResponseSummary = { + description: 'OK', + // @ts-expect-error: The Response Object's `summary` field was added in OpenAPI 3.2. + summary: 'Success', +} satisfies ResponseObject + +export const noQuerystringLocation = { + content: { + 'application/x-www-form-urlencoded': { schema: { type: 'object' } }, + }, + // @ts-expect-error: The `querystring` parameter location was added in OpenAPI 3.2. + in: 'querystring', + name: 'filter', +} satisfies ParameterObject + +export const noCookieStyle = { + in: 'cookie', + name: 'session', + schema: { type: 'string' }, + // @ts-expect-error: The `cookie` parameter style was added in OpenAPI 3.2. + style: 'cookie', +} satisfies ParameterObject + +export const noSelfUri = { + // @ts-expect-error: The `$self` field was added in OpenAPI 3.2. + $self: 'https://example.com/openapi', + info: { title: 'API', version: '1.0.0' }, + openapi: '3.1.2', + paths: {}, +} satisfies OpenAPIObject + +export const noMediaTypesComponent = { + components: { + // @ts-expect-error: The Components Object's `mediaTypes` field was added in OpenAPI 3.2. + mediaTypes: {}, + }, + info: { title: 'API', version: '1.0.0' }, + openapi: '3.1.2', +} satisfies OpenAPIObject + +export const noServerName = { + info: { title: 'API', version: '1.0.0' }, + openapi: '3.1.2', + paths: {}, + servers: [ + { + // @ts-expect-error: The Server Object's `name` field was added in OpenAPI 3.2. + name: 'production', + url: 'https://api.example.com', + }, + ], +} satisfies OpenAPIObject + +export const noTagHierarchy = { + info: { title: 'API', version: '1.0.0' }, + openapi: '3.1.2', + paths: {}, + tags: [ + { + name: 'pets', + // @ts-expect-error: The Tag Object's `parent` and `kind` fields were added in OpenAPI 3.2. + parent: 'animals', + }, + ], +} satisfies OpenAPIObject + +export const noItemSchema = { + // @ts-expect-error: The Media Type Object's `itemSchema` field was added in OpenAPI 3.2. + itemSchema: { type: 'object' }, +} satisfies MediaTypeObject + +export const noDataValue = { + // @ts-expect-error: The Example Object's `dataValue` field was added in OpenAPI 3.2. + dataValue: { id: 1 }, +} satisfies ExampleObject + +export const noDefaultMapping = { + discriminator: { + // @ts-expect-error: The Discriminator Object's `defaultMapping` field was added in OpenAPI 3.2. + defaultMapping: 'Cat', + propertyName: 'kind', + }, + oneOf: [{ type: 'object' }], +} satisfies SchemaObject + +export const noXmlNodeType = { + type: 'string', + // @ts-expect-error: The XML Object's `nodeType` field was added in OpenAPI 3.2. + xml: { nodeType: 'attribute' }, +} satisfies SchemaObject + +export const noDeprecatedSecurityScheme = { + components: { + securitySchemes: { + basic: { + // @ts-expect-error: The Security Scheme Object's `deprecated` field was added in OpenAPI 3.2. + deprecated: true, + scheme: 'basic', + type: 'http', + }, + }, + }, + info: { title: 'API', version: '1.0.0' }, + openapi: '3.1.2', +} satisfies OpenAPIObject + +export const noDeviceAuthorizationFlow = { + components: { + securitySchemes: { + oauth: { + flows: { + // @ts-expect-error: The OAuth Device Authorization flow was added in OpenAPI 3.2. + deviceAuthorization: { + deviceAuthorizationUrl: 'https://auth.example.com/device', + scopes: {}, + tokenUrl: 'https://auth.example.com/token', + }, + }, + type: 'oauth2', + }, + }, + }, + info: { title: 'API', version: '1.0.0' }, + openapi: '3.1.2', +} satisfies OpenAPIObject diff --git a/packages/types/src/v3.1.ts b/packages/types/src/v3.1.ts index 7190b68..94c7b6e 100644 --- a/packages/types/src/v3.1.ts +++ b/packages/types/src/v3.1.ts @@ -19,7 +19,6 @@ import type { ExampleObject, ExternalDocumentationObject, HttpSecuritySchemeObject, - LinkObject, OAuth2SecuritySchemeObject, OpenIdConnectSecuritySchemeObject, ParameterLocation, @@ -39,7 +38,6 @@ export type { ExternalDocumentationObject, HttpSecuritySchemeObject, ImplicitOAuthFlowObject, - LinkObject, OAuth2SecuritySchemeObject, OAuthFlowObject, OAuthFlowObjectBase, @@ -836,6 +834,56 @@ export interface CallbackObject { [expression: string]: PathItemObject } +/** + * The Link Object represents a possible design-time link for a response. The + * presence of a link does not guarantee the caller's ability to successfully + * invoke it, rather it provides a known relationship and traversal mechanism + * between responses and other operations. + * + * A linked operation MUST be identified using either an `operationRef` or + * `operationId` (mutually exclusive). + * + * @see {@link https://spec.openapis.org/oas/v3.1.2.html#link-object} + */ +export interface LinkObject extends SpecificationExtensions { + /** + * A URI reference to an OAS operation. This field is mutually exclusive of + * the `operationId` field, and MUST point to an Operation Object. Relative + * `operationRef` values MAY be used to locate an existing Operation Object + * in the OpenAPI Description. + */ + operationRef?: string + /** + * The name of an existing, resolvable OAS operation, as defined with a + * unique `operationId`. This field is mutually exclusive of the + * `operationRef` field. + */ + operationId?: string + /** + * A map representing parameters to pass to an operation as specified with + * `operationId` or identified via `operationRef`. The key is the parameter + * name to be used (optionally qualified with the parameter location, e.g. + * `path.id` for an `id` parameter in the path), whereas the value can be a + * constant or an expression to be evaluated and passed to the linked + * operation. + */ + parameters?: Record + /** + * A literal value or runtime expression to use as a request body when + * calling the target operation. + */ + requestBody?: unknown + /** + * A description of the link. CommonMark syntax MAY be used for rich text + * representation. + */ + description?: string + /** + * A server object to be used by the target operation. + */ + server?: ServerObject +} + /** * Describes a single header for HTTP responses and for individual parts in * multipart representations. diff --git a/packages/types/src/v3.2.test-d.ts b/packages/types/src/v3.2.test-d.ts index 6e2d3cf..b60de10 100644 --- a/packages/types/src/v3.2.test-d.ts +++ b/packages/types/src/v3.2.test-d.ts @@ -1,4 +1,11 @@ -import type { MediaTypeObject, OpenAPIObject, SchemaObject } from './v3.2' +import type { + HeaderObject, + MediaTypeObject, + OpenAPIObject, + RequestBodyObject, + ResponseObject, + SchemaObject, +} from './v3.2' export const streamingApi = { $self: 'https://example.com/openapi', @@ -243,3 +250,28 @@ export const typedSchemaMismatch = { const: 1, type: 'string', } satisfies SchemaObject + +export const referencedRequestContent = { + content: { + 'text/event-stream': { $ref: '#/components/mediaTypes/EventStream' }, + }, +} satisfies RequestBodyObject + +export const referencedResponseContent = { + content: { + 'text/event-stream': { $ref: '#/components/mediaTypes/EventStream' }, + }, + headers: { + 'X-Trace-Id': { + content: { + 'text/plain': { $ref: '#/components/mediaTypes/TraceId' }, + }, + }, + }, +} satisfies ResponseObject + +export const referencedHeaderContent = { + content: { + 'text/plain': { $ref: '#/components/mediaTypes/TraceId' }, + }, +} satisfies HeaderObject From b47d4aa0026e1e267d5c660942a2db84bab0d105 Mon Sep 17 00:00:00 2001 From: Dinh Le Date: Sun, 6 Sep 2026 21:34:34 +0700 Subject: [PATCH 2/2] test(types): negate the Tag Object's kind field in the 3.1 boundary test The test claimed to cover both parent and kind but only exercised parent. TypeScript reports one excess property per object literal, so kind now lives in its own tag entry with its own @ts-expect-error. --- packages/types/src/v3.1.test-d.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/types/src/v3.1.test-d.ts b/packages/types/src/v3.1.test-d.ts index 74d7929..eca39d6 100644 --- a/packages/types/src/v3.1.test-d.ts +++ b/packages/types/src/v3.1.test-d.ts @@ -239,9 +239,14 @@ export const noTagHierarchy = { openapi: '3.1.2', paths: {}, tags: [ + { + // @ts-expect-error: The Tag Object's `kind` field was added in OpenAPI 3.2. + kind: 'nav', + name: 'animals', + }, { name: 'pets', - // @ts-expect-error: The Tag Object's `parent` and `kind` fields were added in OpenAPI 3.2. + // @ts-expect-error: The Tag Object's `parent` field was added in OpenAPI 3.2. parent: 'animals', }, ],