@tanstack/start-server-core 1.167.16 → 1.167.18

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.
@@ -280,7 +280,8 @@ function createStartHandler(cbOrOptions) {
280
280
  attachRouterServerSsrUtils({
281
281
  router: routerInstance,
282
282
  manifest,
283
- getRequestAssets: () => getStartContext({ throwIfNotFound: false })?.requestAssets
283
+ getRequestAssets: () => getStartContext({ throwIfNotFound: false })?.requestAssets,
284
+ includeUnmatchedRouteAssets: false
284
285
  });
285
286
  routerInstance.update({ additionalContext: { serverContext } });
286
287
  await routerInstance.load();
@@ -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'\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 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\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 const routerInstance = await getRouter()\n\n attachRouterServerSsrUtils({\n router: routerInstance,\n manifest,\n getRequestAssets: () =>\n getStartContext({ throwIfNotFound: false })?.requestAssets,\n })\n\n routerInstance.update({ additionalContext: { serverContext } })\n await routerInstance.load()\n\n if (routerInstance.state.redirect) {\n return routerInstance.state.redirect\n }\n\n // 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 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 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 const handler = handlers[requestMethod] ?? handlers['ANY']\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 return ctx.response\n}\n"],"mappings":";;;;;;;;;;;;AAuMA,SAAS,wBAAwB,MAA6B;AAS5D,QARgB,aACd,EACE,gBAAgB,4BACjB,EACD,GAAG,KAAK,OAAO,OAAO,QAAQ,KAAK,CAAC,KAAK,UAAU;AACjD,SAAO,MAAM;GACb,CACH;;AAiBH,IAAI;AACJ,IAAI;;;;;AAMJ,IAAI;AAEJ,eAAe,cAAgC;CAC7C,MAAM,CAAC,aAAa,YAAY,kBAAkB,MAAM,QAAQ,IAAI;EAElE,OAAO;EAEP,OAAO;EAEP,OAAO;EACR,CAAC;AACF,QAAO;EACQ;EACD;EACI;EACjB;;AAGH,SAAS,aAAa;AACpB,KAAI,CAAC,eACH,kBAAiB,aAAa;AAEhC,QAAO;;;;;;AAOT,SAAS,gBACP,eACuC;AAEvC,KAAI,QAAQ,IAAI,mBAAmB,OACjC,QAAO,iBAAiB,cAAc;AAGxC,KAAI,CAAC,oBACH,uBAAsB,kBAAkB;AAE1C,QAAO;;;;;;;;;AAUT,eAAe,gBACb,eACA,aACA,OACmB;CACnB,MAAM,OAAO,MAAM,gBAAgB,cAAc;CAEjD,MAAM,uBAAuB,YAAY;AACvC,SAAO,cACH,MAAM,wBAAwB,MAAM,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,GACnE,6BAA6B,KAAK;;AAIxC,KAAI,QAAQ,IAAI,mBAAmB,OACjC,QAAO,sBAAsB;AAI/B,KAAI,CAAC,eAAe,OAAO;AACzB,MAAI,CAAC,2BACH,8BAA6B,sBAAsB;AAErD,SAAO;;AAIT,QAAO,sBAAsB;;AAI/B,IAAM,kBAAkB,QAAQ,IAAI,uBAAuB;AAC3D,IAAM,iBAAiB,QAAQ,IAAI;AACnC,IAAM,kBAAkB,QAAQ,IAAI,qBAAqB;AACzD,IAAM,eAAe,QAAQ,IAAI,cAAc;AAC/C,IAAM,SAAA,QAAA,IAAA,aAAkC;AAGxC,IAAM,kBAAkB,SACpB,2KACA;AAEJ,IAAM,eAAe,SACjB,uFACA;AAEJ,SAAS,yBAAgC;AACvC,OAAM,IAAI,MAAM,gBAAgB;;AAGlC,SAAS,qBAA4B;AACnC,OAAM,IAAI,MAAM,aAAa;;;;;AAM/B,SAAS,kBAAkB,OAAmC;AAC5D,QAAO,iBAAiB,YAAY,WAAW,MAAM;;;;;AAMvD,SAAS,gBAAgB,QAAc;AACrC,KAAI,kBAAkB,OAAO,CAC3B,QAAO,EAAE,UAAU,QAAQ;AAE7B,QAAO;;;;;AAMT,SAAS,kBAAkB,aAA0B,KAA0B;CAC7E,IAAI,QAAQ;CAEZ,MAAM,OAAO,OAAO,YAAkC;AAEpD,MAAI,SAAS;AACX,OAAI,QAAQ,QACV,KAAI,UAAU,gBAAgB,IAAI,SAAS,QAAQ,QAAQ;AAG7D,QAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,QAAQ,UACV,KAAI,OAAO,QAAQ;;AAKzB;EACA,MAAM,aAAa,YAAY;AAC/B,MAAI,CAAC,WAAY,QAAO;EAExB,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,WAAW;IAAE,GAAG;IAAK;IAAM,CAAC;WACpC,KAAK;AACZ,OAAI,kBAAkB,IAAI,EAAE;AAC1B,QAAI,WAAW;AACf,WAAO;;AAET,SAAM;;EAGR,MAAM,aAAa,gBAAgB,OAAO;AAC1C,MAAI,YAAY;AACd,OAAI,WAAW,aAAa,KAAA,EAC1B,KAAI,WAAW,WAAW;AAE5B,OAAI,WAAW,QACb,KAAI,UAAU,gBAAgB,IAAI,SAAS,WAAW,QAAQ;;AAIlE,SAAO;;AAGT,QAAO,MAAM;;;;;AAMf,SAAS,oBACP,SACA,WAAoB,OACd;AACN,KAAI,SACF,QAAO;AAET,QAAO,OAAO,QAAc;EAC1B,MAAM,WAAW,MAAM,QAAQ;GAAE,GAAG;GAAK,MAAM;GAAoB,CAAC;AACpE,MAAI,CAAC,SACH,yBAAwB;AAE1B,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCX,SAAgB,mBACd,aAC2B;CAE3B,MAAM,KACJ,OAAO,gBAAgB,aAAa,cAAc,YAAY;CAChE,MAAM,wBACJ,OAAO,gBAAgB,aAAa,KAAA,IAAY,YAAY;CAC9D,MAAM,2BACJ,OAAO,gBAAgB,aACnB,KAAA,IACA,YAAY;CAElB,MAAM,kBACJ,0BAA0B,KAAA,IACtB,6BAA6B,sBAAsB,GACnD,6BAA6B,KAAA,IAC3B,6BACE,+CACE,yBACD,CACF,GACD,KAAA;CAER,MAAM,0BACH,CAAC,CAAC,yBACD,OAAO,0BAA0B,YACjC,YAAY,yBACZ,sBAAsB,WAAW,QAClC,CAAC,CAAC,4BACD,OAAO,6BAA6B,YACpC,yBAAyB,WAAW;CAGxC,MAAM,0BAA0B;CAChC,MAAM,QAAQ,0BAA0B,wBAAwB,QAAQ;CACxE,MAAM,6BACJ,SAAS,QAAQ,IAAI,mBAAmB;CAI1C,IAAI;CAEJ,MAAM,iBAAiB,OACrB,SAC2C;AAC3C,MAAI,CAAC,wBAAyB,QAAO,KAAA;AAErC,MAAI,wBAAwB,SAAS,mBAAmB;AACtD,OAAI,4BAA4B;AAC9B,QAAI,CAAC,6BACH,gCAA+B,QAAQ,QACrC,wBAAwB,gBAAgB,KAAK,CAC9C,CAAC,OAAO,UAAU;AACjB,oCAA+B,KAAA;AAC/B,WAAM;MACN;AAGJ,WAAO;;AAGT,UAAO,wBAAwB,gBAAgB,KAAK;;AAGtD,SAAO,wBAAwB;;AAIjC,KACE,2BACA,SACA,QAAQ,IAAI,mBAAmB,UAC/B,CAAC,4BACD;EAKA,MAAM,iBAAiB,YAAY;GACjC,MAAM,OAAO,MAAM,gBAAgB,KAAA,EAAU;GAC7C,MAAM,cAAc,MAAM,eAAe,EAAE,QAAQ,MAAM,CAAC;AAC1D,UAAO,cACH,MAAM,wBAAwB,MAAM,aAAa,EAAE,OAAO,OAAO,CAAC,GAClE,6BAA6B,KAAK;MACpC;AACJ,+BAA6B;AAC7B,gBAAc,YAAY;AAExB,OAAI,+BAA+B,cACjC,8BAA6B,KAAA;AAE/B,kCAA+B,KAAA;IAC/B;;CAGJ,MAAM,uBAAiD,OACrD,SACA,gBACG;EACH,IAAI,SAA2B;EAC/B,IAAI,gBAAgB;AAEpB,MAAI;GAIF,MAAM,EAAE,KAAK,+BAA+B,iBAAiB,QAAQ,IAAI;GACzE,MAAM,OAAO,IAAI,WAAW,IAAI,SAAS,IAAI;GAC7C,MAAM,SAAS,UAAU,QAAQ;AAEjC,OAAI,2BACF,QAAO,SAAS,SAAS,KAAK,IAAI;GAGpC,MAAM,UAAU,MAAM,YAAY;GAClC,MAAM,eACH,MAAM,QAAQ,WAAW,eAAe,YAAY,IACpD,EAAE;GAEL,MAAM,EAAE,mBAAmB,gCACzB,QAAQ;GAEV,MAAM,wBAAwB;IAC5B,GAAI,aAAa,yBAAyB,EAAE;IAC5C,GAAI,oBAAoB,8BAA8B,EAAE;IACxD;IACD;GAED,MAAM,sBAAsB;IAC1B,GAAG;IACH;IACD;GAGD,MAAM,8BAA8B,aAAa,oBAC7C,mBAAmB,aAAa,kBAAkB,GAClD,EAAE;GAGN,MAAM,6BAA6B,IAAI,IACrC,4BACD;GAGD,MAAM,YAAY,YAAgC;AAChD,QAAI,OAAQ,QAAO;AAEnB,aAAS,MAAM,QAAQ,YAAY,WAAW;IAE9C,IAAI,UAAU;AACd,QAAI,mBAAmB,CAAC,QACtB,WAAU,QAAQ,QAAQ,IAAI,QAAQ,UAAU,KAAK;IAGvD,MAAM,UAAU,oBAAoB,EAClC,gBAAgB,CAAC,KAAK,EACvB,CAAC;AAEF,WAAO,OAAO;KACZ;KACA;KACA,gBAAgB;KAChB,QAAQ,OAAO,QAAQ,UAAU;KAE/B,YAAY,oBAAoB;KAChC,uBAAuB,CACrB,GAAG,oBAAoB,uBACvB,GAAI,OAAO,QAAQ,yBAAyB,EAAE,CAC/C;KAEH,UAAU;KACX,CAAC;AAEF,WAAO;;AAIT,OAAI,kBAAkB,IAAI,SAAS,WAAW,eAAe,EAAE;IAC7D,MAAM,aAAa,IAAI,SACpB,MAAM,eAAe,OAAO,CAC5B,MAAM,IAAI,CAAC;AAEd,QAAI,CAAC,WACH,OAAM,IAAI,MAAM,6CAA6C;IAG/D,MAAM,kBAAkB,OAAO,EAAE,cAAoB;AACnD,YAAO,oBACL;MACE;MACA,cAAc;MACd,+BAA+B;MAC/B;MACA;MACA,aAAa;MACd,QAEC,mBAAmB;MACjB;MACA,SAAS,aAAa;MACtB;MACD,CAAC,CACL;;AAYH,WAAO,wBANK,MAAM,kBAAkB,CAAC,GAHjB,4BAA4B,KAC7C,MAAM,EAAE,QAAQ,OAClB,EACoD,gBAAgB,EAAE;KACrE;KACA,UAAU,IAAI;KACd,SAAS,sBAAsB,aAAa,QAAQ;KACrD,CAAC,EAEgC,UAAU,SAAS,UAAU;;GAIjE,MAAM,gBAAgB,OACpB,eACA,kBACsB;IAEtB,MAAM,eADe,QAAQ,QAAQ,IAAI,SAAS,IAAI,OACrB,MAAM,IAAI;AAO3C,QAAI,CANuB,CAAC,OAAO,YAAY,CAER,MAAM,aAC3C,YAAY,MAAM,SAAS,KAAK,MAAM,CAAC,WAAW,SAAS,CAAC,CAC7D,CAGC,QAAO,SAAS,KACd,EAAE,OAAO,yCAAyC,EAClD,EAAE,QAAQ,KAAK,CAChB;IAGH,MAAM,WAAW,MAAM,gBACrB,eACA,MAAM,eAAe;KAAE,QAAQ;KAAO;KAAS,CAAC,EAChD,MACD;IACD,MAAM,iBAAiB,MAAM,WAAW;AAExC,+BAA2B;KACzB,QAAQ;KACR;KACA,wBACE,gBAAgB,EAAE,iBAAiB,OAAO,CAAC,EAAE;KAChD,CAAC;AAEF,mBAAe,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,CAAC;AAC/D,UAAM,eAAe,MAAM;AAE3B,QAAI,eAAe,MAAM,SACvB,QAAO,eAAe,MAAM;IAI9B,MAAM,MAAM,gBAAgB,EAAE,iBAAiB,OAAO,CAAC;AACvD,UAAM,eAAe,UAAW,UAAU,EACxC,eAAe,KAAK,eACrB,CAAC;IAEF,MAAM,kBAAkB,wBAAwB,EAC9C,QAAQ,gBACT,CAAC;AACF,oBAAgB;AAEhB,WAAO,GAAG;KACR;KACA,QAAQ;KACR;KACD,CAAC;;GAIJ,MAAM,2BAA2B,OAAO,EAAE,cAAoB;AAC5D,WAAO,oBACL;KACE;KACA,cAAc;KACd,+BAA+B;KAC/B;KACA;KACA,aAAa;KACd,EACD,YAAY;AACV,SAAI;AACF,aAAO,MAAM,mBAAmB;OAC9B;OACA;OACA;OACA;OACA;OACA;OACD,CAAC;cACK,KAAK;AACZ,UAAI,eAAe,SACjB,QAAO;AAET,YAAM;;MAGX;;AAeH,UAAO,wBATK,MAAM,kBAChB,CAAC,GAJiB,4BAA4B,KAC7C,MAAM,EAAE,QAAQ,OAClB,EAEkB,yBAAyB,EAC1C;IACE;IACA,UAAU,IAAI;IACd,SAAS,sBAAsB,aAAa,QAAQ;IACrD,CACF,EAEiC,UAAU,SAAS,UAAU;YACvD;AACR,OAAI,UAAU,CAAC,cAKb,QAAO,WAAW,SAAS;AAE7B,YAAS;;;AAIb,QAAO,eAAe,qBAAqB;;AAG7C,eAAe,uBACb,UACA,SACA,WACmB;AACnB,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;AAGT,KAAI,mBAAmB,SAAS,EAAE;AAChC,MAAI,QAAQ,QAAQ,IAAI,iBAAiB,KAAK,OAC5C,QAAO,SAAS,KACd;GAAE,GAAG,SAAS;GAAS,sBAAsB;GAAM,EACnD,EAAE,SAAS,SAAS,SAAS,CAC9B;AAEH,SAAO;;CAGT,MAAM,OAAO,SAAS;AACtB,KAAI,KAAK,MAAM,OAAO,KAAK,OAAO,YAAY,CAAC,KAAK,GAAG,WAAW,IAAI,CACpE,OAAM,IAAI,MACR,oNAAoN,KAAK,UAAU,KAAK,GACzO;AAGH,KACE;EAAC;EAAU;EAAU;EAAO,CAAC,MAC1B,MAAM,OAAQ,KAAc,OAAO,WACrC,CAED,OAAM,IAAI,MACR,+IAA+I,OAAO,KACpJ,KACD,CACE,QAAQ,MAAM,OAAQ,KAAc,OAAO,WAAW,CACtD,KAAK,MAAM,IAAI,EAAE,GAAG,CACpB,KAAK,KAAK,GACd;CAIH,MAAM,YADS,MAAM,WAAW,EACR,gBAAgB,SAAS;AAEjD,KAAI,QAAQ,QAAQ,IAAI,iBAAiB,KAAK,OAC5C,QAAO,SAAS,KACd;EAAE,GAAG,SAAS;EAAS,sBAAsB;EAAM,EACnD,EAAE,SAAS,SAAS,SAAS,CAC9B;AAGH,QAAO;;AAGT,eAAe,mBAAmB,EAChC,WACA,SACA,KACA,eACA,SACA,8BAWoB;CACpB,MAAM,SAAS,MAAM,WAAW;CAEhC,MAAM,WADe,oBAAoB,OAAO,SAAS,IAAI,CAC/B;CAI9B,MAAM,EAAE,eAAe,YAAY,gBACjC,OAAO,iBAAiB,SAAS;CAEnC,MAAM,eAAe,cAAc,YAAY,UAAU,KAAA;CAGzD,MAAM,mBAAiD,EAAE;AAIzD,MAAK,MAAM,SAAS,eAAe;EACjC,MAAM,mBAAmB,MAAM,QAAQ,QAAQ;AAG/C,MAAI,kBAAkB;GACpB,MAAM,YAAY,mBAAmB,iBAAiB;AACtD,QAAK,MAAM,KAAK,UACd,KAAI,CAAC,2BAA2B,IAAI,EAAE,CACpC,kBAAiB,KAAK,EAAE,QAAQ,OAAO;;;CAO/C,MAAM,SAAS,YAAY,QAAQ;AACnC,KAAI,QAAQ,YAAY,cAAc;EACpC,MAAM,WACJ,OAAO,OAAO,aAAa,aACvB,OAAO,SAAS,EAAE,iBAAiB,MAAW,GAAG,CAAC,GAClD,OAAO;EAGb,MAAM,UAAU,SADM,QAAQ,OAAO,aAAa,KACP,SAAS;AAEpD,MAAI,SAAS;GACX,MAAM,WAAW,CAAC,CAAC,WAAW,QAAQ;AAEtC,OAAI,OAAO,YAAY,WACrB,kBAAiB,KAAK,oBAAoB,SAAS,SAAS,CAAC;QACxD;AACL,QAAI,QAAQ,YAAY,QAAQ;KAC9B,MAAM,qBAAqB,mBAAmB,QAAQ,WAAW;AACjE,UAAK,MAAM,KAAK,mBACd,kBAAiB,KAAK,EAAE,QAAQ,OAAO;;AAG3C,QAAI,QAAQ,QACV,kBAAiB,KAAK,oBAAoB,QAAQ,SAAS,SAAS,CAAC;;;;AAO7E,kBAAiB,MAAM,QACrB,cAAc,IAAI,SAAS,cAAc,CAC1C;AASD,SAPY,MAAM,kBAAkB,kBAAkB;EACpD;EACA;EACA,QAAQ;EACR;EACD,CAAC,EAES"}
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'\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 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\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 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 // 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 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 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 const handler = handlers[requestMethod] ?? handlers['ANY']\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 return ctx.response\n}\n"],"mappings":"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"}
@@ -119,5 +119,6 @@ export declare function getResponse(): {
119
119
  status?: number;
120
120
  statusText?: string;
121
121
  get headers(): Headers;
122
+ get errHeaders(): Headers;
122
123
  };
123
124
  export declare function getValidatedQuery<TSchema extends StandardSchemaV1>(schema: StandardSchemaV1): Promise<StandardSchemaV1.InferOutput<TSchema>>;
@@ -33,7 +33,16 @@ function attachResponseHeaders(value, event) {
33
33
  }
34
34
  function requestHandler(handler) {
35
35
  return (request, requestOpts) => {
36
- const h3Event = new H3Event(request);
36
+ let h3Event;
37
+ try {
38
+ h3Event = new H3Event(request);
39
+ } catch (error) {
40
+ if (error instanceof URIError) return new Response(null, {
41
+ status: 400,
42
+ statusText: "Bad Request"
43
+ });
44
+ throw error;
45
+ }
37
46
  return toResponse(attachResponseHeaders(eventStorage.run({ h3Event }, () => handler(request, requestOpts)), h3Event), h3Event);
38
47
  };
39
48
  }
@@ -125,7 +134,10 @@ function setResponseStatus(code, text) {
125
134
  * ```
126
135
  */
127
136
  function getCookies() {
128
- return parseCookies(getH3Event());
137
+ const cookies = parseCookies(getH3Event());
138
+ const definedCookies = Object.create(null);
139
+ for (const [name, value] of Object.entries(cookies)) if (value !== void 0) definedCookies[name] = value;
140
+ return definedCookies;
129
141
  }
130
142
  /**
131
143
  * Get a cookie value by name.
@@ -136,7 +148,7 @@ function getCookies() {
136
148
  * ```
137
149
  */
138
150
  function getCookie(name) {
139
- return getCookies()[name] || void 0;
151
+ return getCookies()[name];
140
152
  }
141
153
  /**
142
154
  * Set a cookie value by name.
@@ -1 +1 @@
1
- {"version":3,"file":"request-response.js","names":[],"sources":["../../src/request-response.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport {\n H3Event,\n clearSession as h3_clearSession,\n deleteCookie as h3_deleteCookie,\n getRequestHost as h3_getRequestHost,\n getRequestIP as h3_getRequestIP,\n getRequestProtocol as h3_getRequestProtocol,\n getRequestURL as h3_getRequestURL,\n getSession as h3_getSession,\n getValidatedQuery as h3_getValidatedQuery,\n parseCookies as h3_parseCookies,\n sanitizeStatusCode as h3_sanitizeStatusCode,\n sanitizeStatusMessage as h3_sanitizeStatusMessage,\n sealSession as h3_sealSession,\n setCookie as h3_setCookie,\n toResponse as h3_toResponse,\n unsealSession as h3_unsealSession,\n updateSession as h3_updateSession,\n useSession as h3_useSession,\n} from 'h3-v2'\nimport type {\n RequestHeaderMap,\n RequestHeaderName,\n ResponseHeaderMap,\n ResponseHeaderName,\n TypedHeaders,\n} from 'fetchdts'\n\nimport type { CookieSerializeOptions } from 'cookie-es'\nimport type {\n Session,\n SessionConfig,\n SessionData,\n SessionManager,\n SessionUpdate,\n} from './session'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { RequestHandler } from './request-handler'\n\ninterface StartEvent {\n h3Event: H3Event\n}\n\n// Use a global symbol to ensure the same AsyncLocalStorage instance is shared\n// across different bundles that may each bundle this module.\nconst GLOBAL_EVENT_STORAGE_KEY = Symbol.for('tanstack-start:event-storage')\n\nconst globalObj = globalThis as typeof globalThis & {\n [GLOBAL_EVENT_STORAGE_KEY]?: AsyncLocalStorage<StartEvent>\n}\n\nif (!globalObj[GLOBAL_EVENT_STORAGE_KEY]) {\n globalObj[GLOBAL_EVENT_STORAGE_KEY] = new AsyncLocalStorage<StartEvent>()\n}\n\nconst eventStorage = globalObj[GLOBAL_EVENT_STORAGE_KEY]\n\nexport type { ResponseHeaderName, RequestHeaderName }\n\ntype HeadersWithGetSetCookie = Headers & {\n getSetCookie?: () => Array<string>\n}\n\ntype MaybePromise<T> = T | Promise<T>\n\nfunction isPromiseLike<T>(value: MaybePromise<T>): value is Promise<T> {\n return typeof (value as Promise<T>).then === 'function'\n}\n\nfunction getSetCookieValues(headers: Headers): Array<string> {\n const headersWithSetCookie = headers as HeadersWithGetSetCookie\n if (typeof headersWithSetCookie.getSetCookie === 'function') {\n return headersWithSetCookie.getSetCookie()\n }\n const value = headers.get('set-cookie')\n return value ? [value] : []\n}\n\nfunction mergeEventResponseHeaders(response: Response, event: H3Event): void {\n if (response.ok) {\n return\n }\n\n const eventSetCookies = getSetCookieValues(event.res.headers)\n if (eventSetCookies.length === 0) {\n return\n }\n\n const responseSetCookies = getSetCookieValues(response.headers)\n response.headers.delete('set-cookie')\n for (const cookie of responseSetCookies) {\n response.headers.append('set-cookie', cookie)\n }\n for (const cookie of eventSetCookies) {\n response.headers.append('set-cookie', cookie)\n }\n}\n\nfunction attachResponseHeaders<T>(\n value: MaybePromise<T>,\n event: H3Event,\n): MaybePromise<T> {\n if (isPromiseLike(value)) {\n return value.then((resolved) => {\n if (resolved instanceof Response) {\n mergeEventResponseHeaders(resolved, event)\n }\n return resolved\n })\n }\n\n if (value instanceof Response) {\n mergeEventResponseHeaders(value, event)\n }\n\n return value\n}\n\nexport function requestHandler<TRegister = unknown>(\n handler: RequestHandler<TRegister>,\n) {\n return (request: Request, requestOpts: any): Promise<Response> | Response => {\n const h3Event = new H3Event(request)\n\n const response = eventStorage.run({ h3Event }, () =>\n handler(request, requestOpts),\n )\n return h3_toResponse(attachResponseHeaders(response, h3Event), h3Event)\n }\n}\n\nfunction getH3Event() {\n const event = eventStorage.getStore()\n if (!event) {\n throw new Error(\n `No StartEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.`,\n )\n }\n return event.h3Event\n}\n\nexport function getRequest(): Request {\n const event = getH3Event()\n return event.req\n}\n\nexport function getRequestHeaders(): TypedHeaders<RequestHeaderMap> {\n // TODO `as any` not needed when fetchdts is updated\n return getH3Event().req.headers as any\n}\n\nexport function getRequestHeader(name: RequestHeaderName): string | undefined {\n return getRequestHeaders().get(name) || undefined\n}\n\nexport function getRequestIP(opts?: {\n /**\n * Use the X-Forwarded-For HTTP header set by proxies.\n *\n * Note: Make sure that this header can be trusted (your application running behind a CDN or reverse proxy) before enabling.\n */\n xForwardedFor?: boolean\n}) {\n return h3_getRequestIP(getH3Event(), opts)\n}\n\n/**\n * Get the request hostname.\n *\n * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.\n *\n * If no host header is found, it will default to \"localhost\".\n */\nexport function getRequestHost(opts?: { xForwardedHost?: boolean }) {\n return h3_getRequestHost(getH3Event(), opts)\n}\n\n/**\n * Get the full incoming request URL.\n *\n * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.\n *\n * If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.\n */\nexport function getRequestUrl(opts?: {\n xForwardedHost?: boolean\n xForwardedProto?: boolean\n}) {\n return h3_getRequestURL(getH3Event(), opts)\n}\n\n/**\n * Get the request protocol.\n *\n * If `x-forwarded-proto` header is set to \"https\", it will return \"https\". You can disable this behavior by setting `xForwardedProto` to `false`.\n *\n * If protocol cannot be determined, it will default to \"http\".\n */\nexport function getRequestProtocol(opts?: {\n xForwardedProto?: boolean\n}): 'http' | 'https' | (string & {}) {\n return h3_getRequestProtocol(getH3Event(), opts)\n}\n\nexport function setResponseHeaders(\n headers: TypedHeaders<ResponseHeaderMap>,\n): void {\n const event = getH3Event()\n for (const [name, value] of Object.entries(headers)) {\n event.res.headers.set(name, value)\n }\n}\n\nexport function getResponseHeaders(): TypedHeaders<ResponseHeaderMap> {\n const event = getH3Event()\n return event.res.headers\n}\n\nexport function getResponseHeader(\n name: ResponseHeaderName,\n): string | undefined {\n const event = getH3Event()\n return event.res.headers.get(name) || undefined\n}\n\nexport function setResponseHeader(\n name: ResponseHeaderName,\n value: string | Array<string>,\n): void {\n const event = getH3Event()\n if (Array.isArray(value)) {\n event.res.headers.delete(name)\n for (const valueItem of value) {\n event.res.headers.append(name, valueItem)\n }\n } else {\n event.res.headers.set(name, value)\n }\n}\nexport function removeResponseHeader(name: ResponseHeaderName): void {\n const event = getH3Event()\n event.res.headers.delete(name)\n}\n\nexport function clearResponseHeaders(\n headerNames?: Array<ResponseHeaderName>,\n): void {\n const event = getH3Event()\n // If headerNames is provided, clear only those headers\n if (headerNames && headerNames.length > 0) {\n for (const name of headerNames) {\n event.res.headers.delete(name)\n }\n // Otherwise, clear all headers\n } else {\n for (const name of event.res.headers.keys()) {\n event.res.headers.delete(name)\n }\n }\n}\n\nexport function getResponseStatus(): number {\n return getH3Event().res.status || 200\n}\n\nexport function setResponseStatus(code?: number, text?: string): void {\n const event = getH3Event()\n if (code) {\n event.res.status = h3_sanitizeStatusCode(code, event.res.status)\n }\n if (text) {\n event.res.statusText = h3_sanitizeStatusMessage(text)\n }\n}\n\n/**\n * Parse the request to get HTTP Cookie header string and return an object of all cookie name-value pairs.\n * @returns Object of cookie name-value pairs\n * ```ts\n * const cookies = getCookies()\n * ```\n */\nexport function getCookies(): Record<string, string> {\n const event = getH3Event()\n return h3_parseCookies(event)\n}\n\n/**\n * Get a cookie value by name.\n * @param name Name of the cookie to get\n * @returns {*} Value of the cookie (String or undefined)\n * ```ts\n * const authorization = getCookie('Authorization')\n * ```\n */\nexport function getCookie(name: string): string | undefined {\n return getCookies()[name] || undefined\n}\n\n/**\n * Set a cookie value by name.\n * @param name Name of the cookie to set\n * @param value Value of the cookie to set\n * @param options {CookieSerializeOptions} Options for serializing the cookie\n * ```ts\n * setCookie('Authorization', '1234567')\n * ```\n */\nexport function setCookie(\n name: string,\n value: string,\n options?: CookieSerializeOptions,\n): void {\n const event = getH3Event()\n h3_setCookie(event, name, value, options)\n}\n\n/**\n * Remove a cookie by name.\n * @param name Name of the cookie to delete\n * @param serializeOptions {CookieSerializeOptions} Cookie options\n * ```ts\n * deleteCookie('SessionId')\n * ```\n */\nexport function deleteCookie(\n name: string,\n options?: CookieSerializeOptions,\n): void {\n const event = getH3Event()\n h3_deleteCookie(event, name, options)\n}\n\nfunction getDefaultSessionConfig(config: SessionConfig): SessionConfig {\n return {\n name: 'start',\n ...config,\n }\n}\n\n/**\n * Create a session manager for the current request.\n */\nexport function useSession<TSessionData extends SessionData = SessionData>(\n config: SessionConfig,\n): Promise<SessionManager<TSessionData>> {\n const event = getH3Event()\n return h3_useSession(event, getDefaultSessionConfig(config))\n}\n/**\n * Get the session for the current request\n */\nexport function getSession<TSessionData extends SessionData = SessionData>(\n config: SessionConfig,\n): Promise<Session<TSessionData>> {\n const event = getH3Event()\n return h3_getSession(event, getDefaultSessionConfig(config))\n}\n\n/**\n * Update the session data for the current request.\n */\nexport function updateSession<TSessionData extends SessionData = SessionData>(\n config: SessionConfig,\n update?: SessionUpdate<TSessionData>,\n): Promise<Session<TSessionData>> {\n const event = getH3Event()\n return h3_updateSession(event, getDefaultSessionConfig(config), update)\n}\n\n/**\n * Encrypt and sign the session data for the current request.\n */\nexport function sealSession(config: SessionConfig): Promise<string> {\n const event = getH3Event()\n return h3_sealSession(event, getDefaultSessionConfig(config))\n}\n/**\n * Decrypt and verify the session data for the current request.\n */\nexport function unsealSession(\n config: SessionConfig,\n sealed: string,\n): Promise<Partial<Session>> {\n const event = getH3Event()\n return h3_unsealSession(event, getDefaultSessionConfig(config), sealed)\n}\n\n/**\n * Clear the session data for the current request.\n */\nexport function clearSession(config: Partial<SessionConfig>): Promise<void> {\n const event = getH3Event()\n return h3_clearSession(event, { name: 'start', ...config })\n}\n\nexport function getResponse() {\n const event = getH3Event()\n return event.res\n}\n\n// not public API (yet)\nexport function getValidatedQuery<TSchema extends StandardSchemaV1>(\n schema: StandardSchemaV1,\n): Promise<StandardSchemaV1.InferOutput<TSchema>> {\n return h3_getValidatedQuery(getH3Event(), schema)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"request-response.js","names":[],"sources":["../../src/request-response.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport {\n H3Event,\n clearSession as h3_clearSession,\n deleteCookie as h3_deleteCookie,\n getRequestHost as h3_getRequestHost,\n getRequestIP as h3_getRequestIP,\n getRequestProtocol as h3_getRequestProtocol,\n getRequestURL as h3_getRequestURL,\n getSession as h3_getSession,\n getValidatedQuery as h3_getValidatedQuery,\n parseCookies as h3_parseCookies,\n sanitizeStatusCode as h3_sanitizeStatusCode,\n sanitizeStatusMessage as h3_sanitizeStatusMessage,\n sealSession as h3_sealSession,\n setCookie as h3_setCookie,\n toResponse as h3_toResponse,\n unsealSession as h3_unsealSession,\n updateSession as h3_updateSession,\n useSession as h3_useSession,\n} from 'h3-v2'\nimport type {\n RequestHeaderMap,\n RequestHeaderName,\n ResponseHeaderMap,\n ResponseHeaderName,\n TypedHeaders,\n} from 'fetchdts'\n\nimport type { CookieSerializeOptions } from 'cookie-es'\nimport type {\n Session,\n SessionConfig,\n SessionData,\n SessionManager,\n SessionUpdate,\n} from './session'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { RequestHandler } from './request-handler'\n\ninterface StartEvent {\n h3Event: H3Event\n}\n\n// Use a global symbol to ensure the same AsyncLocalStorage instance is shared\n// across different bundles that may each bundle this module.\nconst GLOBAL_EVENT_STORAGE_KEY = Symbol.for('tanstack-start:event-storage')\n\nconst globalObj = globalThis as typeof globalThis & {\n [GLOBAL_EVENT_STORAGE_KEY]?: AsyncLocalStorage<StartEvent>\n}\n\nif (!globalObj[GLOBAL_EVENT_STORAGE_KEY]) {\n globalObj[GLOBAL_EVENT_STORAGE_KEY] = new AsyncLocalStorage<StartEvent>()\n}\n\nconst eventStorage = globalObj[GLOBAL_EVENT_STORAGE_KEY]\n\nexport type { ResponseHeaderName, RequestHeaderName }\n\ntype HeadersWithGetSetCookie = Headers & {\n getSetCookie?: () => Array<string>\n}\n\ntype MaybePromise<T> = T | Promise<T>\n\nfunction isPromiseLike<T>(value: MaybePromise<T>): value is Promise<T> {\n return typeof (value as Promise<T>).then === 'function'\n}\n\nfunction getSetCookieValues(headers: Headers): Array<string> {\n const headersWithSetCookie = headers as HeadersWithGetSetCookie\n if (typeof headersWithSetCookie.getSetCookie === 'function') {\n return headersWithSetCookie.getSetCookie()\n }\n const value = headers.get('set-cookie')\n return value ? [value] : []\n}\n\nfunction mergeEventResponseHeaders(response: Response, event: H3Event): void {\n if (response.ok) {\n return\n }\n\n const eventSetCookies = getSetCookieValues(event.res.headers)\n if (eventSetCookies.length === 0) {\n return\n }\n\n const responseSetCookies = getSetCookieValues(response.headers)\n response.headers.delete('set-cookie')\n for (const cookie of responseSetCookies) {\n response.headers.append('set-cookie', cookie)\n }\n for (const cookie of eventSetCookies) {\n response.headers.append('set-cookie', cookie)\n }\n}\n\nfunction attachResponseHeaders<T>(\n value: MaybePromise<T>,\n event: H3Event,\n): MaybePromise<T> {\n if (isPromiseLike(value)) {\n return value.then((resolved) => {\n if (resolved instanceof Response) {\n mergeEventResponseHeaders(resolved, event)\n }\n return resolved\n })\n }\n\n if (value instanceof Response) {\n mergeEventResponseHeaders(value, event)\n }\n\n return value\n}\n\nexport function requestHandler<TRegister = unknown>(\n handler: RequestHandler<TRegister>,\n) {\n return (request: Request, requestOpts: any): Promise<Response> | Response => {\n let h3Event: H3Event\n try {\n h3Event = new H3Event(request)\n } catch (error) {\n if (error instanceof URIError) {\n return new Response(null, {\n status: 400,\n statusText: 'Bad Request',\n })\n }\n throw error\n }\n\n const response = eventStorage.run({ h3Event }, () =>\n handler(request, requestOpts),\n )\n return h3_toResponse(attachResponseHeaders(response, h3Event), h3Event)\n }\n}\n\nfunction getH3Event() {\n const event = eventStorage.getStore()\n if (!event) {\n throw new Error(\n `No StartEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.`,\n )\n }\n return event.h3Event\n}\n\nexport function getRequest(): Request {\n const event = getH3Event()\n return event.req\n}\n\nexport function getRequestHeaders(): TypedHeaders<RequestHeaderMap> {\n // TODO `as any` not needed when fetchdts is updated\n return getH3Event().req.headers as any\n}\n\nexport function getRequestHeader(name: RequestHeaderName): string | undefined {\n return getRequestHeaders().get(name) || undefined\n}\n\nexport function getRequestIP(opts?: {\n /**\n * Use the X-Forwarded-For HTTP header set by proxies.\n *\n * Note: Make sure that this header can be trusted (your application running behind a CDN or reverse proxy) before enabling.\n */\n xForwardedFor?: boolean\n}) {\n return h3_getRequestIP(getH3Event(), opts)\n}\n\n/**\n * Get the request hostname.\n *\n * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.\n *\n * If no host header is found, it will default to \"localhost\".\n */\nexport function getRequestHost(opts?: { xForwardedHost?: boolean }) {\n return h3_getRequestHost(getH3Event(), opts)\n}\n\n/**\n * Get the full incoming request URL.\n *\n * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.\n *\n * If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.\n */\nexport function getRequestUrl(opts?: {\n xForwardedHost?: boolean\n xForwardedProto?: boolean\n}) {\n return h3_getRequestURL(getH3Event(), opts)\n}\n\n/**\n * Get the request protocol.\n *\n * If `x-forwarded-proto` header is set to \"https\", it will return \"https\". You can disable this behavior by setting `xForwardedProto` to `false`.\n *\n * If protocol cannot be determined, it will default to \"http\".\n */\nexport function getRequestProtocol(opts?: {\n xForwardedProto?: boolean\n}): 'http' | 'https' | (string & {}) {\n return h3_getRequestProtocol(getH3Event(), opts)\n}\n\nexport function setResponseHeaders(\n headers: TypedHeaders<ResponseHeaderMap>,\n): void {\n const event = getH3Event()\n for (const [name, value] of Object.entries(headers)) {\n event.res.headers.set(name, value)\n }\n}\n\nexport function getResponseHeaders(): TypedHeaders<ResponseHeaderMap> {\n const event = getH3Event()\n return event.res.headers\n}\n\nexport function getResponseHeader(\n name: ResponseHeaderName,\n): string | undefined {\n const event = getH3Event()\n return event.res.headers.get(name) || undefined\n}\n\nexport function setResponseHeader(\n name: ResponseHeaderName,\n value: string | Array<string>,\n): void {\n const event = getH3Event()\n if (Array.isArray(value)) {\n event.res.headers.delete(name)\n for (const valueItem of value) {\n event.res.headers.append(name, valueItem)\n }\n } else {\n event.res.headers.set(name, value)\n }\n}\nexport function removeResponseHeader(name: ResponseHeaderName): void {\n const event = getH3Event()\n event.res.headers.delete(name)\n}\n\nexport function clearResponseHeaders(\n headerNames?: Array<ResponseHeaderName>,\n): void {\n const event = getH3Event()\n // If headerNames is provided, clear only those headers\n if (headerNames && headerNames.length > 0) {\n for (const name of headerNames) {\n event.res.headers.delete(name)\n }\n // Otherwise, clear all headers\n } else {\n for (const name of event.res.headers.keys()) {\n event.res.headers.delete(name)\n }\n }\n}\n\nexport function getResponseStatus(): number {\n return getH3Event().res.status || 200\n}\n\nexport function setResponseStatus(code?: number, text?: string): void {\n const event = getH3Event()\n if (code) {\n event.res.status = h3_sanitizeStatusCode(code, event.res.status)\n }\n if (text) {\n event.res.statusText = h3_sanitizeStatusMessage(text)\n }\n}\n\n/**\n * Parse the request to get HTTP Cookie header string and return an object of all cookie name-value pairs.\n * @returns Object of cookie name-value pairs\n * ```ts\n * const cookies = getCookies()\n * ```\n */\nexport function getCookies(): Record<string, string> {\n const event = getH3Event()\n const cookies = h3_parseCookies(event)\n const definedCookies: Record<string, string> = Object.create(null)\n\n for (const [name, value] of Object.entries(cookies)) {\n if (value !== undefined) {\n definedCookies[name] = value\n }\n }\n\n return definedCookies\n}\n\n/**\n * Get a cookie value by name.\n * @param name Name of the cookie to get\n * @returns {*} Value of the cookie (String or undefined)\n * ```ts\n * const authorization = getCookie('Authorization')\n * ```\n */\nexport function getCookie(name: string): string | undefined {\n return getCookies()[name]\n}\n\n/**\n * Set a cookie value by name.\n * @param name Name of the cookie to set\n * @param value Value of the cookie to set\n * @param options {CookieSerializeOptions} Options for serializing the cookie\n * ```ts\n * setCookie('Authorization', '1234567')\n * ```\n */\nexport function setCookie(\n name: string,\n value: string,\n options?: CookieSerializeOptions,\n): void {\n const event = getH3Event()\n h3_setCookie(event, name, value, options)\n}\n\n/**\n * Remove a cookie by name.\n * @param name Name of the cookie to delete\n * @param serializeOptions {CookieSerializeOptions} Cookie options\n * ```ts\n * deleteCookie('SessionId')\n * ```\n */\nexport function deleteCookie(\n name: string,\n options?: CookieSerializeOptions,\n): void {\n const event = getH3Event()\n h3_deleteCookie(event, name, options)\n}\n\nfunction getDefaultSessionConfig(config: SessionConfig): SessionConfig {\n return {\n name: 'start',\n ...config,\n }\n}\n\n/**\n * Create a session manager for the current request.\n */\nexport function useSession<TSessionData extends SessionData = SessionData>(\n config: SessionConfig,\n): Promise<SessionManager<TSessionData>> {\n const event = getH3Event()\n return h3_useSession(event, getDefaultSessionConfig(config))\n}\n/**\n * Get the session for the current request\n */\nexport function getSession<TSessionData extends SessionData = SessionData>(\n config: SessionConfig,\n): Promise<Session<TSessionData>> {\n const event = getH3Event()\n return h3_getSession(event, getDefaultSessionConfig(config))\n}\n\n/**\n * Update the session data for the current request.\n */\nexport function updateSession<TSessionData extends SessionData = SessionData>(\n config: SessionConfig,\n update?: SessionUpdate<TSessionData>,\n): Promise<Session<TSessionData>> {\n const event = getH3Event()\n return h3_updateSession(event, getDefaultSessionConfig(config), update)\n}\n\n/**\n * Encrypt and sign the session data for the current request.\n */\nexport function sealSession(config: SessionConfig): Promise<string> {\n const event = getH3Event()\n return h3_sealSession(event, getDefaultSessionConfig(config))\n}\n/**\n * Decrypt and verify the session data for the current request.\n */\nexport function unsealSession(\n config: SessionConfig,\n sealed: string,\n): Promise<Partial<Session>> {\n const event = getH3Event()\n return h3_unsealSession(event, getDefaultSessionConfig(config), sealed)\n}\n\n/**\n * Clear the session data for the current request.\n */\nexport function clearSession(config: Partial<SessionConfig>): Promise<void> {\n const event = getH3Event()\n return h3_clearSession(event, { name: 'start', ...config })\n}\n\nexport function getResponse() {\n const event = getH3Event()\n return event.res\n}\n\n// not public API (yet)\nexport function getValidatedQuery<TSchema extends StandardSchemaV1>(\n schema: StandardSchemaV1,\n): Promise<StandardSchemaV1.InferOutput<TSchema>> {\n return h3_getValidatedQuery(getH3Event(), schema)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-server-core",
3
- "version": "1.167.16",
3
+ "version": "1.167.18",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -63,17 +63,17 @@
63
63
  "node": ">=22.12.0"
64
64
  },
