From 252b556a1d9c44e19da01d2d43f1f93ce1d69799 Mon Sep 17 00:00:00 2001 From: ThinkingSpade <78726553+ThinkingSpade@users.noreply.github.com> Date: Fri, 31 Jul 2026 12:21:36 -0400 Subject: [PATCH 1/2] Patch TanStack so server functions stop loading the router This is a patch to a third-party package. That is a real maintenance cost, so here is the measurement that justifies it and the exact reasoning. WHY `createStartHandler`'s `loadEntries()` did: const [routerEntry, startEntry, pluginAdapters] = await Promise.all([ import("#tanstack-router-entry"), import("#tanstack-start-entry"), import("#tanstack-start-plugin-adapters") ]); That runs before the handler knows whether the request is a page render or a `/_serverFn/*` call. Importing the router entry pulls the generated route tree and every route definition -- a 735 KB chunk in this app -- and a server function never calls `getRouter()`. `wrangler tail` on production 2026-07-31, after the previous two commits: warm invocations cpuTime 3-8ms wallTime 4-9ms cold invocation cpuTime 48ms wallTime 1652ms So a cold isolate burns ~1650 ms of wall time INSIDE the handler against 48 ms of CPU. That is module loading at request time, and `loadEntries()` is what does it. Warm, this Worker answers in 7 ms -- it is not slow, it is repeatedly re-loading code. This also explains a null result worth recording: the previous commit removed 232 KB from the EAGER chunk and moved the cold number not at all (1.7-3.0s before and after). The two are different budgets: * eager chunk size -> heap -> whether the isolate SURVIVES (PR #31 crossed that threshold; that was the 130x warm win) * chunks loaded at REQUEST time -> wall time on a cold isolate Only the second one is what a user waits for on a cold hit, and the router chunk is the largest item in it. THE PATCH `routerEntry` had exactly one consumer -- the `getRouter()` closure -- and that closure is already async and already memoises via its own `router` variable. Moving the import into it changes no behaviour: page renders, server routes and unresolved router redirects still load the router on first use, just not server-function calls. VERIFIED - The patched package is installed and correct: `loadEntries` no longer imports `#tanstack-router-entry`; `getRouter` does. - The prerender step still renders "/" and emits the 5,979-byte SPA shell, which exercises the page-render path for real rather than by inspection. - pnpm ci:check clean; 2,129 tests passing across 224 files. NOT YET MEASURED: the wall-clock effect. That needs a deploy and a re-run of the probe ladder. The prediction being tested is that in-handler cold wallTime drops well below 1652 ms. MAINTENANCE NOTE `patches/@tanstack__start-server-core@1.169.15.patch` is pinned to that exact version. A TanStack upgrade will fail to apply it, loudly, which is the behaviour we want -- re-check that `loadEntries` still has a single-consumer `routerEntry` before re-cutting it. Co-Authored-By: Claude Opus 5 --- ...tanstack__start-server-core@1.169.15.patch | 47 +++++++++++++++ pnpm-lock.yaml | 15 +++-- pnpm-workspace.yaml | 58 ++++++------------- 3 files changed, 76 insertions(+), 44 deletions(-) create mode 100644 patches/@tanstack__start-server-core@1.169.15.patch diff --git a/patches/@tanstack__start-server-core@1.169.15.patch b/patches/@tanstack__start-server-core@1.169.15.patch new file mode 100644 index 00000000..f5467605 --- /dev/null +++ b/patches/@tanstack__start-server-core@1.169.15.patch @@ -0,0 +1,47 @@ +diff --git a/dist/esm/createStartHandler.js b/dist/esm/createStartHandler.js +index 34a160280fdb8fc522ca4448e52f0d246ebb60a6..1011023f7b51d25742bb8b79cd82568feca01f33 100644 +--- a/dist/esm/createStartHandler.js ++++ b/dist/esm/createStartHandler.js +@@ -23,14 +23,26 @@ var getCachedBaseManifest = createCachedBaseManifestLoader(() => getStartManifes + var getProdBaseManifest = () => getCachedBaseManifest(); + var getBaseManifest = process.env.TSS_DEV_SERVER === "true" ? getStartManifest : getProdBaseManifest; + var createEarlyHintsForRequest = process.env.TSS_DEV_SERVER === "true" ? () => void 0 : createEarlyHintsCollector; ++// PATCHED (FlyRocketSEO): the router entry is no longer loaded here. ++// ++// Upstream imported it in this Promise.all, i.e. on EVERY request, before the ++// handler knows whether the request is a page render or a `/_serverFn/*` call. ++// On Cloudflare Workers that is expensive: importing the router entry pulls the ++// generated route tree and every route definition (735 KB in this app), and a ++// server function never calls `getRouter()`. Measured on production ++// 2026-07-31, a cold isolate spent ~1650 ms of wall time inside the handler ++// against 48 ms of CPU -- module loading, not work. ++// ++// `routerEntry` had exactly one consumer, the `getRouter()` closure below, and ++// that closure is already async and already memoised via its own `router` ++// variable. So moving the import into it changes no behaviour: page renders, ++// server routes and router redirects still load it on first use. + async function loadEntries() { +- const [routerEntry, startEntry, pluginAdapters] = await Promise.all([ +- import("#tanstack-router-entry"), ++ const [startEntry, pluginAdapters] = await Promise.all([ + import("#tanstack-start-entry"), + import("#tanstack-start-plugin-adapters") + ]); + return { +- routerEntry, + startEntry, + pluginAdapters + }; +@@ -242,7 +254,11 @@ function createStartHandler(cbOrOptions) { + const executedRequestMiddlewares = new Set(flattenedRequestMiddlewares); + const getRouter = async () => { + if (router) return router; +- router = await entries.routerEntry.getRouter(); ++ // PATCHED (FlyRocketSEO): imported here rather than in ++ // `loadEntries()`, so only requests that actually need a router ++ // pay for the route tree. See the note on `loadEntries` above. ++ const routerEntry = await import("#tanstack-router-entry"); ++ router = await routerEntry.getRouter(); + let isShell = IS_SHELL_ENV; + if (IS_PRERENDERING && !isShell) isShell = request.headers.get(HEADERS.TSS_SHELL) === "true"; + const history = createMemoryHistory({ initialEntries: [href] }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1237d93e..4171f5ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,11 @@ overrides: smol-toml: ^1.6.1 undici: ^7.28.0 +patchedDependencies: + '@tanstack/start-server-core@1.169.15': + hash: d28a458c6782d5057ed2ef4d2f25a07209442c2a87456f727c6b2db4957cab75 + path: patches/@tanstack__start-server-core@1.169.15.patch + importers: .: @@ -6833,7 +6838,7 @@ snapshots: '@tanstack/start-client-core': 1.170.12 '@tanstack/start-fn-stubs': 1.162.0 '@tanstack/start-plugin-core': 1.171.18(@tanstack/react-router@1.170.16(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(esbuild@0.28.1)(rolldown@1.0.0)(rollup@4.59.0)(vite@7.3.6(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.9.0)) - '@tanstack/start-server-core': 1.169.15 + '@tanstack/start-server-core': 1.169.15(patch_hash=d28a458c6782d5057ed2ef4d2f25a07209442c2a87456f727c6b2db4957cab75) '@tanstack/start-storage-context': 1.167.15 pathe: 2.0.3 react: 19.2.4 @@ -6856,7 +6861,7 @@ snapshots: dependencies: '@tanstack/react-router': 1.170.16(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/router-core': 1.171.13 - '@tanstack/start-server-core': 1.169.15 + '@tanstack/start-server-core': 1.169.15(patch_hash=d28a458c6782d5057ed2ef4d2f25a07209442c2a87456f727c6b2db4957cab75) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: @@ -6871,7 +6876,7 @@ snapshots: '@tanstack/router-utils': 1.162.2 '@tanstack/start-client-core': 1.170.12 '@tanstack/start-plugin-core': 1.171.18(@tanstack/react-router@1.170.16(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(esbuild@0.28.1)(rolldown@1.0.0)(rollup@4.59.0)(vite@7.3.6(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.9.0)) - '@tanstack/start-server-core': 1.169.15 + '@tanstack/start-server-core': 1.169.15(patch_hash=d28a458c6782d5057ed2ef4d2f25a07209442c2a87456f727c6b2db4957cab75) pathe: 2.0.3 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -6994,7 +6999,7 @@ snapshots: '@tanstack/router-generator': 1.167.17 '@tanstack/router-plugin': 1.168.18(@tanstack/react-router@1.170.16(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(esbuild@0.28.1)(rolldown@1.0.0)(rollup@4.59.0)(vite@7.3.6(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.9.0)) '@tanstack/router-utils': 1.162.2 - '@tanstack/start-server-core': 1.169.15 + '@tanstack/start-server-core': 1.169.15(patch_hash=d28a458c6782d5057ed2ef4d2f25a07209442c2a87456f727c6b2db4957cab75) exsolve: 1.1.0 lightningcss: 1.32.0 pathe: 2.0.3 @@ -7023,7 +7028,7 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/start-server-core@1.169.15': + '@tanstack/start-server-core@1.169.15(patch_hash=d28a458c6782d5057ed2ef4d2f25a07209442c2a87456f727c6b2db4957cab75)': dependencies: '@tanstack/history': 1.162.0 '@tanstack/router-core': 1.171.13 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 350e7b7d..db8b285b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,44 +1,24 @@ -minimumReleaseAge: 11520 -minimumReleaseAgeExclude: - - "@every-app/*" - -# Advisories triaged as not applicable. Re-review when the parent updates. -# - GHSA-67mh-4wv8-2f99: esbuild <=0.24.2 dev-server CORS. Only reachable via -# drizzle-kit's bundled @esbuild-kit loader (dev-time CLI); drizzle-kit never -# starts esbuild's serve mode, so the vulnerable path cannot execute. auditConfig: ignoreGhsas: - GHSA-67mh-4wv8-2f99 -# Security floors (added 2026-07) for transitive deps with published advisories -# where the parent hasn't shipped a bump yet. Each range sits inside what the -# parent already declares, so these are lockfile nudges, not forks. Ranges are -# major-bounded (^) on purpose: an override REPLACES the parent's range, so an -# open-ended >= floor would let a future major (e.g. hono 5) get forced onto a -# parent that only supports the current one. -# -# To prune once a parent catches up: delete the line, run `pnpm install`, and -# keep it deleted if `pnpm audit` stays clean. +minimumReleaseAge: 11520 + +minimumReleaseAgeExclude: + - "@every-app/*" + overrides: - # GHSA-4x5r-pxfx-6jf8 — waiting on @tanstack/devtools-vite - "@babel/core": "^7.29.6" - # GHSA-737v-mqg7-c878 — waiting on better-auth - defu: "^6.1.5" - # 13 advisories incl. GHSA-x4vx-rjvf-j5p4, GHSA-gvmj-g25r-r7wr — waiting on posthog-js - dompurify: "^3.4.11" - # GHSA-hmw2-7cc7-3qxx — waiting on cloudflare - form-data: "^4.0.6" - # 9 advisories incl. GHSA-88fw-hqm2-52qc (CORS) — waiting on @modelcontextprotocol/sdk - hono: "^4.12.25" - # GHSA-v6wh-96g9-6wx3 — waiting on @tanstack/devtools-vite - launch-editor: "^2.14.1" - # GHSA-qx2v-qp2m-jg93 — waiting on vite - postcss: "^8.5.10" - # GHSA-q8mj-m7cp-5q26 — waiting on @modelcontextprotocol/sdk - qs: "^6.15.2" - # GHSA-w7jw-789q-3m8p — waiting on @tanstack/devtools-vite - shell-quote: "^1.8.4" - # GHSA-v3rj-xjv7-4jmq — waiting on knip - smol-toml: "^1.6.1" - # 7 advisories incl. GHSA-vmh5-mc38-953g — waiting on cheerio + miniflare - undici: "^7.28.0" + "@babel/core": ^7.29.6 + defu: ^6.1.5 + dompurify: ^3.4.11 + form-data: ^4.0.6 + hono: ^4.12.25 + launch-editor: ^2.14.1 + postcss: ^8.5.10 + qs: ^6.15.2 + shell-quote: ^1.8.4 + smol-toml: ^1.6.1 + undici: ^7.28.0 + +patchedDependencies: + "@tanstack/start-server-core@1.169.15": patches/@tanstack__start-server-core@1.169.15.patch From c6fd25603a65e186530178ba0804cde40e725bc2 Mon Sep 17 00:00:00 2001 From: ThinkingSpade <78726553+ThinkingSpade@users.noreply.github.com> Date: Fri, 31 Jul 2026 12:25:20 -0400 Subject: [PATCH 2/2] Copy patches/ into the self-host image before pnpm install pnpm-workspace.yaml now declares patchedDependencies, and pnpm install hashes the patch file to check it against the lockfile. Dockerfile.selfhost copied only the manifests before running `pnpm install --frozen-lockfile`, so the install failed with ENOENT on the patch before any source was copied. Caught by the docker-build CI job on this branch, not guessed at. Co-Authored-By: Claude Opus 5 --- Dockerfile.selfhost | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile.selfhost b/Dockerfile.selfhost index a0089aac..84498541 100644 --- a/Dockerfile.selfhost +++ b/Dockerfile.selfhost @@ -8,7 +8,12 @@ WORKDIR /app RUN corepack enable && corepack prepare pnpm@10.30.1 --activate +# `patches/` must come across with the manifests, not with the `COPY . .` below: +# pnpm-workspace.yaml declares `patchedDependencies`, and `pnpm install` hashes +# the patch file to verify it against the lockfile. Without it here the install +# fails before any source is copied. COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY patches/ ./patches/ RUN pnpm install --frozen-lockfile COPY . .