From 0813d0f730301c6645c779f4743da9704d474463 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 19 Aug 2025 17:35:58 +0800 Subject: [PATCH 01/11] add test --- .../src/routes/serialization-stream/+page.server.ts | 7 +++++++ .../src/routes/serialization-stream/+page.svelte | 11 +++++++++++ packages/kit/test/apps/basics/test/test.js | 7 +++++++ 3 files changed, 25 insertions(+) create mode 100644 packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts create mode 100644 packages/kit/test/apps/basics/src/routes/serialization-stream/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts b/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts new file mode 100644 index 000000000000..9484424e9d2d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts @@ -0,0 +1,7 @@ +import { Foo } from '$lib'; + +export const load = () => { + return { + foo: Promise.resolve(new Foo("It works")) + }; +}; diff --git a/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.svelte b/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.svelte new file mode 100644 index 000000000000..75c6a2f7a800 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.svelte @@ -0,0 +1,11 @@ + + +

+ {#await data.foo} + Loading... + {:then result} + {result.bar()} + {/await} +

diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 55db0bf20db6..a91cd0362ffe 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1560,6 +1560,13 @@ test.describe('Serialization', () => { await page.click('button'); await expect(page.locator('h1')).toHaveText('It works!'); }); + + test('works with streaming', async ({ page, javaScriptEnabled }) => { + test.skip(!javaScriptEnabled, 'skip when JavaScript is disabled'); + + await page.goto('/serialization-stream'); + await expect(page.locator('h1', { hasText: 'It works!'})).toBeVisible(); + }); }); test.describe('getRequestEvent', () => { From 76fd22bc7ce55eee1cf80e05375300580984f40b Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Wed, 20 Aug 2025 10:37:05 +0800 Subject: [PATCH 02/11] add test for single apps --- .../{+page.server.ts => +page.server.js} | 2 +- packages/kit/test/apps/basics/test/test.js | 2 +- packages/kit/test/apps/options-2/src/hooks.js | 9 +++++++++ packages/kit/test/apps/options-2/src/lib/index.js | 9 +++++++++ .../src/routes/serialization-stream/+page.server.js | 7 +++++++ .../src/routes/serialization-stream/+page.svelte | 11 +++++++++++ packages/kit/test/apps/options-2/test/test.js | 5 +++++ 7 files changed, 43 insertions(+), 2 deletions(-) rename packages/kit/test/apps/basics/src/routes/serialization-stream/{+page.server.ts => +page.server.js} (62%) create mode 100644 packages/kit/test/apps/options-2/src/hooks.js create mode 100644 packages/kit/test/apps/options-2/src/lib/index.js create mode 100644 packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.server.js create mode 100644 packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts b/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.js similarity index 62% rename from packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts rename to packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.js index 9484424e9d2d..957f34dc1b76 100644 --- a/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.ts +++ b/packages/kit/test/apps/basics/src/routes/serialization-stream/+page.server.js @@ -2,6 +2,6 @@ import { Foo } from '$lib'; export const load = () => { return { - foo: Promise.resolve(new Foo("It works")) + foo: Promise.resolve(new Foo('It works')) }; }; diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index a91cd0362ffe..ac9755b2c741 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1565,7 +1565,7 @@ test.describe('Serialization', () => { test.skip(!javaScriptEnabled, 'skip when JavaScript is disabled'); await page.goto('/serialization-stream'); - await expect(page.locator('h1', { hasText: 'It works!'})).toBeVisible(); + await expect(page.locator('h1', { hasText: 'It works!' })).toBeVisible(); }); }); diff --git a/packages/kit/test/apps/options-2/src/hooks.js b/packages/kit/test/apps/options-2/src/hooks.js new file mode 100644 index 000000000000..f611d294bd35 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/hooks.js @@ -0,0 +1,9 @@ +import { Foo } from "$lib"; + +/** @type {import("@sveltejs/kit").Transport} */ +export const transport = { + Foo: { + encode: (value) => value instanceof Foo && [value.message], + decode: ([message]) => new Foo(message) + } +}; diff --git a/packages/kit/test/apps/options-2/src/lib/index.js b/packages/kit/test/apps/options-2/src/lib/index.js new file mode 100644 index 000000000000..f9917fd877e2 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/lib/index.js @@ -0,0 +1,9 @@ +export class Foo { + constructor(message) { + this.message = message; + } + + bar() { + return this.message + '!'; + } +} diff --git a/packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.server.js b/packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.server.js new file mode 100644 index 000000000000..957f34dc1b76 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.server.js @@ -0,0 +1,7 @@ +import { Foo } from '$lib'; + +export const load = () => { + return { + foo: Promise.resolve(new Foo('It works')) + }; +}; diff --git a/packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.svelte b/packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.svelte new file mode 100644 index 000000000000..75c6a2f7a800 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/serialization-stream/+page.svelte @@ -0,0 +1,11 @@ + + +