65
65
  "dependencies": {
66
- "h3-v2": "npm:h3@2.0.1-rc.16",
66
+ "h3-v2": "npm:h3@2.0.1-rc.20",
67
67
  "seroval": "^1.5.0",
68
68
  "@tanstack/history": "1.161.6",
69
- "@tanstack/router-core": "1.168.12",
70
- "@tanstack/start-client-core": "1.167.14",
71
- "@tanstack/start-storage-context": "1.166.26"
69
+ "@tanstack/router-core": "1.168.14",
70
+ "@tanstack/start-client-core": "1.167.16",
71
+ "@tanstack/start-storage-context": "1.166.28"
72
72
  },
73
73
  "devDependencies": {
74
74
  "@standard-schema/spec": "^1.0.0",
75
75
  "@tanstack/intent": "^0.0.14",
76
- "cookie-es": "^2.0.0",
76
+ "cookie-es": "^3.0.0",
77
77
  "fetchdts": "^0.1.6",
78
78
  "vite": "*",
79
79
  "@types/node": ">=20"
@@ -700,6 +700,7 @@ export function createStartHandler<TRegister = Register>(
700
700
  manifest,
701
701
  getRequestAssets: () =>
702
702
  getStartContext({ throwIfNotFound: false })?.requestAssets,
703
+ includeUnmatchedRouteAssets: false,
703
704
  })
