From 78c8da314d53ed490faa622b8497f8f74bfc958e Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:32:10 -0700 Subject: [PATCH] Forward docs and PostHog proxies before loading the Start graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both are pure passthroughs to an external origin — they never touch the router, React, or the Effect app — but they lived in Start's request middleware, so each paid Start's lazy loadEntries import of the whole server graph before it could forward. Measured in production by timing the two costs apart on a cold isolate: the graph import is p50 3.1s while the request's own work is p50 33ms. Essentially every request is cold — worker.dispatch ran 1,666 requests across 1,608 isolates — because /mcp dispatches before fetchHandler and so never warms the graph. A /docs page took 3-6s through the Worker against 0.098s straight from the upstream. Move matching and forwarding into edge/passthrough.ts, which imports neither Start nor any app module, and dispatch it from the Worker entry next to marketing. The middleware wrappers stay registered and now source their matching from the same module, so local dev is unchanged and the two cannot drift. Also reverts the temporary latency probes from server.ts. --- apps/cloud/src/edge/docs.ts | 78 ++--------- apps/cloud/src/edge/passthrough.test.ts | 85 ++++++++++++ apps/cloud/src/edge/passthrough.ts | 135 +++++++++++++++++++ apps/cloud/src/edge/posthog.ts | 29 ++-- apps/cloud/src/server.ts | 155 ++-------------------- apps/cloud/src/start-virtual-entries.d.ts | 9 -- 6 files changed, 249 insertions(+), 242 deletions(-) create mode 100644 apps/cloud/src/edge/passthrough.test.ts create mode 100644 apps/cloud/src/edge/passthrough.ts delete mode 100644 apps/cloud/src/start-virtual-entries.d.ts diff --git a/apps/cloud/src/edge/docs.ts b/apps/cloud/src/edge/docs.ts index fc59f5e3c0..fabf3773e0 100644 --- a/apps/cloud/src/edge/docs.ts +++ b/apps/cloud/src/edge/docs.ts @@ -6,83 +6,23 @@ // base path, so the pathname is forwarded UNCHANGED — only the host/proto swap // to the upstream origin (unlike the PostHog proxy, which strips its prefix). // -// Like the PostHog/Sentry tunnels (and unlike the marketing proxy, which needs -// the prod-only `env.MARKETING` service binding), this is a plain external -// `fetch`, so it runs on every host — `/docs` previews against live Mintlify in -// local dev too. `/docs` is distinct from the app-owned `/api/docs` (Swagger), -// so this never shadows an Effect-served route. +// The matching, upstream construction, and client span live in `./passthrough`, +// which server.ts dispatches BEFORE Start loads: forwarding a docs page must +// not pay for the whole server graph. This middleware stays registered so hosts +// that reach Start by another entry (local dev) keep identical behavior; in the +// deployed Worker it is unreachable. `/docs` is distinct from the app-owned +// `/api/docs` (Swagger), so this never shadows an Effect-served route. // --------------------------------------------------------------------------- -import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api"; import { createMiddleware } from "@tanstack/react-start"; -const DOCS_UPSTREAM_HOST = "executor.mintlify.dev"; +import { docsProxyResponse, isDocsPath } from "./passthrough"; -// The proxy fetch gets its own client span: `/docs` requests otherwise render -// as a single opaque server span, and during the Aug 2026 regression there -// was no way to tell upstream (Mintlify/Vercel) latency from worker-side -// dispatch cost. The noop tracer applies when no provider is installed -// (local dev without AXIOM_TOKEN), so this is free there. -const tracer = trace.getTracer("executor-cloud-docs-proxy"); - -export const isDocsPath = (pathname: string) => - pathname === "/docs" || pathname.startsWith("/docs/"); - -// Build the upstream request for an already-classified `/docs` path. Caller -// guarantees `isDocsPath(pathname)` — we only swap the origin and fix up the -// forwarding headers, preserving method, body, path, and query. -export const buildDocsUpstream = (request: Request): Request => { - const url = new URL(request.url); - const forwardedHost = url.host; - - url.hostname = DOCS_UPSTREAM_HOST; - url.protocol = "https:"; - url.port = ""; - - const upstream = new Request(url, request); - // Mintlify keys canonical links off the public host; tell it the real one. - upstream.headers.set("X-Forwarded-Host", forwardedHost); - upstream.headers.set("X-Forwarded-Proto", "https"); - // Never leak the executor.sh session cookie to the docs origin. - upstream.headers.delete("cookie"); - return upstream; -}; +export { buildDocsUpstream, isDocsPath } from "./passthrough"; export const docsProxyMiddleware = createMiddleware({ type: "request" }).server( ({ pathname, request, next }) => { if (!isDocsPath(pathname)) return next(); - return tracer.startActiveSpan( - `http.client ${request.method}`, - { - kind: SpanKind.CLIENT, - attributes: { - "server.address": DOCS_UPSTREAM_HOST, - "url.path": pathname, - "http.request.method": request.method, - }, - }, - async (span) => { - // oxlint-disable-next-line executor/no-try-catch-or-throw -- adapter boundary; observe upstream response/error for span status, then pass both through unchanged - try { - const response = await fetch(buildDocsUpstream(request)); - span.setAttribute("http.response.status_code", response.status); - if (response.status >= 500) { - span.setStatus({ code: SpanStatusCode.ERROR, message: `HTTP ${response.status}` }); - } - return response; - } catch (err) { - // oxlint-disable-next-line executor/no-instanceof-error, executor/no-unknown-error-message -- adapter boundary: fetch rejects untyped; normalized only for the OTel span record, the original error is rethrown below - const cause = err instanceof Error ? err : String(err); - span.recordException(cause); - // oxlint-disable-next-line executor/no-unknown-error-message -- adapter boundary: same normalization as the recordException line above - const message = typeof cause === "string" ? cause : cause.message; - span.setStatus({ code: SpanStatusCode.ERROR, message }); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- adapter boundary; preserve the original rejection for the platform handler - throw err; - } finally { - span.end(); - } - }, - ); + return docsProxyResponse(request, pathname); }, ); diff --git a/apps/cloud/src/edge/passthrough.test.ts b/apps/cloud/src/edge/passthrough.test.ts new file mode 100644 index 0000000000..2f5bbd35ad --- /dev/null +++ b/apps/cloud/src/edge/passthrough.test.ts @@ -0,0 +1,85 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "@effect/vitest"; + +import { + buildDocsUpstream, + buildPosthogUpstream, + isDocsPath, + isPosthogPath, + passthroughResponse, + POSTHOG_PROXY_PATH, +} from "./passthrough"; + +describe("passthrough matching", () => { + it("claims /docs and everything under it, but not /api/docs", () => { + expect(isDocsPath("/docs")).toBe(true); + expect(isDocsPath("/docs/concepts/policies")).toBe(true); + // The app-owned Swagger route must keep reaching the Effect app. + expect(isDocsPath("/api/docs")).toBe(false); + expect(isDocsPath("/docsearch")).toBe(false); + }); + + it("claims the PostHog proxy path and its subtree only", () => { + expect(isPosthogPath(POSTHOG_PROXY_PATH)).toBe(true); + expect(isPosthogPath(`${POSTHOG_PROXY_PATH}/i/v0/e/`)).toBe(true); + expect(isPosthogPath(`${POSTHOG_PROXY_PATH}extra`)).toBe(false); + expect(isPosthogPath("/api/connections")).toBe(false); + }); + + it("returns null for app paths so they fall through to normal dispatch", () => { + expect(passthroughResponse(new Request("https://executor.sh/"), "/")).toBeNull(); + expect( + passthroughResponse(new Request("https://executor.sh/api/connections"), "/api/connections"), + ).toBeNull(); + expect(passthroughResponse(new Request("https://executor.sh/mcp"), "/mcp")).toBeNull(); + }); +}); + +describe("upstream construction", () => { + it("forwards the docs path unchanged and strips the session cookie", () => { + const upstream = buildDocsUpstream( + new Request("https://executor.sh/docs/concepts/policies?x=1", { + headers: { cookie: "wos-session=secret" }, + }), + ); + const url = new URL(upstream.url); + expect(url.hostname).toBe("executor.mintlify.dev"); + expect(url.pathname).toBe("/docs/concepts/policies"); + expect(url.search).toBe("?x=1"); + expect(upstream.headers.get("X-Forwarded-Host")).toBe("executor.sh"); + expect(upstream.headers.get("cookie")).toBeNull(); + }); + + it("strips the proxy prefix for PostHog and splits ingest from assets", () => { + const ingest = buildPosthogUpstream( + new Request(`https://executor.sh${POSTHOG_PROXY_PATH}/i/v0/e/`), + `${POSTHOG_PROXY_PATH}/i/v0/e/`, + ); + expect(new URL(ingest.url).hostname).toBe("us.i.posthog.com"); + expect(new URL(ingest.url).pathname).toBe("/i/v0/e/"); + + const assets = buildPosthogUpstream( + new Request(`https://executor.sh${POSTHOG_PROXY_PATH}/static/array.js`), + `${POSTHOG_PROXY_PATH}/static/array.js`, + ); + expect(new URL(assets.url).hostname).toBe("us-assets.i.posthog.com"); + expect(new URL(assets.url).pathname).toBe("/static/array.js"); + }); +}); + +describe("Start-graph independence", () => { + // The entire point of this module is that server.ts can answer a proxy + // request WITHOUT importing TanStack Start. An import of the app or of + // `@tanstack/react-start` here silently reintroduces the ~3.1s cold-isolate + // `loadEntries` cost this module exists to avoid, and nothing else would + // catch it — the behavior stays correct, only slow. + it("imports neither TanStack Start nor an app module", () => { + const source = readFileSync(new URL("./passthrough.ts", import.meta.url), "utf8"); + const imports = [...source.matchAll(/^\s*import[^"']*["']([^"']+)["']/gm)].map((m) => m[1]); + expect(imports.length).toBeGreaterThan(0); + for (const specifier of imports) { + expect(specifier).not.toMatch(/@tanstack/); + expect(specifier).not.toMatch(/^\.\.\//); + } + }); +}); diff --git a/apps/cloud/src/edge/passthrough.ts b/apps/cloud/src/edge/passthrough.ts new file mode 100644 index 0000000000..12b6229d44 --- /dev/null +++ b/apps/cloud/src/edge/passthrough.ts @@ -0,0 +1,135 @@ +// --------------------------------------------------------------------------- +// Pure passthrough proxies — dispatched from the Worker entry, before Start. +// --------------------------------------------------------------------------- +// +// `/docs` and the PostHog proxy forward to an external origin and never touch +// the router, React, or the Effect app. They lived in Start's request +// middleware, which meant each one still paid Start's lazy `loadEntries` +// import of the whole server graph before it could forward a request. +// +// Measured on production (2026-08-18), splitting the two costs apart on a cold +// isolate: the graph import is p50 **3.1s** while the request's own work is +// p50 **33ms**. And essentially every request is cold — `worker.dispatch` ran +// 1,666 requests across 1,608 isolates (1.04 req/isolate), because `/mcp` +// dispatches before `fetchHandler` and so never warms the graph. A `/docs` +// page took 3-6s through the Worker against 0.098s straight from the upstream. +// +// So these move to the Worker entry, exactly as marketing did: classify and +// forward before anything imports Start. This module therefore must NOT import +// `@tanstack/react-start` or any app module — that import is the cost it +// exists to avoid. +// +// The middleware wrappers in `./docs` and `./posthog` stay registered. In the +// deployed Worker they become unreachable (server.ts answers first), but they +// keep the behavior identical on any host that reaches Start by another entry +// (local dev), and they source their matching from here so the two can't drift. +// --------------------------------------------------------------------------- + +import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api"; + +const DOCS_UPSTREAM_HOST = "executor.mintlify.dev"; +const POSTHOG_INGEST_HOST = "us.i.posthog.com"; +const POSTHOG_ASSETS_HOST = "us-assets.i.posthog.com"; + +export const POSTHOG_PROXY_PATH = `/api/${( + import.meta.env.VITE_PUBLIC_ANALYTICS_PATH ?? "a" +).replace(/^\/+|\/+$/g, "")}`; + +// The proxy fetch gets its own client span: `/docs` requests otherwise render +// as a single opaque server span, and during the Aug 2026 regression there +// was no way to tell upstream (Mintlify/Vercel) latency from worker-side +// dispatch cost. The noop tracer applies when no provider is installed +// (local dev without AXIOM_TOKEN), so this is free there. +const tracer = trace.getTracer("executor-cloud-docs-proxy"); + +export const isDocsPath = (pathname: string): boolean => + pathname === "/docs" || pathname.startsWith("/docs/"); + +export const isPosthogPath = (pathname: string): boolean => + pathname === POSTHOG_PROXY_PATH || pathname.startsWith(`${POSTHOG_PROXY_PATH}/`); + +/** + * Build the upstream request for an already-classified `/docs` path. Caller + * guarantees `isDocsPath(pathname)` — we only swap the origin and fix up the + * forwarding headers, preserving method, body, path, and query. + */ +export const buildDocsUpstream = (request: Request): Request => { + const url = new URL(request.url); + const forwardedHost = url.host; + + url.hostname = DOCS_UPSTREAM_HOST; + url.protocol = "https:"; + url.port = ""; + + const upstream = new Request(url, request); + // Mintlify keys canonical links off the public host; tell it the real one. + upstream.headers.set("X-Forwarded-Host", forwardedHost); + upstream.headers.set("X-Forwarded-Proto", "https"); + // Never leak the executor.sh session cookie to the docs origin. + upstream.headers.delete("cookie"); + return upstream; +}; + +/** Build the upstream request for an already-classified PostHog proxy path. */ +export const buildPosthogUpstream = (request: Request, pathname: string): Request => { + const url = new URL(request.url); + url.hostname = pathname.startsWith(`${POSTHOG_PROXY_PATH}/static/`) + ? POSTHOG_ASSETS_HOST + : POSTHOG_INGEST_HOST; + url.protocol = "https:"; + url.port = ""; + url.pathname = pathname.slice(POSTHOG_PROXY_PATH.length) || "/"; + + const upstream = new Request(url, request); + upstream.headers.delete("cookie"); + return upstream; +}; + +export const docsProxyResponse = (request: Request, pathname: string): Promise => + tracer.startActiveSpan( + `http.client ${request.method}`, + { + kind: SpanKind.CLIENT, + attributes: { + "server.address": DOCS_UPSTREAM_HOST, + "url.path": pathname, + "http.request.method": request.method, + }, + }, + async (span) => { + // oxlint-disable-next-line executor/no-try-catch-or-throw -- adapter boundary; observe upstream response/error for span status, then pass both through unchanged + try { + const response = await fetch(buildDocsUpstream(request)); + span.setAttribute("http.response.status_code", response.status); + if (response.status >= 500) { + span.setStatus({ code: SpanStatusCode.ERROR, message: `HTTP ${response.status}` }); + } + return response; + } catch (err) { + // oxlint-disable-next-line executor/no-instanceof-error, executor/no-unknown-error-message -- adapter boundary: fetch rejects untyped; normalized only for the OTel span record, the original error is rethrown below + const cause = err instanceof Error ? err : String(err); + span.recordException(cause); + // oxlint-disable-next-line executor/no-unknown-error-message -- adapter boundary: same normalization as the recordException line above + const message = typeof cause === "string" ? cause : cause.message; + span.setStatus({ code: SpanStatusCode.ERROR, message }); + // oxlint-disable-next-line executor/no-try-catch-or-throw -- adapter boundary; preserve the original rejection for the platform handler + throw err; + } finally { + span.end(); + } + }, + ); + +/** + * Answer a pure passthrough request without loading the Start server graph. + * Returns `null` when the request belongs to the app, so the caller falls + * through to normal dispatch. + */ +export const passthroughResponse = ( + request: Request, + pathname: string, +): Promise | null => { + if (isDocsPath(pathname)) return docsProxyResponse(request, pathname); + if (isPosthogPath(pathname)) return fetch(buildPosthogUpstream(request, pathname)); + return null; +}; diff --git a/apps/cloud/src/edge/posthog.ts b/apps/cloud/src/edge/posthog.ts index 6badd574f9..a9a035b4d1 100644 --- a/apps/cloud/src/edge/posthog.ts +++ b/apps/cloud/src/edge/posthog.ts @@ -3,33 +3,20 @@ // first-party path and we forward to PostHog's ingest + asset hosts. Keeps // events flowing past adblockers that match *.posthog.com. See // https://posthog.com/docs/advanced/proxy/cloudflare +// +// The matching and forwarding live in `./passthrough`, which server.ts +// dispatches BEFORE Start loads (a proxy must not pay for the server graph). +// This middleware stays registered so hosts that reach Start by another entry +// keep identical behavior; in the deployed Worker it is unreachable. // --------------------------------------------------------------------------- import { createMiddleware } from "@tanstack/react-start"; -const POSTHOG_INGEST_HOST = "us.i.posthog.com"; -const POSTHOG_ASSETS_HOST = "us-assets.i.posthog.com"; -const POSTHOG_PROXY_PATH = `/api/${(import.meta.env.VITE_PUBLIC_ANALYTICS_PATH ?? "a").replace( - /^\/+|\/+$/g, - "", -)}`; +import { buildPosthogUpstream, isPosthogPath } from "./passthrough"; export const posthogProxyMiddleware = createMiddleware({ type: "request" }).server( ({ pathname, request, next }) => { - if (pathname !== POSTHOG_PROXY_PATH && !pathname.startsWith(`${POSTHOG_PROXY_PATH}/`)) { - return next(); - } - - const url = new URL(request.url); - url.hostname = pathname.startsWith(`${POSTHOG_PROXY_PATH}/static/`) - ? POSTHOG_ASSETS_HOST - : POSTHOG_INGEST_HOST; - url.protocol = "https:"; - url.port = ""; - url.pathname = pathname.slice(POSTHOG_PROXY_PATH.length) || "/"; - - const upstream = new Request(url, request); - upstream.headers.delete("cookie"); - return fetch(upstream); + if (!isPosthogPath(pathname)) return next(); + return fetch(buildPosthogUpstream(request, pathname)); }, ); diff --git a/apps/cloud/src/server.ts b/apps/cloud/src/server.ts index 99333d0eaf..0304ab0246 100644 --- a/apps/cloud/src/server.ts +++ b/apps/cloud/src/server.ts @@ -13,6 +13,7 @@ import handler from "@tanstack/react-start/server-entry"; import { isAppOwnedPath } from "./app-paths"; import { marketingProxyRequest } from "./edge/marketing"; +import { passthroughResponse } from "./edge/passthrough"; import { makeCloudMcpAgentHandler } from "./mcp/agent-handler"; import { classifyMcpPath, prepareMcpOrgScope } from "./mcp/mount"; import { parseTraceparent } from "./mcp/traceparent"; @@ -102,70 +103,12 @@ export { McpExecutionOwnerDirectoryDO } from "@executor-js/cloudflare/mcp/execut // until the in-flight export resolves. // --------------------------------------------------------------------------- -const rawFetchHandler = handler.fetch as ( +const fetchHandler = handler.fetch as ( request: Request, env: Env, ctx: ExecutionContext, ) => Response | Promise; -// TEMPORARY DIAGNOSTIC (Aug 2026 latency hunt). -// -// The standing explanation is that Start's lazy `loadEntries` import of the -// 2.56MB server graph costs seconds on the first Start-handled request per -// isolate. That does not fit the numbers: module evaluation is CPU work, but -// these requests report 45-72ms of CPU against 4s of wall time. -// -// So distinguish the two directly. `wasWarm` is false only for the FIRST -// Start-handled request in this isolate — the one that pays the module load. -// If cold requests are slow and warm ones are fast, the graph load is real. -// If both are slow, the cost is per-request work inside Start/Effect and the -// module-load story is wrong. -let startHandledInThisIsolate = false; - -const fetchHandler = async ( - request: Request, - env: Env, - ctx: ExecutionContext, -): Promise => { - const wasWarm = startHandledInThisIsolate; - startHandledInThisIsolate = true; - // Sync the clock on BOTH sides. Without the trailing `scheduler.wait(0)`, - // a handler that performs no I/O leaves `Date.now()` pinned and reports - // 0ms for work that actually took seconds — which is exactly what the - // first run of this probe showed (handlerMs 0 against 6002ms wall). - await scheduler.wait(0); - - // "First Start request in this isolate" does TWO things: it evaluates the - // 2.56MB module graph, and it builds the app's Effect layers (DB, WorkOS) - // for the first time. Those have different fixes, so time them apart. - // Importing the same virtual ids `loadEntries` uses means its cache finds - // the module already evaluated, so `handlerMs` below excludes the load. - const moduleStartedAt = Date.now(); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- temporary diagnostic: a probe failure must not affect the request - try { - await Promise.all([import("#tanstack-router-entry"), import("#tanstack-start-entry")]); - } catch { - // ignored — the timing is the signal - } - await scheduler.wait(0); - const moduleMs = Date.now() - moduleStartedAt; - - const startedAt = Date.now(); - const response = await rawFetchHandler(request, env, ctx); - await scheduler.wait(0); - const handlerMs = Date.now() - startedAt; - console.log( - JSON.stringify({ - probe: "start-graph", - path: new URL(request.url).pathname, - wasWarm, - moduleMs, - handlerMs, - }), - ); - return response; -}; - const tracer = trace.getTracer("executor-cloud-worker"); const traceparentValueFor = (spanContext: SpanContext): string => @@ -237,90 +180,6 @@ const mcpAgentHandler = makeCloudMcpAgentHandler({ const cloudflareHandler: ExportedHandler = { fetch: async (request, env, ctx) => { - // TEMPORARY DIAGNOSTIC (Aug 2026 latency hunt). - // - // workerd freezes `Date.now()` and only advances it when I/O completes, so - // every `Date.now()` delta taken ACROSS the first I/O of a request silently - // includes all wall-clock since the request started — queueing, isolate - // start, module evaluation. That is why per-phase timings kept reporting - // 0ms for everything up to the first await and then a multi-second number - // for whichever await happened to be first: the instrument was measuring - // the clock jump, not the operation. - // - // `scheduler.wait(0)` is an I/O boundary, so awaiting it here forces the - // clock forward before any real work. The delta it absorbs IS the - // pre-work time (queue + start + module eval); every measurement after it - // is honest. - const entryPinned = Date.now(); - await scheduler.wait(0); - const preWorkMs = Date.now() - entryPinned; - const probeUrl = new URL(request.url); - - // With the clock synced above, these two deltas are real durations. They - // answer whether the multi-second cost is specific to the Cache API or - // hits every outbound subrequest from this Worker. - const cacheStartedAt = Date.now(); - const probeCaches = caches as CacheStorage & { readonly default?: Cache }; - await probeCaches.default?.match("https://executor.sh/__probe_never_cached"); - const cacheProbeMs = Date.now() - cacheStartedAt; - - const timerStartedAt = Date.now(); - await scheduler.wait(1); - const timerProbeMs = Date.now() - timerStartedAt; - - // The cache and timer probes are LOCAL. Neither leaves the isolate, and - // both come back in single-digit ms while the same requests take 4-6s. - // This one is a real outbound network subrequest to a small, fast, - // unrelated endpoint — the only class of I/O not yet measured, and the - // one the docs proxy (0.098s direct, 3-6s through the Worker) implicates. - const fetchStartedAt = Date.now(); - // oxlint-disable-next-line executor/no-try-catch-or-throw -- temporary diagnostic: a probe failure must not affect the request - try { - await fetch("https://cloudflare.com/cdn-cgi/trace", { method: "GET" }); - } catch { - // ignored — the timing is the signal, not the result - } - const fetchProbeMs = Date.now() - fetchStartedAt; - - // `fetch()` above resolves when HEADERS arrive — it never reads a body, - // which is why it returns in ~1ms while the request it lives in takes - // seconds. Both genuinely slow operations read a body: the docs proxy - // reads a 179KB page, and the JWKS store does `hit.json()`. Split header - // time from body time on the exact URL the docs proxy uses. - // - // Gated to one cheap path so normal traffic never pays for the probe. - let bodyHeadersMs = -1; - let bodyReadMs = -1; - let bodyBytes = -1; - if (probeUrl.pathname === "/robots.txt") { - // oxlint-disable-next-line executor/no-try-catch-or-throw -- temporary diagnostic: a probe failure must not affect the request - try { - const h0 = Date.now(); - const probeRes = await fetch("https://executor.mintlify.dev/docs/concepts/policies"); - bodyHeadersMs = Date.now() - h0; - const b0 = Date.now(); - const text = await probeRes.text(); - bodyReadMs = Date.now() - b0; - bodyBytes = text.length; - } catch { - // ignored — the timing is the signal - } - } - - console.log( - JSON.stringify({ - probe: "clock-sync", - path: probeUrl.pathname, - preWorkMs, - cacheProbeMs, - timerProbeMs, - fetchProbeMs, - bodyHeadersMs, - bodyReadMs, - bodyBytes, - }), - ); - // Public pages must not enter TanStack Start: its first-request dynamic // import loads the entire React + Effect server graph and can take seconds // on a cold isolate. Classify and service-bind marketing at the Worker @@ -329,6 +188,16 @@ const cloudflareHandler: ExportedHandler = { const marketing: Fetcher | undefined = env.MARKETING; if (marketingRequest && marketing) return marketing.fetch(marketingRequest); + // Same reasoning, same seam: `/docs` and the PostHog proxy forward to an + // external origin and never touch the router, React, or the Effect app. + // Left in Start's middleware they still paid its lazy `loadEntries` import + // first — measured at p50 3.1s on a cold isolate, against p50 33ms for the + // request's own work, on a Worker where 1,666 dispatches spread across + // 1,608 isolates (so nearly every request is cold). Forward before Start. + const passthroughPath = new URL(request.url).pathname; + const passthrough = passthroughResponse(request, passthroughPath); + if (passthrough) return passthrough; + // Browser OTLP ingress — before the server span opens: exporter traffic // must never trace itself (the browser already excludes /v1/traces from // its own tracing for the same reason). diff --git a/apps/cloud/src/start-virtual-entries.d.ts b/apps/cloud/src/start-virtual-entries.d.ts deleted file mode 100644 index c76881feb9..0000000000 --- a/apps/cloud/src/start-virtual-entries.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// TanStack Start's internal virtual server-entry modules (registered by the -// Start vite plugin; the same ids `start-server-core`'s `loadEntries` -// imports). server.ts imports them to time module evaluation separately from -// first-request app initialization — only the evaluation side effect matters, -// so the value shape is left untyped. Kept in a standalone declaration file: -// shorthand ambient modules only register from a non-module file -// (env-augment.d.ts is a module). -declare module "#tanstack-router-entry"; -declare module "#tanstack-start-entry";