From 61066793f07a4330e775ee6e30515cd9d071113e Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Sat, 25 Jul 2026 15:12:22 +0200 Subject: [PATCH 1/4] chore: bump h3 v2 from rc.25 to rc.26 --- .changeset/sour-knives-look.md | 5 + packages/start/package.json | 2 +- packages/start/src/server/handler.ts | 49 +--------- pnpm-lock.yaml | 135 +++++++++------------------ pnpm-workspace.yaml | 1 + 5 files changed, 50 insertions(+), 142 deletions(-) create mode 100644 .changeset/sour-knives-look.md diff --git a/.changeset/sour-knives-look.md b/.changeset/sour-knives-look.md new file mode 100644 index 000000000..6da1592e9 --- /dev/null +++ b/.changeset/sour-knives-look.md @@ -0,0 +1,5 @@ +--- +"@solidjs/start": patch +--- + +Update `h3` to `2.0.1-rc.26`. diff --git a/packages/start/package.json b/packages/start/package.json index 597f07e2d..decf7827b 100644 --- a/packages/start/package.json +++ b/packages/start/package.json @@ -63,7 +63,7 @@ "defu": "^6.1.4", "error-stack-parser": "^2.1.4", "fast-glob": "^3.3.3", - "h3": "^2.0.1-rc.25", + "h3": "^2.0.1-rc.26", "html-to-image": "^1.11.13", "micromatch": "^4.0.8", "oxc-parser": "^0.139.0", diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index c1fea4b4c..cc936704e 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -31,14 +31,7 @@ export function createBaseHandler( const pathname = stripBaseUrl(url.pathname); if (pathname.startsWith(SERVER_FN_BASE)) { - const serverFnResponse = await handleServerFunction(e); - - if (serverFnResponse instanceof Response) - return produceResponseWithEventHeaders(serverFnResponse); - - return new Response(serverFnResponse as any, { - headers: e.res.headers, - }); + return await handleServerFunction(e); } const match = matchAPIRoute(pathname, event.request.method); @@ -52,8 +45,6 @@ export function createBaseHandler( sharedConfig.context = { event }; const res = await fn(event); if (res !== undefined) { - if (res instanceof Response) return produceResponseWithEventHeaders(res); - return res; } if (event.request.method !== "GET") { @@ -218,44 +209,6 @@ function handleStreamCompleteRedirect(context: PageEvent) { }; } -function produceResponseWithEventHeaders(res: Response) { - const event = getRequestEvent()!; - - let ret = res; - - // Response.redirect returns an immutable value, so we clone on any redirect just in case - if (300 <= res.status && res.status < 400) { - const cookies = res.headers.getSetCookie?.() ?? []; - const headers = new Headers(); - res.headers.forEach((value, key) => { - if (key.toLowerCase() !== "set-cookie") { - headers.set(key, value); - } - }); - for (const cookie of cookies) { - headers.append("Set-Cookie", cookie); - } - ret = new Response(res.body, { - status: res.status, - statusText: res.statusText, - headers, - }); - } - - const eventCookies = event.response.headers.getSetCookie?.() ?? []; - for (const cookie of eventCookies) { - ret.headers.append("Set-Cookie", cookie); - } - - for (const [name, value] of event.response.headers) { - if (name.toLowerCase() !== "set-cookie") { - ret.headers.set(name, value); - } - } - - return ret; -} - function stripBaseUrl(path: string) { const base = import.meta.env.SERVER_BASE_URL || import.meta.env.BASE_URL || "/"; return stripPathBase(path, base); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f8caf4dd..fd864fcbb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 0.36.0 tinyglobby: specifier: ^0.2.15 - version: 0.2.15 + version: 0.2.17 tippy.js: specifier: ^6.3.7 version: 6.3.7 @@ -231,10 +231,10 @@ importers: version: 0.5.19(tailwindcss@3.4.19) autoprefixer: specifier: ^10.4.27 - version: 10.4.27(postcss@8.5.8) + version: 10.4.27(postcss@8.5.19) postcss: specifier: ^8.5.8 - version: 8.5.8 + version: 8.5.19 tailwind-merge: specifier: ^3.5.0 version: 3.5.0 @@ -246,7 +246,7 @@ importers: version: 1.0.7(tailwindcss@3.4.19) tinyglobby: specifier: ^0.2.15 - version: 0.2.15 + version: 0.2.17 tippy.js: specifier: ^6.3.7 version: 6.3.7 @@ -348,8 +348,8 @@ importers: specifier: ^3.3.3 version: 3.3.3 h3: - specifier: ^2.0.1-rc.25 - version: 2.0.1-rc.25(crossws@0.4.10(srvx@0.12.0)) + specifier: ^2.0.1-rc.26 + version: 2.0.1-rc.26(crossws@0.4.10(srvx@0.12.4)) html-to-image: specifier: ^1.11.13 version: 1.11.13 @@ -385,7 +385,7 @@ importers: version: 1.2.1 srvx: specifier: ^0.12.0 - version: 0.12.0 + version: 0.12.4 terracotta: specifier: ^1.1.1 version: 1.1.1(solid-js@1.9.11) @@ -2248,8 +2248,8 @@ packages: crossws: optional: true - h3@2.0.1-rc.25: - resolution: {integrity: sha512-uIK++Up28xDt4n0+bSWfVssRivNXpUmMmA6CCiK6j9+utLcBMcFK97Gn9PlSZwAPW9oLy3CjGJ4ag40CY9YYtQ==} + h3@2.0.1-rc.26: + resolution: {integrity: sha512-GDxlvDsKxgjRvG5UBRJYyGJTMWLV30CJ4cV+e7QCTgftDHihrvio1fVPbNembhEr6J4WNm8IWy3fookgyTweLw==} engines: {node: '>=20.11.1'} hasBin: true peerDependencies: @@ -2393,10 +2393,6 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -2618,11 +2614,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.16: resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2797,10 +2788,6 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} @@ -2885,10 +2872,6 @@ packages: resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} - engines: {node: ^10 || ^12 || >=14} - prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -3006,10 +2989,6 @@ packages: peerDependencies: seroval: ^1.0 - seroval@1.5.1: - resolution: {integrity: sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==} - engines: {node: '>=10'} - seroval@1.5.4: resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==} engines: {node: '>=10'} @@ -3100,8 +3079,8 @@ packages: engines: {node: '>=20.16.0'} hasBin: true - srvx@0.12.0: - resolution: {integrity: sha512-c1GYFl3U8Aa/9rX9GTgvJ4tQMgmbX9YX26+km5iYu+d6/fuwpMFJ6d+Z/DtLDiXpDioKcbGTj5jD7+mn94Timw==} + srvx@0.12.4: + resolution: {integrity: sha512-RixzFlMn3dvzDTpKIAXhXrqL4cy6vScNCP0VVwgVbUBU94o+DiXLsFnAZetbyKAEnK6Ox3hzHXWGsyv/Iibv7g==} engines: {node: '>=20.16.0'} hasBin: true @@ -3204,10 +3183,6 @@ packages: resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -4296,7 +4271,7 @@ snapshots: '@parcel/watcher-wasm@2.5.6': dependencies: is-glob: 4.0.3 - picomatch: 4.0.3 + picomatch: 4.0.5 '@parcel/watcher-win32-arm64@2.5.6': optional: true @@ -4312,7 +4287,7 @@ snapshots: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 - picomatch: 4.0.3 + picomatch: 4.0.5 optionalDependencies: '@parcel/watcher-android-arm64': 2.5.6 '@parcel/watcher-darwin-arm64': 2.5.6 @@ -4798,7 +4773,7 @@ snapshots: flatted: 3.4.0 pathe: 2.0.3 sirv: 3.0.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 vitest: 4.1.0(@types/node@25.5.0)(@vitest/browser-playwright@4.1.0)(@vitest/ui@4.1.0)(jsdom@28.1.0)(vite@8.1.4(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)) @@ -4843,13 +4818,13 @@ snapshots: assertion-error@2.0.1: {} - autoprefixer@10.4.27(postcss@8.5.8): + autoprefixer@10.4.27(postcss@8.5.19): dependencies: browserslist: 4.28.1 caniuse-lite: 1.0.30001778 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.8 + postcss: 8.5.19 postcss-value-parser: 4.2.0 babel-plugin-jsx-dom-expressions@0.40.5(@babel/core@7.29.0): @@ -4978,9 +4953,9 @@ snapshots: optionalDependencies: srvx: 0.11.22 - crossws@0.4.10(srvx@0.12.0): + crossws@0.4.10(srvx@0.12.4): optionalDependencies: - srvx: 0.12.0 + srvx: 0.12.4 optional: true css-tree@3.2.1: @@ -5107,6 +5082,7 @@ snapshots: '@esbuild/win32-arm64': 0.27.4 '@esbuild/win32-ia32': 0.27.4 '@esbuild/win32-x64': 0.27.4 + optional: true escalade@3.2.0: {} @@ -5146,10 +5122,6 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 @@ -5243,12 +5215,12 @@ snapshots: optionalDependencies: crossws: 0.4.10(srvx@0.11.22) - h3@2.0.1-rc.25(crossws@0.4.10(srvx@0.12.0)): + h3@2.0.1-rc.26(crossws@0.4.10(srvx@0.12.4)): dependencies: rou3: 0.9.1 - srvx: 0.11.22 + srvx: 0.12.4 optionalDependencies: - crossws: 0.4.10(srvx@0.12.0) + crossws: 0.4.10(srvx@0.12.4) hasown@2.0.2: dependencies: @@ -5381,8 +5353,6 @@ snapshots: jiti@1.21.7: {} - jiti@2.6.1: {} - jiti@2.7.0: {} js-tokens@4.0.0: {} @@ -5496,7 +5466,7 @@ snapshots: get-port-please: 3.2.0 h3: 1.15.6 http-shutdown: 1.2.2 - jiti: 2.6.1 + jiti: 2.7.0 mlly: 1.8.1 node-forge: 1.3.3 pathe: 1.1.2 @@ -5602,8 +5572,6 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} - nanoid@3.3.16: {} nf3@0.3.22: {} @@ -5866,8 +5834,6 @@ snapshots: picomatch@2.3.1: {} - picomatch@4.0.3: {} - picomatch@4.0.5: {} pify@2.3.0: {} @@ -5892,28 +5858,28 @@ snapshots: pngjs@7.0.0: {} - postcss-import@15.1.0(postcss@8.5.8): + postcss-import@15.1.0(postcss@8.5.19): dependencies: - postcss: 8.5.8 + postcss: 8.5.19 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.11 - postcss-js@4.1.0(postcss@8.5.8): + postcss-js@4.1.0(postcss@8.5.19): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.8 + postcss: 8.5.19 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.8): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.19): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 - postcss: 8.5.8 + postcss: 8.5.19 - postcss-nested@6.2.0(postcss@8.5.8): + postcss-nested@6.2.0(postcss@8.5.19): dependencies: - postcss: 8.5.8 + postcss: 8.5.19 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.0.10: @@ -5934,12 +5900,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.8: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - prettier@2.8.8: {} pretty-format@27.5.1: @@ -6053,16 +6013,10 @@ snapshots: semver@7.7.4: {} - seroval-plugins@1.5.1(seroval@1.5.1): - dependencies: - seroval: 1.5.1 - seroval-plugins@1.5.1(seroval@1.5.4): dependencies: seroval: 1.5.4 - seroval@1.5.1: {} - seroval@1.5.4: {} shebang-command@2.0.0: @@ -6097,8 +6051,8 @@ snapshots: solid-js@1.9.11: dependencies: csstype: 3.2.3 - seroval: 1.5.1 - seroval-plugins: 1.5.1(seroval@1.5.1) + seroval: 1.5.4 + seroval-plugins: 1.5.1(seroval@1.5.4) solid-motionone@1.0.4(solid-js@1.9.11): dependencies: @@ -6161,7 +6115,7 @@ snapshots: srvx@0.11.22: {} - srvx@0.12.0: {} + srvx@0.12.4: {} stackback@0.0.2: {} @@ -6198,7 +6152,7 @@ snapshots: lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 supports-preserve-symlinks-flag@1.0.0: {} @@ -6229,11 +6183,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.8 - postcss-import: 15.1.0(postcss@8.5.8) - postcss-js: 4.1.0(postcss@8.5.8) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.8) - postcss-nested: 6.2.0(postcss@8.5.8) + postcss: 8.5.19 + postcss-import: 15.1.0(postcss@8.5.19) + postcss-js: 4.1.0(postcss@8.5.19) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.19) + postcss-nested: 6.2.0(postcss@8.5.19) postcss-selector-parser: 6.1.2 resolve: 1.22.11 sucrase: 3.35.1 @@ -6272,11 +6226,6 @@ snapshots: tinyexec@1.0.4: {} - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.5) @@ -6486,11 +6435,11 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.5 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 vite: 8.1.4(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0) why-is-node-running: 2.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e93b431c2..4c23fd20e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,6 +17,7 @@ ignoredBuiltDependencies: minimumReleaseAgeExclude: - srvx@0.12.0 + - h3@2.0.1-rc.26 onlyBuiltDependencies: - '@parcel/watcher' From e7723e2eae801455841d650cb71bba715a609d46 Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Sat, 25 Jul 2026 15:23:09 +0200 Subject: [PATCH 2/4] refactor: overhaul cookie headers preserve test --- apps/tests/src/e2e/api-call.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/tests/src/e2e/api-call.test.ts b/apps/tests/src/e2e/api-call.test.ts index 32eb475cc..e216f8956 100644 --- a/apps/tests/src/e2e/api-call.test.ts +++ b/apps/tests/src/e2e/api-call.test.ts @@ -36,11 +36,10 @@ test.describe("api calls", () => { // 1. session=abc123 (from response headers) // 2. csrf=xyz789 (from response headers) // 3. event_cookie=from_event (from event.response headers via setHeader) - expect(cookies.length).toBe(3); - - const cookieValues = cookies.join("; "); - expect(cookieValues).toContain("session=abc123"); - expect(cookieValues).toContain("csrf=xyz789"); - expect(cookieValues).toContain("event_cookie=from_event"); + expect(cookies).toStrictEqual([ + "session=abc123; Path=/; HttpOnly", + "csrf=xyz789; Path=/", + "event_cookie=from_event; Path=/", + ]); }); }); From fe2ad73da357b617eb0a4a04999e7bafc6b5c830 Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Fri, 24 Jul 2026 14:41:26 +0200 Subject: [PATCH 3/4] fix: defer handler response until stream shell payload Refs: #2229, h3js/h3#1510 --- .changeset/spotty-jobs-relax.md | 5 +++++ apps/tests/src/e2e/http-header.test.ts | 8 +++++++- apps/tests/src/routes/http-header.tsx | 13 +++++++++++++ packages/start/src/server/handler.ts | 15 ++++++++++----- 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 .changeset/spotty-jobs-relax.md diff --git a/.changeset/spotty-jobs-relax.md b/.changeset/spotty-jobs-relax.md new file mode 100644 index 000000000..3ef55638f --- /dev/null +++ b/.changeset/spotty-jobs-relax.md @@ -0,0 +1,5 @@ +--- +"@solidjs/start": patch +--- + +Fixed event response status and headers set during server-side rendering with deferred async resources, not being applied on the outgoing response. diff --git a/apps/tests/src/e2e/http-header.test.ts b/apps/tests/src/e2e/http-header.test.ts index c26f5a19f..bc9092a3b 100644 --- a/apps/tests/src/e2e/http-header.test.ts +++ b/apps/tests/src/e2e/http-header.test.ts @@ -2,9 +2,15 @@ import { expect, test } from "@playwright/test"; test.describe("http header", () => { // couldn't get this to see the headers but verified in chrome devtools - test.skip("should set http header", async ({ page }) => { + test("should set http header", async ({ page }) => { const response = await page.goto("/http-header"); expect(response?.headers()["test-header"]).toBe("test-value"); }); + + test("should set http header with deferred data", async ({ page }) => { + const response = await page.goto("/http-header"); + + expect(response?.headers()["test-header-async"]).toBe("async-value"); + }); }); diff --git a/apps/tests/src/routes/http-header.tsx b/apps/tests/src/routes/http-header.tsx index 8259da4b0..fae5afc5d 100644 --- a/apps/tests/src/routes/http-header.tsx +++ b/apps/tests/src/routes/http-header.tsx @@ -1,10 +1,23 @@ +import { createAsync, query } from "@solidjs/router"; import { HttpHeader } from "@solidjs/start"; +import { Show } from "solid-js"; +import { setTimeout } from "timers/promises"; + +const getData = query(async () => { + "use server"; + await setTimeout(500); + + return "async-value"; +}, "http-header"); export default function HttpHeaderRoute() { + const data = createAsync(() => getData(), { deferStream: true }); + return (

Http Header

+ {value => }
); } diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index cc936704e..ef68493e8 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -1,6 +1,13 @@ import middleware from "solid-start:middleware"; -import { defineHandler, getCookie, H3, type H3Event, redirect, setCookie } from "h3/generic"; -import { join } from "pathe"; +import { + defineHandler, + getCookie, + H3, + type H3Event, + iterable, + redirect, + setCookie, +} from "h3/generic"; import type { JSX } from "solid-js"; import { sharedConfig } from "solid-js"; import { getRequestEvent, renderToStream, renderToString } from "solid-js/web"; @@ -110,11 +117,9 @@ export function createBaseHandler( if (mode === "async") return await stream; - delete (stream as any).then; - // h3 expects a standard web ReadableStream across runtimes. The adapter // also tolerates cancellation while Solid finishes outstanding work. - return toWebReadableStream(stream); + return iterable(toWebReadableStream(stream)); }), }); From 2b3bf6d5b2626303c40cc8d29eb90ff5e5fe155c Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Sat, 25 Jul 2026 15:37:37 +0200 Subject: [PATCH 4/4] refactor: comment on the TextEncoder usage in render stream conversion --- packages/start/src/server/web-stream.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/start/src/server/web-stream.ts b/packages/start/src/server/web-stream.ts index 900e43039..b609c3e98 100644 --- a/packages/start/src/server/web-stream.ts +++ b/packages/start/src/server/web-stream.ts @@ -3,15 +3,19 @@ type PipeableStream = { }; /** Convert Solid's streaming SSR result into a cancellation-safe web stream. */ -export function toWebReadableStream(stream: PipeableStream): ReadableStream { +export function toWebReadableStream(stream: PipeableStream) { const encoder = new TextEncoder(); let active = true; - return new ReadableStream({ + return new ReadableStream({ start(controller) { stream.pipe({ write(payload) { - if (active) controller.enqueue(encoder.encode(payload)); + if (!active) return; + + // Encoding string to Uint8Array makes sure that + // the stream can be consumed as Response body + controller.enqueue(encoder.encode(payload)); }, end() { if (!active) return;