704
705
 
705
706
  routerInstance.update({ additionalContext: { serverContext } })
@@ -122,7 +122,18 @@ export function requestHandler<TRegister = unknown>(
122
122
  handler: RequestHandler<TRegister>,
123
123
  ) {
124
124
  return (request: Request, requestOpts: any): Promise<Response> | Response => {
125
- const h3Event = new H3Event(request)
125
+ let h3Event: H3Event
126
+ try {
127
+ h3Event = new H3Event(request)
128
+ } catch (error) {
129
+ if (error instanceof URIError) {
130
+ return new Response(null, {
131
+ status: 400,
132
+ statusText: 'Bad Request',
133
+ })
134
+ }
135
+ throw error
136
+ }
126
137
 
127
138
  const response = eventStorage.run({ h3Event }, () =>
128
139
  handler(request, requestOpts),
@@ -284,7 +295,16 @@ export function setResponseStatus(code?: number, text?: string): void {
284
295
  */
285
296
  export function getCookies(): Record<string, string> {
286
297
  const event = getH3Event()
287
- return h3_parseCookies(event)
298
+ const cookies = h3_parseCookies(event)
299
+ const definedCookies: Record<string, string> = Object.create(null)
300
+
301
+ for (const [name, value] of Object.entries(cookies)) {
302
+ if (value !== undefined) {
303
+ definedCookies[name] = value
304
+ }
305
+ }
306
+
307
+ return definedCookies
288
308
  }
289
309
 
290
310
  /**
@@ -296,7 +316,7 @@ export function getCookies(): Record<string, string> {
296
316
  * ```
297
317
  */
298
318
  export function getCookie(name: string): string | undefined {
299
- return getCookies()[name] || undefined
319
+ return getCookies()[name]
300
320
  }
301
321
 
302
322
  /**