@tanstack/start-server-core 1.169.5 → 1.169.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@ import { ServerFunctionSerializationAdapter } from "./serializer/ServerFunctionS
8
8
  import { createMemoryHistory } from "@tanstack/history";
9
9
  import { createCsrfMiddleware, createNullProtoObject, csrfSymbol, flattenMiddlewares, mergeHeaders, safeObjectMerge } from "@tanstack/start-client-core";
10
10
  import { executeRewriteInput, isRedirect, isResolvedRedirect } from "@tanstack/router-core";
11
- import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin } from "@tanstack/router-core/ssr/server";
11
+ import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody } from "@tanstack/router-core/ssr/server";
12
12
  import { getStartContext, runWithStartContext } from "@tanstack/start-storage-context";
13
13
  //#region src/createStartHandler.ts
14
14
  function getStartResponseHeaders(opts) {
@@ -90,18 +90,48 @@ function isSpecialResponse(value) {
90
90
  * Normalize middleware result to context shape
91
91
  */
92
92
  function handleCtxResult(result) {
93
- if (isSpecialResponse(result)) return { response: result };
93
+ if (isSsrResponse(result) || isSpecialResponse(result)) return { response: result };
94
94
  return result;
95
95
  }
96
96
  /**
97
97
  * Execute a middleware chain
98
98
  */
99
- function executeMiddleware(middlewares, ctx) {
99
+ async function executeMiddleware(middlewares, ctx) {
100
100
  let index = -1;
101
+ let streamResponse;
102
+ const setResponse = (response) => {
103
+ if (isSsrResponse(response)) {
104
+ if (response.serverSsrCleanup === "stream") streamResponse = response;
105
+ ctx.response = response.response;
106
+ return;
107
+ }
108
+ ctx.response = response;
109
+ };
110
+ const disposeStreamResponse = async (reason) => {
111
+ const response = streamResponse;
112
+ if (!response) return;
113
+ streamResponse = void 0;
114
+ const currentResponse = ctx.response;
115
+ if (currentResponse === response.response || currentResponse instanceof Response && response.response.body !== null && currentResponse.body === response.response.body) ctx.response = void 0;
116
+ await response.dispose(reason);
117
+ };
118
+ const getFinalResponse = async () => {
119
+ const response = ctx.response;
120
+ if (!response) throwRouteHandlerError();
121
+ if (!streamResponse) return response;
122
+ if (response === streamResponse.response) return streamResponse;
123
+ if (streamResponse.response.body !== null && response.body === streamResponse.response.body) return {
124
+ ...streamResponse,
125
+ response
126
+ };
127
+ await disposeStreamResponse("middleware response replaced");
128
+ return response;
129
+ };
101
130
  const next = async (nextCtx) => {
102
131
  if (nextCtx) {
103
132
  if (nextCtx.context) ctx.context = safeObjectMerge(ctx.context, nextCtx.context);
104
- for (const key of Object.keys(nextCtx)) if (key !== "context") ctx[key] = nextCtx[key];
133
+ for (const key of Object.keys(nextCtx)) if (key === "response") setResponse(nextCtx.response);
134
+ else if (key !== "context") ctx[key] = nextCtx[key];
105
135
  }
106
136
  index++;
107
137
  const middleware = middlewares[index];
@@ -114,19 +144,24 @@ function executeMiddleware(middlewares, ctx) {
114
144
  });
115
145
  } catch (err) {
116
146
  if (isSpecialResponse(err)) {
117
- ctx.response = err;
147
+ setResponse(err);
118
148
  return ctx;
119
149
  }
150
+ await disposeStreamResponse("middleware error");
120
151
  throw err;
121
152
  }
122
153
  const normalized = handleCtxResult(result);
123
154
  if (normalized) {
124
- if (normalized.response !== void 0) ctx.response = normalized.response;
155
+ if (normalized.response !== void 0) setResponse(normalized.response);
125
156
  if (normalized.context) ctx.context = safeObjectMerge(ctx.context, normalized.context);
126
157
  }
127
158
  return ctx;
128
159
  };
129
- return next();
160
+ await next();
161
+ return {
162
+ ctx,
163
+ response: await getFinalResponse()
164
+ };
130
165
  }
131
166
  /**
132
167
  * Wrap a route handler as middleware
@@ -183,7 +218,7 @@ function createStartHandler(cbOrOptions) {
183
218
  if (process.env.TSS_DEV_SERVER !== "true") finalManifestResolver.warmup({ getBaseManifest: () => getBaseManifest(void 0) });
184
219
  const startRequestResolver = async (request, requestOpts) => {
185
220
  let router = null;
186
- let cbWillCleanup = false;
221
+ let responseOwnsCleanup = false;
187
222
  try {
188
223
  const { url, handledProtocolRelativeURL } = getNormalizedURL(request.url);
189
224
  const href = url.pathname + url.search + url.hash;
@@ -240,16 +275,19 @@ function createStartHandler(cbOrOptions) {
240
275
  serverFnId
241
276
  }));
242
277
  };
243
- return handleRedirectResponse((await executeMiddleware([...flattenedRequestMiddlewares.map((d) => d.options.server), serverFnHandler], {
278
+ const { response: middlewareResponse } = await executeMiddleware([...flattenedRequestMiddlewares.map((d) => d.options.server), serverFnHandler], {
244
279
  request,
245
280
  pathname: url.pathname,
246
281
  handlerType: "serverFn",
247
282
  context: createNullProtoObject(requestOpts?.context)
248
- })).response, request, getRouter);
283
+ });
284
+ const result = await handleRedirectResponse(middlewareResponse, request, getRouter);
285
+ responseOwnsCleanup = result.serverSsrCleanup === "stream";
286
+ return result.response;
249
287
  }
250
288
  const executeRouter = async (serverContext, matchedRoutes) => {
251
289
  const acceptParts = (request.headers.get("Accept") || "*/*").split(",");
252
- if (!["*/*", "text/html"].some((mimeType) => acceptParts.some((part) => part.trim().startsWith(mimeType)))) return Response.json({ error: "Only HTML requests are supported here" }, { status: 500 });
290
+ if (!["*/*", "text/html"].some((mimeType) => acceptParts.some((part) => part.trim().startsWith(mimeType)))) return normalizeSsrResponse(Response.json({ error: "Only HTML requests are supported here" }, { status: 500 }));
253
291
  const manifest = await resolveManifestForRequest({
254
292
  request,
255
293
  requestInlineCss: requestOpts?.inlineCss,
@@ -271,18 +309,17 @@ function createStartHandler(cbOrOptions) {
271
309
  });
272
310
  routerInstance.update({ additionalContext: { serverContext } });
273
311
  await routerInstance.load();
274
- if (routerInstance.state.redirect) return routerInstance.state.redirect;
312
+ if (routerInstance.state.redirect) return normalizeSsrResponse(routerInstance.state.redirect);
275
313
  earlyHints?.collectDynamic(routerInstance.stores.matches.get());
276
314
  const ctx = getStartContext({ throwIfNotFound: false });
277
315
  await routerInstance.serverSsr.dehydrate({ requestAssets: ctx?.requestAssets });
278
316
  const responseHeaders = getStartResponseHeaders({ router: routerInstance });
279
317
  earlyHints?.appendResponseHeaders(responseHeaders);
280
- cbWillCleanup = true;
281
- return cb({
318
+ return normalizeSsrResponse(await cb({
282
319
  request,
283
320
  router: routerInstance,
284
321
  responseHeaders
285
- });
322
+ }));
286
323
  };
287
324
  const requestHandlerMiddleware = async ({ context }) => {
288
325
  return runWithStartContext({
@@ -308,41 +345,45 @@ function createStartHandler(cbOrOptions) {
308
345
  }
309
346
  });
310
347
  };
311
- return handleRedirectResponse((await executeMiddleware([...flattenedRequestMiddlewares.map((d) => d.options.server), requestHandlerMiddleware], {
348
+ const { response: middlewareResponse } = await executeMiddleware([...flattenedRequestMiddlewares.map((d) => d.options.server), requestHandlerMiddleware], {
312
349
  request,
313
350
  pathname: url.pathname,
314
351
  handlerType: "router",
315
352
  context: createNullProtoObject(requestOpts?.context)
316
- })).response, request, getRouter);
353
+ });
354
+ const response = await handleRedirectResponse(middlewareResponse, request, getRouter);
355
+ responseOwnsCleanup = response.serverSsrCleanup === "stream";
356
+ return response.response;
317
357
  } finally {
318
- if (router && !cbWillCleanup) router.serverSsr?.cleanup();
358
+ if (router?.serverSsr && !responseOwnsCleanup) router.serverSsr.cleanup();
319
359
  router = null;
320
360
  }
321
361
  };
322
362
  return requestHandler(startRequestResolver);
323
363
  }
