@tanstack/start-server-core 1.167.30 → 1.168.1

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.
@@ -1,140 +1,9 @@
1
1
  import { RequestHandler } from './request-handler.js';
2
2
  import { AnyRouter, Register } from '@tanstack/router-core';
3
3
  import { HandlerCallback } from '@tanstack/router-core/ssr/server';
4
- import { TransformAssetUrls, TransformAssets } from './transformAssetUrls.js';
5
- export interface CreateStartHandlerOptions {
4
+ import { FinalManifestOptions } from './finalManifest.js';
5
+ export interface CreateStartHandlerOptions extends FinalManifestOptions {
6
6
  handler: HandlerCallback<AnyRouter>;
7
- /**
8
- * Transform asset URLs and attributes at runtime, e.g. to prepend a CDN prefix.
9
- *
10
- * **String** — a URL prefix prepended to every asset URL (cached by default):
11
- * ```ts
12
- * createStartHandler({
13
- * handler: defaultStreamHandler,
14
- * transformAssets: 'https://cdn.example.com',
15
- * })
16
- * ```
17
- *
18
- * **Object shorthand** — a URL prefix with optional `crossOrigin`:
19
- * ```ts
20
- * createStartHandler({
21
- * handler: defaultStreamHandler,
22
- * transformAssets: {
23
- * prefix: 'https://cdn.example.com',
24
- * crossOrigin: 'anonymous',
25
- * },
26
- * })
27
- * ```
28
- *
29
- * `crossOrigin` accepts a single value or a per-kind record:
30
- * ```ts
31
- * transformAssets: {
32
- * prefix: 'https://cdn.example.com',
33
- * crossOrigin: {
34
- * modulepreload: 'anonymous',
35
- * stylesheet: 'use-credentials',
36
- * },
37
- * }
38
- * ```
39
- *
40
- * **Callback** — receives `{ kind, url }` and returns either a string URL or
41
- * `{ href, crossOrigin? }` (cached by default — runs once on first request):
42
- * ```ts
43
- * createStartHandler({
44
- * handler: defaultStreamHandler,
45
- * transformAssets: ({ kind, url }) => {
46
- * const href = `https://cdn.example.com${url}`
47
- *
48
- * if (kind === 'modulepreload') {
49
- * return { href, crossOrigin: 'anonymous' }
50
- * }
51
- *
52
- * return { href }
53
- * },
54
- * })
55
- * ```
56
- *
57
- * **Object** — for explicit cache control:
58
- * ```ts
59
- * createStartHandler({
60
- * handler: defaultStreamHandler,
61
- * transformAssets: {
62
- * transform: ({ url }) => {
63
- * const region = getRequest().headers.get('x-region') || 'us'
64
- * return { href: `https://cdn-${region}.example.com${url}` }
65
- * },
66
- * cache: false,
67
- * },
68
- * })
69
- * ```
70
- *
71
- * `kind` is one of `'modulepreload' | 'stylesheet' | 'clientEntry'`.
72
- * `crossOrigin` applies to manifest-managed `<link>` assets.
73
- *
74
- * By default, the transformed manifest is cached after the first request
75
- * (`cache: true`). Set `cache: false` for per-request transforms.
76
- *
77
- * If you're using a cached transform, you can optionally set `warmup: true`
78
- * (object form only) to compute the transformed manifest in the background at
79
- * server startup.
80
- *
81
- * Note: This only transforms URLs managed by TanStack Start's manifest
82
- * (JS preloads, CSS links, and the client entry script). For asset imports
83
- * used directly in components (e.g. `import logo from './logo.svg'`),
84
- * configure Vite's `experimental.renderBuiltUrl` in your vite.config.ts.
85
- */
86
- transformAssets?: TransformAssets;
87
- /**
88
- * @deprecated Use `transformAssets` instead.
89
- *
90
- * **String** — a URL prefix prepended to every asset URL (cached by default):
91
- * ```ts
92
- * createStartHandler({
93
- * handler: defaultStreamHandler,
94
- * transformAssetUrls: 'https://cdn.example.com',
95
- * })
96
- * ```
97
- *
98
- * **Callback** — receives `{ url, type }` and returns a new URL
99
- * (cached by default — runs once on first request):
100
- * ```ts
101
- * createStartHandler({
102
- * handler: defaultStreamHandler,
103
- * transformAssetUrls: ({ url, type }) => {
104
- * return `https://cdn.example.com${url}`
105
- * },
106
- * })
107
- * ```
108
- *
109
- * **Object** — for explicit cache control:
110
- * ```ts
111
- * createStartHandler({
112
- * handler: defaultStreamHandler,
113
- * transformAssetUrls: {
114
- * transform: ({ url }) => {
115
- * const region = getRequest().headers.get('x-region') || 'us'
116
- * return `https://cdn-${region}.example.com${url}`
117
- * },
118
- * cache: false, // transform per-request
119
- * },
120
- * })
121
- * ```
122
- *
123
- * `type` is one of `'modulepreload' | 'stylesheet' | 'clientEntry'`.
124
- *
125
- * By default, the transformed manifest is cached after the first request
126
- * (`cache: true`). Set `cache: false` for per-request transforms.
127
- *
128
- * If you're using a cached transform, you can optionally set `warmup: true`
129
- * (object form only) to compute the transformed manifest in the background at
130
- * server startup.
131
- *
132
- * Note: This only transforms URLs managed by TanStack Start's manifest
133
- * (JS preloads, CSS links, and the client entry script). For asset imports
134
- * used directly in components (e.g. `import logo from './logo.svg'`),
135
- * configure Vite's `experimental.renderBuiltUrl` in your vite.config.ts.
136
- */
137
- transformAssetUrls?: TransformAssetUrls;
138
7
  }
139
8
  /**
140
9
  * Creates the TanStack Start request handler.
@@ -1,12 +1,12 @@
1
1
  import { requestHandler } from "./request-response.js";
2
2
  import { getStartManifest } from "./router-manifest.js";
3
3
  import { handleServerAction } from "./server-functions-handler.js";
4
- import { adaptTransformAssetUrlsConfigToTransformAssets, buildManifestWithClientEntry, resolveTransformAssetsConfig, transformManifestAssets } from "./transformAssetUrls.js";
5
- import { collectDynamicHintsFromMatches, collectStaticHintsFromManifest, createEarlyHintsEvent, createResponseLinkHeaderEntries, getResponseLinkHeaderEntries } from "./early-hints.js";
4
+ import { createEarlyHintsCollector } from "./early-hints.js";
5
+ import { createCachedBaseManifestLoader, createFinalManifestResolver } from "./finalManifest.js";
6
6
  import { HEADERS } from "./constants.js";
7
7
  import { ServerFunctionSerializationAdapter } from "./serializer/ServerFunctionSerializationAdapter.js";
8
8
  import { createMemoryHistory } from "@tanstack/history";
9
- import { createNullProtoObject, flattenMiddlewares, mergeHeaders, safeObjectMerge } from "@tanstack/start-client-core";
9
+ import { createCsrfMiddleware, createNullProtoObject, csrfSymbol, flattenMiddlewares, mergeHeaders, safeObjectMerge } from "@tanstack/start-client-core";
10
10
  import { executeRewriteInput, isRedirect, isResolvedRedirect } from "@tanstack/router-core";
11
11
  import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin } from "@tanstack/router-core/ssr/server";
12
12
  import { getStartContext, runWithStartContext } from "@tanstack/start-storage-context";
@@ -16,66 +16,13 @@ function getStartResponseHeaders(opts) {
16
16
  return match.headers;
17
17
  }));
18
18
  }
19
- function notifyEarlyHints(phase, event, onEarlyHints) {
20
- try {
21
- const result = onEarlyHints(event);
22
- if (result) Promise.resolve(result).catch((err) => {
23
- console.error(`Error sending ${phase} early hints:`, err);
24
- });
25
- } catch (err) {
26
- console.error(`Error sending ${phase} early hints:`, err);
27
- }
28
- }
29
- function getResponseLinkHeaderFilter(responseLinkHeader) {
30
- if (typeof responseLinkHeader !== "object") return;
31
- return responseLinkHeader.filter;
32
- }
33
- function appendResponseLinkHeaders(opts) {
34
- if (!opts.filter) {
35
- for (const entry of opts.entries) opts.responseHeaders.append("Link", entry.link);
36
- return;
37
- }
38
- const links = getResponseLinkHeaderEntries(opts);
39
- for (const link of links) opts.responseHeaders.append("Link", link);
40
- }
41
- function collectResponseLinkHeaderEntries(opts) {
42
- for (let index = 0; index < opts.event.hints.length; index++) opts.entries.push({
43
- phase: opts.phase,
44
- hint: opts.event.hints[index],
45
- link: opts.event.links[index]
46
- });
47
- }
48
- function handleCollectedEarlyHints(opts) {
49
- const event = opts.onEarlyHints ? createEarlyHintsEvent({
50
- phase: opts.phase,
51
- hints: opts.hints,
52
- sentLinks: opts.sentLinks,
53
- sentHints: opts.sentHints
54
- }) : void 0;
55
- if (event) notifyEarlyHints(opts.phase, event, opts.onEarlyHints);
56
- if (!opts.responseLinkHeaderEntries) return;
57
- if (event) {
58
- collectResponseLinkHeaderEntries({
59
- phase: opts.phase,
60
- event,
61
- entries: opts.responseLinkHeaderEntries
62
- });
63
- return;
64
- }
65
- createResponseLinkHeaderEntries({
66
- phase: opts.phase,
67
- hints: opts.hints,
68
- sentLinks: opts.sentLinks,
69
- entries: opts.responseLinkHeaderEntries
70
- });
71
- }
72
19
  var entriesPromise;
73
- var baseManifestPromise;
74
- /**
75
- * Cached final manifest (with client entry script tag). In production,
76
- * this is computed once and reused for every request when caching is enabled.
77
- */
78
- var cachedFinalManifestPromise;
20
+ var hasWarnedMissingCsrfMiddleware = false;
21
+ var defaultCsrfMiddleware = createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === "serverFn" });
22
+ var getCachedBaseManifest = createCachedBaseManifestLoader(() => getStartManifest());
23
+ var getProdBaseManifest = () => getCachedBaseManifest();
24
+ var getBaseManifest = process.env.TSS_DEV_SERVER === "true" ? getStartManifest : getProdBaseManifest;
25
+ var createEarlyHintsForRequest = process.env.TSS_DEV_SERVER === "true" ? () => void 0 : createEarlyHintsCollector;
79
26
  async function loadEntries() {
80
27
  const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
81
28
  import("#tanstack-router-entry"),
@@ -92,33 +39,33 @@ function getEntries() {
92
39
  if (!entriesPromise) entriesPromise = loadEntries();
93
40
  return entriesPromise;
94
41
  }
95
- /**
96
- * Returns the raw manifest data (without client entry script tag baked in).
97
- * In dev mode, always returns fresh data. In prod, cached.
98
- */
99
- function getBaseManifest(matchedRoutes) {
100
- if (process.env.TSS_DEV_SERVER === "true") return getStartManifest(matchedRoutes);
101
- if (!baseManifestPromise) baseManifestPromise = getStartManifest();
102
- return baseManifestPromise;
42
+ function hasCsrfMiddleware(middlewares) {
43
+ return middlewares.some((middleware) => csrfSymbol in middleware);
103
44
  }
104
- /**
105
- * Resolves a final Manifest for a given request.
106
- *
107
- * - No transform: builds client entry script tag and returns (cached in prod).
108
- * - Cached transform: transforms all URLs + builds script tag, caches result.
109
- * - Per-request transform: deep-clones base manifest, transforms per-request.
110
- */
111
- async function resolveManifest(matchedRoutes, transformFn, cache) {
112
- const base = await getBaseManifest(matchedRoutes);
113
- const computeFinalManifest = async () => {
114
- return transformFn ? await transformManifestAssets(base, transformFn, { clone: !cache }) : buildManifestWithClientEntry(base);
115
- };
116
- if (process.env.TSS_DEV_SERVER === "true") return computeFinalManifest();
117
- if (!transformFn || cache) {
118
- if (!cachedFinalManifestPromise) cachedFinalManifestPromise = computeFinalManifest();
119
- return cachedFinalManifestPromise;
120
- }
121
- return computeFinalManifest();
45
+ function warnMissingCsrfMiddlewareOnce() {
46
+ if (hasWarnedMissingCsrfMiddleware) return;
47
+ hasWarnedMissingCsrfMiddleware = true;
48
+ console.warn(`TanStack Start server functions are not protected by the CSRF middleware.
49
+
50
+ Server functions are same-origin RPC endpoints and should be protected from cross-site requests.
51
+
52
+ Add the CSRF middleware in src/start.ts:
53
+
54
+ const csrfMiddleware = createCsrfMiddleware({
55
+ filter: (ctx) => ctx.handlerType === 'serverFn',
56
+ })
57
+
58
+ export const startInstance = createStart(() => ({
59
+ requestMiddleware: [csrfMiddleware],
60
+ }))
61
+
62
+ If you intentionally handle CSRF another way, disable this warning:
63
+
64
+ tanstackStart({
65
+ serverFns: {
66
+ disableCsrfMiddlewareWarning: true,
67
+ },
68
+ })`);
122
69
  }
123
70
  var ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || "/";
124
71
  var SERVER_FN_BASE = process.env.TSS_SERVER_FN_BASE;
@@ -226,41 +173,14 @@ function handlerToMiddleware(handler, mayDefer = false) {
226
173
  * ```
227
174
  */
228
175
  function createStartHandler(cbOrOptions) {
176
+ const handlerOptions = typeof cbOrOptions === "function" ? {} : cbOrOptions;
229
177
  const cb = typeof cbOrOptions === "function" ? cbOrOptions : cbOrOptions.handler;
230
- const transformAssetsOption = typeof cbOrOptions === "function" ? void 0 : cbOrOptions.transformAssets;
231
- const transformAssetUrlsOption = typeof cbOrOptions === "function" ? void 0 : cbOrOptions.transformAssetUrls;
232
- const transformOption = transformAssetsOption !== void 0 ? resolveTransformAssetsConfig(transformAssetsOption) : transformAssetUrlsOption !== void 0 ? resolveTransformAssetsConfig(adaptTransformAssetUrlsConfigToTransformAssets(transformAssetUrlsOption)) : void 0;
233
- const warmupTransformManifest = !!transformAssetsOption && typeof transformAssetsOption === "object" && "warmup" in transformAssetsOption && transformAssetsOption.warmup === true || !!transformAssetUrlsOption && typeof transformAssetUrlsOption === "object" && transformAssetUrlsOption.warmup === true;
234
- const resolvedTransformConfig = transformOption;
235
- const cache = resolvedTransformConfig ? resolvedTransformConfig.cache : true;
236
- const shouldCacheCreateTransform = cache && process.env.TSS_DEV_SERVER !== "true";
237
- let cachedCreateTransformPromise;
238
- const getTransformFn = async (opts) => {
239
- if (!resolvedTransformConfig) return void 0;
240
- if (resolvedTransformConfig.type === "createTransform") {
241
- if (shouldCacheCreateTransform) {
242
- if (!cachedCreateTransformPromise) cachedCreateTransformPromise = Promise.resolve(resolvedTransformConfig.createTransform(opts)).catch((error) => {
243
- cachedCreateTransformPromise = void 0;
244
- throw error;
245
- });
246
- return cachedCreateTransformPromise;
247
- }
248
- return resolvedTransformConfig.createTransform(opts);
249
- }
250
- return resolvedTransformConfig.transformFn;
251
- };
252
- if (warmupTransformManifest && cache && process.env.TSS_DEV_SERVER !== "true" && !cachedFinalManifestPromise) {
253
- const warmupPromise = (async () => {
254
- const base = await getBaseManifest(void 0);
255
- const transformFn = await getTransformFn({ warmup: true });
256
- return transformFn ? await transformManifestAssets(base, transformFn, { clone: false }) : buildManifestWithClientEntry(base);
257
- })();
258
- cachedFinalManifestPromise = warmupPromise;
259
- warmupPromise.catch(() => {
260
- if (cachedFinalManifestPromise === warmupPromise) cachedFinalManifestPromise = void 0;
261
- cachedCreateTransformPromise = void 0;
262
- });
263
- }
178
+ const finalManifestResolver = createFinalManifestResolver({
179
+ ...handlerOptions,
180
+ cacheCreateTransform: process.env.TSS_DEV_SERVER !== "true"
181
+ });
182
+ const resolveManifestForRequest = process.env.TSS_DEV_SERVER === "true" ? finalManifestResolver.resolveUncached : finalManifestResolver.resolveCached;
183
+ if (process.env.TSS_DEV_SERVER !== "true") finalManifestResolver.warmup({ getBaseManifest: () => getBaseManifest(void 0) });
264
184
  const startRequestResolver = async (request, requestOpts) => {
265
185
  let router = null;
266
186
  let cbWillCleanup = false;
@@ -270,6 +190,7 @@ function createStartHandler(cbOrOptions) {
270
190
  const origin = getOrigin(request);
271
191
  if (handledProtocolRelativeURL) return Response.redirect(url, 308);
272
192
  const entries = await getEntries();
193
+ const hasStartInstance = !!entries.startEntry.startInstance;
273
194
  const startOptions = await entries.startEntry.startInstance?.getOptions() || {};
274
195
  const { hasPluginAdapters, pluginSerializationAdapters } = entries.pluginAdapters;
275
196
  const serializationAdapters = [
@@ -279,9 +200,10 @@ function createStartHandler(cbOrOptions) {
279
200
  ];
280
201
  const requestStartOptions = {
281
202
  ...startOptions,
203
+ requestMiddleware: hasStartInstance ? startOptions.requestMiddleware : [defaultCsrfMiddleware],
282
204
  serializationAdapters
283
205
  };
284
- const flattenedRequestMiddlewares = startOptions.requestMiddleware ? flattenMiddlewares(startOptions.requestMiddleware) : [];
206
+ const flattenedRequestMiddlewares = requestStartOptions.requestMiddleware ? flattenMiddlewares(requestStartOptions.requestMiddleware) : [];
285
207
  const executedRequestMiddlewares = new Set(flattenedRequestMiddlewares);
286
208
  const getRouter = async () => {
287
209
  if (router) return router;
@@ -301,6 +223,7 @@ function createStartHandler(cbOrOptions) {
301
223
  return router;
302
224
  };
303
225
  if (SERVER_FN_BASE && url.pathname.startsWith(SERVER_FN_BASE)) {
226
+ if (process.env.NODE_ENV !== "production" && process.env.TSS_DISABLE_CSRF_MIDDLEWARE_WARNING !== "true" && !hasCsrfMiddleware(flattenedRequestMiddlewares)) warnMissingCsrfMiddlewareOnce();
304
227
  const serverFnId = url.pathname.slice(SERVER_FN_BASE.length).split("/")[0];
305
228
  if (!serverFnId) throw new Error("Invalid server action param for serverFnId");
306
229
  const serverFnHandler = async ({ context }) => {
@@ -320,30 +243,25 @@ function createStartHandler(cbOrOptions) {
320
243
  return handleRedirectResponse((await executeMiddleware([...flattenedRequestMiddlewares.map((d) => d.options.server), serverFnHandler], {
321
244
  request,
322
245
  pathname: url.pathname,
246
+ handlerType: "serverFn",
323
247
  context: createNullProtoObject(requestOpts?.context)
324
248
  })).response, request, getRouter);
325
249
  }
326
250
  const executeRouter = async (serverContext, matchedRoutes) => {
327
251
  const acceptParts = (request.headers.get("Accept") || "*/*").split(",");
328
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 });
329
- const manifest = await resolveManifest(matchedRoutes, await getTransformFn({
330
- warmup: false,
331
- request
332
- }), cache);
333
- const onEarlyHints = requestOpts?.onEarlyHints;
334
- const responseLinkHeader = requestOpts?.responseLinkHeader;
335
- const shouldCollectEarlyHints = process.env.TSS_DEV_SERVER !== "true" && (!!onEarlyHints || !!responseLinkHeader);
336
- const sentEarlyHintLinks = shouldCollectEarlyHints ? /* @__PURE__ */ new Set() : void 0;
337
- const sentEarlyHints = onEarlyHints ? new Array() : void 0;
338
- const responseLinkHeaderEntries = shouldCollectEarlyHints && responseLinkHeader ? new Array() : void 0;
339
- const responseLinkHeaderFilter = shouldCollectEarlyHints ? getResponseLinkHeaderFilter(responseLinkHeader) : void 0;
340
- if (shouldCollectEarlyHints && sentEarlyHintLinks && matchedRoutes?.length) handleCollectedEarlyHints({
341
- phase: "static",
342
- hints: collectStaticHintsFromManifest(manifest, matchedRoutes),
343
- sentLinks: sentEarlyHintLinks,
344
- sentHints: sentEarlyHints,
345
- onEarlyHints,
346
- responseLinkHeaderEntries
253
+ const manifest = await resolveManifestForRequest({
254
+ request,
255
+ requestInlineCss: requestOpts?.inlineCss,
256
+ getBaseManifest: () => getBaseManifest(matchedRoutes)
257
+ });
258
+ const earlyHints = createEarlyHintsForRequest({
259
+ onEarlyHints: requestOpts?.onEarlyHints,
260
+ responseLinkHeader: requestOpts?.responseLinkHeader
261
+ });
262
+ earlyHints?.collectStatic({
263
+ manifest,
264
+ matchedRoutes
347
265
  });
348
266
  const routerInstance = await getRouter();
349
267
  attachRouterServerSsrUtils({
@@ -355,22 +273,11 @@ function createStartHandler(cbOrOptions) {
355
273
  routerInstance.update({ additionalContext: { serverContext } });
356
274
  await routerInstance.load();
357
275
  if (routerInstance.state.redirect) return routerInstance.state.redirect;
358
- if (shouldCollectEarlyHints && sentEarlyHintLinks) handleCollectedEarlyHints({
359
- phase: "dynamic",
360
- hints: collectDynamicHintsFromMatches(routerInstance.stores.matches.get()),
361
- sentLinks: sentEarlyHintLinks,
362
- sentHints: sentEarlyHints,
363
- onEarlyHints,
364
- responseLinkHeaderEntries
365
- });
276
+ earlyHints?.collectDynamic(routerInstance.stores.matches.get());
366
277
  const ctx = getStartContext({ throwIfNotFound: false });
367
278
  await routerInstance.serverSsr.dehydrate({ requestAssets: ctx?.requestAssets });
368
279
  const responseHeaders = getStartResponseHeaders({ router: routerInstance });
369
- if (responseLinkHeaderEntries?.length) appendResponseLinkHeaders({
370
- responseHeaders,
371
- entries: responseLinkHeaderEntries,
372
- filter: responseLinkHeaderFilter
373
- });
280
+ earlyHints?.appendResponseHeaders(responseHeaders);
374
281
  cbWillCleanup = true;
375
282
  return cb({
376
283
  request,
@@ -405,6 +312,7 @@ function createStartHandler(cbOrOptions) {
405
312
  return handleRedirectResponse((await executeMiddleware([...flattenedRequestMiddlewares.map((d) => d.options.server), requestHandlerMiddleware], {
406
313
  request,
407
314
  pathname: url.pathname,
315
+ handlerType: "router",
408
316
  context: createNullProtoObject(requestOpts?.context)
409
317
  })).response, request, getRouter);
410
318
  } finally {
@@ -474,7 +382,8 @@ async function handleServerRoutes({ getRouter, request, url, executeRouter, cont
474
382
  request,
475
383
  context,
476
384
  params: routeParams,
477
- pathname
385
+ pathname,
386
+ handlerType: "router"
478
387
  });
479
388
  if (isHeadFallback) {
480
389
  if (!ctx.response) throwRouteHandlerError();
@@ -1 +1 @@
1
- {"version":3,"file":"createStartHandler.js","names":[],"sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n createNullProtoObject,\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 {\n adaptTransformAssetUrlsConfigToTransformAssets,\n buildManifestWithClientEntry,\n resolveTransformAssetsConfig,\n transformManifestAssets,\n} from './transformAssetUrls'\nimport {\n collectDynamicHintsFromMatches,\n collectStaticHintsFromManifest,\n createEarlyHintsEvent,\n createResponseLinkHeaderEntries,\n getResponseLinkHeaderEntries,\n} from './early-hints'\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 EarlyHint,\n EarlyHintsEvent,\n EarlyHintsPhase,\n OnEarlyHints,\n ResponseLinkHeaderEntry,\n ResponseLinkHeaderFilter,\n ResponseLinkHeaderOptions,\n} from './early-hints'\nimport type {\n AnyRoute,\n AnyRouter,\n AnySerializationAdapter,\n Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\nimport type {\n StartManifestWithClientEntry,\n TransformAssetUrls,\n TransformAssets,\n TransformAssetsFn,\n} from './transformAssetUrls'\n\ntype TODO = any\n\ntype AnyMiddlewareServerFn =\n | AnyRequestMiddleware['options']['server']\n | AnyFunctionMiddleware['options']['server']\n\nexport interface CreateStartHandlerOptions {\n handler: HandlerCallback<AnyRouter>\n /**\n * Transform asset URLs and attributes at runtime, e.g. to prepend a CDN prefix.\n *\n * **String** — a URL prefix prepended to every asset URL (cached by default):\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: 'https://cdn.example.com',\n * })\n * ```\n *\n * **Object shorthand** — a URL prefix with optional `crossOrigin`:\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: {\n * prefix: 'https://cdn.example.com',\n * crossOrigin: 'anonymous',\n * },\n * })\n * ```\n *\n * `crossOrigin` accepts a single value or a per-kind record:\n * ```ts\n * transformAssets: {\n * prefix: 'https://cdn.example.com',\n * crossOrigin: {\n * modulepreload: 'anonymous',\n * stylesheet: 'use-credentials',\n * },\n * }\n * ```\n *\n * **Callback** — receives `{ kind, url }` and returns either a string URL or\n * `{ href, crossOrigin? }` (cached by default — runs once on first request):\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: ({ kind, url }) => {\n * const href = `https://cdn.example.com${url}`\n *\n * if (kind === 'modulepreload') {\n * return { href, crossOrigin: 'anonymous' }\n * }\n *\n * return { href }\n * },\n * })\n * ```\n *\n * **Object** — for explicit cache control:\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssets: {\n * transform: ({ url }) => {\n * const region = getRequest().headers.get('x-region') || 'us'\n * return { href: `https://cdn-${region}.example.com${url}` }\n * },\n * cache: false,\n * },\n * })\n * ```\n *\n * `kind` is one of `'modulepreload' | 'stylesheet' | 'clientEntry'`.\n * `crossOrigin` applies to manifest-managed `<link>` assets.\n *\n * By default, the transformed manifest is cached after the first request\n * (`cache: true`). Set `cache: false` for per-request transforms.\n *\n * If you're using a cached transform, you can optionally set `warmup: true`\n * (object form only) to compute the transformed manifest in the background at\n * server startup.\n *\n * Note: This only transforms URLs managed by TanStack Start's manifest\n * (JS preloads, CSS links, and the client entry script). For asset imports\n * used directly in components (e.g. `import logo from './logo.svg'`),\n * configure Vite's `experimental.renderBuiltUrl` in your vite.config.ts.\n */\n transformAssets?: TransformAssets\n /**\n * @deprecated Use `transformAssets` instead.\n *\n * **String** — a URL prefix prepended to every asset URL (cached by default):\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssetUrls: 'https://cdn.example.com',\n * })\n * ```\n *\n * **Callback** — receives `{ url, type }` and returns a new URL\n * (cached by default — runs once on first request):\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssetUrls: ({ url, type }) => {\n * return `https://cdn.example.com${url}`\n * },\n * })\n * ```\n *\n * **Object** — for explicit cache control:\n * ```ts\n * createStartHandler({\n * handler: defaultStreamHandler,\n * transformAssetUrls: {\n * transform: ({ url }) => {\n * const region = getRequest().headers.get('x-region') || 'us'\n * return `https://cdn-${region}.example.com${url}`\n * },\n * cache: false, // transform per-request\n * },\n * })\n * ```\n *\n * `type` is one of `'modulepreload' | 'stylesheet' | 'clientEntry'`.\n *\n * By default, the transformed manifest is cached after the first request\n * (`cache: true`). Set `cache: false` for per-request transforms.\n *\n * If you're using a cached transform, you can optionally set `warmup: true`\n * (object form only) to compute the transformed manifest in the background at\n * server startup.\n *\n * Note: This only transforms URLs managed by TanStack Start's manifest\n * (JS preloads, CSS links, and the client entry script). For asset imports\n * used directly in components (e.g. `import logo from './logo.svg'`),\n * configure Vite's `experimental.renderBuiltUrl` in your vite.config.ts.\n */\n transformAssetUrls?: TransformAssetUrls\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\nfunction notifyEarlyHints(\n phase: EarlyHintsPhase,\n event: EarlyHintsEvent,\n onEarlyHints: OnEarlyHints,\n) {\n try {\n const result = onEarlyHints(event)\n if (result) {\n void Promise.resolve(result).catch((err) => {\n console.error(`Error sending ${phase} early hints:`, err)\n })\n }\n } catch (err) {\n console.error(`Error sending ${phase} early hints:`, err)\n }\n}\n\nfunction getResponseLinkHeaderFilter(\n responseLinkHeader: boolean | ResponseLinkHeaderOptions | undefined,\n): ResponseLinkHeaderFilter | undefined {\n if (typeof responseLinkHeader !== 'object') {\n return undefined\n }\n\n return responseLinkHeader.filter\n}\n\nfunction appendResponseLinkHeaders(opts: {\n responseHeaders: Headers\n entries: ReadonlyArray<ResponseLinkHeaderEntry>\n filter?: ResponseLinkHeaderFilter\n}) {\n if (!opts.filter) {\n for (const entry of opts.entries) {\n opts.responseHeaders.append('Link', entry.link)\n }\n return\n }\n\n const links = getResponseLinkHeaderEntries(opts)\n\n for (const link of links) {\n opts.responseHeaders.append('Link', link)\n }\n}\n\nfunction collectResponseLinkHeaderEntries(opts: {\n phase: EarlyHintsPhase\n event: EarlyHintsEvent\n entries: Array<ResponseLinkHeaderEntry>\n}) {\n for (let index = 0; index < opts.event.hints.length; index++) {\n opts.entries.push({\n phase: opts.phase,\n hint: opts.event.hints[index]!,\n link: opts.event.links[index]!,\n })\n }\n}\n\nfunction handleCollectedEarlyHints(opts: {\n phase: EarlyHintsPhase\n hints: ReadonlyArray<EarlyHint>\n sentLinks: Set<string>\n sentHints?: Array<EarlyHint>\n onEarlyHints?: OnEarlyHints\n responseLinkHeaderEntries?: Array<ResponseLinkHeaderEntry>\n}) {\n const event = opts.onEarlyHints\n ? createEarlyHintsEvent({\n phase: opts.phase,\n hints: opts.hints,\n sentLinks: opts.sentLinks,\n sentHints: opts.sentHints!,\n })\n : undefined\n\n if (event) {\n notifyEarlyHints(opts.phase, event, opts.onEarlyHints!)\n }\n\n if (!opts.responseLinkHeaderEntries) return\n\n if (event) {\n collectResponseLinkHeaderEntries({\n phase: opts.phase,\n event,\n entries: opts.responseLinkHeaderEntries,\n })\n return\n }\n\n createResponseLinkHeaderEntries({\n phase: opts.phase,\n hints: opts.hints,\n sentLinks: opts.sentLinks,\n entries: opts.responseLinkHeaderEntries,\n })\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 baseManifestPromise: Promise<StartManifestWithClientEntry> | undefined\n\n/**\n * Cached final manifest (with client entry script tag). In production,\n * this is computed once and reused for every request when caching is enabled.\n */\nlet cachedFinalManifestPromise: Promise<Manifest> | undefined\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\n/**\n * Returns the raw manifest data (without client entry script tag baked in).\n * In dev mode, always returns fresh data. In prod, cached.\n */\nfunction getBaseManifest(\n matchedRoutes?: ReadonlyArray<AnyRoute>,\n): Promise<StartManifestWithClientEntry> {\n // In dev mode, always get fresh manifest (no caching) to include route-specific dev styles\n if (process.env.TSS_DEV_SERVER === 'true') {\n return getStartManifest(matchedRoutes)\n }\n // In prod, cache the base manifest\n if (!baseManifestPromise) {\n baseManifestPromise = getStartManifest()\n }\n return baseManifestPromise\n}\n\n/**\n * Resolves a final Manifest for a given request.\n *\n * - No transform: builds client entry script tag and returns (cached in prod).\n * - Cached transform: transforms all URLs + builds script tag, caches result.\n * - Per-request transform: deep-clones base manifest, transforms per-request.\n */\nasync function resolveManifest(\n matchedRoutes: ReadonlyArray<AnyRoute> | undefined,\n transformFn: TransformAssetsFn | undefined,\n cache: boolean,\n): Promise<Manifest> {\n const base = await getBaseManifest(matchedRoutes)\n\n const computeFinalManifest = async () => {\n return transformFn\n ? await transformManifestAssets(base, transformFn, { clone: !cache })\n : buildManifestWithClientEntry(base)\n }\n\n // In dev, always compute fresh to include route-specific dev styles.\n if (process.env.TSS_DEV_SERVER === 'true') {\n return computeFinalManifest()\n }\n\n // In prod, cache unless we're explicitly doing per-request transforms.\n if (!transformFn || cache) {\n if (!cachedFinalManifestPromise) {\n cachedFinalManifestPromise = computeFinalManifest()\n }\n return cachedFinalManifestPromise\n }\n\n // Per-request transform — deep-clone and transform every time.\n return computeFinalManifest()\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 // Normalize the overloaded argument\n const cb: HandlerCallback<AnyRouter> =\n typeof cbOrOptions === 'function' ? cbOrOptions : cbOrOptions.handler\n const transformAssetsOption: TransformAssets | undefined =\n typeof cbOrOptions === 'function' ? undefined : cbOrOptions.transformAssets\n const transformAssetUrlsOption: TransformAssetUrls | undefined =\n typeof cbOrOptions === 'function'\n ? undefined\n : cbOrOptions.transformAssetUrls\n\n const transformOption =\n transformAssetsOption !== undefined\n ? resolveTransformAssetsConfig(transformAssetsOption)\n : transformAssetUrlsOption !== undefined\n ? resolveTransformAssetsConfig(\n adaptTransformAssetUrlsConfigToTransformAssets(\n transformAssetUrlsOption,\n ),\n )\n : undefined\n\n const warmupTransformManifest =\n (!!transformAssetsOption &&\n typeof transformAssetsOption === 'object' &&\n 'warmup' in transformAssetsOption &&\n transformAssetsOption.warmup === true) ||\n (!!transformAssetUrlsOption &&\n typeof transformAssetUrlsOption === 'object' &&\n transformAssetUrlsOption.warmup === true)\n\n // Pre-resolve the transform function and cache flag\n const resolvedTransformConfig = transformOption\n const cache = resolvedTransformConfig ? resolvedTransformConfig.cache : true\n const shouldCacheCreateTransform =\n cache && process.env.TSS_DEV_SERVER !== 'true'\n\n // Memoize a single createTransform() result when caching is enabled outside\n // of the dev server.\n let cachedCreateTransformPromise: Promise<TransformAssetsFn> | undefined\n\n const getTransformFn = async (\n opts: { warmup: true } | { warmup: false; request: Request },\n ): Promise<TransformAssetsFn | undefined> => {\n if (!resolvedTransformConfig) return undefined\n\n if (resolvedTransformConfig.type === 'createTransform') {\n if (shouldCacheCreateTransform) {\n if (!cachedCreateTransformPromise) {\n cachedCreateTransformPromise = Promise.resolve(\n resolvedTransformConfig.createTransform(opts),\n ).catch((error) => {\n cachedCreateTransformPromise = undefined\n throw error\n })\n }\n\n return cachedCreateTransformPromise\n }\n\n return resolvedTransformConfig.createTransform(opts)\n }\n\n return resolvedTransformConfig.transformFn\n }\n\n // Background warmup for cached transforms (production only)\n if (\n warmupTransformManifest &&\n cache &&\n process.env.TSS_DEV_SERVER !== 'true' &&\n !cachedFinalManifestPromise\n ) {\n // NOTE: Do not call resolveManifest() here.\n // resolveManifest() reads from cachedFinalManifestPromise, and since we set\n // cachedFinalManifestPromise to this warmup promise, that would create a\n // self-referential promise and hang forever.\n const warmupPromise = (async () => {\n const base = await getBaseManifest(undefined)\n const transformFn = await getTransformFn({ warmup: true })\n return transformFn\n ? await transformManifestAssets(base, transformFn, { clone: false })\n : buildManifestWithClientEntry(base)\n })()\n cachedFinalManifestPromise = warmupPromise\n warmupPromise.catch(() => {\n // If warmup fails, allow the next request to retry.\n if (cachedFinalManifestPromise === warmupPromise) {\n cachedFinalManifestPromise = undefined\n }\n cachedCreateTransformPromise = 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 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 serializationAdapters,\n }\n\n // Flatten request middlewares once\n const flattenedRequestMiddlewares = startOptions.requestMiddleware\n ? flattenMiddlewares(startOptions.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 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 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 resolveManifest(\n matchedRoutes,\n await getTransformFn({ warmup: false, request }),\n cache,\n )\n\n const onEarlyHints = requestOpts?.onEarlyHints\n const responseLinkHeader = requestOpts?.responseLinkHeader\n const shouldCollectEarlyHints =\n process.env.TSS_DEV_SERVER !== 'true' &&\n (!!onEarlyHints || !!responseLinkHeader)\n const sentEarlyHintLinks = shouldCollectEarlyHints\n ? new Set<string>()\n : undefined\n const sentEarlyHints = onEarlyHints ? new Array<EarlyHint>() : undefined\n const responseLinkHeaderEntries =\n shouldCollectEarlyHints && responseLinkHeader\n ? new Array<ResponseLinkHeaderEntry>()\n : undefined\n const responseLinkHeaderFilter = shouldCollectEarlyHints\n ? getResponseLinkHeaderFilter(responseLinkHeader)\n : undefined\n\n if (\n shouldCollectEarlyHints &&\n sentEarlyHintLinks &&\n matchedRoutes?.length\n ) {\n const hints = collectStaticHintsFromManifest(manifest, matchedRoutes)\n handleCollectedEarlyHints({\n phase: 'static',\n hints,\n sentLinks: sentEarlyHintLinks,\n sentHints: sentEarlyHints,\n onEarlyHints,\n responseLinkHeaderEntries,\n })\n }\n\n const routerInstance = await getRouter()\n\n attachRouterServerSsrUtils({\n router: routerInstance,\n manifest,\n getRequestAssets: () =>\n getStartContext({ throwIfNotFound: false })?.requestAssets,\n includeUnmatchedRouteAssets: false,\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 if (shouldCollectEarlyHints && sentEarlyHintLinks) {\n const loadedMatches = routerInstance.stores.matches.get()\n const hints = collectDynamicHintsFromMatches(loadedMatches)\n handleCollectedEarlyHints({\n phase: 'dynamic',\n hints,\n sentLinks: sentEarlyHintLinks,\n sentHints: sentEarlyHints,\n onEarlyHints,\n responseLinkHeaderEntries,\n })\n }\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 if (responseLinkHeaderEntries?.length) {\n appendResponseLinkHeaders({\n responseHeaders,\n entries: responseLinkHeaderEntries,\n filter: responseLinkHeaderFilter,\n })\n }\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 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 })\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":"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"}
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 includeUnmatchedRouteAssets: false,\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":"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"}