From 30522b60a4448874ffec61122e36fa408ff68ba4 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Tue, 3 Feb 2026 16:05:07 +0300 Subject: [PATCH 01/11] improve performance --- packages/start/src/server/handler.ts | 29 +++++++++++++++++-- .../src/server/server-functions-handler.ts | 18 ++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index d91453d93..702c63a2d 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -15,6 +15,30 @@ import { getExpectedRedirectStatus } from "./util.ts"; const SERVER_FN_BASE = "/_server"; +try { + const nodeHTTP = await import("http"); + const http2 = await import("http2"); + + function patchListen(proto: any) { + if (!proto || proto.__patched) return; + + const original = proto.listen; + proto.listen = function (...args: any[]) { + globalThis.canSendFastNodeStreams = true; + return original.apply(this, args); + }; + + proto.__patched = true; + } + + // http + https + patchListen(nodeHTTP.Server.prototype); + + // http2 (discover prototypes safely) + patchListen(Object.getPrototypeOf(http2.createServer())); + patchListen(Object.getPrototypeOf(http2.createSecureServer())); +} catch {} + export function createBaseHandler( createPageEvent: (e: FetchEvent) => Promise, fn: (context: PageEvent) => JSX.Element, @@ -24,8 +48,7 @@ export function createBaseHandler( middleware: middleware.length ? middleware.map(decorateMiddleware) : undefined, handler: decorateHandler(async (e: H3Event) => { const event = getRequestEvent()!; - const url = new URL(event.request.url); - const pathname = stripBaseUrl(url.pathname); + const pathname = stripBaseUrl(e.url.pathname); if (pathname.startsWith(SERVER_FN_BASE)) { const serverFnResponse = await handleServerFunction(e); @@ -114,7 +137,7 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled - if (globalThis.USING_SOLID_START_DEV_SERVER) return stream; + if (globalThis.canSendFastNodeStreams) return stream; // returning stream directly breaks cloudflare workers const { writable, readable } = new TransformStream(); diff --git a/packages/start/src/server/server-functions-handler.ts b/packages/start/src/server/server-functions-handler.ts index 160672684..994a29325 100644 --- a/packages/start/src/server/server-functions-handler.ts +++ b/packages/start/src/server/server-functions-handler.ts @@ -76,7 +76,7 @@ export async function handleServerFunction(h3Event: H3Event) { const serverReference = request.headers.get("X-Server-Id"); const instance = request.headers.get("X-Server-Instance"); const singleFlight = request.headers.has("X-Single-Flight"); - const url = new URL(request.url); + const url = h3Event.url; let functionId: string | undefined | null; if (serverReference) { // invariant(typeof serverReference === "string", "Invalid server function"); @@ -96,7 +96,7 @@ export async function handleServerFunction(h3Event: H3Event) { let parsed: any[] = []; // grab bound arguments from url when no JS - if (!instance || h3Event.method === "GET") { + if (!instance || request.method === "GET") { const args = url.searchParams.get("args"); if (args) { const json = JSON.parse(args); @@ -121,7 +121,7 @@ export async function handleServerFunction(h3Event: H3Event) { }); } } - if (h3Event.method === "POST") { + if (request.method === "POST") { const contentType = request.headers.get("content-type"); if ( @@ -176,7 +176,7 @@ export async function handleServerFunction(h3Event: H3Event) { } // handle no JS success case - if (!instance) return handleNoJS(result, request, parsed); + if (!instance) return handleNoJS(result, h3Event, parsed); h3Event.res.headers.set("content-type", "text/javascript"); @@ -200,7 +200,7 @@ export async function handleServerFunction(h3Event: H3Event) { h3Event.res.headers.set("X-Error", error.replace(/[\r\n]+/g, "")); } else { - x = handleNoJS(x, request, parsed, true); + x = handleNoJS(x, h3Event, parsed, true); } if (instance) { h3Event.res.headers.set("content-type", "text/javascript"); @@ -210,8 +210,8 @@ export async function handleServerFunction(h3Event: H3Event) { } } -function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boolean) { - const url = new URL(request.url); +function handleNoJS(result: any, h3Event: H3Event, parsed: any[], thrown?: boolean) { + const url = h3Event.url; const isError = result instanceof Error; let statusCode = 302; let headers: Headers; @@ -226,7 +226,7 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole } } else headers = new Headers({ - Location: new URL(request.headers.get("referer")!).toString(), + Location: new URL(h3Event.req.headers.get("referer")!).toString(), }); if (result) { headers.append( @@ -293,7 +293,7 @@ async function handleSingleFlight(sourceEvent: FetchEvent, result: any): Promise if (result.headers.has("Location")) url = new URL( result.headers.get("Location")!, - new URL(sourceEvent.request.url).origin + import.meta.env.BASE_URL, + sourceEvent.nativeEvent.url.origin + import.meta.env.BASE_URL, ).toString(); } const event = { ...sourceEvent } as PageEvent; From f10822a962acb0d79cba54ad8c93ab11f240fa79 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Tue, 3 Feb 2026 16:07:42 +0300 Subject: [PATCH 02/11] add type --- packages/start/src/internal.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/start/src/internal.d.ts b/packages/start/src/internal.d.ts index dd4ef0e40..4a0db6ddf 100644 --- a/packages/start/src/internal.d.ts +++ b/packages/start/src/internal.d.ts @@ -10,5 +10,6 @@ declare module "h3" { import type { Rollup } from "vite"; declare global { var START_CLIENT_BUNDLE: Rollup.OutputBundle; + var canSendFastNodeStreams: boolean | undefined; var USING_SOLID_START_DEV_SERVER: boolean | undefined; } From 56ce0fcf06d966f351720a9883221e967583b70a Mon Sep 17 00:00:00 2001 From: huseeiin Date: Tue, 3 Feb 2026 16:10:44 +0300 Subject: [PATCH 03/11] add changeset --- .changeset/fruity-banks-sit.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fruity-banks-sit.md diff --git a/.changeset/fruity-banks-sit.md b/.changeset/fruity-banks-sit.md new file mode 100644 index 000000000..bc726eaf1 --- /dev/null +++ b/.changeset/fruity-banks-sit.md @@ -0,0 +1,5 @@ +--- +"@solidjs/start": minor +--- + +improve performance by ~38% From f5bdb444497f751de5083da2454375523abd728d Mon Sep 17 00:00:00 2001 From: huseeiin Date: Tue, 3 Feb 2026 18:08:31 +0300 Subject: [PATCH 04/11] fix casing --- packages/start/src/internal.d.ts | 2 +- packages/start/src/server/handler.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/start/src/internal.d.ts b/packages/start/src/internal.d.ts index 4a0db6ddf..0738b8c91 100644 --- a/packages/start/src/internal.d.ts +++ b/packages/start/src/internal.d.ts @@ -10,6 +10,6 @@ declare module "h3" { import type { Rollup } from "vite"; declare global { var START_CLIENT_BUNDLE: Rollup.OutputBundle; - var canSendFastNodeStreams: boolean | undefined; + var CAN_SEND_FAST_NODE_STREAMS: boolean | undefined; var USING_SOLID_START_DEV_SERVER: boolean | undefined; } diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index 702c63a2d..dd5dca33f 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -24,7 +24,7 @@ try { const original = proto.listen; proto.listen = function (...args: any[]) { - globalThis.canSendFastNodeStreams = true; + globalThis.CAN_SEND_FAST_NODE_STREAMS = true; return original.apply(this, args); }; @@ -137,7 +137,7 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled - if (globalThis.canSendFastNodeStreams) return stream; + if (globalThis.CAN_SEND_FAST_NODE_STREAMS) return stream; // returning stream directly breaks cloudflare workers const { writable, readable } = new TransformStream(); From bd8d2508413d8f05de025031fe5590631b022eef Mon Sep 17 00:00:00 2001 From: huseeiin Date: Wed, 4 Feb 2026 10:12:47 +0300 Subject: [PATCH 05/11] simpler check of nodejs runtime --- packages/start/src/server/handler.ts | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index dd5dca33f..93f9acfb4 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -15,30 +15,6 @@ import { getExpectedRedirectStatus } from "./util.ts"; const SERVER_FN_BASE = "/_server"; -try { - const nodeHTTP = await import("http"); - const http2 = await import("http2"); - - function patchListen(proto: any) { - if (!proto || proto.__patched) return; - - const original = proto.listen; - proto.listen = function (...args: any[]) { - globalThis.CAN_SEND_FAST_NODE_STREAMS = true; - return original.apply(this, args); - }; - - proto.__patched = true; - } - - // http + https - patchListen(nodeHTTP.Server.prototype); - - // http2 (discover prototypes safely) - patchListen(Object.getPrototypeOf(http2.createServer())); - patchListen(Object.getPrototypeOf(http2.createSecureServer())); -} catch {} - export function createBaseHandler( createPageEvent: (e: FetchEvent) => Promise, fn: (context: PageEvent) => JSX.Element, @@ -137,7 +113,8 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled - if (globalThis.CAN_SEND_FAST_NODE_STREAMS) return stream; + // send fast node streams (for now this is only available in nodejs) + if (e.runtime?.name === "node") return stream; // returning stream directly breaks cloudflare workers const { writable, readable } = new TransformStream(); From 432f2e37e3c76ce9270bac2e9710d302106ba24e Mon Sep 17 00:00:00 2001 From: huseeiin Date: Wed, 4 Feb 2026 10:24:55 +0300 Subject: [PATCH 06/11] delete type --- packages/start/src/internal.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/start/src/internal.d.ts b/packages/start/src/internal.d.ts index 0738b8c91..dd4ef0e40 100644 --- a/packages/start/src/internal.d.ts +++ b/packages/start/src/internal.d.ts @@ -10,6 +10,5 @@ declare module "h3" { import type { Rollup } from "vite"; declare global { var START_CLIENT_BUNDLE: Rollup.OutputBundle; - var CAN_SEND_FAST_NODE_STREAMS: boolean | undefined; var USING_SOLID_START_DEV_SERVER: boolean | undefined; } From e031fe8cf57fdfa9e3983bb2631b3d87a5430b38 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Wed, 4 Feb 2026 15:30:47 +0300 Subject: [PATCH 07/11] enable node streams in bun and deno --- packages/start/src/config/index.ts | 5 +++++ packages/start/src/server/handler.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/start/src/config/index.ts b/packages/start/src/config/index.ts index 4b1c82179..a7fad0d78 100644 --- a/packages/start/src/config/index.ts +++ b/packages/start/src/config/index.ts @@ -57,12 +57,17 @@ export function solidStart(options?: SolidStartOptions): Array { configEnvironment(name) { return { resolve: { + noExternal: ["h3"], // remove when https://github.com/solidjs/vite-plugin-solid/pull/228 is released externalConditions: ["solid", "node"], }, }; }, async config(_, env) { + _.ssr ??= {}; + _.ssr.resolve ??= {}; + _.ssr.resolve.conditions ??= []; + _.ssr.resolve.conditions.push("generic"); const clientInput = [handlers.client]; if (env.command === "build") { const clientRouter: BaseFileSystemRouter = (globalThis as any).ROUTERS.client; diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index 93f9acfb4..61f5601be 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -12,6 +12,7 @@ import { matchAPIRoute } from "./routes.ts"; import { handleServerFunction } from "./server-functions-handler.ts"; import type { APIEvent, FetchEvent, HandlerOptions, PageEvent } from "./types.ts"; import { getExpectedRedirectStatus } from "./util.ts"; +import { FastResponse } from "srvx/node"; const SERVER_FN_BASE = "/_server"; @@ -114,7 +115,7 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled // send fast node streams (for now this is only available in nodejs) - if (e.runtime?.name === "node") return stream; + if (e.runtime?.name === "node") return new FastResponse(stream); // returning stream directly breaks cloudflare workers const { writable, readable } = new TransformStream(); From 746490168c410043f0bf4b8f57235dc7647c10d9 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Wed, 4 Feb 2026 15:39:31 +0300 Subject: [PATCH 08/11] add ts-expect-error --- packages/start/src/server/handler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index 61f5601be..055be5d21 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -115,6 +115,7 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled // send fast node streams (for now this is only available in nodejs) + // @ts-expect-error if (e.runtime?.name === "node") return new FastResponse(stream); // returning stream directly breaks cloudflare workers From 8fe1e787593f80ba119e135b2e9dfe31ca6c4180 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Wed, 4 Feb 2026 15:59:00 +0300 Subject: [PATCH 09/11] update comment --- packages/start/src/server/handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index 055be5d21..e7f76e386 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -114,7 +114,7 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled - // send fast node streams (for now this is only available in nodejs) + // send fast node streams // @ts-expect-error if (e.runtime?.name === "node") return new FastResponse(stream); From 7b706a9ec0447ecbefa2390b4afbd59cf3a2c809 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Thu, 5 Feb 2026 10:16:21 +0300 Subject: [PATCH 10/11] include srvx --- packages/start/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/start/src/config/index.ts b/packages/start/src/config/index.ts index a7fad0d78..747b30f23 100644 --- a/packages/start/src/config/index.ts +++ b/packages/start/src/config/index.ts @@ -57,7 +57,7 @@ export function solidStart(options?: SolidStartOptions): Array { configEnvironment(name) { return { resolve: { - noExternal: ["h3"], + noExternal: ["h3", "srvx"], // remove when https://github.com/solidjs/vite-plugin-solid/pull/228 is released externalConditions: ["solid", "node"], }, From c30dc297891c9499dc09dcfa951414240c6a4626 Mon Sep 17 00:00:00 2001 From: huseeiin Date: Thu, 5 Feb 2026 10:36:31 +0300 Subject: [PATCH 11/11] revert include srvx --- packages/start/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/start/src/config/index.ts b/packages/start/src/config/index.ts index 747b30f23..a7fad0d78 100644 --- a/packages/start/src/config/index.ts +++ b/packages/start/src/config/index.ts @@ -57,7 +57,7 @@ export function solidStart(options?: SolidStartOptions): Array { configEnvironment(name) { return { resolve: { - noExternal: ["h3", "srvx"], + noExternal: ["h3"], // remove when https://github.com/solidjs/vite-plugin-solid/pull/228 is released externalConditions: ["solid", "node"], },