From bd43ef66a1f0b42aedddc7704a809061dec8e488 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 04:15:14 +0000 Subject: [PATCH 1/4] Initial plan From f3222014e51a803df3ac7267eeff3fb31c22e9a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 04:18:55 +0000 Subject: [PATCH 2/4] fix: avoid double-sending fastify nest replies Agent-Logs-Url: https://github.com/middleapi/orpc/sessions/829b39c1-87cd-476d-a059-02112ecd12a2 --- packages/nest/src/implement.test.ts | 17 ++++++++++++++++- packages/nest/src/implement.ts | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/nest/src/implement.test.ts b/packages/nest/src/implement.test.ts index 317e04f07..0ca47548a 100644 --- a/packages/nest/src/implement.test.ts +++ b/packages/nest/src/implement.test.ts @@ -9,6 +9,7 @@ import { FastifyAdapter } from '@nestjs/platform-fastify' import { Test } from '@nestjs/testing' import { oc, ORPCError } from '@orpc/contract' import { implement, lazy } from '@orpc/server' +import * as StandardServerFastify from '@orpc/standard-server-fastify' import * as StandardServerNode from '@orpc/standard-server-node' import supertest from 'supertest' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -16,6 +17,7 @@ import * as z from 'zod' import { Implement } from './implement' import { ORPCModule } from './module' +const sendStandardFastifyResponseSpy = vi.spyOn(StandardServerFastify, 'sendStandardResponse') const sendStandardResponseSpy = vi.spyOn(StandardServerNode, 'sendStandardResponse') beforeEach(() => { @@ -311,6 +313,8 @@ describe('@Implement', async () => { }) it('partial working on fastify', async () => { + const logs: string[] = [] + @Controller() class FastifyController { @Implement(contract.ping) @@ -324,7 +328,16 @@ describe('@Implement', async () => { controllers: [FastifyController], }).compile() - const app = moduleRef.createNestApplication(new FastifyAdapter()) + const app = moduleRef.createNestApplication(new FastifyAdapter({ + logger: { + level: 'info', + stream: { + write: (message: string) => { + logs.push(message) + }, + }, + }, + })) await app.init() await app.getHttpAdapter().getInstance().ready() @@ -357,6 +370,8 @@ describe('@Implement', async () => { expect(req).toBeDefined() expect(req!.method).toEqual('POST') expect(req!.url).toEqual('/ping?param=value¶m2[]=value2¶m2[]=value3') + expect(sendStandardFastifyResponseSpy).not.toHaveBeenCalled() + expect(logs.join('')).not.toContain('Reply was already sent') }) it('should pass correct signal and lastEventId', async () => { diff --git a/packages/nest/src/implement.ts b/packages/nest/src/implement.ts index 55ed6f2a9..737ed2398 100644 --- a/packages/nest/src/implement.ts +++ b/packages/nest/src/implement.ts @@ -163,8 +163,10 @@ export class ImplementInterceptor implements NestInterceptor { }) if (result.matched) { + const sendResponseInterceptors = toArray(this.config.sendResponseInterceptors) + return intercept( - toArray(this.config.sendResponseInterceptors), + sendResponseInterceptors, { request: req, response: res, standardResponse: result.response }, async ({ response, standardResponse }) => { if (isHono) { @@ -172,6 +174,17 @@ export class ImplementInterceptor implements NestInterceptor { return (response as HonoContext).newResponse(fetchResponse.body, fetchResponse) } else if (isFastify) { + if (sendResponseInterceptors.length === 0) { + const headers = { ...standardResponse.headers } + const body = StandardServerNode.toNodeHttpBody(standardResponse.body, headers, this.config) + + ;(response as FastifyReply) + .status(standardResponse.status) + .headers(StandardServerNode.toNodeHttpHeaders(headers)) + + return body + } + await StandardServerFastify.sendStandardResponse(response as FastifyReply, standardResponse, this.config) } else { From aaac5026311d87a50b6e47b2e809886533d06ac9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 04:20:50 +0000 Subject: [PATCH 3/4] test: clarify fastify reply regression assertions Agent-Logs-Url: https://github.com/middleapi/orpc/sessions/829b39c1-87cd-476d-a059-02112ecd12a2 --- packages/nest/src/implement.test.ts | 6 +++--- packages/nest/src/implement.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/nest/src/implement.test.ts b/packages/nest/src/implement.test.ts index 0ca47548a..03a0afc60 100644 --- a/packages/nest/src/implement.test.ts +++ b/packages/nest/src/implement.test.ts @@ -313,7 +313,7 @@ describe('@Implement', async () => { }) it('partial working on fastify', async () => { - const logs: string[] = [] + const fastifyLogMessages: string[] = [] @Controller() class FastifyController { @@ -333,7 +333,7 @@ describe('@Implement', async () => { level: 'info', stream: { write: (message: string) => { - logs.push(message) + fastifyLogMessages.push(message) }, }, }, @@ -371,7 +371,7 @@ describe('@Implement', async () => { expect(req!.method).toEqual('POST') expect(req!.url).toEqual('/ping?param=value¶m2[]=value2¶m2[]=value3') expect(sendStandardFastifyResponseSpy).not.toHaveBeenCalled() - expect(logs.join('')).not.toContain('Reply was already sent') + expect(fastifyLogMessages.join('')).not.toContain('Reply was already sent') }) it('should pass correct signal and lastEventId', async () => { diff --git a/packages/nest/src/implement.ts b/packages/nest/src/implement.ts index 737ed2398..66b46e713 100644 --- a/packages/nest/src/implement.ts +++ b/packages/nest/src/implement.ts @@ -177,8 +177,9 @@ export class ImplementInterceptor implements NestInterceptor { if (sendResponseInterceptors.length === 0) { const headers = { ...standardResponse.headers } const body = StandardServerNode.toNodeHttpBody(standardResponse.body, headers, this.config) + const fastifyReply = response as FastifyReply - ;(response as FastifyReply) + fastifyReply .status(standardResponse.status) .headers(StandardServerNode.toNodeHttpHeaders(headers)) From b85f2183285eb57be8c7bc46fb71be2bd26b3de6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 04:22:25 +0000 Subject: [PATCH 4/4] test: rename fastify log capture variable Agent-Logs-Url: https://github.com/middleapi/orpc/sessions/829b39c1-87cd-476d-a059-02112ecd12a2 --- packages/nest/src/implement.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nest/src/implement.test.ts b/packages/nest/src/implement.test.ts index 03a0afc60..68842deeb 100644 --- a/packages/nest/src/implement.test.ts +++ b/packages/nest/src/implement.test.ts @@ -313,7 +313,7 @@ describe('@Implement', async () => { }) it('partial working on fastify', async () => { - const fastifyLogMessages: string[] = [] + const capturedFastifyLogs: string[] = [] @Controller() class FastifyController { @@ -333,7 +333,7 @@ describe('@Implement', async () => { level: 'info', stream: { write: (message: string) => { - fastifyLogMessages.push(message) + capturedFastifyLogs.push(message) }, }, }, @@ -371,7 +371,7 @@ describe('@Implement', async () => { expect(req!.method).toEqual('POST') expect(req!.url).toEqual('/ping?param=value¶m2[]=value2¶m2[]=value3') expect(sendStandardFastifyResponseSpy).not.toHaveBeenCalled() - expect(fastifyLogMessages.join('')).not.toContain('Reply was already sent') + expect(capturedFastifyLogs.join('')).not.toContain('Reply was already sent') }) it('should pass correct signal and lastEventId', async () => {