diff --git a/packages/docs/src/pages/advanced/MultipleEntrypoints.mdx b/packages/docs/src/pages/advanced/MultipleEntrypoints.mdx index 4d68f8e..3f050e6 100644 --- a/packages/docs/src/pages/advanced/MultipleEntrypoints.mdx +++ b/packages/docs/src/pages/advanced/MultipleEntrypoints.mdx @@ -202,7 +202,7 @@ dist/public/ ├── blog/ │ └── post-1.html ├── funstack__/ -│ └── fun:rsc-payload/ +│ └── fun__rsc-payload/ │ ├── a1b2c3d4.txt # RSC payload for index.html │ ├── e5f6g7h8.txt # RSC payload for about.html │ ├── i9j0k1l2.txt # RSC payload for blog/post-1.html diff --git a/packages/docs/src/pages/api/Defer.mdx b/packages/docs/src/pages/api/Defer.mdx index 3f8a610..2a5d693 100644 --- a/packages/docs/src/pages/api/Defer.mdx +++ b/packages/docs/src/pages/api/Defer.mdx @@ -65,7 +65,7 @@ interface DeferOptions { ``` - **name:** An optional identifier to help with debugging. When provided: - - In development mode, the name is included in the RSC payload file name (e.g., `/funstack__/fun:rsc-payload/HomePage-b5698be72eea3c37`) + - In development mode, the name is included in the RSC payload file name (e.g., `/funstack__/fun__rsc-payload/HomePage-b5698be72eea3c37`) - In production mode, the name is logged when the payload file is emitted ### Returns @@ -105,6 +105,6 @@ Using the `name` option makes it easier to identify which deferred component cor By default, FUNSTACK Static puts the entire app (``) into one RSC payload (`/funstack__/index.txt`). The client fetches this payload to render your SPA. -When you use `defer()`, FUNSTACK Static creates **additional RSC payloads** for the rendering result of the element. This results in an additional emit of RSC payload files like `/funstack__/fun:rsc-payload/b5698be72eea3c37`. If you provide a `name` option, the file name will include it (e.g., `/funstack__/fun:rsc-payload/HomePage-b5698be72eea3c37`). +When you use `defer()`, FUNSTACK Static creates **additional RSC payloads** for the rendering result of the element. This results in an additional emit of RSC payload files like `/funstack__/fun__rsc-payload/b5698be72eea3c37`. If you provide a `name` option, the file name will include it (e.g., `/funstack__/fun__rsc-payload/HomePage-b5698be72eea3c37`). -In the main RSC payload, the `defer` call is replaced with a client component ``. This component is responsible for fetching the additional RSC payload from client and renders it when it's ready. +In the main RSC payload, the `defer` call is replaced with a client component ``. This component is responsible for fetching the additional RSC payload from client and renders it when it's ready. diff --git a/packages/docs/src/pages/api/FunstackStatic.mdx b/packages/docs/src/pages/api/FunstackStatic.mdx index 87c8d48..030f9d8 100644 --- a/packages/docs/src/pages/api/FunstackStatic.mdx +++ b/packages/docs/src/pages/api/FunstackStatic.mdx @@ -229,19 +229,19 @@ Sentry.init({ ### rscPayloadDir (optional) **Type:** `string` -**Default:** `"fun:rsc-payload"` +**Default:** `"fun__rsc-payload"` Directory name used for RSC payload files in the build output. The final file paths follow the pattern `/funstack__/{rscPayloadDir}/{hash}.txt`. -Change this if your hosting platform has issues with the default directory name. For example, Cloudflare Workers redirects URLs containing colons to percent-encoded equivalents, adding an extra round trip. +Change this if your hosting platform has issues with the default directory name. -**Important:** The value is used as a marker for string replacement during the build process. Choose a value that is unique enough that it does not appear in your application's source code. The default value `"fun:rsc-payload"` is designed to be unlikely to collide with user code. +**Important:** The value is used as a marker for string replacement during the build process. Choose a value that is unique enough that it does not appear in your application's source code. The default value `"fun__rsc-payload"` is designed to be unlikely to collide with user code. ```typescript funstackStatic({ root: "./src/root.tsx", app: "./src/App.tsx", - rscPayloadDir: "fun-rsc-payload", // Avoid colons for Cloudflare Workers + rscPayloadDir: "my-custom-rsc-payload", }); ``` diff --git a/packages/docs/src/pages/learn/HowItWorks.mdx b/packages/docs/src/pages/learn/HowItWorks.mdx index 34c352a..4364764 100644 --- a/packages/docs/src/pages/learn/HowItWorks.mdx +++ b/packages/docs/src/pages/learn/HowItWorks.mdx @@ -45,12 +45,12 @@ dist/public │ ├── root-DvE5ENz2.css │ └── rsc-D0fjt5Ie.js ├── funstack__ -│ └── fun:rsc-payload +│ └── fun__rsc-payload │ └── db1923b9b6507ab4.txt └── index.html ``` -The RSC payload files under `funstack__` are loaded by the client-side code to bootstrap the application with server-rendered content. The `fun:rsc-payload` directory name is [configurable](/api/funstack-static#rscpayloaddir-optional) via the `rscPayloadDir` option. +The RSC payload files under `funstack__` are loaded by the client-side code to bootstrap the application with server-rendered content. The `fun__rsc-payload` directory name is [configurable](/api/funstack-static#rscpayloaddir-optional) via the `rscPayloadDir` option. This can been seen as an **optimized version of traditional client-only SPAs**, where the entire application is bundled into JavaScript files. By using RSC, some of the rendering work is offloaded to the build time, resulting in smaller JavaScript bundles combined with RSC payloads that require less client-side processing (parsing is easier, no JavaScript execution needed). diff --git a/packages/docs/src/pages/learn/OptimizingPayloads.mdx b/packages/docs/src/pages/learn/OptimizingPayloads.mdx index 1945e59..bcff74a 100644 --- a/packages/docs/src/pages/learn/OptimizingPayloads.mdx +++ b/packages/docs/src/pages/learn/OptimizingPayloads.mdx @@ -8,7 +8,7 @@ Without any optimization, your entire application is rendered into one RSC paylo ``` dist/public/funstack__/ -└── fun:rsc-payload/ +└── fun__rsc-payload/ └── b62ec6668fd49300.txt ← Contains everything ``` @@ -75,7 +75,7 @@ After building with route-level `defer()`, your output looks like this: ``` dist/public/funstack__/ -└── fun:rsc-payload/ +└── fun__rsc-payload/ ├── a3f2b1c9d8e7f6a5.txt ← Home page ├── b5698be72eea3c37.txt ← About page ├── b62ec6668fd49300.txt ← Main app shell diff --git a/packages/static/design/multiple-entries.md b/packages/static/design/multiple-entries.md index 7aec6de..ba7ac08 100644 --- a/packages/static/design/multiple-entries.md +++ b/packages/static/design/multiple-entries.md @@ -477,7 +477,7 @@ dist/public/ ├── blog/ │ └── post-1.html # path: "blog/post-1.html" ├── funstack__/ -│ └── fun:rsc-payload/ +│ └── fun__rsc-payload/ │ ├── a1b2c3d4e5f6g7h8.txt # RSC payload for index.html │ ├── i9j0k1l2m3n4o5p6.txt # RSC payload for about.html │ ├── q7r8s9t0u1v2w3x4.txt # RSC payload for blog/post-1.html diff --git a/packages/static/e2e/tests/build.spec.ts b/packages/static/e2e/tests/build.spec.ts index c278fc3..cd5e87b 100644 --- a/packages/static/e2e/tests/build.spec.ts +++ b/packages/static/e2e/tests/build.spec.ts @@ -20,7 +20,7 @@ test.describe("Build output verification", () => { expect(html).toContain("__FUNSTACK_APP_ENTRY__"); // Verify the RSC payload is preloaded expect(html).toContain('rel="preload"'); - expect(html).toContain("funstack__/fun:rsc-payload/"); + expect(html).toContain("funstack__/fun__rsc-payload/"); }); test("generates RSC payload files at /funstack__/*.txt", async ({ @@ -32,7 +32,7 @@ test.describe("Build output verification", () => { // Look for the RSC payload in preload link or FUNSTACK config const rscPayloadMatch = html.match( - /funstack__\/fun:rsc-payload\/[^"'\s]+\.txt/, + /funstack__\/fun__rsc-payload\/[^"'\s]+\.txt/, ); expect(rscPayloadMatch).not.toBeNull(); diff --git a/packages/static/e2e/tests/hydration.spec.ts b/packages/static/e2e/tests/hydration.spec.ts index 99c301d..b13c10b 100644 --- a/packages/static/e2e/tests/hydration.spec.ts +++ b/packages/static/e2e/tests/hydration.spec.ts @@ -62,7 +62,10 @@ test.describe("Client-side hydration", () => { page.on("request", (request) => { const url = request.url(); - if (url.includes("funstack__/fun:rsc-payload/") && url.endsWith(".txt")) { + if ( + url.includes("funstack__/fun__rsc-payload/") && + url.endsWith(".txt") + ) { rscRequests.push(url); } }); diff --git a/packages/static/e2e/tests/multi-entry.spec.ts b/packages/static/e2e/tests/multi-entry.spec.ts index 9b9078b..67657fb 100644 --- a/packages/static/e2e/tests/multi-entry.spec.ts +++ b/packages/static/e2e/tests/multi-entry.spec.ts @@ -11,7 +11,7 @@ test.describe("Multi-entry build output", () => { expect(html).toContain(""); expect(html).toContain(" { expect(html).toContain(""); expect(html).toContain(" { @@ -36,10 +36,10 @@ test.describe("Multi-entry build output", () => { // Both pages should reference RSC payloads const homePayloadMatch = homeHtml.match( - /funstack__\/fun:rsc-payload\/[^"'\s]+\.txt/, + /funstack__\/fun__rsc-payload\/[^"'\s]+\.txt/, ); const aboutPayloadMatch = aboutHtml.match( - /funstack__\/fun:rsc-payload\/[^"'\s]+\.txt/, + /funstack__\/fun__rsc-payload\/[^"'\s]+\.txt/, ); expect(homePayloadMatch).not.toBeNull(); diff --git a/packages/static/src/build/dependencyGraph.test.ts b/packages/static/src/build/dependencyGraph.test.ts index 551e3d2..2e52d6c 100644 --- a/packages/static/src/build/dependencyGraph.test.ts +++ b/packages/static/src/build/dependencyGraph.test.ts @@ -30,15 +30,15 @@ describe("findReferencedIds", () => { }); it("finds IDs with special characters", () => { - const content = "Reference to fun:rsc-payload/abc-123 here"; + const content = "Reference to fun__rsc-payload/abc-123 here"; const allKnownIds = new Set([ - "fun:rsc-payload/abc-123", - "fun:rsc-payload/def-456", + "fun__rsc-payload/abc-123", + "fun__rsc-payload/def-456", ]); const result = findReferencedIds(content, allKnownIds); - expect(result).toEqual(new Set(["fun:rsc-payload/abc-123"])); + expect(result).toEqual(new Set(["fun__rsc-payload/abc-123"])); }); }); diff --git a/packages/static/src/build/rscProcessor.ts b/packages/static/src/build/rscProcessor.ts index 8d363e4..e9a59ae 100644 --- a/packages/static/src/build/rscProcessor.ts +++ b/packages/static/src/build/rscProcessor.ts @@ -26,7 +26,7 @@ interface RawComponent { * * @param deferRegistryIterator - Iterator yielding components with { id, data } * @param appRscStream - The main RSC stream - * @param rscPayloadDir - Directory name used as a prefix for RSC payload IDs (e.g. "fun:rsc-payload") + * @param rscPayloadDir - Directory name used as a prefix for RSC payload IDs (e.g. "fun__rsc-payload") * @param context - Optional context for logging warnings */ export async function processRscComponents( diff --git a/packages/static/src/plugin/index.ts b/packages/static/src/plugin/index.ts index 481b580..b909562 100644 --- a/packages/static/src/plugin/index.ts +++ b/packages/static/src/plugin/index.ts @@ -31,13 +31,13 @@ interface FunstackStaticBaseOptions { * The final path will be `/funstack__/{rscPayloadDir}/{hash}.txt`. * * Change this if your hosting platform has issues with the default - * directory name (e.g. Cloudflare Workers redirects URLs containing colons). + * directory name. * * The value is used as a marker for string replacement during the build * process, so it should be unique enough that it does not appear in your * application's source code. * - * @default "fun:rsc-payload" + * @default "fun__rsc-payload" */ rscPayloadDir?: string; } diff --git a/packages/static/src/rsc/rscModule.ts b/packages/static/src/rsc/rscModule.ts index 6624126..686c17f 100644 --- a/packages/static/src/rsc/rscModule.ts +++ b/packages/static/src/rsc/rscModule.ts @@ -1,11 +1,11 @@ /** * Default directory name for RSC payload files. */ -export const defaultRscPayloadDir = "fun:rsc-payload"; +export const defaultRscPayloadDir = "fun__rsc-payload"; /** * Combines the RSC payload directory with a raw ID to form a - * namespaced payload ID (e.g. "fun:rsc-payload/abc123"). + * namespaced payload ID (e.g. "fun__rsc-payload/abc123"). */ export function getPayloadIDFor( rawId: string,