Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions apps/cloud/scripts/start-closure.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
// cold isolate before any request is served.
// start - the TanStack Start server graph, reached through the lazy
// `loadEntries` dynamic imports. Evaluated on the first request
// that enters the app.
// that enters the app - i.e. a page request.
// app - the Effect app plane. `/api/*` dispatches at the Worker entry and
// skips Start entirely, so an API request evaluates this instead of
// `start`; reported separately because the two planes now diverge.
//
// Anything reachable only through a dynamic import is not counted: making a
// heavy dependency lazy is exactly the outcome this rewards.
Expand Down Expand Up @@ -81,6 +84,9 @@ const startRoots = [...graph.get(ENTRY).dynamic].filter((f) =>
/(start|router|tanstack)/.test(name(f)),
);
const start = closure(startRoots);
const appRoots = [...graph.get(ENTRY).dynamic].filter((f) => /\/app-[A-Za-z0-9_-]+\.js$/.test(f));
const app = closure([ENTRY, ...appRoots]);
// The budget tracks the worst plane: whichever costs a cold isolate more.
const evaluated = new Set([...startup, ...start]);

const report = (label, files) => {
Expand All @@ -93,7 +99,10 @@ const report = (label, files) => {

report("startup", startup);
report("start", start);
console.log(`\ntotal evaluated on a warm-path request: ${mb(bytes(evaluated))}`);
console.log(`\npage request (startup + start): ${mb(bytes(evaluated))}`);
console.log(
`API request (startup + app): ${mb(bytes(app))}${appRoots.length ? "" : " [no app chunk - /api still routes through Start]"}`,
);
const lazyOnly = [...graph.keys()].filter((f) => !evaluated.has(f));
console.log(
`deferred behind dynamic import: ${mb(bytes(lazyOnly))} (${lazyOnly.length} chunks)`,
Expand Down
38 changes: 37 additions & 1 deletion apps/cloud/src/app-paths.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "@effect/vitest";

import { isAppOwnedPath } from "./app-paths";
import { isAppOwnedPath, servedByAppPlane } from "./app-paths";

// Guards the start.ts dispatch decision: every surface the unified app handler
// serves must be classified app-owned (forwarded to `app.handler`), and Start's
Expand Down Expand Up @@ -62,3 +62,39 @@ describe("isAppOwnedPath", () => {
});
}
});

describe("app-plane dispatch", () => {
// These two are the whole risk of dispatching `/api` before Start: both still
// return a response if routed early, just the wrong one, so nothing else would
// catch a regression here.
it("leaves the Sentry tunnel POST to Start's middleware", () => {
expect(servedByAppPlane("/api/sentry-tunnel", "POST")).toBe(false);
// Only the POST is claimed; anything else under that path is ordinary API.
expect(servedByAppPlane("/api/sentry-tunnel", "GET")).toBe(true);
});

it("leaves the OAuth callback to Start, for the signed-out redirect", () => {
expect(servedByAppPlane("/api/oauth/callback", "GET")).toBe(false);
expect(servedByAppPlane("/api/oauth/callback", "POST")).toBe(false);
});

const appPlane = [
"/api/connections",
"/api/tools",
"/api/integrations",
"/api/account/members",
"/api/docs",
"/api/billing/checkout",
];
for (const pathname of appPlane) {
it(`serves ${pathname} without entering Start`, () => {
expect(servedByAppPlane(pathname, "GET")).toBe(true);
});
}

it("never claims a non-API path, however app-owned", () => {
expect(servedByAppPlane("/mcp", "POST")).toBe(false);
expect(servedByAppPlane("/", "GET")).toBe(false);
expect(servedByAppPlane("/.well-known/oauth-authorization-server", "GET")).toBe(false);
});
});
28 changes: 28 additions & 0 deletions apps/cloud/src/app-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,31 @@ export const isApiPath = (pathname: string) => pathname === "/api" || pathname.s

export const isAppOwnedPath = (pathname: string) =>
isApiPath(pathname) || classifyMcpPath(pathname) !== null;

// ---------------------------------------------------------------------------
// Which plane serves an app-owned path: the Effect app directly, or TanStack
// Start's middleware chain.
//
// Everything under `/api` is pure Effect and touches no part of the router,
// React, or SSR — so `server.ts` dispatches it at the Worker entry and skips
// Start's lazy `loadEntries` import entirely. Two paths must NOT take that
// shortcut, because Start's request middleware claims them BEFORE the app
// handler would ever see them:
//
// POST /api/sentry-tunnel - `sentryTunnelMiddleware` forwards the envelope
// to Sentry; the app has no such route.
// /api/oauth/callback - `oauthCallbackSignInMiddleware` redirects a
// signed-out visitor to /login, and start.ts
// rewrites the org-scoped `state` before handing
// off. Routing it early would drop both.
//
// Getting this wrong is silent: the request still gets a response, just the
// wrong one, which is why it is classified here and tested rather than being
// an inline condition at the dispatch site.
// ---------------------------------------------------------------------------

