@tanstack/start-server-core 1.136.9 → 1.136.13

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,5 +1,5 @@
1
1
  import { createMemoryHistory } from "@tanstack/history";
2
- import { flattenMiddlewares, json, mergeHeaders } from "@tanstack/start-client-core";
2
+ import { json, flattenMiddlewares, mergeHeaders } from "@tanstack/start-client-core";
3
3
  import { isRedirect, isResolvedRedirect, executeRewriteInput } from "@tanstack/router-core";
4
4
  import { getOrigin, attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
5
5
  import { runWithStartContext } from "@tanstack/start-storage-context";
@@ -36,146 +36,167 @@ function createStartHandler(cb) {
36
36
  routerEntry
37
37
  };
38
38
  };
39
- const originalFetch = globalThis.fetch;
40
39
  const startRequestResolver = async (request, requestOpts) => {
41
- const origin = getOrigin(request);
42
- globalThis.fetch = async function(input, init) {
43
- function resolve(url2, requestOptions) {
44
- const fetchRequest = new Request(url2, requestOptions);
45
- return startRequestResolver(fetchRequest, requestOpts);
46
- }
47
- if (typeof input === "string" && input.startsWith("/")) {
48
- const url2 = new URL(input, origin);
49
- return resolve(url2, init);
50
- } else if (typeof input === "object" && "url" in input && typeof input.url === "string" && input.url.startsWith("/")) {
51
- const url2 = new URL(input.url, origin);
52
- return resolve(url2, init);
53
- }
54
- return originalFetch(input, init);
55
- };
56
- const url = new URL(request.url);
57
- const href = url.href.replace(url.origin, "");
58
40
  let router = null;
59
- const getRouter = async () => {
60
- if (router) return router;
61
- router = await (await getEntries()).routerEntry.getRouter();
62
- const isPrerendering = process.env.TSS_PRERENDERING === "true";
63
- let isShell = process.env.TSS_SHELL === "true";
64
- if (isPrerendering && !isShell) {
65
- isShell = request.headers.get(HEADERS.TSS_SHELL) === "true";
66
- }
67
- const history = createMemoryHistory({
68
- initialEntries: [href]
69
- });
70
- router.update({
71
- history,
72
- isShell,
73
- isPrerendering,
74
- origin: router.options.origin ?? origin,
75
- ...{
76
- defaultSsr: startOptions.defaultSsr,
77
- serializationAdapters: [
78
- ...startOptions.serializationAdapters || [],
79
- ...router.options.serializationAdapters || []
80
- ]
81
- },
82
- basepath: ROUTER_BASEPATH
83
- });
84
- return router;
85
- };
86
- const startOptions = await (await getEntries()).startEntry.startInstance?.getOptions() || {};
87
- startOptions.serializationAdapters = startOptions.serializationAdapters || [];
88
- startOptions.serializationAdapters.push(ServerFunctionSerializationAdapter);
89
- const requestHandlerMiddleware = handlerToMiddleware(
90
- async ({ context }) => {
91
- const response2 = await runWithStartContext(
92
- {
93
- getRouter,
94
- startOptions,
95
- contextAfterGlobalMiddlewares: context,
96
- request
41
+ try {
42
+ const origin = getOrigin(request);
43
+ const url = new URL(request.url);
44
+ const href = url.href.replace(url.origin, "");
45
+ const startOptions = await (await getEntries()).startEntry.startInstance?.getOptions() || {};
46
+ const serializationAdapters = [
47
+ ...startOptions.serializationAdapters || [],
48
+ ServerFunctionSerializationAdapter
49
+ ];
50
+ const requestStartOptions = {
51
+ ...startOptions,
52
+ serializationAdapters
53
+ };
54
+ const getRouter = async () => {
55
+ if (router) return router;
56
+ router = await (await getEntries()).routerEntry.getRouter();
57
+ const isPrerendering = process.env.TSS_PRERENDERING === "true";
58
+ let isShell = process.env.TSS_SHELL === "true";
59
+ if (isPrerendering && !isShell) {
60
+ isShell = request.headers.get(HEADERS.TSS_SHELL) === "true";
61
+ }
62
+ const history = createMemoryHistory({
63
+ initialEntries: [href]
64
+ });
65
+ router.update({
66
+ history,
67
+ isShell,
68
+ isPrerendering,
69
+ origin: router.options.origin ?? origin,
70
+ ...{
71
+ defaultSsr: requestStartOptions.defaultSsr,
72
+ serializationAdapters: [
73
+ ...requestStartOptions.serializationAdapters,
74
+ ...router.options.serializationAdapters || []
75
+ ]
97
76
  },
98
- async () => {
99
- try {
100
- if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {
101
- return await handleServerAction({
102
- request,
103
- context: requestOpts?.context
104
- });
105
- }
106
- const executeRouter = async ({
107
- serverContext
108
- }) => {
109
- const requestAcceptHeader = request.headers.get("Accept") || "*/*";
110
- const splitRequestAcceptHeader = requestAcceptHeader.split(",");
111
- const supportedMimeTypes = ["*/*", "text/html"];
112
- const isRouterAcceptSupported = supportedMimeTypes.some(
113
- (mimeType) => splitRequestAcceptHeader.some(
114
- (acceptedMimeType) => acceptedMimeType.trim().startsWith(mimeType)
115
- )
116
- );
117
- if (!isRouterAcceptSupported) {
118
- return json(
119
- {
120
- error: "Only HTML requests are supported here"
121
- },
122
- {
123
- status: 500
124
- }
125
- );
126
- }
127
- if (startRoutesManifest === null) {
128
- startRoutesManifest = await getStartManifest();
129
- }
130
- const router2 = await getRouter();
131
- attachRouterServerSsrUtils({
132
- router: router2,
133
- manifest: startRoutesManifest
134
- });
135
- router2.update({ additionalContext: { serverContext } });
136
- await router2.load();
137
- if (router2.state.redirect) {
138
- return router2.state.redirect;
77
+ basepath: ROUTER_BASEPATH
78
+ });
79
+ return router;
80
+ };
81
+ const requestHandlerMiddleware = handlerToMiddleware(
82
+ async ({ context }) => {
83
+ const response2 = await runWithStartContext(
84
+ {
85
+ getRouter,
86
+ startOptions: requestStartOptions,
87
+ contextAfterGlobalMiddlewares: context,
88
+ request
89
+ },
90
+ async () => {
91
+ try {
92
+ if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {
93
+ return await handleServerAction({
94
+ request,
95
+ context: requestOpts?.context
96
+ });
139
97
  }
140
- await router2.serverSsr.dehydrate();
141
- const responseHeaders = getStartResponseHeaders({ router: router2 });
142
- const response4 = await cb({
98
+ const executeRouter = async ({
99
+ serverContext
100
+ }) => {
101
+ const requestAcceptHeader = request.headers.get("Accept") || "*/*";
102
+ const splitRequestAcceptHeader = requestAcceptHeader.split(",");
103
+ const supportedMimeTypes = ["*/*", "text/html"];
104
+ const isRouterAcceptSupported = supportedMimeTypes.some(
105
+ (mimeType) => splitRequestAcceptHeader.some(
106
+ (acceptedMimeType) => acceptedMimeType.trim().startsWith(mimeType)
107
+ )
108
+ );
109
+ if (!isRouterAcceptSupported) {
110
+ return json(
111
+ {
112
+ error: "Only HTML requests are supported here"
113
+ },
114
+ {
115
+ status: 500
116
+ }
117
+ );
118
+ }
119
+ if (startRoutesManifest === null) {
120
+ startRoutesManifest = await getStartManifest();
121
+ }
122
+ const router2 = await getRouter();
123
+ attachRouterServerSsrUtils({
124
+ router: router2,
125
+ manifest: startRoutesManifest
126
+ });
127
+ router2.update({ additionalContext: { serverContext } });
128
+ await router2.load();
129
+ if (router2.state.redirect) {
130
+ return router2.state.redirect;
131
+ }
132
+ await router2.serverSsr.dehydrate();
133
+ const responseHeaders = getStartResponseHeaders({ router: router2 });
134
+ const response4 = await cb({
135
+ request,
136
+ router: router2,
137
+ responseHeaders
138
+ });
139
+ return response4;
140
+ };
141
+ const response3 = await handleServerRoutes({
142
+ getRouter,
143
143
  request,
144
- router: router2,
145
- responseHeaders
144
+ executeRouter,
145
+ context
146
146
  });
147
- return response4;
148
- };
149
- const response3 = await handleServerRoutes({
150
- getRouter,
151
- request,
152
- executeRouter,
153
- context
154
- });
155
- return response3;
156
- } catch (err) {
157
- if (err instanceof Response) {
158
- return err;
147
+ return response3;
148
+ } catch (err) {
149
+ if (err instanceof Response) {
150
+ return err;
151
+ }
152
+ throw err;
159
153
  }
160
- throw err;
161
154
  }
155
+ );
156
+ return response2;
157
+ }
158
+ );
159
+ const flattenedMiddlewares = startOptions.requestMiddleware ? flattenMiddlewares(startOptions.requestMiddleware) : [];
160
+ const middlewares = flattenedMiddlewares.map((d) => d.options.server);
161
+ const ctx = await executeMiddleware(
162
+ [...middlewares, requestHandlerMiddleware],
163
+ {
164
+ request,
165
+ context: requestOpts?.context || {}
166
+ }
167
+ );
168
+ const response = ctx.response;
169
+ if (isRedirect(response)) {
170
+ if (isResolvedRedirect(response)) {
171
+ if (request.headers.get("x-tsr-redirect") === "manual") {
172
+ return json(
173
+ {
174
+ ...response.options,
175
+ isSerializedRedirect: true
176
+ },
177
+ {
178
+ headers: response.headers
179
+ }
180
+ );
162
181
  }
163
- );
164
- return response2;
165
- }
166
- );
167
- const flattenedMiddlewares = startOptions.requestMiddleware ? flattenMiddlewares(startOptions.requestMiddleware) : [];
168
- const middlewares = flattenedMiddlewares.map((d) => d.options.server);
169
- const ctx = await executeMiddleware(
170
- [...middlewares, requestHandlerMiddleware],
171
- {
172
- request,
173
- context: requestOpts?.context || {}
174
- }
175
- );
176
- const response = ctx.response;
177
- if (isRedirect(response)) {
178
- if (isResolvedRedirect(response)) {
182
+ return response;
183
+ }
184
+ if (response.options.to && typeof response.options.to === "string" && !response.options.to.startsWith("/")) {
185
+ throw new Error(
186
+ `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(response.options)}`
187
+ );
188
+ }
189
+ if (["params", "search", "hash"].some(
190
+ (d) => typeof response.options[d] === "function"
191
+ )) {
192
+ throw new Error(
193
+ `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(
194
+ response.options
195
+ ).filter((d) => typeof response.options[d] === "function").map((d) => `"${d}"`).join(", ")}`
196
+ );
197
+ }
198
+ const router2 = await getRouter();
199
+ const redirect = router2.resolveRedirect(response);
179
200
  if (request.headers.get("x-tsr-redirect") === "manual") {
180
201
  return json(
181
202
  {
@@ -187,38 +208,14 @@ function createStartHandler(cb) {
187
208
  }
188
209
  );
189
210
  }
190
- return response;
191
- }
192
- if (response.options.to && typeof response.options.to === "string" && !response.options.to.startsWith("/")) {
193
- throw new Error(
194
- `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(response.options)}`
195
- );
211
+ return redirect;
196
212
  }
197
- if (["params", "search", "hash"].some(
198
- (d) => typeof response.options[d] === "function"
199
- )) {
200
- throw new Error(
201
- `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(
202
- response.options
203
- ).filter((d) => typeof response.options[d] === "function").map((d) => `"${d}"`).join(", ")}`
204
- );
213
+ return response;
214
+ } finally {
215
+ if (router) {
216
+ router = null;
205
217
  }
206
- const router2 = await getRouter();
207
- const redirect = router2.resolveRedirect(response);
208
- if (request.headers.get("x-tsr-redirect") === "manual") {
209
- return json(
210
- {
211
- ...response.options,
212
- isSerializedRedirect: true
213
- },
214
- {
215
- headers: response.headers
216
- }
217
- );
218
- }
219
- return redirect;
220
218
  }
221
- return response;
222
219
  };
223
220
  return requestHandler(startRequestResolver);
224
221
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createStartHandler.js","sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n flattenMiddlewares,\n json,\n mergeHeaders,\n} from '@tanstack/start-client-core'\nimport {\n executeRewriteInput,\n isRedirect,\n isResolvedRedirect,\n} from '@tanstack/router-core'\nimport {\n attachRouterServerSsrUtils,\n getOrigin,\n} from '@tanstack/router-core/ssr/server'\nimport { runWithStartContext } from '@tanstack/start-storage-context'\nimport { requestHandler } from './request-response'\nimport { getStartManifest } from './router-manifest'\nimport { handleServerAction } from './server-functions-handler'\n\nimport { HEADERS } from './constants'\nimport { ServerFunctionSerializationAdapter } from './serializer/ServerFunctionSerializationAdapter'\nimport type {\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 Awaitable,\n Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\n\ntype TODO = any\n\nfunction getStartResponseHeaders(opts: { router: AnyRouter }) {\n const headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=utf-8',\n },\n ...opts.router.state.matches.map((match) => {\n return match.headers\n }),\n )\n return headers\n}\n\nexport function createStartHandler<TRegister = Register>(\n cb: HandlerCallback<AnyRouter>,\n): RequestHandler<TRegister> {\n const ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || '/'\n let startRoutesManifest: Manifest | null = null\n let startEntry: StartEntry | null = null\n let routerEntry: RouterEntry | null = null\n const getEntries = async (): Promise<{\n startEntry: StartEntry\n routerEntry: RouterEntry\n }> => {\n if (routerEntry === null) {\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 routerEntry = await import('#tanstack-router-entry')\n }\n if (startEntry === null) {\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 startEntry = await import('#tanstack-start-entry')\n }\n return {\n startEntry: startEntry as unknown as StartEntry,\n routerEntry: routerEntry as unknown as RouterEntry,\n }\n }\n\n const originalFetch = globalThis.fetch\n\n const startRequestResolver: RequestHandler<Register> = async (\n request,\n requestOpts,\n ) => {\n const origin = getOrigin(request)\n\n // Patching fetch function to use our request resolver\n // if the input starts with `/` which is a common pattern for\n // client-side routing.\n // When we encounter similar requests, we can assume that the\n // user wants to use the same origin as the current request.\n globalThis.fetch = async function (input, init) {\n function resolve(url: URL, requestOptions: RequestInit | undefined) {\n const fetchRequest = new Request(url, requestOptions)\n return startRequestResolver(fetchRequest, requestOpts)\n }\n\n if (typeof input === 'string' && input.startsWith('/')) {\n // e.g: fetch('/api/data')\n const url = new URL(input, origin)\n return resolve(url, init)\n } else if (\n typeof input === 'object' &&\n 'url' in input &&\n typeof input.url === 'string' &&\n input.url.startsWith('/')\n ) {\n // e.g: fetch(new Request('/api/data'))\n const url = new URL(input.url, origin)\n return resolve(url, init)\n }\n\n // If not, it should just use the original fetch\n return originalFetch(input, init)\n } as typeof fetch\n\n const url = new URL(request.url)\n const href = url.href.replace(url.origin, '')\n\n let router: AnyRouter | null = null\n const getRouter = async () => {\n if (router) return router\n router = await (await getEntries()).routerEntry.getRouter()\n\n // Update the client-side router with the history\n const isPrerendering = process.env.TSS_PRERENDERING === 'true'\n // env var is set during dev is SPA mode is enabled\n let isShell = process.env.TSS_SHELL === 'true'\n if (isPrerendering && !isShell) {\n // only read the shell header if we are prerendering\n // to avoid runtime behavior changes by injecting this header\n // the header is set by the prerender plugin\n isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'\n }\n\n // Create a history for the client-side router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n router.update({\n history,\n isShell,\n isPrerendering,\n origin: router.options.origin ?? origin,\n ...{\n defaultSsr: startOptions.defaultSsr,\n serializationAdapters: [\n ...(startOptions.serializationAdapters || []),\n ...(router.options.serializationAdapters || []),\n ],\n },\n basepath: ROUTER_BASEPATH,\n })\n return router\n }\n\n const startOptions: AnyStartInstanceOptions =\n (await (await getEntries()).startEntry.startInstance?.getOptions()) ||\n ({} as AnyStartInstanceOptions)\n startOptions.serializationAdapters =\n startOptions.serializationAdapters || []\n // insert start specific default serialization adapters\n startOptions.serializationAdapters.push(ServerFunctionSerializationAdapter)\n\n const requestHandlerMiddleware = handlerToMiddleware(\n async ({ context }) => {\n const response = await runWithStartContext(\n {\n getRouter,\n startOptions,\n contextAfterGlobalMiddlewares: context,\n request,\n },\n async () => {\n try {\n // First, let's attempt to handle server functions\n if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {\n return await handleServerAction({\n request,\n context: requestOpts?.context,\n })\n }\n\n const executeRouter = async ({\n serverContext,\n }: {\n serverContext: any\n }) => {\n const requestAcceptHeader =\n request.headers.get('Accept') || '*/*'\n const splitRequestAcceptHeader = requestAcceptHeader.split(',')\n\n const supportedMimeTypes = ['*/*', 'text/html']\n const isRouterAcceptSupported = supportedMimeTypes.some(\n (mimeType) =>\n splitRequestAcceptHeader.some((acceptedMimeType) =>\n acceptedMimeType.trim().startsWith(mimeType),\n ),\n )\n\n if (!isRouterAcceptSupported) {\n return json(\n {\n error: 'Only HTML requests are supported here',\n },\n {\n status: 500,\n },\n )\n }\n\n // if the startRoutesManifest is not loaded yet, load it once\n if (startRoutesManifest === null) {\n startRoutesManifest = await getStartManifest()\n }\n const router = await getRouter()\n attachRouterServerSsrUtils({\n router,\n manifest: startRoutesManifest,\n })\n\n router.update({ additionalContext: { serverContext } })\n await router.load()\n\n // If there was a redirect, skip rendering the page at all\n if (router.state.redirect) {\n return router.state.redirect\n }\n\n await router.serverSsr!.dehydrate()\n\n const responseHeaders = getStartResponseHeaders({ router })\n const response = await cb({\n request,\n router,\n responseHeaders,\n })\n\n return response\n }\n\n const response = await handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n context,\n })\n\n return response\n } catch (err) {\n if (err instanceof Response) {\n return err\n }\n\n throw err\n }\n },\n )\n return response\n },\n )\n\n const flattenedMiddlewares = startOptions.requestMiddleware\n ? flattenMiddlewares(startOptions.requestMiddleware)\n : []\n const middlewares = flattenedMiddlewares.map((d) => d.options.server)\n const ctx = await executeMiddleware(\n [...middlewares, requestHandlerMiddleware],\n {\n request,\n\n context: requestOpts?.context || {},\n },\n )\n\n const response: Response = ctx.response\n\n if (isRedirect(response)) {\n if (isResolvedRedirect(response)) {\n if (request.headers.get('x-tsr-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n return response\n }\n if (\n response.options.to &&\n typeof response.options.to === 'string' &&\n !response.options.to.startsWith('/')\n ) {\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(response.options)}`,\n )\n }\n\n if (\n ['params', 'search', 'hash'].some(\n (d) => typeof (response.options as any)[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 response.options,\n )\n .filter((d) => typeof (response.options as any)[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-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n\n return redirect\n }\n\n return response\n }\n\n return requestHandler(startRequestResolver)\n}\n\nasync function handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n context,\n}: {\n getRouter: () => Awaitable<AnyRouter>\n request: Request\n executeRouter: ({\n serverContext,\n }: {\n serverContext: any\n }) => Promise<Response>\n context: any\n}) {\n const router = await getRouter()\n let url = new URL(request.url)\n url = executeRewriteInput(router.rewrite, url)\n const pathname = url.pathname\n const { matchedRoutes, foundRoute, routeParams } =\n router.getMatchedRoutes(pathname)\n\n // TODO: Error handling? What happens when its `throw redirect()` vs `throw new Error()`?\n\n const middlewares = flattenMiddlewares(\n matchedRoutes.flatMap((r) => r.options.server?.middleware).filter(Boolean),\n ).map((d) => d.options.server)\n\n const server = foundRoute?.options.server\n\n if (server) {\n if (server.handlers) {\n const handlers =\n typeof server.handlers === 'function'\n ? server.handlers({\n createHandlers: (d: any) => d,\n })\n : server.handlers\n\n const requestMethod = request.method.toUpperCase() as RouteMethod\n\n // Attempt to find the method in the handlers\n const handler = handlers[requestMethod] ?? handlers['ANY']\n\n // If a method is found, execute the handler\n if (handler) {\n const mayDefer = !!foundRoute.options.component\n if (typeof handler === 'function') {\n middlewares.push(handlerToMiddleware(handler, mayDefer))\n } else {\n const { middleware } = handler\n if (middleware && middleware.length) {\n middlewares.push(\n ...flattenMiddlewares(middleware).map((d) => d.options.server),\n )\n }\n if (handler.handler) {\n middlewares.push(handlerToMiddleware(handler.handler, mayDefer))\n }\n }\n }\n }\n }\n\n // eventually, execute the router\n middlewares.push(\n handlerToMiddleware((ctx) => executeRouter({ serverContext: ctx.context })),\n )\n\n const ctx = await executeMiddleware(middlewares, {\n request,\n context,\n params: routeParams,\n pathname,\n })\n\n const response: Response = ctx.response\n\n return response\n}\n\nfunction throwRouteHandlerError() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\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 )\n }\n throw new Error('Internal Server Error')\n}\n\nfunction throwIfMayNotDefer() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\n `You cannot defer to the app router if there is no component defined on this route.`,\n )\n }\n throw new Error('Internal Server Error')\n}\nfunction handlerToMiddleware(\n handler: RouteMethodHandlerFn<any, AnyRoute, any, any, any, any, any>,\n mayDefer: boolean = false,\n) {\n if (mayDefer) {\n return handler as TODO\n }\n return async ({ next: _next, ...rest }: TODO) => {\n const response = await handler({ ...rest, next: throwIfMayNotDefer })\n if (!response) {\n throwRouteHandlerError()\n }\n return response\n }\n}\n\nfunction executeMiddleware(middlewares: TODO, ctx: TODO) {\n let index = -1\n\n const next = async (ctx: TODO) => {\n index++\n const middleware = middlewares[index]\n if (!middleware) return ctx\n\n let result\n try {\n result = await middleware({\n ...ctx,\n // Allow the middleware to call the next middleware in the chain\n next: async (nextCtx: TODO) => {\n // Allow the caller to extend the context for the next middleware\n const nextResult = await next({\n ...ctx,\n ...nextCtx,\n context: {\n ...ctx.context,\n ...(nextCtx?.context || {}),\n },\n })\n\n // Merge the result into the context\\\n return Object.assign(ctx, handleCtxResult(nextResult))\n },\n // Allow the middleware result to extend the return context\n })\n } catch (err: TODO) {\n if (isSpecialResponse(err)) {\n result = {\n response: err,\n }\n } else {\n throw err\n }\n }\n\n // Merge the middleware result into the context, just in case it\n // returns a partial context\n return Object.assign(ctx, handleCtxResult(result))\n }\n\n return handleCtxResult(next(ctx))\n}\n\nfunction handleCtxResult(result: TODO) {\n if (isSpecialResponse(result)) {\n return {\n response: result,\n }\n }\n\n return result\n}\n\nfunction isSpecialResponse(err: TODO) {\n return isResponse(err) || isRedirect(err)\n}\n\nfunction isResponse(response: Response): response is Response {\n return response instanceof Response\n}\n"],"names":["url","response","router","ctx"],"mappings":"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"}
1
+ {"version":3,"file":"createStartHandler.js","sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n flattenMiddlewares,\n json,\n mergeHeaders,\n} from '@tanstack/start-client-core'\nimport {\n executeRewriteInput,\n isRedirect,\n isResolvedRedirect,\n} from '@tanstack/router-core'\nimport {\n attachRouterServerSsrUtils,\n getOrigin,\n} from '@tanstack/router-core/ssr/server'\nimport { runWithStartContext } from '@tanstack/start-storage-context'\nimport { requestHandler } from './request-response'\nimport { getStartManifest } from './router-manifest'\nimport { handleServerAction } from './server-functions-handler'\n\nimport { HEADERS } from './constants'\nimport { ServerFunctionSerializationAdapter } from './serializer/ServerFunctionSerializationAdapter'\nimport type {\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 Awaitable,\n Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\n\ntype TODO = any\n\nfunction getStartResponseHeaders(opts: { router: AnyRouter }) {\n const headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=utf-8',\n },\n ...opts.router.state.matches.map((match) => {\n return match.headers\n }),\n )\n return headers\n}\n\nexport function createStartHandler<TRegister = Register>(\n cb: HandlerCallback<AnyRouter>,\n): RequestHandler<TRegister> {\n const ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || '/'\n let startRoutesManifest: Manifest | null = null\n let startEntry: StartEntry | null = null\n let routerEntry: RouterEntry | null = null\n const getEntries = async (): Promise<{\n startEntry: StartEntry\n routerEntry: RouterEntry\n }> => {\n if (routerEntry === null) {\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 routerEntry = await import('#tanstack-router-entry')\n }\n if (startEntry === null) {\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 startEntry = await import('#tanstack-start-entry')\n }\n return {\n startEntry: startEntry as unknown as StartEntry,\n routerEntry: routerEntry as unknown as RouterEntry,\n }\n }\n\n const startRequestResolver: RequestHandler<Register> = async (\n request,\n requestOpts,\n ) => {\n let router: AnyRouter | null = null as AnyRouter | null\n try {\n const origin = getOrigin(request)\n\n const url = new URL(request.url)\n const href = url.href.replace(url.origin, '')\n\n const startOptions: AnyStartInstanceOptions =\n (await (await getEntries()).startEntry.startInstance?.getOptions()) ||\n ({} as AnyStartInstanceOptions)\n\n const serializationAdapters = [\n ...(startOptions.serializationAdapters || []),\n ServerFunctionSerializationAdapter,\n ]\n\n const requestStartOptions = {\n ...startOptions,\n serializationAdapters,\n }\n\n const getRouter = async () => {\n if (router) return router\n router = await (await getEntries()).routerEntry.getRouter()\n\n // Update the client-side router with the history\n const isPrerendering = process.env.TSS_PRERENDERING === 'true'\n // env var is set during dev is SPA mode is enabled\n let isShell = process.env.TSS_SHELL === 'true'\n if (isPrerendering && !isShell) {\n // only read the shell header if we are prerendering\n // to avoid runtime behavior changes by injecting this header\n // the header is set by the prerender plugin\n isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'\n }\n\n // Create a history for the client-side router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n router.update({\n history,\n isShell,\n isPrerendering,\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 return router\n }\n\n const requestHandlerMiddleware = handlerToMiddleware(\n async ({ context }) => {\n const response = await runWithStartContext(\n {\n getRouter,\n startOptions: requestStartOptions,\n contextAfterGlobalMiddlewares: context,\n request,\n },\n async () => {\n try {\n // First, let's attempt to handle server functions\n if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {\n return await handleServerAction({\n request,\n context: requestOpts?.context,\n })\n }\n\n const executeRouter = async ({\n serverContext,\n }: {\n serverContext: any\n }) => {\n const requestAcceptHeader =\n request.headers.get('Accept') || '*/*'\n const splitRequestAcceptHeader =\n requestAcceptHeader.split(',')\n\n const supportedMimeTypes = ['*/*', 'text/html']\n const isRouterAcceptSupported = supportedMimeTypes.some(\n (mimeType) =>\n splitRequestAcceptHeader.some((acceptedMimeType) =>\n acceptedMimeType.trim().startsWith(mimeType),\n ),\n )\n\n if (!isRouterAcceptSupported) {\n return json(\n {\n error: 'Only HTML requests are supported here',\n },\n {\n status: 500,\n },\n )\n }\n\n // if the startRoutesManifest is not loaded yet, load it once\n if (startRoutesManifest === null) {\n startRoutesManifest = await getStartManifest()\n }\n const router = await getRouter()\n attachRouterServerSsrUtils({\n router,\n manifest: startRoutesManifest,\n })\n\n router.update({ additionalContext: { serverContext } })\n await router.load()\n\n // If there was a redirect, skip rendering the page at all\n if (router.state.redirect) {\n return router.state.redirect\n }\n\n await router.serverSsr!.dehydrate()\n\n const responseHeaders = getStartResponseHeaders({ router })\n const response = await cb({\n request,\n router,\n responseHeaders,\n })\n\n return response\n }\n\n const response = await handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n context,\n })\n\n return response\n } catch (err) {\n if (err instanceof Response) {\n return err\n }\n\n throw err\n }\n },\n )\n return response\n },\n )\n\n const flattenedMiddlewares = startOptions.requestMiddleware\n ? flattenMiddlewares(startOptions.requestMiddleware)\n : []\n const middlewares = flattenedMiddlewares.map((d) => d.options.server)\n const ctx = await executeMiddleware(\n [...middlewares, requestHandlerMiddleware],\n {\n request,\n\n context: requestOpts?.context || {},\n },\n )\n\n const response: Response = ctx.response\n\n if (isRedirect(response)) {\n if (isResolvedRedirect(response)) {\n if (request.headers.get('x-tsr-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n return response\n }\n if (\n response.options.to &&\n typeof response.options.to === 'string' &&\n !response.options.to.startsWith('/')\n ) {\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(response.options)}`,\n )\n }\n\n if (\n ['params', 'search', 'hash'].some(\n (d) => typeof (response.options as any)[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 response.options,\n )\n .filter((d) => typeof (response.options as any)[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-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n\n return redirect\n }\n\n return response\n } finally {\n if (router) {\n router = null\n }\n }\n }\n\n return requestHandler(startRequestResolver)\n}\n\nasync function handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n context,\n}: {\n getRouter: () => Awaitable<AnyRouter>\n request: Request\n executeRouter: ({\n serverContext,\n }: {\n serverContext: any\n }) => Promise<Response>\n context: any\n}) {\n const router = await getRouter()\n let url = new URL(request.url)\n url = executeRewriteInput(router.rewrite, url)\n const pathname = url.pathname\n const { matchedRoutes, foundRoute, routeParams } =\n router.getMatchedRoutes(pathname)\n\n // TODO: Error handling? What happens when its `throw redirect()` vs `throw new Error()`?\n\n const middlewares = flattenMiddlewares(\n matchedRoutes.flatMap((r) => r.options.server?.middleware).filter(Boolean),\n ).map((d) => d.options.server)\n\n const server = foundRoute?.options.server\n\n if (server) {\n if (server.handlers) {\n const handlers =\n typeof server.handlers === 'function'\n ? server.handlers({\n createHandlers: (d: any) => d,\n })\n : server.handlers\n\n const requestMethod = request.method.toUpperCase() as RouteMethod\n\n // Attempt to find the method in the handlers\n const handler = handlers[requestMethod] ?? handlers['ANY']\n\n // If a method is found, execute the handler\n if (handler) {\n const mayDefer = !!foundRoute.options.component\n if (typeof handler === 'function') {\n middlewares.push(handlerToMiddleware(handler, mayDefer))\n } else {\n const { middleware } = handler\n if (middleware && middleware.length) {\n middlewares.push(\n ...flattenMiddlewares(middleware).map((d) => d.options.server),\n )\n }\n if (handler.handler) {\n middlewares.push(handlerToMiddleware(handler.handler, mayDefer))\n }\n }\n }\n }\n }\n\n // eventually, execute the router\n middlewares.push(\n handlerToMiddleware((ctx) => executeRouter({ serverContext: ctx.context })),\n )\n\n const ctx = await executeMiddleware(middlewares, {\n request,\n context,\n params: routeParams,\n pathname,\n })\n\n const response: Response = ctx.response\n\n return response\n}\n\nfunction throwRouteHandlerError() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\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 )\n }\n throw new Error('Internal Server Error')\n}\n\nfunction throwIfMayNotDefer() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\n `You cannot defer to the app router if there is no component defined on this route.`,\n )\n }\n throw new Error('Internal Server Error')\n}\nfunction handlerToMiddleware(\n handler: RouteMethodHandlerFn<any, AnyRoute, any, any, any, any, any>,\n mayDefer: boolean = false,\n) {\n if (mayDefer) {\n return handler as TODO\n }\n return async ({ next: _next, ...rest }: TODO) => {\n const response = await handler({ ...rest, next: throwIfMayNotDefer })\n if (!response) {\n throwRouteHandlerError()\n }\n return response\n }\n}\n\nfunction executeMiddleware(middlewares: TODO, ctx: TODO) {\n let index = -1\n\n const next = async (ctx: TODO) => {\n index++\n const middleware = middlewares[index]\n if (!middleware) return ctx\n\n let result\n try {\n result = await middleware({\n ...ctx,\n // Allow the middleware to call the next middleware in the chain\n next: async (nextCtx: TODO) => {\n // Allow the caller to extend the context for the next middleware\n const nextResult = await next({\n ...ctx,\n ...nextCtx,\n context: {\n ...ctx.context,\n ...(nextCtx?.context || {}),\n },\n })\n\n // Merge the result into the context\\\n return Object.assign(ctx, handleCtxResult(nextResult))\n },\n // Allow the middleware result to extend the return context\n })\n } catch (err: TODO) {\n if (isSpecialResponse(err)) {\n result = {\n response: err,\n }\n } else {\n throw err\n }\n }\n\n // Merge the middleware result into the context, just in case it\n // returns a partial context\n return Object.assign(ctx, handleCtxResult(result))\n }\n\n return handleCtxResult(next(ctx))\n}\n\nfunction handleCtxResult(result: TODO) {\n if (isSpecialResponse(result)) {\n return {\n response: result,\n }\n }\n\n return result\n}\n\nfunction isSpecialResponse(err: TODO) {\n return isResponse(err) || isRedirect(err)\n}\n\nfunction isResponse(response: Response): response is Response {\n return response instanceof Response\n}\n"],"names":["response","router","ctx"],"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.136.9",
3
+ "version": "1.136.13",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -63,10 +63,10 @@
63
63
  "h3-v2": "npm:h3@2.0.0-beta.4",
64
64
  "seroval": "^1.3.2",
65
65
  "tiny-invariant": "^1.3.3",
66
- "@tanstack/router-core": "1.136.9",
67
- "@tanstack/start-storage-context": "1.136.9",
68
66
  "@tanstack/history": "1.133.28",
69
- "@tanstack/start-client-core": "1.136.9"
67
+ "@tanstack/router-core": "1.136.13",
68
+ "@tanstack/start-client-core": "1.136.13",
69
+ "@tanstack/start-storage-context": "1.136.13"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@standard-schema/spec": "^1.0.0",
@@ -76,208 +76,225 @@ export function createStartHandler<TRegister = Register>(
76
76
  }
77
77
  }
78
78
 
79
- const originalFetch = globalThis.fetch
80
-
81
79
  const startRequestResolver: RequestHandler<Register> = async (
82
80
  request,
83
81
  requestOpts,
84
82
  ) => {
85
- const origin = getOrigin(request)
86
-
87
- // Patching fetch function to use our request resolver
88
- // if the input starts with `/` which is a common pattern for
89
- // client-side routing.
90
- // When we encounter similar requests, we can assume that the
91
- // user wants to use the same origin as the current request.
92
- globalThis.fetch = async function (input, init) {
93
- function resolve(url: URL, requestOptions: RequestInit | undefined) {
94
- const fetchRequest = new Request(url, requestOptions)
95
- return startRequestResolver(fetchRequest, requestOpts)
96
- }
83
+ let router: AnyRouter | null = null as AnyRouter | null
84
+ try {
85
+ const origin = getOrigin(request)
97
86
 
98
- if (typeof input === 'string' && input.startsWith('/')) {
99
- // e.g: fetch('/api/data')
100
- const url = new URL(input, origin)
101
- return resolve(url, init)
102
- } else if (
103
- typeof input === 'object' &&
104
- 'url' in input &&
105
- typeof input.url === 'string' &&
106
- input.url.startsWith('/')
107
- ) {
108
- // e.g: fetch(new Request('/api/data'))
109
- const url = new URL(input.url, origin)
110
- return resolve(url, init)
111
- }
87
+ const url = new URL(request.url)
88
+ const href = url.href.replace(url.origin, '')
112
89
 
113
- // If not, it should just use the original fetch
114
- return originalFetch(input, init)
115
- } as typeof fetch
116
-
117
- const url = new URL(request.url)
118
- const href = url.href.replace(url.origin, '')
119
-
120
- let router: AnyRouter | null = null
121
- const getRouter = async () => {
122
- if (router) return router
123
- router = await (await getEntries()).routerEntry.getRouter()
124
-
125
- // Update the client-side router with the history
126
- const isPrerendering = process.env.TSS_PRERENDERING === 'true'
127
- // env var is set during dev is SPA mode is enabled
128
- let isShell = process.env.TSS_SHELL === 'true'
129
- if (isPrerendering && !isShell) {
130
- // only read the shell header if we are prerendering
131
- // to avoid runtime behavior changes by injecting this header
132
- // the header is set by the prerender plugin
133
- isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'
134
- }
90
+ const startOptions: AnyStartInstanceOptions =
91
+ (await (await getEntries()).startEntry.startInstance?.getOptions()) ||
92
+ ({} as AnyStartInstanceOptions)
135
93
 
136
- // Create a history for the client-side router
137
- const history = createMemoryHistory({
138
- initialEntries: [href],
139
- })
94
+ const serializationAdapters = [
95
+ ...(startOptions.serializationAdapters || []),
96
+ ServerFunctionSerializationAdapter,
97
+ ]
140
98
 
141
- router.update({
142
- history,
143
- isShell,
144
- isPrerendering,
145
- origin: router.options.origin ?? origin,
146
- ...{
147
- defaultSsr: startOptions.defaultSsr,
148
- serializationAdapters: [
149
- ...(startOptions.serializationAdapters || []),
150
- ...(router.options.serializationAdapters || []),
151
- ],
152
- },
153
- basepath: ROUTER_BASEPATH,
154
- })
155
- return router
156
- }
99
+ const requestStartOptions = {
100
+ ...startOptions,
101
+ serializationAdapters,
102
+ }
157
103
 
158
- const startOptions: AnyStartInstanceOptions =
159
- (await (await getEntries()).startEntry.startInstance?.getOptions()) ||
160
- ({} as AnyStartInstanceOptions)
161
- startOptions.serializationAdapters =
162
- startOptions.serializationAdapters || []
163
- // insert start specific default serialization adapters
164
- startOptions.serializationAdapters.push(ServerFunctionSerializationAdapter)
165
-
166
- const requestHandlerMiddleware = handlerToMiddleware(
167
- async ({ context }) => {
168
- const response = await runWithStartContext(
169
- {
170
- getRouter,
171
- startOptions,
172
- contextAfterGlobalMiddlewares: context,
173
- request,
174
- },
175
- async () => {
176
- try {
177
- // First, let's attempt to handle server functions
178
- if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {
179
- return await handleServerAction({
180
- request,
181
- context: requestOpts?.context,
182
- })
183
- }
104
+ const getRouter = async () => {
105
+ if (router) return router
106
+ router = await (await getEntries()).routerEntry.getRouter()
107
+
108
+ // Update the client-side router with the history
109
+ const isPrerendering = process.env.TSS_PRERENDERING === 'true'
110
+ // env var is set during dev is SPA mode is enabled
111
+ let isShell = process.env.TSS_SHELL === 'true'
112
+ if (isPrerendering && !isShell) {
113
+ // only read the shell header if we are prerendering
114
+ // to avoid runtime behavior changes by injecting this header
115
+ // the header is set by the prerender plugin
116
+ isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'
117
+ }
184
118
 
185
- const executeRouter = async ({
186
- serverContext,
187
- }: {
188
- serverContext: any
189
- }) => {
190
- const requestAcceptHeader =
191
- request.headers.get('Accept') || '*/*'
192
- const splitRequestAcceptHeader = requestAcceptHeader.split(',')
193
-
194
- const supportedMimeTypes = ['*/*', 'text/html']
195
- const isRouterAcceptSupported = supportedMimeTypes.some(
196
- (mimeType) =>
197
- splitRequestAcceptHeader.some((acceptedMimeType) =>
198
- acceptedMimeType.trim().startsWith(mimeType),
199
- ),
200
- )
201
-
202
- if (!isRouterAcceptSupported) {
203
- return json(
204
- {
205
- error: 'Only HTML requests are supported here',
206
- },
207
- {
208
- status: 500,
209
- },
210
- )
211
- }
119
+ // Create a history for the client-side router
120
+ const history = createMemoryHistory({
121
+ initialEntries: [href],
122
+ })
123
+
124
+ router.update({
125
+ history,
126
+ isShell,
127
+ isPrerendering,
128
+ origin: router.options.origin ?? origin,
129
+ ...{
130
+ defaultSsr: requestStartOptions.defaultSsr,
131
+ serializationAdapters: [
132
+ ...requestStartOptions.serializationAdapters,
133
+ ...(router.options.serializationAdapters || []),
134
+ ],
135
+ },
136
+ basepath: ROUTER_BASEPATH,
137
+ })
138
+ return router
139
+ }
212
140
 
213
- // if the startRoutesManifest is not loaded yet, load it once
214
- if (startRoutesManifest === null) {
215
- startRoutesManifest = await getStartManifest()
141
+ const requestHandlerMiddleware = handlerToMiddleware(
142
+ async ({ context }) => {
143
+ const response = await runWithStartContext(
144
+ {
145
+ getRouter,
146
+ startOptions: requestStartOptions,
147
+ contextAfterGlobalMiddlewares: context,
148
+ request,
149
+ },
150
+ async () => {
151
+ try {
152
+ // First, let's attempt to handle server functions
153
+ if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {
154
+ return await handleServerAction({
155
+ request,
156
+ context: requestOpts?.context,
157
+ })
216
158
  }
217
- const router = await getRouter()
218
- attachRouterServerSsrUtils({
219
- router,
220
- manifest: startRoutesManifest,
221
- })
222
159
 
223
- router.update({ additionalContext: { serverContext } })
224
- await router.load()
160
+ const executeRouter = async ({
161
+ serverContext,
162
+ }: {
163
+ serverContext: any
164
+ }) => {
165
+ const requestAcceptHeader =
166
+ request.headers.get('Accept') || '*/*'
167
+ const splitRequestAcceptHeader =
168
+ requestAcceptHeader.split(',')
169
+
170
+ const supportedMimeTypes = ['*/*', 'text/html']
171
+ const isRouterAcceptSupported = supportedMimeTypes.some(
172
+ (mimeType) =>
173
+ splitRequestAcceptHeader.some((acceptedMimeType) =>
174
+ acceptedMimeType.trim().startsWith(mimeType),
175
+ ),
176
+ )
225
177
 
226
- // If there was a redirect, skip rendering the page at all
227
- if (router.state.redirect) {
228
- return router.state.redirect
178
+ if (!isRouterAcceptSupported) {
179
+ return json(
180
+ {
181
+ error: 'Only HTML requests are supported here',
182
+ },
183
+ {
184
+ status: 500,
185
+ },
186
+ )
187
+ }
188
+
189
+ // if the startRoutesManifest is not loaded yet, load it once
190
+ if (startRoutesManifest === null) {
191
+ startRoutesManifest = await getStartManifest()
192
+ }
193
+ const router = await getRouter()
194
+ attachRouterServerSsrUtils({
195
+ router,
196
+ manifest: startRoutesManifest,
197
+ })
198
+
199
+ router.update({ additionalContext: { serverContext } })
200
+ await router.load()
201
+
202
+ // If there was a redirect, skip rendering the page at all
203
+ if (router.state.redirect) {
204
+ return router.state.redirect
205
+ }
206
+
207
+ await router.serverSsr!.dehydrate()
208
+
209
+ const responseHeaders = getStartResponseHeaders({ router })
210
+ const response = await cb({
211
+ request,
212
+ router,
213
+ responseHeaders,
214
+ })
215
+
216
+ return response
229
217
  }
230
218
 
231
- await router.serverSsr!.dehydrate()
232
-
233
- const responseHeaders = getStartResponseHeaders({ router })
234
- const response = await cb({
219
+ const response = await handleServerRoutes({
220
+ getRouter,
235
221
  request,
236
- router,
237
- responseHeaders,
222
+ executeRouter,
223
+ context,
238
224
  })
239
225
 
240
226
  return response
241
- }
227
+ } catch (err) {
228
+ if (err instanceof Response) {
229
+ return err
230
+ }
242
231
 
243
- const response = await handleServerRoutes({
244
- getRouter,
245
- request,
246
- executeRouter,
247
- context,
248
- })
249
-
250
- return response
251
- } catch (err) {
252
- if (err instanceof Response) {
253
- return err
232
+ throw err
254
233
  }
234
+ },
235
+ )
236
+ return response
237
+ },
238
+ )
239
+
240
+ const flattenedMiddlewares = startOptions.requestMiddleware
241
+ ? flattenMiddlewares(startOptions.requestMiddleware)
242
+ : []
243
+ const middlewares = flattenedMiddlewares.map((d) => d.options.server)
244
+ const ctx = await executeMiddleware(
245
+ [...middlewares, requestHandlerMiddleware],
246
+ {
247
+ request,
248
+
249
+ context: requestOpts?.context || {},
250
+ },
251
+ )
252
+
253
+ const response: Response = ctx.response
254
+
255
+ if (isRedirect(response)) {
256
+ if (isResolvedRedirect(response)) {
257
+ if (request.headers.get('x-tsr-redirect') === 'manual') {
258
+ return json(
259
+ {
260
+ ...response.options,
261
+ isSerializedRedirect: true,
262
+ },
263
+ {
264
+ headers: response.headers,
265
+ },
266
+ )
267
+ }
268
+ return response
269
+ }
270
+ if (
271
+ response.options.to &&
272
+ typeof response.options.to === 'string' &&
273
+ !response.options.to.startsWith('/')
274
+ ) {
275
+ throw new Error(
276
+ `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(response.options)}`,
277
+ )
278
+ }
255
279
 
256
- throw err
257
- }
258
- },
259
- )
260
- return response
261
- },
262
- )
263
-
264
- const flattenedMiddlewares = startOptions.requestMiddleware
265
- ? flattenMiddlewares(startOptions.requestMiddleware)
266
- : []
267
- const middlewares = flattenedMiddlewares.map((d) => d.options.server)
268
- const ctx = await executeMiddleware(
269
- [...middlewares, requestHandlerMiddleware],
270
- {
271
- request,
272
-
273
- context: requestOpts?.context || {},
274
- },
275
- )
280
+ if (
281
+ ['params', 'search', 'hash'].some(
282
+ (d) => typeof (response.options as any)[d] === 'function',
283
+ )
284
+ ) {
285
+ throw new Error(
286
+ `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(
287
+ response.options,
288
+ )
289
+ .filter((d) => typeof (response.options as any)[d] === 'function')
290
+ .map((d) => `"${d}"`)
291
+ .join(', ')}`,
292
+ )
293
+ }
276
294
 
277
- const response: Response = ctx.response
295
+ const router = await getRouter()
296
+ const redirect = router.resolveRedirect(response)
278
297
 
279
- if (isRedirect(response)) {
280
- if (isResolvedRedirect(response)) {
281
298
  if (request.headers.get('x-tsr-redirect') === 'manual') {
282
299
  return json(
283
300
  {
@@ -289,52 +306,16 @@ export function createStartHandler<TRegister = Register>(
289
306
  },
290
307
  )
291
308
  }
292
- return response
293
- }
294
- if (
295
- response.options.to &&
296
- typeof response.options.to === 'string' &&
297
- !response.options.to.startsWith('/')
298
- ) {
299
- throw new Error(
300
- `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(response.options)}`,
301
- )
302
- }
303
309
 
304
- if (
305
- ['params', 'search', 'hash'].some(
306
- (d) => typeof (response.options as any)[d] === 'function',
307
- )
308
- ) {
309
- throw new Error(
310
- `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(
311
- response.options,
312
- )
313
- .filter((d) => typeof (response.options as any)[d] === 'function')
314
- .map((d) => `"${d}"`)
315
- .join(', ')}`,
316
- )
310
+ return redirect
317
311
  }
318
312
 
319
- const router = await getRouter()
320
- const redirect = router.resolveRedirect(response)
321
-
322
- if (request.headers.get('x-tsr-redirect') === 'manual') {
323
- return json(
324
- {
325
- ...response.options,
326
- isSerializedRedirect: true,
327
- },
328
- {
329
- headers: response.headers,
330
- },
331
- )
313
+ return response
314
+ } finally {
315
+ if (router) {
316
+ router = null
332
317
  }
333
-
334
- return redirect
335
318
  }
336
-
337
- return response
338
319
  }
339
320
 
340
321
  return requestHandler(startRequestResolver)