From 43e0087ab9056664c10e422014975a4e50f47cb3 Mon Sep 17 00:00:00 2001 From: Sanath Date: Tue, 6 Oct 2026 09:21:25 +0530 Subject: [PATCH] fix: preserve declared response headers in operation metadata --- packages/forge/openapi-resolver.test.ts | 51 +++++++++++++++++++++++++ packages/forge/openapi-resolver.ts | 20 ++++++++++ 2 files changed, 71 insertions(+) diff --git a/packages/forge/openapi-resolver.test.ts b/packages/forge/openapi-resolver.test.ts index 3645b5dc9..00b61a8a4 100644 --- a/packages/forge/openapi-resolver.test.ts +++ b/packages/forge/openapi-resolver.test.ts @@ -3,6 +3,57 @@ import { test } from 'node:test'; import { Forge } from './forge.ts'; import { populateOperationMap, resolveOperation } from './openapi-resolver.ts'; +test('response metadata preserves inline and referenced headers', () => { + populateOperationMap({ + paths: { + '/uploads': { + post: { + operationId: 'create-upload', + responses: { + '201': { $ref: '#/components/responses/UploadCreated' }, + '204': { description: 'No content' }, + }, + }, + }, + }, + components: { + schemas: { UploadUrl: { type: 'string', format: 'uri' } }, + headers: { + UploadLocation: { + description: 'Upload URL', + required: true, + schema: { $ref: '#/components/schemas/UploadUrl' }, + }, + }, + responses: { + UploadCreated: { + description: 'Created', + headers: { + Location: { $ref: '#/components/headers/UploadLocation' }, + 'Upload-Version': { schema: { type: 'string' } }, + }, + }, + }, + }, + }); + + const operation = resolveOperation('create-upload'); + assert.ok(operation); + assert.deepEqual(operation.responses['201'], { + description: 'Created', + content: {}, + headers: { + Location: { + description: 'Upload URL', + required: true, + schema: { type: 'string', format: 'uri' }, + }, + 'Upload-Version': { schema: { type: 'string' } }, + }, + }); + assert.deepEqual(operation.responses['204'], { description: 'No content', content: {} }); +}); + test('parameter metadata separates resolved type from schema identity', () => { populateOperationMap({ paths: { diff --git a/packages/forge/openapi-resolver.ts b/packages/forge/openapi-resolver.ts index 2e53f7347..ea196a776 100644 --- a/packages/forge/openapi-resolver.ts +++ b/packages/forge/openapi-resolver.ts @@ -65,6 +65,7 @@ interface Operation { interface ResponseObject { $ref?: string; description?: string; + headers?: Record; /** Keyed by media type: `"application/json"`, `"application/pdf"`, `"text/plain"`, ... */ content?: Record; } @@ -285,6 +286,13 @@ export interface ResponseInfo { * Empty object when the spec declares the status with no `content` (e.g. `204`). */ content: Record; + headers?: Record; +} + +export interface ResponseHeaderInfo { + description?: string; + required?: boolean; + schema?: SchemaRef; } /** @@ -1501,6 +1509,18 @@ function extractResponses(responses: Operation['responses']): Record