324
364
  async function handleRedirectResponse(response, request, getRouter) {
325
- if (!isRedirect(response)) return response;
326
- if (isResolvedRedirect(response)) {
327
- if (request.headers.get("x-tsr-serverFn") === "true") return Response.json({
328
- ...response.options,
365
+ const ssrResponse = normalizeSsrResponse(response);
366
+ if (!isRedirect(ssrResponse.response)) return ssrResponse;
367
+ if (isResolvedRedirect(ssrResponse.response)) {
368
+ if (request.headers.get("x-tsr-serverFn") === "true") return replaceSsrResponse(ssrResponse, Response.json({
369
+ ...ssrResponse.response.options,
329
370
  isSerializedRedirect: true
330
- }, { headers: response.headers });
331
- return response;
371
+ }, { headers: ssrResponse.response.headers }), "redirect response replaced");
372
+ return ssrResponse;
332
373
  }
333
- const opts = response.options;
374
+ const opts = ssrResponse.response.options;
334
375
  if (opts.to && typeof opts.to === "string" && !opts.to.startsWith("/")) throw new Error(`Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's "to" property accepts an internal path only. Use the "href" property to provide an external URL. Received: ${JSON.stringify(opts)}`);
335
376
  if ([
336
377
  "params",
337
378
  "search",
338
379
  "hash"
339
380
  ].some((d) => typeof opts[d] === "function")) throw new Error(`Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(opts).filter((d) => typeof opts[d] === "function").map((d) => `"${d}"`).join(", ")}`);
340
- const redirect = (await getRouter()).resolveRedirect(response);
341
- if (request.headers.get("x-tsr-serverFn") === "true") return Response.json({
342
- ...response.options,
381
+ const redirect = (await getRouter()).resolveRedirect(ssrResponse.response);
382
+ if (request.headers.get("x-tsr-serverFn") === "true") return replaceSsrResponse(ssrResponse, Response.json({
383
+ ...ssrResponse.response.options,
343
384
  isSerializedRedirect: true
344
- }, { headers: response.headers });
345
- return redirect;
385
+ }, { headers: ssrResponse.response.headers }), "redirect response replaced");
386
+ return replaceSsrResponse(ssrResponse, redirect, "redirect response replaced");
346
387
  }
347
388
  async function handleServerRoutes({ getRouter, request, url, executeRouter, context, executedRequestMiddlewares }) {
348
389
  const router = await getRouter();
@@ -376,8 +417,8 @@ async function handleServerRoutes({ getRouter, request, url, executeRouter, cont
376
417
  }
377
418
  }
378
419
  }
379
- routeMiddlewares.push((ctx) => executeRouter(ctx.context, matchedRoutes));
380
- const ctx = await executeMiddleware(routeMiddlewares, {
420
+ routeMiddlewares.push(((ctx) => executeRouter(ctx.context, matchedRoutes)));
421
+ const { ctx, response } = await executeMiddleware(routeMiddlewares, {
381
422
  request,
382
423
  context,
383
424
  params: routeParams,
@@ -386,10 +427,9 @@ async function handleServerRoutes({ getRouter, request, url, executeRouter, cont
386
427
  });
387
428
  if (isHeadFallback) {
388
429
  if (!ctx.response) throwRouteHandlerError();
389
- const resolved = await handleRedirectResponse(ctx.response, request, getRouter);
390
- return new Response(null, resolved);
430
+ return stripSsrResponseBody(await handleRedirectResponse(response, request, getRouter), "HEAD body stripped");
391
431
  }
392
- return ctx.response;
432
+ return normalizeSsrResponse(response);
393
433
  }
394
434
  //#endregion
395
435
  export { createStartHandler };
@@ -1 +1 @@
1
- {"version":3,"file":"createStartHandler.js","names":[],"sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n createCsrfMiddleware,\n createNullProtoObject,\n csrfSymbol,\n flattenMiddlewares,\n mergeHeaders,\n safeObjectMerge,\n} from '@tanstack/start-client-core'\nimport {\n executeRewriteInput,\n isRedirect,\n isResolvedRedirect,\n} from '@tanstack/router-core'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from '@tanstack/router-core/ssr/server'\nimport {\n getStartContext,\n runWithStartContext,\n} from '@tanstack/start-storage-context'\nimport { requestHandler } from './request-response'\nimport { getStartManifest } from './router-manifest'\nimport { handleServerAction } from './server-functions-handler'\nimport { createEarlyHintsCollector } from './early-hints'\nimport {\n createCachedBaseManifestLoader,\n createFinalManifestResolver,\n} from './finalManifest'\n\nimport { HEADERS } from './constants'\nimport { ServerFunctionSerializationAdapter } from './serializer/ServerFunctionSerializationAdapter'\nimport type {\n AnyFunctionMiddleware,\n AnyRequestMiddleware,\n AnyStartInstanceOptions,\n RouteMethod,\n RouteMethodHandlerFn,\n RouterEntry,\n StartEntry,\n} from '@tanstack/start-client-core'\nimport type { RequestHandler } from './request-handler'\nimport type {\n AnyRoute,\n AnyRouter,\n AnySerializationAdapter,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\nimport type { FinalManifestOptions } from './finalManifest'\n\ntype TODO = any\n\ntype AnyMiddlewareServerFn =\n | AnyRequestMiddleware['options']['server']\n | AnyFunctionMiddleware['options']['server']\n\nexport interface CreateStartHandlerOptions extends FinalManifestOptions {\n handler: HandlerCallback<AnyRouter>\n}\n\nfunction getStartResponseHeaders(opts: { router: AnyRouter }) {\n const headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=utf-8',\n },\n ...opts.router.stores.matches.get().map((match) => {\n return match.headers\n }),\n )\n return headers\n}\n\ninterface PluginAdaptersEntry {\n hasPluginAdapters: boolean\n pluginSerializationAdapters: Array<AnySerializationAdapter>\n}\n\ninterface Entries {\n startEntry: StartEntry\n routerEntry: RouterEntry\n pluginAdapters: PluginAdaptersEntry\n}\n\n// Cached entries - promises stored immediately to prevent concurrent imports\n// that can cause race conditions during module initialization\nlet entriesPromise: Promise<Entries> | undefined\nlet hasWarnedMissingCsrfMiddleware = false\nconst defaultCsrfMiddleware = createCsrfMiddleware({\n filter: (ctx) => ctx.handlerType === 'serverFn',\n})\nconst getCachedBaseManifest = createCachedBaseManifestLoader(() =>\n getStartManifest(),\n)\nconst getProdBaseManifest: typeof getStartManifest = () =>\n getCachedBaseManifest()\nconst getBaseManifest =\n process.env.TSS_DEV_SERVER === 'true' ? getStartManifest : getProdBaseManifest\nconst createEarlyHintsForRequest: typeof createEarlyHintsCollector =\n process.env.TSS_DEV_SERVER === 'true'\n ? () => undefined\n : createEarlyHintsCollector\n\nasync function loadEntries(): Promise<Entries> {\n const [routerEntry, startEntry, pluginAdapters] = await Promise.all([\n // @ts-ignore When building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n import('#tanstack-router-entry'),\n // @ts-ignore When building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n import('#tanstack-start-entry'),\n // @ts-ignore When building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n import('#tanstack-start-plugin-adapters'),\n ])\n return {\n routerEntry: routerEntry as unknown as RouterEntry,\n startEntry: startEntry as unknown as StartEntry,\n pluginAdapters: pluginAdapters as unknown as PluginAdaptersEntry,\n }\n}\n\nfunction getEntries() {\n if (!entriesPromise) {\n entriesPromise = loadEntries()\n }\n return entriesPromise\n}\n\nfunction hasCsrfMiddleware(\n middlewares: Array<AnyRequestMiddleware | AnyFunctionMiddleware>,\n): boolean {\n return middlewares.some((middleware) => csrfSymbol in middleware)\n}\n\nfunction warnMissingCsrfMiddlewareOnce() {\n if (hasWarnedMissingCsrfMiddleware) return\n hasWarnedMissingCsrfMiddleware = true\n\n console.warn(`TanStack Start server functions are not protected by the CSRF middleware.\n\nServer functions are same-origin RPC endpoints and should be protected from cross-site requests.\n\nAdd the CSRF middleware in src/start.ts:\n\n const csrfMiddleware = createCsrfMiddleware({\n filter: (ctx) => ctx.handlerType === 'serverFn',\n })\n\n export const startInstance = createStart(() => ({\n requestMiddleware: [csrfMiddleware],\n }))\n\nIf you intentionally handle CSRF another way, disable this warning:\n\n tanstackStart({\n serverFns: {\n disableCsrfMiddlewareWarning: true,\n },\n })`)\n}\n\n// Pre-computed constants\nconst ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || '/'\nconst SERVER_FN_BASE = process.env.TSS_SERVER_FN_BASE\nconst IS_PRERENDERING = process.env.TSS_PRERENDERING === 'true'\nconst IS_SHELL_ENV = process.env.TSS_SHELL === 'true'\nconst IS_DEV = process.env.NODE_ENV === 'development'\n\n// Reusable error messages\nconst ERR_NO_RESPONSE = IS_DEV\n ? `It looks like you forgot to return a response from your server route handler. If you want to defer to the app router, make sure to have a component set in this route.`\n : 'Internal Server Error'\n\nconst ERR_NO_DEFER = IS_DEV\n ? `You cannot defer to the app router if there is no component defined on this route.`\n : 'Internal Server Error'\n\nfunction throwRouteHandlerError(): never {\n throw new Error(ERR_NO_RESPONSE)\n}\n\nfunction throwIfMayNotDefer(): never {\n throw new Error(ERR_NO_DEFER)\n}\n\n/**\n * Check if a value is a special response (Response or Redirect)\n */\nfunction isSpecialResponse(value: unknown): value is Response {\n return value instanceof Response || isRedirect(value)\n}\n\n/**\n * Normalize middleware result to context shape\n */\nfunction handleCtxResult(result: TODO) {\n if (isSpecialResponse(result)) {\n return { response: result }\n }\n return result\n}\n\n/**\n * Execute a middleware chain\n */\nfunction executeMiddleware(middlewares: Array<TODO>, ctx: TODO): Promise<TODO> {\n let index = -1\n\n const next = async (nextCtx?: TODO): Promise<TODO> => {\n // Merge context if provided using safeObjectMerge for prototype pollution prevention\n if (nextCtx) {\n if (nextCtx.context) {\n ctx.context = safeObjectMerge(ctx.context, nextCtx.context)\n }\n // Copy own properties except context (Object.keys returns only own enumerable properties)\n for (const key of Object.keys(nextCtx)) {\n if (key !== 'context') {\n ctx[key] = nextCtx[key]\n }\n }\n }\n\n index++\n const middleware = middlewares[index]\n if (!middleware) return ctx\n\n let result: TODO\n try {\n result = await middleware({ ...ctx, next })\n } catch (err) {\n if (isSpecialResponse(err)) {\n ctx.response = err\n return ctx\n }\n throw err\n }\n\n const normalized = handleCtxResult(result)\n if (normalized) {\n if (normalized.response !== undefined) {\n ctx.response = normalized.response\n }\n if (normalized.context) {\n ctx.context = safeObjectMerge(ctx.context, normalized.context)\n }\n }\n\n return ctx\n }\n\n return next()\n}\n\n/**\n * Wrap a route handler as middleware\n */\nfunction handlerToMiddleware(\n handler: RouteMethodHandlerFn<any, AnyRoute, any, any, any, any, any>,\n mayDefer: boolean = false,\n): TODO {\n if (mayDefer) {\n return handler\n }\n return async (ctx: TODO) => {\n const response = await handler({ ...ctx, next: throwIfMayNotDefer })\n if (!response) {\n throwRouteHandlerError()\n }\n return response\n }\n}\n\n/**\n * Creates the TanStack Start request handler.\n *\n * @example Backwards-compatible usage (handler callback only):\n * ```ts\n * export default createStartHandler(defaultStreamHandler)\n * ```\n *\n * @example With CDN URL rewriting:\n * ```ts\n * export default createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: 'https://cdn.example.com',\n * })\n * ```\n *\n * @example With per-request URL rewriting:\n * ```ts\n * export default createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: {\n * transform: ({ url }) => {\n * const cdnBase = getRequest().headers.get('x-cdn-base') || ''\n * return { href: `${cdnBase}${url}` }\n * },\n * cache: false,\n * },\n * })\n * ```\n */\nexport function createStartHandler<TRegister = Register>(\n cbOrOptions: HandlerCallback<AnyRouter> | CreateStartHandlerOptions,\n): RequestHandler<TRegister> {\n const handlerOptions: FinalManifestOptions =\n typeof cbOrOptions === 'function' ? {} : cbOrOptions\n const cb: HandlerCallback<AnyRouter> =\n typeof cbOrOptions === 'function' ? cbOrOptions : cbOrOptions.handler\n const finalManifestResolver = createFinalManifestResolver({\n ...handlerOptions,\n cacheCreateTransform: process.env.TSS_DEV_SERVER !== 'true',\n })\n const resolveManifestForRequest =\n process.env.TSS_DEV_SERVER === 'true'\n ? finalManifestResolver.resolveUncached\n : finalManifestResolver.resolveCached\n\n if (process.env.TSS_DEV_SERVER !== 'true') {\n finalManifestResolver.warmup({\n getBaseManifest: () => getBaseManifest(undefined),\n })\n }\n\n const startRequestResolver: RequestHandler<Register> = async (\n request,\n requestOpts,\n ) => {\n let router: AnyRouter | null = null as AnyRouter | null\n let cbWillCleanup = false as boolean\n\n try {\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n // during normalization paths like '//posts' are flattened to '/posts'.\n // in these cases we would prefer to redirect to the new path\n const { url, handledProtocolRelativeURL } = getNormalizedURL(request.url)\n const href = url.pathname + url.search + url.hash\n const origin = getOrigin(request)\n\n if (handledProtocolRelativeURL) {\n return Response.redirect(url, 308)\n }\n\n const entries = await getEntries()\n const hasStartInstance = !!entries.startEntry.startInstance\n const startOptions: AnyStartInstanceOptions =\n (await entries.startEntry.startInstance?.getOptions()) ||\n ({} as AnyStartInstanceOptions)\n\n const { hasPluginAdapters, pluginSerializationAdapters } =\n entries.pluginAdapters\n\n const serializationAdapters = [\n ...(startOptions.serializationAdapters || []),\n ...(hasPluginAdapters ? pluginSerializationAdapters : []),\n ServerFunctionSerializationAdapter,\n ]\n\n const requestStartOptions = {\n ...startOptions,\n requestMiddleware: hasStartInstance\n ? startOptions.requestMiddleware\n : [defaultCsrfMiddleware],\n serializationAdapters,\n }\n\n // Flatten request middlewares once\n const flattenedRequestMiddlewares = requestStartOptions.requestMiddleware\n ? flattenMiddlewares(requestStartOptions.requestMiddleware)\n : []\n\n // Create set for deduplication\n const executedRequestMiddlewares = new Set<TODO>(\n flattenedRequestMiddlewares,\n )\n\n // Memoized router getter\n const getRouter = async (): Promise<AnyRouter> => {\n if (router) return router\n\n router = await entries.routerEntry.getRouter()\n\n let isShell = IS_SHELL_ENV\n if (IS_PRERENDERING && !isShell) {\n isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'\n }\n\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n router.update({\n history,\n isShell,\n isPrerendering: IS_PRERENDERING,\n origin: router.options.origin ?? origin,\n ...{\n defaultSsr: requestStartOptions.defaultSsr,\n serializationAdapters: [\n ...requestStartOptions.serializationAdapters,\n ...(router.options.serializationAdapters || []),\n ],\n },\n basepath: ROUTER_BASEPATH,\n })\n\n return router\n }\n\n // Check for server function requests first (early exit)\n if (SERVER_FN_BASE && url.pathname.startsWith(SERVER_FN_BASE)) {\n if (\n process.env.NODE_ENV !== 'production' &&\n process.env.TSS_DISABLE_CSRF_MIDDLEWARE_WARNING !== 'true' &&\n !hasCsrfMiddleware(flattenedRequestMiddlewares)\n ) {\n warnMissingCsrfMiddlewareOnce()\n }\n\n const serverFnId = url.pathname\n .slice(SERVER_FN_BASE.length)\n .split('/')[0]\n\n if (!serverFnId) {\n throw new Error('Invalid server action param for serverFnId')\n }\n\n const serverFnHandler = async ({ context }: TODO) => {\n return runWithStartContext(\n {\n getRouter,\n startOptions: requestStartOptions,\n contextAfterGlobalMiddlewares: context,\n request,\n executedRequestMiddlewares,\n handlerType: 'serverFn',\n },\n () =>\n handleServerAction({\n request,\n context: requestOpts?.context,\n serverFnId,\n }),\n )\n }\n\n const middlewares = flattenedRequestMiddlewares.map(\n (d) => d.options.server,\n )\n const ctx = await executeMiddleware([...middlewares, serverFnHandler], {\n request,\n pathname: url.pathname,\n handlerType: 'serverFn',\n context: createNullProtoObject(requestOpts?.context),\n })\n\n return handleRedirectResponse(ctx.response, request, getRouter)\n }\n\n // Router execution function\n const executeRouter = async (\n serverContext: TODO,\n matchedRoutes?: ReadonlyArray<AnyRoute>,\n ): Promise<Response> => {\n const acceptHeader = request.headers.get('Accept') || '*/*'\n const acceptParts = acceptHeader.split(',')\n const supportedMimeTypes = ['*/*', 'text/html']\n\n const isSupported = supportedMimeTypes.some((mimeType) =>\n acceptParts.some((part) => part.trim().startsWith(mimeType)),\n )\n\n if (!isSupported) {\n return Response.json(\n { error: 'Only HTML requests are supported here' },\n { status: 500 },\n )\n }\n\n const manifest = await resolveManifestForRequest({\n request,\n requestInlineCss: requestOpts?.inlineCss,\n getBaseManifest: () => getBaseManifest(matchedRoutes),\n })\n\n const earlyHints = createEarlyHintsForRequest({\n onEarlyHints: requestOpts?.onEarlyHints,\n responseLinkHeader: requestOpts?.responseLinkHeader,\n })\n\n earlyHints?.collectStatic({ manifest, matchedRoutes })\n\n const routerInstance = await getRouter()\n\n attachRouterServerSsrUtils({\n router: routerInstance,\n manifest,\n getRequestAssets: () =>\n getStartContext({ throwIfNotFound: false })?.requestAssets,\n })\n\n routerInstance.update({ additionalContext: { serverContext } })\n await routerInstance.load()\n\n if (routerInstance.state.redirect) {\n return routerInstance.state.redirect\n }\n\n earlyHints?.collectDynamic(routerInstance.stores.matches.get())\n\n // Pass request-scoped assets to dehydrate for manifest injection\n const ctx = getStartContext({ throwIfNotFound: false })\n await routerInstance.serverSsr!.dehydrate({\n requestAssets: ctx?.requestAssets,\n })\n\n const responseHeaders = getStartResponseHeaders({\n router: routerInstance,\n })\n earlyHints?.appendResponseHeaders(responseHeaders)\n cbWillCleanup = true\n\n return cb({\n request,\n router: routerInstance,\n responseHeaders,\n })\n }\n\n // Main request handler\n const requestHandlerMiddleware = async ({ context }: TODO) => {\n return runWithStartContext(\n {\n getRouter,\n startOptions: requestStartOptions,\n contextAfterGlobalMiddlewares: context,\n request,\n executedRequestMiddlewares,\n handlerType: 'router',\n },\n async () => {\n try {\n return await handleServerRoutes({\n getRouter,\n request,\n url,\n executeRouter,\n context,\n executedRequestMiddlewares,\n })\n } catch (err) {\n if (err instanceof Response) {\n return err\n }\n throw err\n }\n },\n )\n }\n\n const middlewares = flattenedRequestMiddlewares.map(\n (d) => d.options.server,\n )\n const ctx = await executeMiddleware(\n [...middlewares, requestHandlerMiddleware],\n {\n request,\n pathname: url.pathname,\n handlerType: 'router',\n context: createNullProtoObject(requestOpts?.context),\n },\n )\n\n return handleRedirectResponse(ctx.response, request, getRouter)\n } finally {\n if (router && !cbWillCleanup) {\n // Clean up router SSR state if it was set up but won't be cleaned up by the callback\n // (e.g., in redirect cases or early returns before the callback is invoked).\n // When the callback runs, it handles cleanup (either via transformStreamWithRouter\n // for streaming, or directly in renderRouterToString for non-streaming).\n router.serverSsr?.cleanup()\n }\n router = null\n }\n }\n\n return requestHandler(startRequestResolver)\n}\n\nasync function handleRedirectResponse(\n response: Response,\n request: Request,\n getRouter: () => Promise<AnyRouter>,\n): Promise<Response> {\n if (!isRedirect(response)) {\n return response\n }\n\n if (isResolvedRedirect(response)) {\n if (request.headers.get('x-tsr-serverFn') === 'true') {\n return Response.json(\n { ...response.options, isSerializedRedirect: true },\n { headers: response.headers },\n )\n }\n return response\n }\n\n const opts = response.options\n if (opts.to && typeof opts.to === 'string' && !opts.to.startsWith('/')) {\n throw new Error(\n `Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's \"to\" property accepts an internal path only. Use the \"href\" property to provide an external URL. Received: ${JSON.stringify(opts)}`,\n )\n }\n\n if (\n ['params', 'search', 'hash'].some(\n (d) => typeof (opts as TODO)[d] === 'function',\n )\n ) {\n throw new Error(\n `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(\n opts,\n )\n .filter((d) => typeof (opts as TODO)[d] === 'function')\n .map((d) => `\"${d}\"`)\n .join(', ')}`,\n )\n }\n\n const router = await getRouter()\n const redirect = router.resolveRedirect(response)\n\n if (request.headers.get('x-tsr-serverFn') === 'true') {\n return Response.json(\n { ...response.options, isSerializedRedirect: true },\n { headers: response.headers },\n )\n }\n\n return redirect\n}\n\nasync function handleServerRoutes({\n getRouter,\n request,\n url,\n executeRouter,\n context,\n executedRequestMiddlewares,\n}: {\n getRouter: () => Promise<AnyRouter>\n request: Request\n url: URL\n executeRouter: (\n serverContext: any,\n matchedRoutes?: ReadonlyArray<AnyRoute>,\n ) => Promise<Response>\n context: any\n executedRequestMiddlewares: Set<AnyRequestMiddleware>\n}): Promise<Response> {\n const router = await getRouter()\n const rewrittenUrl = executeRewriteInput(router.rewrite, url)\n const pathname = rewrittenUrl.pathname\n // this will perform a fuzzy match, however for server routes we need an exact match\n // if the route is not an exact match, executeRouter will handle rendering the app router\n // the match will be cached internally, so no extra work is done during the app router render\n const { matchedRoutes, foundRoute, routeParams } =\n router.getMatchedRoutes(pathname)\n\n const isExactMatch = foundRoute && routeParams['**'] === undefined\n\n // Collect and dedupe route middlewares\n const routeMiddlewares: Array<AnyMiddlewareServerFn> = []\n\n // Collect middleware from matched routes, filtering out those already executed\n // in the request phase\n for (const route of matchedRoutes) {\n const serverMiddleware = route.options.server?.middleware as\n | Array<AnyRequestMiddleware>\n | undefined\n if (serverMiddleware) {\n const flattened = flattenMiddlewares(serverMiddleware)\n for (const m of flattened) {\n if (!executedRequestMiddlewares.has(m)) {\n routeMiddlewares.push(m.options.server)\n }\n }\n }\n }\n\n // Add handler middleware if exact match\n const server = foundRoute?.options.server\n let isHeadFallback = false\n if (server?.handlers && isExactMatch) {\n const handlers =\n typeof server.handlers === 'function'\n ? server.handlers({ createHandlers: (d: any) => d })\n : server.handlers\n\n const requestMethod = request.method.toUpperCase() as RouteMethod\n // Per RFC 9110 §9.3.2, HEAD must return the same header fields as GET.\n // Priority for HEAD: explicit HEAD handler → GET → ANY (last resort).\n const handler =\n requestMethod === 'HEAD'\n ? (handlers['HEAD'] ?? handlers['GET'] ?? handlers['ANY'])\n : (handlers[requestMethod] ?? handlers['ANY'])\n isHeadFallback =\n requestMethod === 'HEAD' && handler !== undefined && !handlers['HEAD']\n\n if (handler) {\n const mayDefer = !!foundRoute.options.component\n\n if (typeof handler === 'function') {\n routeMiddlewares.push(handlerToMiddleware(handler, mayDefer))\n } else {\n if (handler.middleware?.length) {\n const handlerMiddlewares = flattenMiddlewares(handler.middleware)\n for (const m of handlerMiddlewares) {\n routeMiddlewares.push(m.options.server)\n }\n }\n if (handler.handler) {\n routeMiddlewares.push(handlerToMiddleware(handler.handler, mayDefer))\n }\n }\n }\n }\n\n // Final middleware: execute router with matched routes for dev styles\n routeMiddlewares.push((ctx: TODO) =>\n executeRouter(ctx.context, matchedRoutes),\n )\n\n const ctx = await executeMiddleware(routeMiddlewares, {\n request,\n context,\n params: routeParams,\n pathname,\n handlerType: 'router',\n })\n\n // RFC 9110 §9.3.2: HEAD must carry the same header fields as GET but no body.\n // Resolve any redirect before stripping so the Location header survives.\n if (isHeadFallback) {\n if (!ctx.response) {\n throwRouteHandlerError()\n }\n\n const resolved = await handleRedirectResponse(\n ctx.response,\n request,\n getRouter,\n )\n return new Response(null, resolved)\n }\n\n return ctx.response\n}\n"],"mappings":";;;;;;;;;;;;;AA+DA,SAAS,wBAAwB,MAA6B;CAS5D,OARgB,aACd,EACE,gBAAgB,2BAClB,GACA,GAAG,KAAK,OAAO,OAAO,QAAQ,IAAI,EAAE,KAAK,UAAU;EACjD,OAAO,MAAM;CACf,CAAC,CAEI;AACT;AAeA,IAAI;AACJ,IAAI,iCAAiC;AACrC,IAAM,wBAAwB,qBAAqB,EACjD,SAAS,QAAQ,IAAI,gBAAgB,WACvC,CAAC;AACD,IAAM,wBAAwB,qCAC5B,iBAAiB,CACnB;AACA,IAAM,4BACJ,sBAAsB;AACxB,IAAM,kBACJ,QAAQ,IAAI,mBAAmB,SAAS,mBAAmB;AAC7D,IAAM,6BACJ,QAAQ,IAAI,mBAAmB,eACrB,KAAA,IACN;AAEN,eAAe,cAAgC;CAC7C,MAAM,CAAC,aAAa,YAAY,kBAAkB,MAAM,QAAQ,IAAI;EAElE,OAAO;EAEP,OAAO;EAEP,OAAO;CACT,CAAC;CACD,OAAO;EACQ;EACD;EACI;CAClB;AACF;AAEA,SAAS,aAAa;CACpB,IAAI,CAAC,gBACH,iBAAiB,YAAY;CAE/B,OAAO;AACT;AAEA,SAAS,kBACP,aACS;CACT,OAAO,YAAY,MAAM,eAAe,cAAc,UAAU;AAClE;AAEA,SAAS,gCAAgC;CACvC,IAAI,gCAAgC;CACpC,iCAAiC;CAEjC,QAAQ,KAAK;;;;;;;;;;;;;;;;;;;;KAoBV;AACL;AAGA,IAAM,kBAAkB,QAAQ,IAAI,uBAAuB;AAC3D,IAAM,iBAAiB,QAAQ,IAAI;AACnC,IAAM,kBAAkB,QAAQ,IAAI,qBAAqB;AACzD,IAAM,eAAe,QAAQ,IAAI,cAAc;AAC/C,IAAM,SAAA,QAAA,IAAA,aAAkC;AAGxC,IAAM,kBAAkB,SACpB,2KACA;AAEJ,IAAM,eAAe,SACjB,uFACA;AAEJ,SAAS,yBAAgC;CACvC,MAAM,IAAI,MAAM,eAAe;AACjC;AAEA,SAAS,qBAA4B;CACnC,MAAM,IAAI,MAAM,YAAY;AAC9B;;;;AAKA,SAAS,kBAAkB,OAAmC;CAC5D,OAAO,iBAAiB,YAAY,WAAW,KAAK;AACtD;;;;AAKA,SAAS,gBAAgB,QAAc;CACrC,IAAI,kBAAkB,MAAM,GAC1B,OAAO,EAAE,UAAU,OAAO;CAE5B,OAAO;AACT;;;;AAKA,SAAS,kBAAkB,aAA0B,KAA0B;CAC7E,IAAI,QAAQ;CAEZ,MAAM,OAAO,OAAO,YAAkC;EAEpD,IAAI,SAAS;GACX,IAAI,QAAQ,SACV,IAAI,UAAU,gBAAgB,IAAI,SAAS,QAAQ,OAAO;GAG5D,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,QAAQ,WACV,IAAI,OAAO,QAAQ;EAGzB;EAEA;EACA,MAAM,aAAa,YAAY;EAC/B,IAAI,CAAC,YAAY,OAAO;EAExB,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,WAAW;IAAE,GAAG;IAAK;GAAK,CAAC;EAC5C,SAAS,KAAK;GACZ,IAAI,kBAAkB,GAAG,GAAG;IAC1B,IAAI,WAAW;IACf,OAAO;GACT;GACA,MAAM;EACR;EAEA,MAAM,aAAa,gBAAgB,MAAM;EACzC,IAAI,YAAY;GACd,IAAI,WAAW,aAAa,KAAA,GAC1B,IAAI,WAAW,WAAW;GAE5B,IAAI,WAAW,SACb,IAAI,UAAU,gBAAgB,IAAI,SAAS,WAAW,OAAO;EAEjE;EAEA,OAAO;CACT;CAEA,OAAO,KAAK;AACd;;;;AAKA,SAAS,oBACP,SACA,WAAoB,OACd;CACN,IAAI,UACF,OAAO;CAET,OAAO,OAAO,QAAc;EAC1B,MAAM,WAAW,MAAM,QAAQ;GAAE,GAAG;GAAK,MAAM;EAAmB,CAAC;EACnE,IAAI,CAAC,UACH,uBAAuB;EAEzB,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,mBACd,aAC2B;CAC3B,MAAM,iBACJ,OAAO,gBAAgB,aAAa,CAAC,IAAI;CAC3C,MAAM,KACJ,OAAO,gBAAgB,aAAa,cAAc,YAAY;CAChE,MAAM,wBAAwB,4BAA4B;EACxD,GAAG;EACH,sBAAsB,QAAQ,IAAI,mBAAmB;CACvD,CAAC;CACD,MAAM,4BACJ,QAAQ,IAAI,mBAAmB,SAC3B,sBAAsB,kBACtB,sBAAsB;CAE5B,IAAI,QAAQ,IAAI,mBAAmB,QACjC,sBAAsB,OAAO,EAC3B,uBAAuB,gBAAgB,KAAA,CAAS,EAClD,CAAC;CAGH,MAAM,uBAAiD,OACrD,SACA,gBACG;EACH,IAAI,SAA2B;EAC/B,IAAI,gBAAgB;EAEpB,IAAI;GAIF,MAAM,EAAE,KAAK,+BAA+B,iBAAiB,QAAQ,GAAG;GACxE,MAAM,OAAO,IAAI,WAAW,IAAI,SAAS,IAAI;GAC7C,MAAM,SAAS,UAAU,OAAO;GAEhC,IAAI,4BACF,OAAO,SAAS,SAAS,KAAK,GAAG;GAGnC,MAAM,UAAU,MAAM,WAAW;GACjC,MAAM,mBAAmB,CAAC,CAAC,QAAQ,WAAW;GAC9C,MAAM,eACH,MAAM,QAAQ,WAAW,eAAe,WAAW,KACnD,CAAC;GAEJ,MAAM,EAAE,mBAAmB,gCACzB,QAAQ;GAEV,MAAM,wBAAwB;IAC5B,GAAI,aAAa,yBAAyB,CAAC;IAC3C,GAAI,oBAAoB,8BAA8B,CAAC;IACvD;GACF;GAEA,MAAM,sBAAsB;IAC1B,GAAG;IACH,mBAAmB,mBACf,aAAa,oBACb,CAAC,qBAAqB;IAC1B;GACF;GAGA,MAAM,8BAA8B,oBAAoB,oBACpD,mBAAmB,oBAAoB,iBAAiB,IACxD,CAAC;GAGL,MAAM,6BAA6B,IAAI,IACrC,2BACF;GAGA,MAAM,YAAY,YAAgC;IAChD,IAAI,QAAQ,OAAO;IAEnB,SAAS,MAAM,QAAQ,YAAY,UAAU;IAE7C,IAAI,UAAU;IACd,IAAI,mBAAmB,CAAC,SACtB,UAAU,QAAQ,QAAQ,IAAI,QAAQ,SAAS,MAAM;IAGvD,MAAM,UAAU,oBAAoB,EAClC,gBAAgB,CAAC,IAAI,EACvB,CAAC;IAED,OAAO,OAAO;KACZ;KACA;KACA,gBAAgB;KAChB,QAAQ,OAAO,QAAQ,UAAU;KAE/B,YAAY,oBAAoB;KAChC,uBAAuB,CACrB,GAAG,oBAAoB,uBACvB,GAAI,OAAO,QAAQ,yBAAyB,CAAC,CAC/C;KAEF,UAAU;IACZ,CAAC;IAED,OAAO;GACT;GAGA,IAAI,kBAAkB,IAAI,SAAS,WAAW,cAAc,GAAG;IAC7D,IAAA,QAAA,IAAA,aAC2B,gBACzB,QAAQ,IAAI,wCAAwC,UACpD,CAAC,kBAAkB,2BAA2B,GAE9C,8BAA8B;IAGhC,MAAM,aAAa,IAAI,SACpB,MAAM,eAAe,MAAM,EAC3B,MAAM,GAAG,EAAE;IAEd,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,4CAA4C;IAG9D,MAAM,kBAAkB,OAAO,EAAE,cAAoB;KACnD,OAAO,oBACL;MACE;MACA,cAAc;MACd,+BAA+B;MAC/B;MACA;MACA,aAAa;KACf,SAEE,mBAAmB;MACjB;MACA,SAAS,aAAa;MACtB;KACF,CAAC,CACL;IACF;IAYA,OAAO,wBAAuB,MAPZ,kBAAkB,CAAC,GAHjB,4BAA4B,KAC7C,MAAM,EAAE,QAAQ,MAEqB,GAAa,eAAe,GAAG;KACrE;KACA,UAAU,IAAI;KACd,aAAa;KACb,SAAS,sBAAsB,aAAa,OAAO;IACrD,CAAC,GAEiC,UAAU,SAAS,SAAS;GAChE;GAGA,MAAM,gBAAgB,OACpB,eACA,kBACsB;IAEtB,MAAM,eADe,QAAQ,QAAQ,IAAI,QAAQ,KAAK,OACrB,MAAM,GAAG;IAO1C,IAAI,CAJgB,CAFQ,OAAO,WAEf,EAAmB,MAAM,aAC3C,YAAY,MAAM,SAAS,KAAK,KAAK,EAAE,WAAW,QAAQ,CAAC,CAGxD,GACH,OAAO,SAAS,KACd,EAAE,OAAO,wCAAwC,GACjD,EAAE,QAAQ,IAAI,CAChB;IAGF,MAAM,WAAW,MAAM,0BAA0B;KAC/C;KACA,kBAAkB,aAAa;KAC/B,uBAAuB,gBAAgB,aAAa;IACtD,CAAC;IAED,MAAM,aAAa,2BAA2B;KAC5C,cAAc,aAAa;KAC3B,oBAAoB,aAAa;IACnC,CAAC;IAED,YAAY,cAAc;KAAE;KAAU;IAAc,CAAC;IAErD,MAAM,iBAAiB,MAAM,UAAU;IAEvC,2BAA2B;KACzB,QAAQ;KACR;KACA,wBACE,gBAAgB,EAAE,iBAAiB,MAAM,CAAC,GAAG;IACjD,CAAC;IAED,eAAe,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,CAAC;IAC9D,MAAM,eAAe,KAAK;IAE1B,IAAI,eAAe,MAAM,UACvB,OAAO,eAAe,MAAM;IAG9B,YAAY,eAAe,eAAe,OAAO,QAAQ,IAAI,CAAC;IAG9D,MAAM,MAAM,gBAAgB,EAAE,iBAAiB,MAAM,CAAC;IACtD,MAAM,eAAe,UAAW,UAAU,EACxC,eAAe,KAAK,cACtB,CAAC;IAED,MAAM,kBAAkB,wBAAwB,EAC9C,QAAQ,eACV,CAAC;IACD,YAAY,sBAAsB,eAAe;IACjD,gBAAgB;IAEhB,OAAO,GAAG;KACR;KACA,QAAQ;KACR;IACF,CAAC;GACH;GAGA,MAAM,2BAA2B,OAAO,EAAE,cAAoB;IAC5D,OAAO,oBACL;KACE;KACA,cAAc;KACd,+BAA+B;KAC/B;KACA;KACA,aAAa;IACf,GACA,YAAY;KACV,IAAI;MACF,OAAO,MAAM,mBAAmB;OAC9B;OACA;OACA;OACA;OACA;OACA;MACF,CAAC;KACH,SAAS,KAAK;MACZ,IAAI,eAAe,UACjB,OAAO;MAET,MAAM;KACR;IACF,CACF;GACF;GAeA,OAAO,wBAAuB,MAVZ,kBAChB,CAAC,GAJiB,4BAA4B,KAC7C,MAAM,EAAE,QAAQ,MAGb,GAAa,wBAAwB,GACzC;IACE;IACA,UAAU,IAAI;IACd,aAAa;IACb,SAAS,sBAAsB,aAAa,OAAO;GACrD,CACF,GAEkC,UAAU,SAAS,SAAS;EAChE,UAAU;GACR,IAAI,UAAU,CAAC,eAKb,OAAO,WAAW,QAAQ;GAE5B,SAAS;EACX;CACF;CAEA,OAAO,eAAe,oBAAoB;AAC5C;AAEA,eAAe,uBACb,UACA,SACA,WACmB;CACnB,IAAI,CAAC,WAAW,QAAQ,GACtB,OAAO;CAGT,IAAI,mBAAmB,QAAQ,GAAG;EAChC,IAAI,QAAQ,QAAQ,IAAI,gBAAgB,MAAM,QAC5C,OAAO,SAAS,KACd;GAAE,GAAG,SAAS;GAAS,sBAAsB;EAAK,GAClD,EAAE,SAAS,SAAS,QAAQ,CAC9B;EAEF,OAAO;CACT;CAEA,MAAM,OAAO,SAAS;CACtB,IAAI,KAAK,MAAM,OAAO,KAAK,OAAO,YAAY,CAAC,KAAK,GAAG,WAAW,GAAG,GACnE,MAAM,IAAI,MACR,oNAAoN,KAAK,UAAU,IAAI,GACzO;CAGF,IACE;EAAC;EAAU;EAAU;CAAM,EAAE,MAC1B,MAAM,OAAQ,KAAc,OAAO,UACtC,GAEA,MAAM,IAAI,MACR,+IAA+I,OAAO,KACpJ,IACF,EACG,QAAQ,MAAM,OAAQ,KAAc,OAAO,UAAU,EACrD,KAAK,MAAM,IAAI,EAAE,EAAE,EACnB,KAAK,IAAI,GACd;CAIF,MAAM,YAAW,MADI,UAAU,GACP,gBAAgB,QAAQ;CAEhD,IAAI,QAAQ,QAAQ,IAAI,gBAAgB,MAAM,QAC5C,OAAO,SAAS,KACd;EAAE,GAAG,SAAS;EAAS,sBAAsB;CAAK,GAClD,EAAE,SAAS,SAAS,QAAQ,CAC9B;CAGF,OAAO;AACT;AAEA,eAAe,mBAAmB,EAChC,WACA,SACA,KACA,eACA,SACA,8BAWoB;CACpB,MAAM,SAAS,MAAM,UAAU;CAE/B,MAAM,WADe,oBAAoB,OAAO,SAAS,GACxC,EAAa;CAI9B,MAAM,EAAE,eAAe,YAAY,gBACjC,OAAO,iBAAiB,QAAQ;CAElC,MAAM,eAAe,cAAc,YAAY,UAAU,KAAA;CAGzD,MAAM,mBAAiD,CAAC;CAIxD,KAAK,MAAM,SAAS,eAAe;EACjC,MAAM,mBAAmB,MAAM,QAAQ,QAAQ;EAG/C,IAAI,kBAAkB;GACpB,MAAM,YAAY,mBAAmB,gBAAgB;GACrD,KAAK,MAAM,KAAK,WACd,IAAI,CAAC,2BAA2B,IAAI,CAAC,GACnC,iBAAiB,KAAK,EAAE,QAAQ,MAAM;EAG5C;CACF;CAGA,MAAM,SAAS,YAAY,QAAQ;CACnC,IAAI,iBAAiB;CACrB,IAAI,QAAQ,YAAY,cAAc;EACpC,MAAM,WACJ,OAAO,OAAO,aAAa,aACvB,OAAO,SAAS,EAAE,iBAAiB,MAAW,EAAE,CAAC,IACjD,OAAO;EAEb,MAAM,gBAAgB,QAAQ,OAAO,YAAY;EAGjD,MAAM,UACJ,kBAAkB,SACb,SAAS,WAAW,SAAS,UAAU,SAAS,SAChD,SAAS,kBAAkB,SAAS;EAC3C,iBACE,kBAAkB,UAAU,YAAY,KAAA,KAAa,CAAC,SAAS;EAEjE,IAAI,SAAS;GACX,MAAM,WAAW,CAAC,CAAC,WAAW,QAAQ;GAEtC,IAAI,OAAO,YAAY,YACrB,iBAAiB,KAAK,oBAAoB,SAAS,QAAQ,CAAC;QACvD;IACL,IAAI,QAAQ,YAAY,QAAQ;KAC9B,MAAM,qBAAqB,mBAAmB,QAAQ,UAAU;KAChE,KAAK,MAAM,KAAK,oBACd,iBAAiB,KAAK,EAAE,QAAQ,MAAM;IAE1C;IACA,IAAI,QAAQ,SACV,iBAAiB,KAAK,oBAAoB,QAAQ,SAAS,QAAQ,CAAC;GAExE;EACF;CACF;CAGA,iBAAiB,MAAM,QACrB,cAAc,IAAI,SAAS,aAAa,CAC1C;CAEA,MAAM,MAAM,MAAM,kBAAkB,kBAAkB;EACpD;EACA;EACA,QAAQ;EACR;EACA,aAAa;CACf,CAAC;CAID,IAAI,gBAAgB;EAClB,IAAI,CAAC,IAAI,UACP,uBAAuB;EAGzB,MAAM,WAAW,MAAM,uBACrB,IAAI,UACJ,SACA,SACF;EACA,OAAO,IAAI,SAAS,MAAM,QAAQ;CACpC;CAEA,OAAO,IAAI;AACb"}
1
+ {"version":3,"file":"createStartHandler.js","names":[],"sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n createCsrfMiddleware,\n createNullProtoObject,\n csrfSymbol,\n flattenMiddlewares,\n mergeHeaders,\n safeObjectMerge,\n} from '@tanstack/start-client-core'\nimport {\n executeRewriteInput,\n isRedirect,\n isResolvedRedirect,\n} from '@tanstack/router-core'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n isSsrResponse,\n normalizeSsrResponse,\n replaceSsrResponse,\n stripSsrResponseBody,\n} from '@tanstack/router-core/ssr/server'\nimport {\n getStartContext,\n runWithStartContext,\n} from '@tanstack/start-storage-context'\nimport { requestHandler } from './request-response'\nimport { getStartManifest } from './router-manifest'\nimport { handleServerAction } from './server-functions-handler'\nimport { createEarlyHintsCollector } from './early-hints'\nimport {\n createCachedBaseManifestLoader,\n createFinalManifestResolver,\n} from './finalManifest'\n\nimport { HEADERS } from './constants'\nimport { ServerFunctionSerializationAdapter } from './serializer/ServerFunctionSerializationAdapter'\nimport type {\n AnyFunctionMiddleware,\n AnyRequestMiddleware,\n AnyStartInstanceOptions,\n RouteMethod,\n RouteMethodHandlerFn,\n RouterEntry,\n StartEntry,\n} from '@tanstack/start-client-core'\nimport type { RequestHandler } from './request-handler'\nimport type {\n AnyRoute,\n AnyRouter,\n AnySerializationAdapter,\n Register,\n} from '@tanstack/router-core'\nimport type {\n HandlerCallback,\n HandlerCallbackResult,\n SsrResponse,\n} from '@tanstack/router-core/ssr/server'\nimport type { FinalManifestOptions } from './finalManifest'\n\ntype TODO = any\n\ntype AnyMiddlewareServerFn =\n | AnyRequestMiddleware['options']['server']\n | AnyFunctionMiddleware['options']['server']\n\nexport interface CreateStartHandlerOptions extends FinalManifestOptions {\n handler: HandlerCallback<AnyRouter>\n}\n\nfunction getStartResponseHeaders(opts: { router: AnyRouter }) {\n const headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=utf-8',\n },\n ...opts.router.stores.matches.get().map((match) => {\n return match.headers\n }),\n )\n return headers\n}\n\ninterface PluginAdaptersEntry {\n hasPluginAdapters: boolean\n pluginSerializationAdapters: Array<AnySerializationAdapter>\n}\n\ninterface Entries {\n startEntry: StartEntry\n routerEntry: RouterEntry\n pluginAdapters: PluginAdaptersEntry\n}\n\n// Cached entries - promises stored immediately to prevent concurrent imports\n// that can cause race conditions during module initialization\nlet entriesPromise: Promise<Entries> | undefined\nlet hasWarnedMissingCsrfMiddleware = false\nconst defaultCsrfMiddleware = createCsrfMiddleware({\n filter: (ctx) => ctx.handlerType === 'serverFn',\n})\nconst getCachedBaseManifest = createCachedBaseManifestLoader(() =>\n getStartManifest(),\n)\nconst getProdBaseManifest: typeof getStartManifest = () =>\n getCachedBaseManifest()\nconst getBaseManifest =\n process.env.TSS_DEV_SERVER === 'true' ? getStartManifest : getProdBaseManifest\nconst createEarlyHintsForRequest: typeof createEarlyHintsCollector =\n process.env.TSS_DEV_SERVER === 'true'\n ? () => undefined\n : createEarlyHintsCollector\n\nasync function loadEntries(): Promise<Entries> {\n const [routerEntry, startEntry, pluginAdapters] = await Promise.all([\n // @ts-ignore When building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n import('#tanstack-router-entry'),\n // @ts-ignore When building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n import('#tanstack-start-entry'),\n // @ts-ignore When building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n import('#tanstack-start-plugin-adapters'),\n ])\n return {\n routerEntry: routerEntry as unknown as RouterEntry,\n startEntry: startEntry as unknown as StartEntry,\n pluginAdapters: pluginAdapters as unknown as PluginAdaptersEntry,\n }\n}\n\nfunction getEntries() {\n if (!entriesPromise) {\n entriesPromise = loadEntries()\n }\n return entriesPromise\n}\n\nfunction hasCsrfMiddleware(\n middlewares: Array<AnyRequestMiddleware | AnyFunctionMiddleware>,\n): boolean {\n return middlewares.some((middleware) => csrfSymbol in middleware)\n}\n\nfunction warnMissingCsrfMiddlewareOnce() {\n if (hasWarnedMissingCsrfMiddleware) return\n hasWarnedMissingCsrfMiddleware = true\n\n console.warn(`TanStack Start server functions are not protected by the CSRF middleware.\n\nServer functions are same-origin RPC endpoints and should be protected from cross-site requests.\n\nAdd the CSRF middleware in src/start.ts:\n\n const csrfMiddleware = createCsrfMiddleware({\n filter: (ctx) => ctx.handlerType === 'serverFn',\n })\n\n export const startInstance = createStart(() => ({\n requestMiddleware: [csrfMiddleware],\n }))\n\nIf you intentionally handle CSRF another way, disable this warning:\n\n tanstackStart({\n serverFns: {\n disableCsrfMiddlewareWarning: true,\n },\n })`)\n}\n\n// Pre-computed constants\nconst ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || '/'\nconst SERVER_FN_BASE = process.env.TSS_SERVER_FN_BASE\nconst IS_PRERENDERING = process.env.TSS_PRERENDERING === 'true'\nconst IS_SHELL_ENV = process.env.TSS_SHELL === 'true'\nconst IS_DEV = process.env.NODE_ENV === 'development'\n\n// Reusable error messages\nconst ERR_NO_RESPONSE = IS_DEV\n ? `It looks like you forgot to return a response from your server route handler. If you want to defer to the app router, make sure to have a component set in this route.`\n : 'Internal Server Error'\n\nconst ERR_NO_DEFER = IS_DEV\n ? `You cannot defer to the app router if there is no component defined on this route.`\n : 'Internal Server Error'\n\nfunction throwRouteHandlerError(): never {\n throw new Error(ERR_NO_RESPONSE)\n}\n\nfunction throwIfMayNotDefer(): never {\n throw new Error(ERR_NO_DEFER)\n}\n\n/**\n * Check if a value is a special response (Response or Redirect)\n */\nfunction isSpecialResponse(value: unknown): value is Response {\n return value instanceof Response || isRedirect(value)\n}\n\n/**\n * Normalize middleware result to context shape\n */\nfunction handleCtxResult(result: TODO) {\n if (isSsrResponse(result) || isSpecialResponse(result)) {\n return { response: result }\n }\n return result\n}\n\n/**\n * Execute a middleware chain\n */\nasync function executeMiddleware(\n middlewares: Array<TODO>,\n ctx: TODO,\n): Promise<{ ctx: TODO; response: HandlerCallbackResult }> {\n let index = -1\n let streamResponse:\n | Extract<SsrResponse, { serverSsrCleanup: 'stream' }>\n | undefined\n\n const setResponse = (response: TODO) => {\n if (isSsrResponse(response)) {\n if (response.serverSsrCleanup === 'stream') {\n streamResponse = response\n }\n ctx.response = response.response\n return\n }\n\n ctx.response = response\n }\n\n const disposeStreamResponse = async (reason: string) => {\n const response = streamResponse\n if (!response) {\n return\n }\n\n streamResponse = undefined\n const currentResponse = ctx.response\n if (\n currentResponse === response.response ||\n (currentResponse instanceof Response &&\n response.response.body !== null &&\n currentResponse.body === response.response.body)\n ) {\n ctx.response = undefined\n }\n await response.dispose(reason)\n }\n\n const getFinalResponse = async (): Promise<HandlerCallbackResult> => {\n const response = ctx.response\n if (!response) {\n throwRouteHandlerError()\n }\n\n if (!streamResponse) {\n return response\n }\n\n if (response === streamResponse.response) {\n return streamResponse\n }\n\n if (\n streamResponse.response.body !== null &&\n response.body === streamResponse.response.body\n ) {\n return { ...streamResponse, response }\n }\n\n await disposeStreamResponse('middleware response replaced')\n return response\n }\n\n const next = async (nextCtx?: TODO): Promise<TODO> => {\n // Merge context if provided using safeObjectMerge for prototype pollution prevention\n if (nextCtx) {\n if (nextCtx.context) {\n ctx.context = safeObjectMerge(ctx.context, nextCtx.context)\n }\n // Copy own properties except context (Object.keys returns only own enumerable properties)\n for (const key of Object.keys(nextCtx)) {\n if (key === 'response') {\n setResponse(nextCtx.response)\n } else if (key !== 'context') {\n ctx[key] = nextCtx[key]\n }\n }\n }\n\n index++\n const middleware = middlewares[index]\n if (!middleware) return ctx\n\n let result: TODO\n try {\n result = await middleware({ ...ctx, next })\n } catch (err) {\n if (isSpecialResponse(err)) {\n setResponse(err)\n return ctx\n }\n await disposeStreamResponse('middleware error')\n throw err\n }\n\n const normalized = handleCtxResult(result)\n if (normalized) {\n if (normalized.response !== undefined) {\n setResponse(normalized.response)\n }\n if (normalized.context) {\n ctx.context = safeObjectMerge(ctx.context, normalized.context)\n }\n }\n\n return ctx\n }\n\n await next()\n return { ctx, response: await getFinalResponse() }\n}\n\n/**\n * Wrap a route handler as middleware\n */\nfunction handlerToMiddleware(\n handler: RouteMethodHandlerFn<any, AnyRoute, any, any, any, any, any>,\n mayDefer: boolean = false,\n): TODO {\n if (mayDefer) {\n return handler\n }\n return async (ctx: TODO) => {\n const response = await handler({ ...ctx, next: throwIfMayNotDefer })\n if (!response) {\n throwRouteHandlerError()\n }\n return response\n }\n}\n\n/**\n * Creates the TanStack Start request handler.\n *\n * @example Backwards-compatible usage (handler callback only):\n * ```ts\n * export default createStartHandler(defaultStreamHandler)\n * ```\n *\n * @example With CDN URL rewriting:\n * ```ts\n * export default createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: 'https://cdn.example.com',\n * })\n * ```\n *\n * @example With per-request URL rewriting:\n * ```ts\n * export default createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: {\n * transform: ({ url }) => {\n * const cdnBase = getRequest().headers.get('x-cdn-base') || ''\n * return { href: `${cdnBase}${url}` }\n * },\n * cache: false,\n * },\n * })\n * ```\n */\nexport function createStartHandler<TRegister = Register>(\n cbOrOptions: HandlerCallback<AnyRouter> | CreateStartHandlerOptions,\n): RequestHandler<TRegister> {\n const handlerOptions: FinalManifestOptions =\n typeof cbOrOptions === 'function' ? {} : cbOrOptions\n const cb: HandlerCallback<AnyRouter> =\n typeof cbOrOptions === 'function' ? cbOrOptions : cbOrOptions.handler\n const finalManifestResolver = createFinalManifestResolver({\n ...handlerOptions,\n cacheCreateTransform: process.env.TSS_DEV_SERVER !== 'true',\n })\n const resolveManifestForRequest =\n process.env.TSS_DEV_SERVER === 'true'\n ? finalManifestResolver.resolveUncached\n : finalManifestResolver.resolveCached\n\n if (process.env.TSS_DEV_SERVER !== 'true') {\n finalManifestResolver.warmup({\n getBaseManifest: () => getBaseManifest(undefined),\n })\n }\n\n const startRequestResolver: RequestHandler<Register> = async (\n request,\n requestOpts,\n ) => {\n let router: AnyRouter | null = null as AnyRouter | null\n let responseOwnsCleanup = false as boolean\n\n try {\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n // during normalization paths like '//posts' are flattened to '/posts'.\n // in these cases we would prefer to redirect to the new path\n const { url, handledProtocolRelativeURL } = getNormalizedURL(request.url)\n const href = url.pathname + url.search + url.hash\n const origin = getOrigin(request)\n\n if (handledProtocolRelativeURL) {\n return Response.redirect(url, 308)\n }\n\n const entries = await getEntries()\n const hasStartInstance = !!entries.startEntry.startInstance\n const startOptions: AnyStartInstanceOptions =\n (await entries.startEntry.startInstance?.getOptions()) ||\n ({} as AnyStartInstanceOptions)\n\n const { hasPluginAdapters, pluginSerializationAdapters } =\n entries.pluginAdapters\n\n const serializationAdapters = [\n ...(startOptions.serializationAdapters || []),\n ...(hasPluginAdapters ? pluginSerializationAdapters : []),\n ServerFunctionSerializationAdapter,\n ]\n\n const requestStartOptions = {\n ...startOptions,\n requestMiddleware: hasStartInstance\n ? startOptions.requestMiddleware\n : [defaultCsrfMiddleware],\n serializationAdapters,\n }\n\n // Flatten request middlewares once\n const flattenedRequestMiddlewares = requestStartOptions.requestMiddleware\n ? flattenMiddlewares(requestStartOptions.requestMiddleware)\n : []\n\n // Create set for deduplication\n const executedRequestMiddlewares = new Set<TODO>(\n flattenedRequestMiddlewares,\n )\n\n // Memoized router getter\n const getRouter = async (): Promise<AnyRouter> => {\n if (router) return router\n\n router = await entries.routerEntry.getRouter()\n\n let isShell = IS_SHELL_ENV\n if (IS_PRERENDERING && !isShell) {\n isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'\n }\n\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n router.update({\n history,\n isShell,\n isPrerendering: IS_PRERENDERING,\n origin: router.options.origin ?? origin,\n ...{\n defaultSsr: requestStartOptions.defaultSsr,\n serializationAdapters: [\n ...requestStartOptions.serializationAdapters,\n ...(router.options.serializationAdapters || []),\n ],\n },\n basepath: ROUTER_BASEPATH,\n })\n\n return router\n }\n\n // Check for server function requests first (early exit)\n if (SERVER_FN_BASE && url.pathname.startsWith(SERVER_FN_BASE)) {\n if (\n process.env.NODE_ENV !== 'production' &&\n process.env.TSS_DISABLE_CSRF_MIDDLEWARE_WARNING !== 'true' &&\n !hasCsrfMiddleware(flattenedRequestMiddlewares)\n ) {\n warnMissingCsrfMiddlewareOnce()\n }\n\n const serverFnId = url.pathname\n .slice(SERVER_FN_BASE.length)\n .split('/')[0]\n\n if (!serverFnId) {\n throw new Error('Invalid server action param for serverFnId')\n }\n\n const serverFnHandler = async ({ context }: TODO) => {\n return runWithStartContext(\n {\n getRouter,\n startOptions: requestStartOptions,\n contextAfterGlobalMiddlewares: context,\n request,\n executedRequestMiddlewares,\n handlerType: 'serverFn',\n },\n () =>\n handleServerAction({\n request,\n context: requestOpts?.context,\n serverFnId,\n }),\n )\n }\n\n const middlewares = flattenedRequestMiddlewares.map(\n (d) => d.options.server,\n )\n const { response: middlewareResponse } = await executeMiddleware(\n [...middlewares, serverFnHandler],\n {\n request,\n pathname: url.pathname,\n handlerType: 'serverFn',\n context: createNullProtoObject(requestOpts?.context),\n },\n )\n\n const result = await handleRedirectResponse(\n middlewareResponse,\n request,\n getRouter,\n )\n responseOwnsCleanup = result.serverSsrCleanup === 'stream'\n return result.response\n }\n\n // Router execution function\n const executeRouter = async (\n serverContext: TODO,\n matchedRoutes?: ReadonlyArray<AnyRoute>,\n ): Promise<SsrResponse> => {\n const acceptHeader = request.headers.get('Accept') || '*/*'\n const acceptParts = acceptHeader.split(',')\n const supportedMimeTypes = ['*/*', 'text/html']\n\n const isSupported = supportedMimeTypes.some((mimeType) =>\n acceptParts.some((part) => part.trim().startsWith(mimeType)),\n )\n\n if (!isSupported) {\n return normalizeSsrResponse(\n Response.json(\n { error: 'Only HTML requests are supported here' },\n { status: 500 },\n ),\n )\n }\n\n const manifest = await resolveManifestForRequest({\n request,\n requestInlineCss: requestOpts?.inlineCss,\n getBaseManifest: () => getBaseManifest(matchedRoutes),\n })\n\n const earlyHints = createEarlyHintsForRequest({\n onEarlyHints: requestOpts?.onEarlyHints,\n responseLinkHeader: requestOpts?.responseLinkHeader,\n })\n\n earlyHints?.collectStatic({ manifest, matchedRoutes })\n\n const routerInstance = await getRouter()\n\n attachRouterServerSsrUtils({\n router: routerInstance,\n manifest,\n getRequestAssets: () =>\n getStartContext({ throwIfNotFound: false })?.requestAssets,\n })\n\n routerInstance.update({ additionalContext: { serverContext } })\n await routerInstance.load()\n\n if (routerInstance.state.redirect) {\n return normalizeSsrResponse(routerInstance.state.redirect)\n }\n\n earlyHints?.collectDynamic(routerInstance.stores.matches.get())\n\n // Pass request-scoped assets to dehydrate for manifest injection\n const ctx = getStartContext({ throwIfNotFound: false })\n await routerInstance.serverSsr!.dehydrate({\n requestAssets: ctx?.requestAssets,\n })\n\n const responseHeaders = getStartResponseHeaders({\n router: routerInstance,\n })\n earlyHints?.appendResponseHeaders(responseHeaders)\n const response = await cb({\n request,\n router: routerInstance,\n responseHeaders,\n })\n return normalizeSsrResponse(response)\n }\n\n // Main request handler\n const requestHandlerMiddleware = async ({ context }: TODO) => {\n return runWithStartContext(\n {\n getRouter,\n startOptions: requestStartOptions,\n contextAfterGlobalMiddlewares: context,\n request,\n executedRequestMiddlewares,\n handlerType: 'router',\n },\n async () => {\n try {\n return await handleServerRoutes({\n getRouter,\n request,\n url,\n executeRouter,\n context,\n executedRequestMiddlewares,\n })\n } catch (err) {\n if (err instanceof Response) {\n return err\n }\n throw err\n }\n },\n )\n }\n\n const middlewares = flattenedRequestMiddlewares.map(\n (d) => d.options.server,\n )\n const { response: middlewareResponse } = await executeMiddleware(\n [...middlewares, requestHandlerMiddleware],\n {\n request,\n pathname: url.pathname,\n handlerType: 'router',\n context: createNullProtoObject(requestOpts?.context),\n },\n )\n\n const response = await handleRedirectResponse(\n middlewareResponse,\n request,\n getRouter,\n )\n responseOwnsCleanup = response.serverSsrCleanup === 'stream'\n return response.response\n } finally {\n if (router?.serverSsr && !responseOwnsCleanup) {\n // Clean up router SSR state if it was set up but won't be cleaned up by the callback\n // (e.g., in redirect cases or early returns before the callback is invoked).\n // Transformed streaming response bodies clean up when consumed/cancelled.\n router.serverSsr.cleanup()\n }\n router = null\n }\n }\n\n return requestHandler(startRequestResolver)\n}\n\nasync function handleRedirectResponse(\n response: HandlerCallbackResult,\n request: Request,\n getRouter: () => Promise<AnyRouter>,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(response)\n if (!isRedirect(ssrResponse.response)) {\n return ssrResponse\n }\n\n if (isResolvedRedirect(ssrResponse.response)) {\n if (request.headers.get('x-tsr-serverFn') === 'true') {\n return replaceSsrResponse(\n ssrResponse,\n Response.json(\n { ...ssrResponse.response.options, isSerializedRedirect: true },\n { headers: ssrResponse.response.headers },\n ),\n 'redirect response replaced',\n )\n }\n return ssrResponse\n }\n\n const opts = ssrResponse.response.options\n if (opts.to && typeof opts.to === 'string' && !opts.to.startsWith('/')) {\n throw new Error(\n `Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's \"to\" property accepts an internal path only. Use the \"href\" property to provide an external URL. Received: ${JSON.stringify(opts)}`,\n )\n }\n\n if (\n ['params', 'search', 'hash'].some(\n (d) => typeof (opts as TODO)[d] === 'function',\n )\n ) {\n throw new Error(\n `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(\n opts,\n )\n .filter((d) => typeof (opts as TODO)[d] === 'function')\n .map((d) => `\"${d}\"`)\n .join(', ')}`,\n )\n }\n\n const router = await getRouter()\n const redirect = router.resolveRedirect(ssrResponse.response)\n\n if (request.headers.get('x-tsr-serverFn') === 'true') {\n return replaceSsrResponse(\n ssrResponse,\n Response.json(\n { ...ssrResponse.response.options, isSerializedRedirect: true },\n { headers: ssrResponse.response.headers },\n ),\n 'redirect response replaced',\n )\n }\n\n return replaceSsrResponse(ssrResponse, redirect, 'redirect response replaced')\n}\n\nasync function handleServerRoutes({\n getRouter,\n request,\n url,\n executeRouter,\n context,\n executedRequestMiddlewares,\n}: {\n getRouter: () => Promise<AnyRouter>\n request: Request\n url: URL\n executeRouter: (\n serverContext: any,\n matchedRoutes?: ReadonlyArray<AnyRoute>,\n ) => Promise<SsrResponse>\n context: any\n executedRequestMiddlewares: Set<AnyRequestMiddleware>\n}): Promise<SsrResponse> {\n const router = await getRouter()\n const rewrittenUrl = executeRewriteInput(router.rewrite, url)\n const pathname = rewrittenUrl.pathname\n // this will perform a fuzzy match, however for server routes we need an exact match\n // if the route is not an exact match, executeRouter will handle rendering the app router\n // the match will be cached internally, so no extra work is done during the app router render\n const { matchedRoutes, foundRoute, routeParams } =\n router.getMatchedRoutes(pathname)\n\n const isExactMatch = foundRoute && routeParams['**'] === undefined\n\n // Collect and dedupe route middlewares\n const routeMiddlewares: Array<AnyMiddlewareServerFn> = []\n\n // Collect middleware from matched routes, filtering out those already executed\n // in the request phase\n for (const route of matchedRoutes) {\n const serverMiddleware = route.options.server?.middleware as\n | Array<AnyRequestMiddleware>\n | undefined\n if (serverMiddleware) {\n const flattened = flattenMiddlewares(serverMiddleware)\n for (const m of flattened) {\n if (!executedRequestMiddlewares.has(m)) {\n routeMiddlewares.push(m.options.server)\n }\n }\n }\n }\n\n // Add handler middleware if exact match\n const server = foundRoute?.options.server\n let isHeadFallback = false\n if (server?.handlers && isExactMatch) {\n const handlers =\n typeof server.handlers === 'function'\n ? server.handlers({ createHandlers: (d: any) => d })\n : server.handlers\n\n const requestMethod = request.method.toUpperCase() as RouteMethod\n // Per RFC 9110 §9.3.2, HEAD must return the same header fields as GET.\n // Priority for HEAD: explicit HEAD handler → GET → ANY (last resort).\n const handler =\n requestMethod === 'HEAD'\n ? (handlers['HEAD'] ?? handlers['GET'] ?? handlers['ANY'])\n : (handlers[requestMethod] ?? handlers['ANY'])\n isHeadFallback =\n requestMethod === 'HEAD' && handler !== undefined && !handlers['HEAD']\n\n if (handler) {\n const mayDefer = !!foundRoute.options.component\n\n if (typeof handler === 'function') {\n routeMiddlewares.push(handlerToMiddleware(handler, mayDefer))\n } else {\n if (handler.middleware?.length) {\n const handlerMiddlewares = flattenMiddlewares(handler.middleware)\n for (const m of handlerMiddlewares) {\n routeMiddlewares.push(m.options.server)\n }\n }\n if (handler.handler) {\n routeMiddlewares.push(handlerToMiddleware(handler.handler, mayDefer))\n }\n }\n }\n }\n\n // Final middleware: execute router with matched routes for dev styles\n routeMiddlewares.push(((ctx: TODO) =>\n executeRouter(ctx.context, matchedRoutes)) as TODO)\n\n const { ctx, response } = await executeMiddleware(routeMiddlewares, {\n request,\n context,\n params: routeParams,\n pathname,\n handlerType: 'router',\n })\n\n // RFC 9110 §9.3.2: HEAD must carry the same header fields as GET but no body.\n // Resolve any redirect before stripping so the Location header survives.\n if (isHeadFallback) {\n if (!ctx.response) {\n throwRouteHandlerError()\n }\n\n const resolved = await handleRedirectResponse(response, request, getRouter)\n return stripSsrResponseBody(resolved, 'HEAD body stripped')\n }\n\n return normalizeSsrResponse(response)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-server-core",
3
- "version": "1.169.5",
3
+ "version": "1.169.7",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -70,9 +70,9 @@
70
70
  "h3-v2": "npm:h3@2.0.1-rc.20",
71
71
  "seroval": "^1.5.4",
72
72
  "@tanstack/history": "1.162.0",
73
- "@tanstack/router-core": "1.171.6",
74
- "@tanstack/start-client-core": "1.170.4",
75
- "@tanstack/start-storage-context": "1.167.8"
73
+ "@tanstack/router-core": "1.171.8",
74
+ "@tanstack/start-client-core": "1.170.6",
75
+ "@tanstack/start-storage-context": "1.167.10"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@standard-schema/spec": "^1.0.0",
@@ -16,6 +16,10 @@ import {
16
16
  attachRouterServerSsrUtils,
17
17
  getNormalizedURL,
18
18
  getOrigin,
19
+ isSsrResponse,
20
+ normalizeSsrResponse,
21
+ replaceSsrResponse,
22
+ stripSsrResponseBody,
19
23
  } from '@tanstack/router-core/ssr/server'
20
24
  import {
21
25
  getStartContext,
@@ -48,7 +52,11 @@ import type {
48
52
  AnySerializationAdapter,
49
53
  Register,
50
54
  } from '@tanstack/router-core'
51
- import type { HandlerCallback } from '@tanstack/router-core/ssr/server'
55
+ import type {
56
+ HandlerCallback,
57
+ HandlerCallbackResult,
58
+ SsrResponse,
59
+ } from '@tanstack/router-core/ssr/server'
52
60
  import type { FinalManifestOptions } from './finalManifest'
53
61
 
54
62
  type TODO = any
@@ -194,7 +202,7 @@ function isSpecialResponse(value: unknown): value is Response {
194
202
  * Normalize middleware result to context shape
195
203
  */
196
204
  function handleCtxResult(result: TODO) {
197
- if (isSpecialResponse(result)) {
205
+ if (isSsrResponse(result) || isSpecialResponse(result)) {
198
206
  return { response: result }
199
207
  }
200
208
  return result
@@ -203,8 +211,70 @@ function handleCtxResult(result: TODO) {
203
211
  /**
204
212
  * Execute a middleware chain
205
213
  */
206
- function executeMiddleware(middlewares: Array<TODO>, ctx: TODO): Promise<TODO> {
214
+ async function executeMiddleware(
215
+ middlewares: Array<TODO>,
216
+ ctx: TODO,
217
+ ): Promise<{ ctx: TODO; response: HandlerCallbackResult }> {
207
218
  let index = -1
219
+ let streamResponse:
220
+ | Extract<SsrResponse, { serverSsrCleanup: 'stream' }>
221
+ | undefined
222
+
223
+ const setResponse = (response: TODO) => {
224
+ if (isSsrResponse(response)) {
225
+ if (response.serverSsrCleanup === 'stream') {
226
+ streamResponse = response
227
+ }
228
+ ctx.response = response.response
229
+ return
230
+ }
231
+
232
+ ctx.response = response
233
+ }
234
+
235
+ const disposeStreamResponse = async (reason: string) => {
236
+ const response = streamResponse
237
+ if (!response) {
238
+ return
239
+ }
240
+
241
+ streamResponse = undefined
242
+ const currentResponse = ctx.response
243
+ if (
244
+ currentResponse === response.response ||
245
+ (currentResponse instanceof Response &&
246
+ response.response.body !== null &&
247
+ currentResponse.body === response.response.body)
248
+ ) {
249
+ ctx.response = undefined
250
+ }
251
+ await response.dispose(reason)
252
+ }
253
+
254
+ const getFinalResponse = async (): Promise<HandlerCallbackResult> => {
255
+ const response = ctx.response
256
+ if (!response) {
257
+ throwRouteHandlerError()
258
+ }
259
+
260
+ if (!streamResponse) {
261
+ return response
262
+ }
263
+
264
+ if (response === streamResponse.response) {
265
+ return streamResponse
266
+ }
267
+
268
+ if (
269
+ streamResponse.response.body !== null &&
270
+ response.body === streamResponse.response.body
271
+ ) {
272
+ return { ...streamResponse, response }
273
+ }
274
+
275
+ await disposeStreamResponse('middleware response replaced')
276
+ return response
277
+ }
208
278
 
209
279
  const next = async (nextCtx?: TODO): Promise<TODO> => {
210
280
  // Merge context if provided using safeObjectMerge for prototype pollution prevention
@@ -214,7 +284,9 @@ function executeMiddleware(middlewares: Array<TODO>, ctx: TODO): Promise<TODO> {
214
284
  }
215
285
  // Copy own properties except context (Object.keys returns only own enumerable properties)
216
286
  for (const key of Object.keys(nextCtx)) {
217
- if (key !== 'context') {
287
+ if (key === 'response') {
288
+ setResponse(nextCtx.response)
289
+ } else if (key !== 'context') {
218
290
  ctx[key] = nextCtx[key]
219
291
  }
220
292
  }
@@ -229,16 +301,17 @@ function executeMiddleware(middlewares: Array<TODO>, ctx: TODO): Promise<TODO> {
229
301
  result = await middleware({ ...ctx, next })
230
302
  } catch (err) {
231
303
  if (isSpecialResponse(err)) {
232
- ctx.response = err
304
+ setResponse(err)
233
305
  return ctx
234
306
  }
307
+ await disposeStreamResponse('middleware error')
235
308
  throw err
236
309
  }
237
310
 
238
311
  const normalized = handleCtxResult(result)
239
312
  if (normalized) {
240
313
  if (normalized.response !== undefined) {
241
- ctx.response = normalized.response
314
+ setResponse(normalized.response)
242
315
  }
243
316
  if (normalized.context) {
244
317
  ctx.context = safeObjectMerge(ctx.context, normalized.context)
@@ -248,7 +321,8 @@ function executeMiddleware(middlewares: Array<TODO>, ctx: TODO): Promise<TODO> {
248
321
  return ctx
249
322
  }
250
323
 
251
- return next()
324
+ await next()
325
+ return { ctx, response: await getFinalResponse() }
252
326
  }
253
327
 
254
328
  /**
@@ -327,7 +401,7 @@ export function createStartHandler<TRegister = Register>(
327
401
  requestOpts,
328
402
  ) => {
329
403
  let router: AnyRouter | null = null as AnyRouter | null
330
- let cbWillCleanup = false as boolean
404
+ let responseOwnsCleanup = false as boolean
331
405
 
332
406
  try {
333
407
  // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.
@@ -447,21 +521,30 @@ export function createStartHandler<TRegister = Register>(
447
521
  const middlewares = flattenedRequestMiddlewares.map(
448
522
  (d) => d.options.server,
449
523
  )
450
- const ctx = await executeMiddleware([...middlewares, serverFnHandler], {
451
- request,
452
- pathname: url.pathname,
453
- handlerType: 'serverFn',
454
- context: createNullProtoObject(requestOpts?.context),
455
- })
524
+ const { response: middlewareResponse } = await executeMiddleware(
525
+ [...middlewares, serverFnHandler],
526
+ {
527
+ request,
528
+ pathname: url.pathname,
529
+ handlerType: 'serverFn',
530
+ context: createNullProtoObject(requestOpts?.context),
531
+ },
532
+ )
456
533
 
457
- return handleRedirectResponse(ctx.response, request, getRouter)
534
+ const result = await handleRedirectResponse(
535
+ middlewareResponse,
536
+ request,
537
+ getRouter,
538
+ )
539
+ responseOwnsCleanup = result.serverSsrCleanup === 'stream'
540
+ return result.response
458
541
  }
459
542
 
460
543
  // Router execution function
461
544
  const executeRouter = async (
462
545
  serverContext: TODO,
463
546
  matchedRoutes?: ReadonlyArray<AnyRoute>,
464
- ): Promise<Response> => {
547
+ ): Promise<SsrResponse> => {
465
548
  const acceptHeader = request.headers.get('Accept') || '*/*'
466
549
  const acceptParts = acceptHeader.split(',')
467
550
  const supportedMimeTypes = ['*/*', 'text/html']
@@ -471,9 +554,11 @@ export function createStartHandler<TRegister = Register>(
471
554
  )
472
555
 
473
556
  if (!isSupported) {
474
- return Response.json(
475
- { error: 'Only HTML requests are supported here' },
476
- { status: 500 },
557
+ return normalizeSsrResponse(
558
+ Response.json(
559
+ { error: 'Only HTML requests are supported here' },
560
+ { status: 500 },
561
+ ),
477
562
  )
478
563
  }
479
564
 
@@ -503,7 +588,7 @@ export function createStartHandler<TRegister = Register>(
503
588
  await routerInstance.load()
504
589
 
505
590
  if (routerInstance.state.redirect) {
506
- return routerInstance.state.redirect
591
+ return normalizeSsrResponse(routerInstance.state.redirect)
507
592
  }
508
593
 
509
594
  earlyHints?.collectDynamic(routerInstance.stores.matches.get())
@@ -518,13 +603,12 @@ export function createStartHandler<TRegister = Register>(
518
603
  router: routerInstance,
519
604
  })
520
605
  earlyHints?.appendResponseHeaders(responseHeaders)
521
- cbWillCleanup = true
522
-
523
- return cb({
606
+ const response = await cb({
524
607
  request,
525
608
  router: routerInstance,
526
609
  responseHeaders,
527
610
  })
611
+ return normalizeSsrResponse(response)
528
612
  }
529
613
 
530
614
  // Main request handler
@@ -561,7 +645,7 @@ export function createStartHandler<TRegister = Register>(
561
645
  const middlewares = flattenedRequestMiddlewares.map(
562
646
  (d) => d.options.server,
563
647
  )
564
- const ctx = await executeMiddleware(
648
+ const { response: middlewareResponse } = await executeMiddleware(
565
649
  [...middlewares, requestHandlerMiddleware],
566
650
  {
567
651
  request,
@@ -571,14 +655,19 @@ export function createStartHandler<TRegister = Register>(
571
655
  },
572
656
  )
573
657
 
574
- return handleRedirectResponse(ctx.response, request, getRouter)
658
+ const response = await handleRedirectResponse(
659
+ middlewareResponse,
660
+ request,
661
+ getRouter,
662
+ )
663
+ responseOwnsCleanup = response.serverSsrCleanup === 'stream'
664
+ return response.response
575
665
  } finally {
576
- if (router && !cbWillCleanup) {
666
+ if (router?.serverSsr && !responseOwnsCleanup) {
577
667
  // Clean up router SSR state if it was set up but won't be cleaned up by the callback
578
668
  // (e.g., in redirect cases or early returns before the callback is invoked).
579
- // When the callback runs, it handles cleanup (either via transformStreamWithRouter
580
- // for streaming, or directly in renderRouterToString for non-streaming).
581
- router.serverSsr?.cleanup()
669
+ // Transformed streaming response bodies clean up when consumed/cancelled.
670
+ router.serverSsr.cleanup()
582
671
  }
583
672
  router = null
584
673
  }
@@ -588,25 +677,30 @@ export function createStartHandler<TRegister = Register>(
588
677
  }
589
678
 
590
679
  async function handleRedirectResponse(
591
- response: Response,
680
+ response: HandlerCallbackResult,
592
681
  request: Request,
593
682
  getRouter: () => Promise<AnyRouter>,
594
- ): Promise<Response> {
595
- if (!isRedirect(response)) {
596
- return response
683
+ ): Promise<SsrResponse> {
684
+ const ssrResponse = normalizeSsrResponse(response)
685
+ if (!isRedirect(ssrResponse.response)) {
686
+ return ssrResponse
597
687
  }
598
688
 
599
- if (isResolvedRedirect(response)) {
689
+ if (isResolvedRedirect(ssrResponse.response)) {
600
690
  if (request.headers.get('x-tsr-serverFn') === 'true') {
601
- return Response.json(
602
- { ...response.options, isSerializedRedirect: true },
603
- { headers: response.headers },
691
+ return replaceSsrResponse(
692
+ ssrResponse,
693
+ Response.json(
694
+ { ...ssrResponse.response.options, isSerializedRedirect: true },
695
+ { headers: ssrResponse.response.headers },
696
+ ),
697
+ 'redirect response replaced',
604
698
  )
605
699
  }
606
- return response
700
+ return ssrResponse
607
701
  }
608
702
 
609
- const opts = response.options
703
+ const opts = ssrResponse.response.options
610
704
  if (opts.to && typeof opts.to === 'string' && !opts.to.startsWith('/')) {
611
705
  throw new Error(
612
706
  `Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's "to" property accepts an internal path only. Use the "href" property to provide an external URL. Received: ${JSON.stringify(opts)}`,
@@ -629,16 +723,20 @@ async function handleRedirectResponse(
629
723
  }
630
724
 
631
725
  const router = await getRouter()
632
- const redirect = router.resolveRedirect(response)
726
+ const redirect = router.resolveRedirect(ssrResponse.response)
633
727
 
634
728
  if (request.headers.get('x-tsr-serverFn') === 'true') {
635
- return Response.json(
636
- { ...response.options, isSerializedRedirect: true },
637
- { headers: response.headers },
729
+ return replaceSsrResponse(
730
+ ssrResponse,
731
+ Response.json(
732
+ { ...ssrResponse.response.options, isSerializedRedirect: true },
733
+ { headers: ssrResponse.response.headers },
734
+ ),
735
+ 'redirect response replaced',
638
736
  )
639
737
  }
640
738
 
641
- return redirect
739
+ return replaceSsrResponse(ssrResponse, redirect, 'redirect response replaced')
642
740
  }
643
741
 
644
742
  async function handleServerRoutes({
@@ -655,10 +753,10 @@ async function handleServerRoutes({
655
753
  executeRouter: (
656
754
  serverContext: any,
657
755
  matchedRoutes?: ReadonlyArray<AnyRoute>,
658
- ) => Promise<Response>
756
+ ) => Promise<SsrResponse>
659
757
  context: any
660
758
  executedRequestMiddlewares: Set<AnyRequestMiddleware>
661
- }): Promise<Response> {
759
+ }): Promise<SsrResponse> {
662
760
  const router = await getRouter()
663
761
  const rewrittenUrl = executeRewriteInput(router.rewrite, url)
664
762
  const pathname = rewrittenUrl.pathname
@@ -728,11 +826,10 @@ async function handleServerRoutes({
728
826
  }
729
827
 
730
828
  // Final middleware: execute router with matched routes for dev styles
731
- routeMiddlewares.push((ctx: TODO) =>
732
- executeRouter(ctx.context, matchedRoutes),
733
- )
829
+ routeMiddlewares.push(((ctx: TODO) =>
830
+ executeRouter(ctx.context, matchedRoutes)) as TODO)
734
831
 
735
- const ctx = await executeMiddleware(routeMiddlewares, {
832
+ const { ctx, response } = await executeMiddleware(routeMiddlewares, {
736
833
  request,
737
834
  context,
738
835
  params: routeParams,
@@ -747,13 +844,9 @@ async function handleServerRoutes({
747
844
  throwRouteHandlerError()
748
845
  }
749
846
 
750
- const resolved = await handleRedirectResponse(
751
- ctx.response,
752
- request,
753
- getRouter,
754
- )
755
- return new Response(null, resolved)
847
+ const resolved = await handleRedirectResponse(response, request, getRouter)
848
+ return stripSsrResponseBody(resolved, 'HEAD body stripped')
756
849
  }
757
850
 
758
- return ctx.response
851
+ return normalizeSsrResponse(response)
759
852
  }