From fcc7cd9e2f65bc92d5096a3ea14f928875f383f1 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 19 May 2026 16:37:34 +0800 Subject: [PATCH 1/4] Progress --- .idea/workspace.xml | 88 ++++++++++++++++++++++++++++++++++ package.json | 2 +- src/adapters/node-http/core.ts | 2 +- src/generator/index.ts | 2 +- src/generator/schema.ts | 2 +- src/types.ts | 2 +- src/utils/procedure.ts | 2 +- src/utils/zod.ts | 2 +- 8 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..6365bf9d --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + { + "lastFilter": { + "state": "OPEN", + "assignee": "Sam152" + } +} + { + "selectedUrlAndAccountId": { + "url": "git@github.com:mcampa/trpc-to-openapi.git", + "accountId": "4254b8b1-0587-4893-b61f-6458cf4bfeaf" + } +} + + + + + { + "associatedIndex": 2 +} + + + + { + "keyToString": { + "ModuleVcsDetector.initialDetectionPerformed": "true", + "RunOnceActivity.MCP Project settings loaded": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.git.unshallow": "true", + "RunOnceActivity.typescript.service.memoryLimit.init": "true", + "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true", + "git-widget-placeholder": "gentler-migration-from-v3", + "javascript.preferred.runtime.type.id": "node", + "junie.onboarding.icon.badge.shown": "true", + "last_opened_file_path": "/Users/sam.becker/Sites/trpc-to-openapi", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "pnpm", + "to.speed.mode.migration.done": "true", + "ts.external.directory.path": "/Users/sam.becker/Sites/trpc-to-openapi/node_modules/typescript/lib", + "vue.rearranger.settings.migration": "true" + } +} + + + + 1779174617442 + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 94895ab7..922f28a9 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ }, "peerDependencies": { "@trpc/server": "^11.1.0", - "zod": "^4.0.0", + "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^5.0.1" }, "dependencies": { diff --git a/src/adapters/node-http/core.ts b/src/adapters/node-http/core.ts index a2774540..a2a18554 100644 --- a/src/adapters/node-http/core.ts +++ b/src/adapters/node-http/core.ts @@ -5,7 +5,7 @@ import { type NodeHTTPResponse, } from '@trpc/server/adapters/node-http'; import { getErrorShape, TRPCRequestInfo } from '@trpc/server/unstable-core-do-not-import'; -import { ZodArray, ZodError, ZodTypeAny } from 'zod'; +import { ZodArray, ZodError, ZodTypeAny } from 'zod/v4'; import { NodeHTTPRequest } from '../../types'; import { generateOpenApiDocument } from '../../generator'; import { diff --git a/src/generator/index.ts b/src/generator/index.ts index df389ab8..8cca195f 100644 --- a/src/generator/index.ts +++ b/src/generator/index.ts @@ -1,5 +1,5 @@ import { ZodOpenApiObject, ZodOpenApiPathsObject, createDocument } from 'zod-openapi'; -import { ZodSchema } from 'zod'; +import { ZodSchema } from 'zod/v4'; import { OpenApiMeta, diff --git a/src/generator/schema.ts b/src/generator/schema.ts index a278ccc2..db7c1b52 100644 --- a/src/generator/schema.ts +++ b/src/generator/schema.ts @@ -1,5 +1,5 @@ import { TRPCError } from '@trpc/server'; -import { ZodObject, ZodAny, z } from 'zod'; +import { ZodObject, ZodAny, z } from 'zod/v4'; import { ZodOpenApiContentObject, ZodOpenApiParameters, diff --git a/src/types.ts b/src/types.ts index bc8cf0b7..3ba293af 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,7 +7,7 @@ import type { RouterRecord, } from '@trpc/server/unstable-core-do-not-import'; import { IncomingMessage } from 'http'; -import type { ZodObject } from 'zod'; +import type { ZodObject } from 'zod/v4'; import type { $ZodIssue } from 'zod/v4/core'; export { type OpenAPIObject, type SecuritySchemeObject } from 'openapi3-ts/oas31'; diff --git a/src/utils/procedure.ts b/src/utils/procedure.ts index 0568640f..a132c690 100644 --- a/src/utils/procedure.ts +++ b/src/utils/procedure.ts @@ -1,5 +1,5 @@ import { TRPCProcedureType } from '@trpc/server'; -import { ZodObject, z } from 'zod'; +import { ZodObject, z } from 'zod/v4'; import { OpenApiMeta, OpenApiProcedure, OpenApiProcedureRecord } from '../types'; diff --git a/src/utils/zod.ts b/src/utils/zod.ts index ae58f180..f54daffb 100644 --- a/src/utils/zod.ts +++ b/src/utils/zod.ts @@ -1,4 +1,4 @@ -import { ZodObject, ZodRawShape, ZodType, z } from 'zod'; +import { ZodObject, ZodRawShape, ZodType, z } from 'zod/v4'; import type { $ZodType, $ZodTypes } from 'zod/v4/core'; import type { $ZodTypeDef } from 'zod/v4/core/schemas'; From fc3e3c62d8c2c50b321e42383e33ebec4e2297f4 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 19 May 2026 16:41:38 +0800 Subject: [PATCH 2/4] Progress --- .idea/workspace.xml | 88 --------------------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 6365bf9d..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - { - "lastFilter": { - "state": "OPEN", - "assignee": "Sam152" - } -} - { - "selectedUrlAndAccountId": { - "url": "git@github.com:mcampa/trpc-to-openapi.git", - "accountId": "4254b8b1-0587-4893-b61f-6458cf4bfeaf" - } -} - - - - - { - "associatedIndex": 2 -} - - - - { - "keyToString": { - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.MCP Project settings loaded": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.git.unshallow": "true", - "RunOnceActivity.typescript.service.memoryLimit.init": "true", - "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true", - "git-widget-placeholder": "gentler-migration-from-v3", - "javascript.preferred.runtime.type.id": "node", - "junie.onboarding.icon.badge.shown": "true", - "last_opened_file_path": "/Users/sam.becker/Sites/trpc-to-openapi", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "pnpm", - "to.speed.mode.migration.done": "true", - "ts.external.directory.path": "/Users/sam.becker/Sites/trpc-to-openapi/node_modules/typescript/lib", - "vue.rearranger.settings.migration": "true" - } -} - - - - 1779174617442 - - - - - - \ No newline at end of file From 80f2033b124822c79d6a718902d59b9b99db2966 Mon Sep 17 00:00:00 2001 From: Mario Campa Date: Fri, 22 May 2026 08:42:07 -0700 Subject: [PATCH 3/4] test: align test files with zod/v4 imports and fix examples typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test files were importing z from 'zod' while src/ now imports from 'zod/v4'. TypeScript treats those as nominally distinct types, and the mismatch causes type instantiation to explode through tRPC's generic machinery, OOMing tsc. Also cast the named-object examples in the multipleExamples test to preserve existing runtime behavior — zod-openapi v5 types meta examples as unknown[], but the runtime still accepts the v3-style record form. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/adapters/express.test.ts | 2 +- test/adapters/fastify.test.ts | 2 +- test/adapters/fetch.test.ts | 2 +- test/adapters/koa.test.ts | 2 +- test/adapters/next.test.ts | 2 +- test/adapters/nuxt.test.ts | 2 +- test/adapters/standalone.test.ts | 2 +- test/generator.test.ts | 6 ++++-- 8 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/adapters/express.test.ts b/test/adapters/express.test.ts index cd7a8ba4..abbaa295 100644 --- a/test/adapters/express.test.ts +++ b/test/adapters/express.test.ts @@ -1,7 +1,7 @@ import { initTRPC } from '@trpc/server'; import express from 'express'; import fetch from 'node-fetch'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiExpressMiddlewareOptions, diff --git a/test/adapters/fastify.test.ts b/test/adapters/fastify.test.ts index 2fb4cd46..15b67483 100644 --- a/test/adapters/fastify.test.ts +++ b/test/adapters/fastify.test.ts @@ -1,7 +1,7 @@ import { initTRPC } from '@trpc/server'; import fastify from 'fastify'; import fetch from 'node-fetch'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiFastifyPluginOptions, diff --git a/test/adapters/fetch.test.ts b/test/adapters/fetch.test.ts index e103cada..c309ff91 100644 --- a/test/adapters/fetch.test.ts +++ b/test/adapters/fetch.test.ts @@ -1,7 +1,7 @@ import { TRPCError, initTRPC } from '@trpc/server'; import fetch from 'node-fetch'; import superjson from 'superjson'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiFetchHandlerOptions, diff --git a/test/adapters/koa.test.ts b/test/adapters/koa.test.ts index 1c1ce410..f79eb622 100644 --- a/test/adapters/koa.test.ts +++ b/test/adapters/koa.test.ts @@ -1,7 +1,7 @@ import { initTRPC } from '@trpc/server'; import Koa from 'koa'; import fetch from 'node-fetch'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiKoaMiddlewareOptions, diff --git a/test/adapters/next.test.ts b/test/adapters/next.test.ts index e02b350a..b5f3e939 100644 --- a/test/adapters/next.test.ts +++ b/test/adapters/next.test.ts @@ -3,7 +3,7 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { IncomingHttpHeaders, IncomingMessage } from 'http'; import { NextApiRequestCookies, NextApiRequestQuery } from 'next/dist/server/api-utils'; import { Socket } from 'net'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiNextHandlerOptions, diff --git a/test/adapters/nuxt.test.ts b/test/adapters/nuxt.test.ts index 40c5d728..c4db5356 100644 --- a/test/adapters/nuxt.test.ts +++ b/test/adapters/nuxt.test.ts @@ -2,7 +2,7 @@ import { initTRPC } from '@trpc/server'; import { H3Event } from 'h3'; import httpMocks, { RequestMethod } from 'node-mocks-http'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiNuxtHandlerOptions, diff --git a/test/adapters/standalone.test.ts b/test/adapters/standalone.test.ts index 0903c387..9c9bcecb 100644 --- a/test/adapters/standalone.test.ts +++ b/test/adapters/standalone.test.ts @@ -4,7 +4,7 @@ import { createHTTPHandler } from '@trpc/server/adapters/standalone'; import { Server } from 'http'; import fetch from 'node-fetch'; import superjson from 'superjson'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { CreateOpenApiHttpHandlerOptions, diff --git a/test/generator.test.ts b/test/generator.test.ts index cd22eeb8..90218ee5 100644 --- a/test/generator.test.ts +++ b/test/generator.test.ts @@ -1,6 +1,6 @@ import { initTRPC } from '@trpc/server'; import { observable } from '@trpc/server/observable'; -import { z } from 'zod'; +import { z } from 'zod/v4'; import { GenerateOpenApiDocumentOptions, OpenApiMeta, generateOpenApiDocument } from '../src'; import * as zodUtils from '../src/utils/zod'; @@ -3165,7 +3165,9 @@ describe('generator', () => { .input( z .object({ name: z.string() }) - .meta({ examples: { Lily: { name: 'Lily' }, John: { name: 'John' } } }), + .meta({ + examples: { Lily: { name: 'Lily' }, John: { name: 'John' } } as never, + }), ) .output(z.object({ output: z.string() })) .mutation(({ input }) => ({ From 4ab0e19f3c16efcfab6ebfcce751456786269b50 Mon Sep 17 00:00:00 2001 From: Mario Campa Date: Fri, 22 May 2026 09:00:34 -0700 Subject: [PATCH 4/4] refactor: split type-only zod/v4 imports and drop unused ZodTypeAny MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Copilot review on #161: - ZodSchema, ZodObject, ZodAny, ZodRawShape, ZodType, ZodError are only used in type positions — move them to `import type` so stricter TypeScript settings (verbatimModuleSyntax, preserveValueImports) don't emit unused value imports. - ZodTypeAny was imported in node-http/core.ts but never referenced — remove it. - Replace the broad `as never` cast on the multipleExamples test with `as unknown as unknown[]` plus a comment explaining why the runtime still accepts the v3-style named-record form. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/adapters/node-http/core.ts | 3 ++- src/generator/index.ts | 2 +- src/generator/schema.ts | 3 ++- src/utils/procedure.ts | 3 ++- src/utils/zod.ts | 3 ++- test/generator.test.ts | 5 ++++- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/adapters/node-http/core.ts b/src/adapters/node-http/core.ts index a2a18554..2d0f8ec0 100644 --- a/src/adapters/node-http/core.ts +++ b/src/adapters/node-http/core.ts @@ -5,7 +5,8 @@ import { type NodeHTTPResponse, } from '@trpc/server/adapters/node-http'; import { getErrorShape, TRPCRequestInfo } from '@trpc/server/unstable-core-do-not-import'; -import { ZodArray, ZodError, ZodTypeAny } from 'zod/v4'; +import { ZodArray } from 'zod/v4'; +import type { ZodError } from 'zod/v4'; import { NodeHTTPRequest } from '../../types'; import { generateOpenApiDocument } from '../../generator'; import { diff --git a/src/generator/index.ts b/src/generator/index.ts index 8cca195f..92edf23d 100644 --- a/src/generator/index.ts +++ b/src/generator/index.ts @@ -1,5 +1,5 @@ import { ZodOpenApiObject, ZodOpenApiPathsObject, createDocument } from 'zod-openapi'; -import { ZodSchema } from 'zod/v4'; +import type { ZodSchema } from 'zod/v4'; import { OpenApiMeta, diff --git a/src/generator/schema.ts b/src/generator/schema.ts index db7c1b52..7930b005 100644 --- a/src/generator/schema.ts +++ b/src/generator/schema.ts @@ -1,5 +1,6 @@ import { TRPCError } from '@trpc/server'; -import { ZodObject, ZodAny, z } from 'zod/v4'; +import { z } from 'zod/v4'; +import type { ZodObject, ZodAny } from 'zod/v4'; import { ZodOpenApiContentObject, ZodOpenApiParameters, diff --git a/src/utils/procedure.ts b/src/utils/procedure.ts index a132c690..1ba78da5 100644 --- a/src/utils/procedure.ts +++ b/src/utils/procedure.ts @@ -1,5 +1,6 @@ import { TRPCProcedureType } from '@trpc/server'; -import { ZodObject, z } from 'zod/v4'; +import { z } from 'zod/v4'; +import type { ZodObject } from 'zod/v4'; import { OpenApiMeta, OpenApiProcedure, OpenApiProcedureRecord } from '../types'; diff --git a/src/utils/zod.ts b/src/utils/zod.ts index f54daffb..7e553316 100644 --- a/src/utils/zod.ts +++ b/src/utils/zod.ts @@ -1,4 +1,5 @@ -import { ZodObject, ZodRawShape, ZodType, z } from 'zod/v4'; +import { z } from 'zod/v4'; +import type { ZodObject, ZodRawShape, ZodType } from 'zod/v4'; import type { $ZodType, $ZodTypes } from 'zod/v4/core'; import type { $ZodTypeDef } from 'zod/v4/core/schemas'; diff --git a/test/generator.test.ts b/test/generator.test.ts index 90218ee5..c5576530 100644 --- a/test/generator.test.ts +++ b/test/generator.test.ts @@ -3165,8 +3165,11 @@ describe('generator', () => { .input( z .object({ name: z.string() }) + // zod-openapi v5 types `meta.examples` as `unknown[]`, but its runtime + // still accepts the v3-style named-record form used here (and emits it + // through to the output schema verbatim — see snapshot below). .meta({ - examples: { Lily: { name: 'Lily' }, John: { name: 'John' } } as never, + examples: { Lily: { name: 'Lily' }, John: { name: 'John' } } as unknown as unknown[], }), ) .output(z.object({ output: z.string() }))