From 4f93d0a9bf7187c22a8b7c6eedb5723f8af60802 Mon Sep 17 00:00:00 2001 From: gnezim Date: Wed, 15 Apr 2026 00:48:54 +0300 Subject: [PATCH] Implement SSR stream nonce injection as workaround for React #24883 --- .../middleware/nonce-stream-transform.test.ts | 77 +++++++++++++++++++ .../middleware/nonce-stream-transform.ts | 68 ++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 src/server/middleware/nonce-stream-transform.test.ts create mode 100644 src/server/middleware/nonce-stream-transform.ts diff --git a/src/server/middleware/nonce-stream-transform.test.ts b/src/server/middleware/nonce-stream-transform.test.ts new file mode 100644 index 00000000..37a3cc23 --- /dev/null +++ b/src/server/middleware/nonce-stream-transform.test.ts @@ -0,0 +1,77 @@ +import { describe, expect, it } from "vitest"; +import { Readable } from "node:stream"; +import { wrapSsrStreamWithNonce } from "./nonce-stream-transform.js"; + +function streamToString(stream: NodeJS.ReadableStream): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + stream.on("data", (chunk: Buffer) => chunks.push(Buffer.from(chunk))); + stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8"))); + stream.on("error", reject); + }); +} + +function createStream(chunks: string[]): NodeJS.ReadableStream { + return new Readable({ + read() { + for (const chunk of chunks) { + this.push(chunk); + } + this.push(null); + }, + }); +} + +const NONCE = "test-nonce-abc123"; + +describe("wrapSsrStreamWithNonce", () => { + it("injects nonce on bare "]); + const output = wrapSsrStreamWithNonce(input, NONCE); + const result = await streamToString(output); + expect(result).toContain(``]); + const output = wrapSsrStreamWithNonce(input, NONCE); + const result = await streamToString(output); + expect(result).toContain(``]); + const output = wrapSsrStreamWithNonce(input, NONCE); + const result = await streamToString(output); + expect(result).toContain(``]); + const output = wrapSsrStreamWithNonce(input, NONCE); + const result = await streamToString(output); + expect(result).toBe(``); + expect(result).not.toContain(NONCE); + }); + + it("handles multiple script tags in one chunk", async () => { + const input = createStream([ + ``, + ]); + const output = wrapSsrStreamWithNonce(input, NONCE); + const result = await streamToString(output); + expect(result).toContain(``); + expect(result).toContain(``); + expect(result).toContain(``, + ]); + const output = wrapSsrStreamWithNonce(input, NONCE); + const result = await streamToString(output); + expect(result).toContain(`