From a9804afe71fe5876963b3a6f5972a3e5d50dbdca Mon Sep 17 00:00:00 2001 From: Yusuke Wada Date: Thu, 28 Nov 2024 18:35:21 +0900 Subject: [PATCH] fix(zod-openapi): return `Response` if response is not text or JSON (#853) * fix(zod-openapi): return `Response` if response is not text or JSON Co-authored-by: sushichan044 * fixed tests and correct types * add changeset --------- Co-authored-by: sushichan044 --- .changeset/fluffy-doors-carry.md | 5 +++ packages/zod-openapi/src/index.ts | 37 ++++++++++++++++----- packages/zod-openapi/test/handler.test-d.ts | 24 +++++++++++++ packages/zod-openapi/test/index.test-d.ts | 2 +- packages/zod-openapi/test/index.test.ts | 6 ++-- 5 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 .changeset/fluffy-doors-carry.md diff --git a/.changeset/fluffy-doors-carry.md b/.changeset/fluffy-doors-carry.md new file mode 100644 index 00000000..234d0d6b --- /dev/null +++ b/.changeset/fluffy-doors-carry.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +fix: return `Response` if response is not text or JSON diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 0318ef81..62cb592f 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -26,7 +26,7 @@ import type { ValidationTargets, } from 'hono' import type { MergePath, MergeSchemaPath } from 'hono/types' -import type { JSONParsed, RemoveBlankRecord } from 'hono/utils/types' +import type { JSONParsed, JSONValue, RemoveBlankRecord, SimplifyDeepArray } from 'hono/utils/types' import type { ClientErrorStatusCode, InfoStatusCode, @@ -69,6 +69,28 @@ type IsForm = T extends string : never : never +type ReturnJsonOrTextOrResponse< + ContentType, + Content, + Status extends keyof StatusCodeRangeDefinitions | StatusCode +> = ContentType extends string + ? ContentType extends `application/${infer Start}json${infer _End}` + ? Start extends '' | `${string}+` | `vnd.${string}+` + ? TypedResponse< + SimplifyDeepArray extends JSONValue + ? JSONValue extends SimplifyDeepArray + ? never + : JSONParsed + : never, + ExtractStatusCode, + 'json' + > + : never + : ContentType extends `text/plain${infer _Rest}` + ? TypedResponse, 'text'> + : Response + : never + type RequestPart = Part extends keyof R['request'] ? R['request'][Part] : {} @@ -173,14 +195,13 @@ type ExtractStatusCode = T extends keyof Status ? StatusCodeRangeDefinitions[T] : T export type RouteConfigToTypedResponse = { - [Status in keyof R['responses'] & RouteConfigStatusCode]: IsJson< - keyof R['responses'][Status]['content'] - > extends never + [Status in keyof R['responses'] & + RouteConfigStatusCode]: undefined extends R['responses'][Status]['content'] ? TypedResponse<{}, ExtractStatusCode, string> - : TypedResponse< - JSONParsed>, - ExtractStatusCode, - 'json' | 'text' + : ReturnJsonOrTextOrResponse< + keyof R['responses'][Status]['content'], + ExtractContent, + Status > }[keyof R['responses'] & RouteConfigStatusCode] diff --git a/packages/zod-openapi/test/handler.test-d.ts b/packages/zod-openapi/test/handler.test-d.ts index 9e1dea14..f8b56172 100644 --- a/packages/zod-openapi/test/handler.test-d.ts +++ b/packages/zod-openapi/test/handler.test-d.ts @@ -123,4 +123,28 @@ describe('supports async handler', () => { > > }) + + test('Route accepts a response other than the response from c.json() and c.text()', () => { + const route = createRoute({ + method: 'get', + path: '/html', + responses: { + 200: { + content: { + 'text/html': { + schema: z.string(), + }, + }, + description: 'Return HTML', + }, + }, + }) + + const handler: RouteHandler = (c) => { + return c.html('

Hello from html

') + } + + const hono = new OpenAPIHono() + hono.openapi(route, handler) + }) }) diff --git a/packages/zod-openapi/test/index.test-d.ts b/packages/zod-openapi/test/index.test-d.ts index fc24b654..e23ab9de 100644 --- a/packages/zod-openapi/test/index.test-d.ts +++ b/packages/zod-openapi/test/index.test-d.ts @@ -76,7 +76,7 @@ describe('Types', () => { id: number message: string } - outputFormat: 'json' | 'text' + outputFormat: 'json' status: 200 } } diff --git a/packages/zod-openapi/test/index.test.ts b/packages/zod-openapi/test/index.test.ts index bbb1bf43..375c325e 100644 --- a/packages/zod-openapi/test/index.test.ts +++ b/packages/zod-openapi/test/index.test.ts @@ -1779,21 +1779,21 @@ describe('RouteConfigToTypedResponse', () => { age: number }, 200, - 'json' | 'text' + 'json' > | TypedResponse< { ok: boolean }, 400, - 'json' | 'text' + 'json' > | TypedResponse< { ok: boolean }, ServerErrorStatusCode, - 'json' | 'text' + 'json' > type verify = Expect> })