export const isStartOwnedApiPath = (pathname: string, method: string): boolean =>
(pathname === "/api/sentry-tunnel" && method === "POST") || pathname === "/api/oauth/callback";

export const servedByAppPlane = (pathname: string, method: string): boolean =>
isApiPath(pathname) && !isStartOwnedApiPath(pathname, method);
54 changes: 48 additions & 6 deletions apps/cloud/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import * as Sentry from "@sentry/cloudflare";
import handler from "@tanstack/react-start/server-entry";

import { isAppOwnedPath } from "./app-paths";
import { isAppOwnedPath, servedByAppPlane } from "./app-paths";
import { marketingProxyRequest } from "./edge/marketing";
import { passthroughResponse } from "./edge/passthrough";
import { makeCloudMcpAgentHandler } from "./mcp/agent-handler";
Expand Down Expand Up @@ -244,6 +244,42 @@ const markStartGraphEntered = (): void => {
startGraphEntered = true;
};

// ---------------------------------------------------------------------------
// Serving `/api/*` without entering TanStack Start.
// ---------------------------------------------------------------------------
//
// Everything under `/api` is the Effect app (`ExecutorApp.make`'s web handler)
// and uses no part of the router, React, or SSR. But it was dispatched from a
// Start *request middleware*, so reaching it meant paying Start's lazy
// `loadEntries` import of the whole server graph first. Measured on production
// 2026-08-19, splitting `/api/*` by whether the isolate had already loaded that
// graph: warm p50 **186ms**, cold p50 **2129ms**, with 28% of API requests cold.
// The dashboard fires many `/api/*` calls in parallel and waits for the slowest,
// so that cold tail is what the app actually feels like.
//
// So `/api` joins marketing, `/docs`, the PostHog proxy and `/mcp` at the Worker
// entry: classify and dispatch before anything touches Start. The evaluated
// closure for an API request drops from the full Start graph to the Worker's own
// (see `scripts/start-closure.mjs`).
//
// `servedByAppPlane` (./app-paths) decides which paths qualify — two under
// `/api` are claimed by Start's middleware first and must keep their old route.

// Instantiated on the first request that needs it and memoized per isolate,
// mirroring `start.ts`'s `getApp`. The import stays dynamic so an isolate that
// only serves pages or proxies never evaluates the app graph at all.
let appPlane: ReturnType<typeof import("./app").cloudApiHandler> | undefined;
let appGraphEntered = false;

const getAppPlane = async (): Promise<NonNullable<typeof appPlane>> => {
if (appPlane === undefined) {
const { cloudApiHandler } = await import("./app");
appPlane = cloudApiHandler();
appGraphEntered = true;
}
return appPlane;
};

const cloudflareHandler: ExportedHandler<Env> = {
fetch: async (request, env, ctx) => {
isolateRequestSeq += 1;
Expand Down Expand Up @@ -330,13 +366,19 @@ const cloudflareHandler: ExportedHandler<Env> = {
span.setAttribute("executor.start_graph.entered", startGraphEntered);
span.setAttribute("executor.isolate.id", isolate.id);
span.setAttribute("executor.isolate.age_ms", isolate.ageMs);
// Which plane served this: "app" skipped the Start graph entirely, so
// `start_graph.entered` says nothing about it. `app_graph.entered`
// is the app-plane analogue - false means this request paid for the
// Effect graph's first evaluation in this isolate.
const appPlaneRequest = servedByAppPlane(url.pathname, request.method);
span.setAttribute("executor.dispatch.plane", appPlaneRequest ? "app" : "start");
if (appPlaneRequest) span.setAttribute("executor.app_graph.entered", appGraphEntered);
// oxlint-disable-next-line executor/no-try-catch-or-throw -- adapter boundary; observe response/error for span status, keep the flush alive past the response
try {
const response = await fetchHandler(
withTraceparent(request, span.spanContext()),
env,
ctx,
);
const traced = withTraceparent(request, span.spanContext());
const response = appPlaneRequest
? await (await getAppPlane()).handler(prepareMcpOrgScope(traced))
: await fetchHandler(traced, env, ctx);
span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);
return response;
} catch (err) {
Expand Down
Loading