From 590c1ccd0a21f7afe8c28fcc55ab1d0b5254fe72 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 28 Jul 2026 16:11:43 +0100 Subject: [PATCH] fix: invoke `end` callback when no chunk is passed --- src/stream/writable.ts | 5 ++++- test/index.test.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/stream/writable.ts b/src/stream/writable.ts index adba5f1..ff1e704 100644 --- a/src/stream/writable.ts +++ b/src/stream/writable.ts @@ -96,12 +96,15 @@ export class Writable extends EventEmitter implements NodeStream.Writable { const data = arg1 === callback ? undefined : arg1; if (data) { const encoding = arg2 === callback ? undefined : arg2; - this.write(data, encoding, callback); + this.write(data, encoding); } this.writableEnded = true; this.writableFinished = true; this.emit("close"); this.emit("finish"); + if (callback) { + callback(); + } return this; } diff --git a/test/index.test.ts b/test/index.test.ts index 23cd3b8..88f0aee 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -112,3 +112,44 @@ describe("fetchNodeRequestHandler", () => { }); }); }); + +describe("ServerResponse#end callback", () => { + it("invokes callback with no chunk", async () => { + const calls: string[] = []; + const res = await fetchNodeRequestHandler((_req, res) => { + res.on("finish", () => calls.push("finish")); + res.end(() => calls.push("callback")); + }, "/test"); + expect(await res.text()).toBe(""); + expect(calls).toEqual(["finish", "callback"]); + }); + + it("invokes callback once with chunk", async () => { + const calls: string[] = []; + const res = await fetchNodeRequestHandler((_req, res) => { + res.on("finish", () => calls.push("finish")); + res.end("hello", () => calls.push("callback")); + }, "/test"); + expect(await res.text()).toBe("hello"); + expect(calls).toEqual(["finish", "callback"]); + }); + + it("invokes callback once with chunk and encoding", async () => { + let calls = 0; + const res = await fetchNodeRequestHandler((_req, res) => { + res.end("hello", "utf8", () => calls++); + }, "/test"); + expect(await res.text()).toBe("hello"); + expect(calls).toBe(1); + }); + + it("invokes callback on an already ended response", async () => { + let calls = 0; + const res = await fetchNodeRequestHandler((_req, res) => { + res.end("hello"); + res.end(() => calls++); + }, "/test"); + expect(await res.text()).toBe("hello"); + expect(calls).toBe(1); + }); +});