Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/shy-laws-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@opennextjs/cloudflare": patch
---

fix: patch the createInlinedDataReadableStream function not to use the type: "bytes" option in ReadableStream constructor

workerd seems not to correctly support the type: "bytes" option in ReadableStream constructor, that breaks inlined flight data streaming over 4kB.
By removing the type: "bytes" option, the inlined flight data will not be split into multiple chunks, and the streaming will work correctly.

(fixes #1225)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type { Plugin } from "esbuild";

import { getOpenNextConfig } from "../../../api/config.js";
import { normalizePath } from "../../utils/normalize-path.js";
import { patchFlightReadableStreamType } from "../patches/plugins/flight-readable-stream-type.js";
import { patchResRevalidate } from "../patches/plugins/res-revalidate.js";
import { patchTurbopackRuntime } from "../patches/plugins/turbopack.js";
import { patchUseCacheIO } from "../patches/plugins/use-cache.js";
Expand Down Expand Up @@ -209,6 +210,7 @@ async function generateBundle(
awsPatches.patchBackgroundRevalidation,
awsPatches.patchNodeEnvironment,
// Cloudflare specific patches
patchFlightReadableStreamType,
patchResRevalidate,
patchUseCacheIO,
patchTurbopackRuntime,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
import { describe, expect, test } from "vitest";

import { rule } from "./flight-readable-stream-type.js";

describe("patchFlightReadableStreamType", () => {
test("removes type property of ReadableStream constructor only in createInlinedDataReadableStream", () => {
const code = `
new ReadableStream({type: "bytes", start(e){ e.enqueue(n); }});

function tl(e,t,r){
let n = t ? '<script nonce="">' : "<script>";
return new ReadableStream({type: "bytes", start(e){ e.enqueue(n); }});
}

function tl2(e,t,r){
return new ReadableStream({type: "bytes", start(e){ e.enqueue(n); }});
}
`;

expect(patchCode(code, rule)).toMatchInlineSnapshot(
`
"new ReadableStream({type: "bytes", start(e){ e.enqueue(n); }});

function tl(e,t,r){
let n = t ? '<script nonce="">' : "<script>";
return new ReadableStream({start(e){ e.enqueue(n); }});
}

function tl2(e,t,r){
return new ReadableStream({type: "bytes", start(e){ e.enqueue(n); }});
}
"
`
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* This patch will remove the `type: "bytes"` option from the `ReadableStream` constructor in the
* createInlinedDataReadableStream function, which provides inline script tag chunks for writing hydration data to the client.
* Since workerd has no enough support for the `bytes` type, this patch will remove it to avoid errors related to RSC Flight streams.
*/

import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
import type { CodePatcher } from "@opennextjs/aws/build/patch/codePatcher.js";
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";

export const patchFlightReadableStreamType: CodePatcher = {
name: "patch-flight-readable-stream-type",
patches: [
{
pathFilter: getCrossPlatformPathRegex(String.raw`next-server/.*\.runtime\.prod\.js$`, {
escape: false,
}),
contentFilter: /new ReadableStream\(\{type:"bytes",/,
patchCode: async ({ code }) => patchCode(code, rule),
},
],
};

export const rule = `
rule:
kind: object
pattern: "{ type: \\"bytes\\", $$$REST }"
inside:
stopBy: end
kind: new_expression
has:
field: constructor
pattern: ReadableStream
inside:
kind: function_declaration
stopBy: end
has:
regex: "<script>"
Comment thread
e-chan1007 marked this conversation as resolved.

fix: "{$$$REST}"
`;