+ {#await data.foo} + Loading... + {:then result} + {result.bar()} + {/await} +

diff --git a/packages/kit/test/apps/options-2/test/test.js b/packages/kit/test/apps/options-2/test/test.js index 4af842e6f7ee..f3df40146a8d 100644 --- a/packages/kit/test/apps/options-2/test/test.js +++ b/packages/kit/test/apps/options-2/test/test.js @@ -190,4 +190,9 @@ test.describe("bundleStrategy: 'single'", () => { await page.goto('/basepath/deserialize'); await expect(page.locator('p')).toHaveText('Hello world!'); }); + + test('serialization works with streaming', async ({ page }) => { + await page.goto('/basepath/serialization-stream'); + await expect(page.locator('h1', { hasText: 'It works!' })).toBeVisible(); + }); }); From 2bb66479da53bccff22e6557235262210a38b05f Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Wed, 20 Aug 2025 14:07:29 +0800 Subject: [PATCH 03/11] wrap statement in app import --- packages/kit/src/runtime/app/forms.js | 2 +- packages/kit/src/runtime/client/bundle.js | 2 ++ .../kit/src/runtime/server/page/render.js | 27 ++++++++++++++++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/runtime/app/forms.js b/packages/kit/src/runtime/app/forms.js index f4d0a352f628..1142609fe859 100644 --- a/packages/kit/src/runtime/app/forms.js +++ b/packages/kit/src/runtime/app/forms.js @@ -33,7 +33,7 @@ export function deserialize(result) { if (parsed.data) { // the decoders should never be initialised at the top-level because `app` - // will not initialised yet if `kit.output.bundleStrategy` is 'single' or 'inline' + // will not be initialised yet if `kit.output.bundleStrategy` is 'single' or 'inline' parsed.data = devalue.parse(parsed.data, BROWSER ? client_app.decoders : server_app.decoders); } diff --git a/packages/kit/src/runtime/client/bundle.js b/packages/kit/src/runtime/client/bundle.js index 7a915d3110a4..f375d02e8355 100644 --- a/packages/kit/src/runtime/client/bundle.js +++ b/packages/kit/src/runtime/client/bundle.js @@ -13,3 +13,5 @@ import * as app from '__sveltekit/manifest'; export function start(element, options) { void kit.start(app, element, options); } + +export { app }; diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 0d22352839bf..f871737f57cd 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -297,7 +297,9 @@ export async function render_response({ options, branch.map((b) => b.server_data), csp, - global + global, + client, + prefixed ); if (page_config.ssr && page_config.csr) { @@ -622,14 +624,18 @@ export async function render_response({ * @param {Array} nodes * @param {import('./csp.js').Csp} csp * @param {string} global + * @param {NonNullable} client + * @param {(path: string) => string} prefixed * @returns {{ data: string, chunks: AsyncIterable | null }} */ -function get_data(event, event_state, options, nodes, csp, global) { +function get_data(event, event_state, options, nodes, csp, global, client, prefixed) { let promise_id = 1; let count = 0; const { iterator, push, done } = create_async_iterator(); + const app = client.inline ? `__sveltekit_${options.version_hash}.app.app` : 'app'; + /** @param {any} thing */ function replacer(thing) { if (typeof thing?.then === 'function') { @@ -665,7 +671,20 @@ function get_data(event, event_state, options, nodes, csp, global) { } const nonce = csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''; - push(`${global}.resolve(${str})\n`); + const code = `${global}.resolve(${str})`; + if (!client.inline && str.includes("app.decode('")) { + if (client.app) { + push( + `import(${prefixed(client.app)}).then((app)=>{${code}})\n` + ); + } else { + push( + `import(${prefixed(client.start)}).then(({app})=>{${code}})\n` + ); + } + } else { + push(`${code}\n`); + } if (count === 0) done(); } ); @@ -675,7 +694,7 @@ function get_data(event, event_state, options, nodes, csp, global) { for (const key in options.hooks.transport) { const encoded = options.hooks.transport[key].encode(thing); if (encoded) { - return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`; + return `${app}.decode('${key}', ${devalue.uneval(encoded, replacer)})`; } } } From 926a55ecab7e209007bcbb461aa765a38f00eec7 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Wed, 20 Aug 2025 14:07:39 +0800 Subject: [PATCH 04/11] add test for inlined apps --- packages/kit/test/apps/options-3/package.json | 23 ++++++++++++++++++ .../test/apps/options-3/playwright.config.js | 1 + packages/kit/test/apps/options-3/src/app.html | 11 +++++++++ packages/kit/test/apps/options-3/src/hooks.js | 9 +++++++ .../kit/test/apps/options-3/src/lib/index.js | 9 +++++++ .../apps/options-3/src/routes/+layout.svelte | 7 ++++++ .../serialization-stream/+page.server.js | 7 ++++++ .../routes/serialization-stream/+page.svelte | 11 +++++++++ .../kit/test/apps/options-3/svelte.config.js | 10 ++++++++ packages/kit/test/apps/options-3/test/test.js | 18 ++++++++++++++ .../kit/test/apps/options-3/tsconfig.json | 8 +++++++ .../kit/test/apps/options-3/vite.config.js | 18 ++++++++++++++ pnpm-lock.yaml | 24 +++++++++++++++++++ 13 files changed, 156 insertions(+) create mode 100644 packages/kit/test/apps/options-3/package.json create mode 100644 packages/kit/test/apps/options-3/playwright.config.js create mode 100644 packages/kit/test/apps/options-3/src/app.html create mode 100644 packages/kit/test/apps/options-3/src/hooks.js create mode 100644 packages/kit/test/apps/options-3/src/lib/index.js create mode 100644 packages/kit/test/apps/options-3/src/routes/+layout.svelte create mode 100644 packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.server.js create mode 100644 packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.svelte create mode 100644 packages/kit/test/apps/options-3/svelte.config.js create mode 100644 packages/kit/test/apps/options-3/test/test.js create mode 100644 packages/kit/test/apps/options-3/tsconfig.json create mode 100644 packages/kit/test/apps/options-3/vite.config.js diff --git a/packages/kit/test/apps/options-3/package.json b/packages/kit/test/apps/options-3/package.json new file mode 100644 index 000000000000..78543175148f --- /dev/null +++ b/packages/kit/test/apps/options-3/package.json @@ -0,0 +1,23 @@ +{ + "name": "test-options-3", + "private": true, + "version": "0.0.1", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync", + "check": "svelte-kit sync && tsc && svelte-check", + "test": "playwright test" + }, + "devDependencies": { + "@sveltejs/adapter-node": "workspace:^", + "@sveltejs/kit": "workspace:^", + "@sveltejs/vite-plugin-svelte": "catalog:", + "svelte": "^5.35.5", + "svelte-check": "^4.1.1", + "typescript": "^5.5.4", + "vite": "catalog:" + }, + "type": "module" +} diff --git a/packages/kit/test/apps/options-3/playwright.config.js b/packages/kit/test/apps/options-3/playwright.config.js new file mode 100644 index 000000000000..33d36b651014 --- /dev/null +++ b/packages/kit/test/apps/options-3/playwright.config.js @@ -0,0 +1 @@ +export { config as default } from '../../utils.js'; diff --git a/packages/kit/test/apps/options-3/src/app.html b/packages/kit/test/apps/options-3/src/app.html new file mode 100644 index 000000000000..b8ba4699b2ba --- /dev/null +++ b/packages/kit/test/apps/options-3/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/packages/kit/test/apps/options-3/src/hooks.js b/packages/kit/test/apps/options-3/src/hooks.js new file mode 100644 index 000000000000..f611d294bd35 --- /dev/null +++ b/packages/kit/test/apps/options-3/src/hooks.js @@ -0,0 +1,9 @@ +import { Foo } from "$lib"; + +/** @type {import("@sveltejs/kit").Transport} */ +export const transport = { + Foo: { + encode: (value) => value instanceof Foo && [value.message], + decode: ([message]) => new Foo(message) + } +}; diff --git a/packages/kit/test/apps/options-3/src/lib/index.js b/packages/kit/test/apps/options-3/src/lib/index.js new file mode 100644 index 000000000000..f9917fd877e2 --- /dev/null +++ b/packages/kit/test/apps/options-3/src/lib/index.js @@ -0,0 +1,9 @@ +export class Foo { + constructor(message) { + this.message = message; + } + + bar() { + return this.message + '!'; + } +} diff --git a/packages/kit/test/apps/options-3/src/routes/+layout.svelte b/packages/kit/test/apps/options-3/src/routes/+layout.svelte new file mode 100644 index 000000000000..5e1f1fed86c2 --- /dev/null +++ b/packages/kit/test/apps/options-3/src/routes/+layout.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.server.js b/packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.server.js new file mode 100644 index 000000000000..957f34dc1b76 --- /dev/null +++ b/packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.server.js @@ -0,0 +1,7 @@ +import { Foo } from '$lib'; + +export const load = () => { + return { + foo: Promise.resolve(new Foo('It works')) + }; +}; diff --git a/packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.svelte b/packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.svelte new file mode 100644 index 000000000000..75c6a2f7a800 --- /dev/null +++ b/packages/kit/test/apps/options-3/src/routes/serialization-stream/+page.svelte @@ -0,0 +1,11 @@ + + +

+ {#await data.foo} + Loading... + {:then result} + {result.bar()} + {/await} +

diff --git a/packages/kit/test/apps/options-3/svelte.config.js b/packages/kit/test/apps/options-3/svelte.config.js new file mode 100644 index 000000000000..bcfcfccabb1f --- /dev/null +++ b/packages/kit/test/apps/options-3/svelte.config.js @@ -0,0 +1,10 @@ +/** @type {import('@sveltejs/kit').Config} */ +const config = { + kit: { + output: { + bundleStrategy: 'inline' + } + } +}; + +export default config; diff --git a/packages/kit/test/apps/options-3/test/test.js b/packages/kit/test/apps/options-3/test/test.js new file mode 100644 index 000000000000..69a753c4bafd --- /dev/null +++ b/packages/kit/test/apps/options-3/test/test.js @@ -0,0 +1,18 @@ +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; +import { expect } from '@playwright/test'; +import { test } from '../../../utils.js'; + +/** @typedef {import('@playwright/test').Response} Response */ + +test.describe.configure({ mode: 'parallel' }); + +test.describe("bundleStrategy: 'inline'", () => { + test.skip(({ javaScriptEnabled }) => !javaScriptEnabled || !!process.env.DEV); + + test('serialization works with streaming', async ({ page }) => { + await page.goto('/serialization-stream'); + await expect(page.locator('h1', { hasText: 'It works!' })).toBeVisible(); + }); +}); diff --git a/packages/kit/test/apps/options-3/tsconfig.json b/packages/kit/test/apps/options-3/tsconfig.json new file mode 100644 index 000000000000..b1096bf168cd --- /dev/null +++ b/packages/kit/test/apps/options-3/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "noEmit": true + }, + "extends": "./.svelte-kit/tsconfig.json" +} diff --git a/packages/kit/test/apps/options-3/vite.config.js b/packages/kit/test/apps/options-3/vite.config.js new file mode 100644 index 000000000000..69200cdb7cd8 --- /dev/null +++ b/packages/kit/test/apps/options-3/vite.config.js @@ -0,0 +1,18 @@ +import * as path from 'node:path'; +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + build: { + minify: false + }, + clearScreen: false, + plugins: [sveltekit()], + server: { + fs: { + allow: [path.resolve('../../../src')] + } + } +}; + +export default config; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 643098904aaa..13b5d6dd4927 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -714,6 +714,30 @@ importers: specifier: 'catalog:' version: 6.3.5(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + packages/kit/test/apps/options-3: + devDependencies: + '@sveltejs/adapter-node': + specifier: workspace:^ + version: link:../../../../adapter-node + '@sveltejs/kit': + specifier: workspace:^ + version: link:../../.. + '@sveltejs/vite-plugin-svelte': + specifier: 'catalog:' + version: 6.0.0-next.3(svelte@5.35.5)(vite@6.3.5(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) + svelte: + specifier: ^5.35.5 + version: 5.35.5 + svelte-check: + specifier: ^4.1.1 + version: 4.1.1(picomatch@4.0.3)(svelte@5.35.5)(typescript@5.8.3) + typescript: + specifier: ^5.5.4 + version: 5.8.3 + vite: + specifier: 'catalog:' + version: 6.3.5(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + packages/kit/test/apps/prerendered-app-error-pages: devDependencies: '@sveltejs/kit': From 2b4d59b4a638601b082b65e213b338aea32587c8 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Wed, 20 Aug 2025 14:19:50 +0800 Subject: [PATCH 05/11] format --- packages/kit/test/apps/options-2/src/hooks.js | 2 +- packages/kit/test/apps/options-3/src/hooks.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/test/apps/options-2/src/hooks.js b/packages/kit/test/apps/options-2/src/hooks.js index f611d294bd35..238cc7744f2c 100644 --- a/packages/kit/test/apps/options-2/src/hooks.js +++ b/packages/kit/test/apps/options-2/src/hooks.js @@ -1,4 +1,4 @@ -import { Foo } from "$lib"; +import { Foo } from '$lib'; /** @type {import("@sveltejs/kit").Transport} */ export const transport = { diff --git a/packages/kit/test/apps/options-3/src/hooks.js b/packages/kit/test/apps/options-3/src/hooks.js index f611d294bd35..238cc7744f2c 100644 --- a/packages/kit/test/apps/options-3/src/hooks.js +++ b/packages/kit/test/apps/options-3/src/hooks.js @@ -1,4 +1,4 @@ -import { Foo } from "$lib"; +import { Foo } from '$lib'; /** @type {import("@sveltejs/kit").Transport} */ export const transport = { From b65e18c9ea6cde12ea005d8fafc70376424a31fc Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Wed, 20 Aug 2025 14:25:58 +0800 Subject: [PATCH 06/11] changeset --- .changeset/brown-seals-rhyme.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/brown-seals-rhyme.md diff --git a/.changeset/brown-seals-rhyme.md b/.changeset/brown-seals-rhyme.md new file mode 100644 index 000000000000..ecdcb7b1f756 --- /dev/null +++ b/.changeset/brown-seals-rhyme.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: correctly decode custom types streamed from a server load function From 2ef8eac642e49dbf1320824672168d2e2f2dee15 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 20 Aug 2025 22:27:57 +0800 Subject: [PATCH 07/11] fix lint errors --- packages/kit/test/apps/options-3/test/test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/kit/test/apps/options-3/test/test.js b/packages/kit/test/apps/options-3/test/test.js index 69a753c4bafd..b85f5c61265b 100644 --- a/packages/kit/test/apps/options-3/test/test.js +++ b/packages/kit/test/apps/options-3/test/test.js @@ -1,6 +1,4 @@ -import path from 'node:path'; import process from 'node:process'; -import { fileURLToPath } from 'node:url'; import { expect } from '@playwright/test'; import { test } from '../../../utils.js'; From 8ef5668b90285f85ae4ea4409f4930ec416ada10 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 20 Aug 2025 14:59:30 -0400 Subject: [PATCH 08/11] fix --- packages/kit/src/runtime/server/page/render.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index f871737f57cd..1d5c489a2a85 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -675,11 +675,11 @@ function get_data(event, event_state, options, nodes, csp, global, client, prefi if (!client.inline && str.includes("app.decode('")) { if (client.app) { push( - `import(${prefixed(client.app)}).then((app)=>{${code}})\n` + `import(${s(prefixed(client.app))}).then((app)=>{${code}})\n` ); } else { push( - `import(${prefixed(client.start)}).then(({app})=>{${code}})\n` + `import(${s(prefixed(client.start))}).then(({app})=>{${code}})\n` ); } } else { From f4ec516e397de8732c6d892eb32b2faf869b3469 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 20 Aug 2025 15:20:20 -0400 Subject: [PATCH 09/11] leaner payloads --- .../kit/src/runtime/server/page/render.js | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 1d5c489a2a85..8d16cce996d8 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -368,9 +368,21 @@ export async function render_response({ deferred.set(id, { fulfil, reject }); })`); + let prelude = `const [data, error] = fn();`; + + if (client.app) { + prelude = `const app = await import(${s(prefixed(client.app))}) + const [data, error] = fn(app);`; + } else { + prelude = `const { app } = await import(${s(prefixed(client.start))}) + const [data, error] = fn(app);`; + } + // When resolving, the id might not yet be available due to the data // be evaluated upon init of kit, so we use a timeout to retry - properties.push(`resolve: ({ id, data, error }) => { + properties.push(`resolve: async (id, fn) => { + ${prelude} + const try_to_resolve = () => { if (!deferred.has(id)) { setTimeout(try_to_resolve, 0); @@ -658,7 +670,7 @@ function get_data(event, event_state, options, nodes, csp, global, client, prefi let str; try { - str = devalue.uneval({ id, data, error }, replacer); + str = devalue.uneval([data], replacer); } catch { error = await handle_error_and_jsonify( event, @@ -667,24 +679,13 @@ function get_data(event, event_state, options, nodes, csp, global, client, prefi new Error(`Failed to serialize promise while rendering ${event.route.id}`) ); data = undefined; - str = devalue.uneval({ id, data, error }, replacer); + str = devalue.uneval([null, error], replacer); } const nonce = csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''; - const code = `${global}.resolve(${str})`; - if (!client.inline && str.includes("app.decode('")) { - if (client.app) { - push( - `import(${s(prefixed(client.app))}).then((app)=>{${code}})\n` - ); - } else { - push( - `import(${s(prefixed(client.start))}).then(({app})=>{${code}})\n` - ); - } - } else { - push(`${code}\n`); - } + push( + `${global}.resolve(${id}, ${str.includes('app.decode') ? `(app) => ${str}` : `() => ${str}`})\n` + ); if (count === 0) done(); } ); From e856b3ff31e9326e3399c78000d52379ce58b18a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 20 Aug 2025 15:32:27 -0400 Subject: [PATCH 10/11] fix --- .../kit/src/runtime/server/page/render.js | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 8d16cce996d8..1cec01233e71 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -297,9 +297,7 @@ export async function render_response({ options, branch.map((b) => b.server_data), csp, - global, - client, - prefixed + global ); if (page_config.ssr && page_config.csr) { @@ -368,16 +366,23 @@ export async function render_response({ deferred.set(id, { fulfil, reject }); })`); - let prelude = `const [data, error] = fn();`; + let app_declaration = ''; - if (client.app) { - prelude = `const app = await import(${s(prefixed(client.app))}) - const [data, error] = fn(app);`; - } else { - prelude = `const { app } = await import(${s(prefixed(client.start))}) - const [data, error] = fn(app);`; + if (Object.keys(options.hooks.transport).length > 0) { + if (client.inline) { + app_declaration = `const app = __sveltekit_${options.version_hash}.app.app;`; + } else if (client.app) { + app_declaration = `const app = await import(${s(prefixed(client.app))});`; + } else { + app_declaration = `const { app } = await import(${s(prefixed(client.start))});`; + } } + const prelude = app_declaration + ? `${app_declaration} + const [data, error] = fn(app);` + : `const [data, error] = fn();`; + // When resolving, the id might not yet be available due to the data // be evaluated upon init of kit, so we use a timeout to retry properties.push(`resolve: async (id, fn) => { @@ -636,18 +641,14 @@ export async function render_response({ * @param {Array} nodes * @param {import('./csp.js').Csp} csp * @param {string} global - * @param {NonNullable} client - * @param {(path: string) => string} prefixed * @returns {{ data: string, chunks: AsyncIterable | null }} */ -function get_data(event, event_state, options, nodes, csp, global, client, prefixed) { +function get_data(event, event_state, options, nodes, csp, global) { let promise_id = 1; let count = 0; const { iterator, push, done } = create_async_iterator(); - const app = client.inline ? `__sveltekit_${options.version_hash}.app.app` : 'app'; - /** @param {any} thing */ function replacer(thing) { if (typeof thing?.then === 'function') { @@ -670,7 +671,7 @@ function get_data(event, event_state, options, nodes, csp, global, client, prefi let str; try { - str = devalue.uneval([data], replacer); + str = devalue.uneval(error ? [, error] : [data], replacer); } catch { error = await handle_error_and_jsonify( event, @@ -695,7 +696,7 @@ function get_data(event, event_state, options, nodes, csp, global, client, prefi for (const key in options.hooks.transport) { const encoded = options.hooks.transport[key].encode(thing); if (encoded) { - return `${app}.decode('${key}', ${devalue.uneval(encoded, replacer)})`; + return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`; } } } From 23af149ced7d1f5cae5bde800a199f63aa16343c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 20 Aug 2025 15:39:41 -0400 Subject: [PATCH 11/11] tweak --- packages/kit/src/runtime/server/page/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 1cec01233e71..bb55ce707613 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -680,7 +680,7 @@ function get_data(event, event_state, options, nodes, csp, global) { new Error(`Failed to serialize promise while rendering ${event.route.id}`) ); data = undefined; - str = devalue.uneval([null, error], replacer); + str = devalue.uneval([, error], replacer); } const nonce = csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : '';