@tanstack/start-server-core 1.139.10 → 1.139.12

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.
@@ -38,6 +38,7 @@ function createStartHandler(cb) {
38
38
  };
39
39
  const startRequestResolver = async (request, requestOpts) => {
40
40
  let router = null;
41
+ let cbWillCleanup = false;
41
42
  try {
42
43
  const origin = getOrigin(request);
43
44
  const url = new URL(request.url);
@@ -131,6 +132,7 @@ function createStartHandler(cb) {
131
132
  }
132
133
  await router2.serverSsr.dehydrate();
133
134
  const responseHeaders = getStartResponseHeaders({ router: router2 });
135
+ cbWillCleanup = true;
134
136
  const response4 = await cb({
135
137
  request,
136
138
  router: router2,
@@ -212,9 +214,10 @@ function createStartHandler(cb) {
212
214
  }
213
215
  return response;
214
216
  } finally {
215
- if (router) {
216
- router = null;
217
+ if (router && !cbWillCleanup) {
218
+ router.serverSsr?.cleanup();
217
219
  }
220
+ router = null;
218
221
  }
219
222
  };
220
223
  return requestHandler(startRequestResolver);
@@ -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 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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"}
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 // Track whether the callback will handle cleanup\n let cbWillCleanup = false as boolean\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 // Mark that the callback will handle cleanup\n cbWillCleanup = true\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 && !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 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":";;;;;;;;;;AAyCA,SAAS,wBAAwB,MAA6B;AAC5D,QAAM,UAAU;AAAA,IACd;AAAA,MACE,gBAAgB;AAAA,IAAA;AAAA,IAElB,GAAG,KAAK,OAAO,MAAM,QAAQ,IAAI,CAAC,UAAU;AAC1C,aAAO,MAAM;AAAA,IACf,CAAC;AAAA,EAAA;AAEH,SAAO;AACT;AAEO,SAAS,mBACd,IAC2B;AAC3B,QAAM,kBAAkB,QAAQ,IAAI,uBAAuB;AAC3D,MAAI,sBAAuC;AAC3C,MAAI,aAAgC;AACpC,MAAI,cAAkC;AACtC,QAAM,aAAa,YAGb;AACJ,QAAI,gBAAgB,MAAM;AAExB,oBAAc,MAAM,OAAO,wBAAwB;AAAA,IACrD;AACA,QAAI,eAAe,MAAM;AAEvB,mBAAa,MAAM,OAAO,uBAAuB;AAAA,IACnD;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,QAAM,uBAAiD,OACrD,SACA,gBACG;AACH,QAAI,SAA2B;AAE/B,QAAI,gBAAgB;AACpB,QAAI;AACF,YAAM,SAAS,UAAU,OAAO;AAEhC,YAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,YAAM,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAE5C,YAAM,eACH,OAAO,MAAM,WAAA,GAAc,WAAW,eAAe,WAAA,KACrD,CAAA;AAEH,YAAM,wBAAwB;AAAA,QAC5B,GAAI,aAAa,yBAAyB,CAAA;AAAA,QAC1C;AAAA,MAAA;AAGF,YAAM,sBAAsB;AAAA,QAC1B,GAAG;AAAA,QACH;AAAA,MAAA;AAGF,YAAM,YAAY,YAAY;AAC5B,YAAI,OAAQ,QAAO;AACnB,iBAAS,OAAO,MAAM,WAAA,GAAc,YAAY,UAAA;AAGhD,cAAM,iBAAiB,QAAQ,IAAI,qBAAqB;AAExD,YAAI,UAAU,QAAQ,IAAI,cAAc;AACxC,YAAI,kBAAkB,CAAC,SAAS;AAI9B,oBAAU,QAAQ,QAAQ,IAAI,QAAQ,SAAS,MAAM;AAAA,QACvD;AAGA,cAAM,UAAU,oBAAoB;AAAA,UAClC,gBAAgB,CAAC,IAAI;AAAA,QAAA,CACtB;AAED,eAAO,OAAO;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ,OAAO,QAAQ,UAAU;AAAA,UACjC,GAAG;AAAA,YACD,YAAY,oBAAoB;AAAA,YAChC,uBAAuB;AAAA,cACrB,GAAG,oBAAoB;AAAA,cACvB,GAAI,OAAO,QAAQ,yBAAyB,CAAA;AAAA,YAAC;AAAA,UAC/C;AAAA,UAEF,UAAU;AAAA,QAAA,CACX;AACD,eAAO;AAAA,MACT;AAEA,YAAM,2BAA2B;AAAA,QAC/B,OAAO,EAAE,QAAA,MAAc;AACrB,gBAAMA,YAAW,MAAM;AAAA,YACrB;AAAA,cACE;AAAA,cACA,cAAc;AAAA,cACd,+BAA+B;AAAA,cAC/B;AAAA,YAAA;AAAA,YAEF,YAAY;AACV,kBAAI;AAEF,oBAAI,KAAK,WAAW,QAAQ,IAAI,kBAAkB,GAAG;AACnD,yBAAO,MAAM,mBAAmB;AAAA,oBAC9B;AAAA,oBACA,SAAS,aAAa;AAAA,kBAAA,CACvB;AAAA,gBACH;AAEA,sBAAM,gBAAgB,OAAO;AAAA,kBAC3B;AAAA,gBAAA,MAGI;AACJ,wBAAM,sBACJ,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AACnC,wBAAM,2BACJ,oBAAoB,MAAM,GAAG;AAE/B,wBAAM,qBAAqB,CAAC,OAAO,WAAW;AAC9C,wBAAM,0BAA0B,mBAAmB;AAAA,oBACjD,CAAC,aACC,yBAAyB;AAAA,sBAAK,CAAC,qBAC7B,iBAAiB,KAAA,EAAO,WAAW,QAAQ;AAAA,oBAAA;AAAA,kBAC7C;AAGJ,sBAAI,CAAC,yBAAyB;AAC5B,2BAAO;AAAA,sBACL;AAAA,wBACE,OAAO;AAAA,sBAAA;AAAA,sBAET;AAAA,wBACE,QAAQ;AAAA,sBAAA;AAAA,oBACV;AAAA,kBAEJ;AAGA,sBAAI,wBAAwB,MAAM;AAChC,0CAAsB,MAAM,iBAAA;AAAA,kBAC9B;AACA,wBAAMC,UAAS,MAAM,UAAA;AACrB,6CAA2B;AAAA,oBACzB,QAAAA;AAAAA,oBACA,UAAU;AAAA,kBAAA,CACX;AAEDA,0BAAO,OAAO,EAAE,mBAAmB,EAAE,cAAA,GAAiB;AACtD,wBAAMA,QAAO,KAAA;AAGb,sBAAIA,QAAO,MAAM,UAAU;AACzB,2BAAOA,QAAO,MAAM;AAAA,kBACtB;AAEA,wBAAMA,QAAO,UAAW,UAAA;AAExB,wBAAM,kBAAkB,wBAAwB,EAAE,QAAAA,SAAQ;AAE1D,kCAAgB;AAChB,wBAAMD,YAAW,MAAM,GAAG;AAAA,oBACxB;AAAA,oBACA,QAAAC;AAAAA,oBACA;AAAA,kBAAA,CACD;AAED,yBAAOD;AAAAA,gBACT;AAEA,sBAAMA,YAAW,MAAM,mBAAmB;AAAA,kBACxC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,gBAAA,CACD;AAED,uBAAOA;AAAAA,cACT,SAAS,KAAK;AACZ,oBAAI,eAAe,UAAU;AAC3B,yBAAO;AAAA,gBACT;AAEA,sBAAM;AAAA,cACR;AAAA,YACF;AAAA,UAAA;AAEF,iBAAOA;AAAAA,QACT;AAAA,MAAA;AAGF,YAAM,uBAAuB,aAAa,oBACtC,mBAAmB,aAAa,iBAAiB,IACjD,CAAA;AACJ,YAAM,cAAc,qBAAqB,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM;AACpE,YAAM,MAAM,MAAM;AAAA,QAChB,CAAC,GAAG,aAAa,wBAAwB;AAAA,QACzC;AAAA,UACE;AAAA,UAEA,SAAS,aAAa,WAAW,CAAA;AAAA,QAAC;AAAA,MACpC;AAGF,YAAM,WAAqB,IAAI;AAE/B,UAAI,WAAW,QAAQ,GAAG;AACxB,YAAI,mBAAmB,QAAQ,GAAG;AAChC,cAAI,QAAQ,QAAQ,IAAI,gBAAgB,MAAM,UAAU;AACtD,mBAAO;AAAA,cACL;AAAA,gBACE,GAAG,SAAS;AAAA,gBACZ,sBAAsB;AAAA,cAAA;AAAA,cAExB;AAAA,gBACE,SAAS,SAAS;AAAA,cAAA;AAAA,YACpB;AAAA,UAEJ;AACA,iBAAO;AAAA,QACT;AACA,YACE,SAAS,QAAQ,MACjB,OAAO,SAAS,QAAQ,OAAO,YAC/B,CAAC,SAAS,QAAQ,GAAG,WAAW,GAAG,GACnC;AACA,gBAAM,IAAI;AAAA,YACR,oNAAoN,KAAK,UAAU,SAAS,OAAO,CAAC;AAAA,UAAA;AAAA,QAExP;AAEA,YACE,CAAC,UAAU,UAAU,MAAM,EAAE;AAAA,UAC3B,CAAC,MAAM,OAAQ,SAAS,QAAgB,CAAC,MAAM;AAAA,QAAA,GAEjD;AACA,gBAAM,IAAI;AAAA,YACR,+IAA+I,OAAO;AAAA,cACpJ,SAAS;AAAA,YAAA,EAER,OAAO,CAAC,MAAM,OAAQ,SAAS,QAAgB,CAAC,MAAM,UAAU,EAChE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EACnB,KAAK,IAAI,CAAC;AAAA,UAAA;AAAA,QAEjB;AAEA,cAAMC,UAAS,MAAM,UAAA;AACrB,cAAM,WAAWA,QAAO,gBAAgB,QAAQ;AAEhD,YAAI,QAAQ,QAAQ,IAAI,gBAAgB,MAAM,UAAU;AACtD,iBAAO;AAAA,YACL;AAAA,cACE,GAAG,SAAS;AAAA,cACZ,sBAAsB;AAAA,YAAA;AAAA,YAExB;AAAA,cACE,SAAS,SAAS;AAAA,YAAA;AAAA,UACpB;AAAA,QAEJ;AAEA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT,UAAA;AACE,UAAI,UAAU,CAAC,eAAe;AAK5B,eAAO,WAAW,QAAA;AAAA,MACpB;AACA,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO,eAAe,oBAAoB;AAC5C;AAEA,eAAe,mBAAmB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,SAAS,MAAM,UAAA;AACrB,MAAI,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC7B,QAAM,oBAAoB,OAAO,SAAS,GAAG;AAC7C,QAAM,WAAW,IAAI;AACrB,QAAM,EAAE,eAAe,YAAY,gBACjC,OAAO,iBAAiB,QAAQ;AAIlC,QAAM,cAAc;AAAA,IAClB,cAAc,QAAQ,CAAC,MAAM,EAAE,QAAQ,QAAQ,UAAU,EAAE,OAAO,OAAO;AAAA,EAAA,EACzE,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM;AAE7B,QAAM,SAAS,YAAY,QAAQ;AAEnC,MAAI,QAAQ;AACV,QAAI,OAAO,UAAU;AACnB,YAAM,WACJ,OAAO,OAAO,aAAa,aACvB,OAAO,SAAS;AAAA,QACd,gBAAgB,CAAC,MAAW;AAAA,MAAA,CAC7B,IACD,OAAO;AAEb,YAAM,gBAAgB,QAAQ,OAAO,YAAA;AAGrC,YAAM,UAAU,SAAS,aAAa,KAAK,SAAS,KAAK;AAGzD,UAAI,SAAS;AACX,cAAM,WAAW,CAAC,CAAC,WAAW,QAAQ;AACtC,YAAI,OAAO,YAAY,YAAY;AACjC,sBAAY,KAAK,oBAAoB,SAAS,QAAQ,CAAC;AAAA,QACzD,OAAO;AACL,gBAAM,EAAE,eAAe;AACvB,cAAI,cAAc,WAAW,QAAQ;AACnC,wBAAY;AAAA,cACV,GAAG,mBAAmB,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM;AAAA,YAAA;AAAA,UAEjE;AACA,cAAI,QAAQ,SAAS;AACnB,wBAAY,KAAK,oBAAoB,QAAQ,SAAS,QAAQ,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,cAAY;AAAA,IACV,oBAAoB,CAACC,SAAQ,cAAc,EAAE,eAAeA,KAAI,SAAS,CAAC;AAAA,EAAA;AAG5E,QAAM,MAAM,MAAM,kBAAkB,aAAa;AAAA,IAC/C;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EAAA,CACD;AAED,QAAM,WAAqB,IAAI;AAE/B,SAAO;AACT;AAEA,SAAS,yBAAyB;AAChC,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,QAAM,IAAI,MAAM,uBAAuB;AACzC;AAEA,SAAS,qBAAqB;AAC5B,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,QAAM,IAAI,MAAM,uBAAuB;AACzC;AACA,SAAS,oBACP,SACA,WAAoB,OACpB;AACA,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AACA,SAAO,OAAO,EAAE,MAAM,OAAO,GAAG,WAAiB;AAC/C,UAAM,WAAW,MAAM,QAAQ,EAAE,GAAG,MAAM,MAAM,oBAAoB;AACpE,QAAI,CAAC,UAAU;AACb,6BAAA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,aAAmB,KAAW;AACvD,MAAI,QAAQ;AAEZ,QAAM,OAAO,OAAOA,SAAc;AAChC;AACA,UAAM,aAAa,YAAY,KAAK;AACpC,QAAI,CAAC,WAAY,QAAOA;AAExB,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,WAAW;AAAA,QACxB,GAAGA;AAAAA;AAAAA,QAEH,MAAM,OAAO,YAAkB;AAE7B,gBAAM,aAAa,MAAM,KAAK;AAAA,YAC5B,GAAGA;AAAAA,YACH,GAAG;AAAA,YACH,SAAS;AAAA,cACP,GAAGA,KAAI;AAAA,cACP,GAAI,SAAS,WAAW,CAAA;AAAA,YAAC;AAAA,UAC3B,CACD;AAGD,iBAAO,OAAO,OAAOA,MAAK,gBAAgB,UAAU,CAAC;AAAA,QACvD;AAAA;AAAA,MAAA,CAED;AAAA,IACH,SAAS,KAAW;AAClB,UAAI,kBAAkB,GAAG,GAAG;AAC1B,iBAAS;AAAA,UACP,UAAU;AAAA,QAAA;AAAA,MAEd,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAIA,WAAO,OAAO,OAAOA,MAAK,gBAAgB,MAAM,CAAC;AAAA,EACnD;AAEA,SAAO,gBAAgB,KAAK,GAAG,CAAC;AAClC;AAEA,SAAS,gBAAgB,QAAc;AACrC,MAAI,kBAAkB,MAAM,GAAG;AAC7B,WAAO;AAAA,MACL,UAAU;AAAA,IAAA;AAAA,EAEd;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,KAAW;AACpC,SAAO,WAAW,GAAG,KAAK,WAAW,GAAG;AAC1C;AAEA,SAAS,WAAW,UAA0C;AAC5D,SAAO,oBAAoB;AAC7B;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-server-core",
3
- "version": "1.139.10",
3
+ "version": "1.139.12",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -64,9 +64,9 @@
64
64
  "seroval": "^1.4.0",
65
65
  "tiny-invariant": "^1.3.3",
66
66
  "@tanstack/history": "1.139.0",
67
- "@tanstack/router-core": "1.139.10",
68
- "@tanstack/start-client-core": "1.139.10",
69
- "@tanstack/start-storage-context": "1.139.10"
67
+ "@tanstack/start-client-core": "1.139.12",
68
+ "@tanstack/router-core": "1.139.12",
69
+ "@tanstack/start-storage-context": "1.139.12"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@standard-schema/spec": "^1.0.0",
@@ -81,6 +81,8 @@ export function createStartHandler<TRegister = Register>(
81
81
  requestOpts,
82
82
  ) => {
83
83
  let router: AnyRouter | null = null as AnyRouter | null
84
+ // Track whether the callback will handle cleanup
85
+ let cbWillCleanup = false as boolean
84
86
  try {
85
87
  const origin = getOrigin(request)
86
88
 
@@ -207,6 +209,8 @@ export function createStartHandler<TRegister = Register>(
207
209
  await router.serverSsr!.dehydrate()
208
210
 
209
211
  const responseHeaders = getStartResponseHeaders({ router })
212
+ // Mark that the callback will handle cleanup
213
+ cbWillCleanup = true
210
214
  const response = await cb({
211
215
  request,
212
216
  router,
@@ -312,9 +316,14 @@ export function createStartHandler<TRegister = Register>(
312
316
 
313
317
  return response
314
318
  } finally {
315
- if (router) {
316
- router = null
319
+ if (router && !cbWillCleanup) {
320
+ // Clean up router SSR state if it was set up but won't be cleaned up by the callback
321
+ // (e.g., in redirect cases or early returns before the callback is invoked).
322
+ // When the callback runs, it handles cleanup (either via transformStreamWithRouter
323
+ // for streaming, or directly in renderRouterToString for non-streaming).
324
+ router.serverSsr?.cleanup()
317
325
  }
326
+ router = null
318
327
  }
319
328
  }
320
329