@tanstack/start-server-core 1.158.3 → 1.158.4
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.
|
@@ -119,9 +119,12 @@ function createStartHandler(cb) {
|
|
|
119
119
|
let router = null;
|
|
120
120
|
let cbWillCleanup = false;
|
|
121
121
|
try {
|
|
122
|
-
const url = getNormalizedURL(request.url);
|
|
122
|
+
const { url, handledProtocolRelativeURL } = getNormalizedURL(request.url);
|
|
123
123
|
const href = url.pathname + url.search + url.hash;
|
|
124
124
|
const origin = getOrigin(request);
|
|
125
|
+
if (handledProtocolRelativeURL) {
|
|
126
|
+
return Response.redirect(url, 308);
|
|
127
|
+
}
|
|
125
128
|
const entries = await getEntries();
|
|
126
129
|
const startOptions = await entries.startEntry.startInstance?.getOptions() || {};
|
|
127
130
|
const serializationAdapters = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStartHandler.js","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 { 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 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 Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\n\ntype TODO = any\n\ntype AnyMiddlewareServerFn =\n | AnyRequestMiddleware['options']['server']\n | AnyFunctionMiddleware['options']['server']\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\n// Cached entries - promises stored immediately to prevent concurrent imports\n// that can cause race conditions during module initialization\nlet entriesPromise:\n | Promise<{\n startEntry: StartEntry\n routerEntry: RouterEntry\n }>\n | undefined\nlet manifestPromise: Promise<Manifest> | undefined\n\nasync function loadEntries() {\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 const routerEntry = (await import('#tanstack-router-entry')) as RouterEntry\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 const startEntry = (await import('#tanstack-start-entry')) as StartEntry\n return { startEntry, routerEntry }\n}\n\nfunction getEntries() {\n if (!entriesPromise) {\n entriesPromise = loadEntries()\n }\n return entriesPromise\n}\n\nfunction getManifest(matchedRoutes?: ReadonlyArray<AnyRoute>) {\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 manifest\n if (!manifestPromise) {\n manifestPromise = getStartManifest()\n }\n return manifestPromise\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\nexport function createStartHandler<TRegister = Register>(\n cb: HandlerCallback<AnyRouter>,\n): RequestHandler<TRegister> {\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 const url = getNormalizedURL(request.url)\n const href = url.pathname + url.search + url.hash\n const origin = getOrigin(request)\n\n const entries = await getEntries()\n const startOptions: AnyStartInstanceOptions =\n (await entries.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 // 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 },\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 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 getManifest(matchedRoutes)\n const routerInstance = await getRouter()\n\n attachRouterServerSsrUtils({\n router: routerInstance,\n manifest,\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 await routerInstance.serverSsr!.dehydrate()\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 },\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 { request, context: createNullProtoObject(requestOpts?.context) },\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"],"names":["middlewares","ctx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createStartHandler.js","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 { 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 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 Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\n\ntype TODO = any\n\ntype AnyMiddlewareServerFn =\n | AnyRequestMiddleware['options']['server']\n | AnyFunctionMiddleware['options']['server']\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\n// Cached entries - promises stored immediately to prevent concurrent imports\n// that can cause race conditions during module initialization\nlet entriesPromise:\n | Promise<{\n startEntry: StartEntry\n routerEntry: RouterEntry\n }>\n | undefined\nlet manifestPromise: Promise<Manifest> | undefined\n\nasync function loadEntries() {\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 const routerEntry = (await import('#tanstack-router-entry')) as RouterEntry\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 const startEntry = (await import('#tanstack-start-entry')) as StartEntry\n return { startEntry, routerEntry }\n}\n\nfunction getEntries() {\n if (!entriesPromise) {\n entriesPromise = loadEntries()\n }\n return entriesPromise\n}\n\nfunction getManifest(matchedRoutes?: ReadonlyArray<AnyRoute>) {\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 manifest\n if (!manifestPromise) {\n manifestPromise = getStartManifest()\n }\n return manifestPromise\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\nexport function createStartHandler<TRegister = Register>(\n cb: HandlerCallback<AnyRouter>,\n): RequestHandler<TRegister> {\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 serializationAdapters = [\n ...(startOptions.serializationAdapters || []),\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 },\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 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 getManifest(matchedRoutes)\n const routerInstance = await getRouter()\n\n attachRouterServerSsrUtils({\n router: routerInstance,\n manifest,\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 await routerInstance.serverSsr!.dehydrate()\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 },\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 { request, context: createNullProtoObject(requestOpts?.context) },\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"],"names":["middlewares","ctx"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-server-core",
|
|
3
|
-
"version": "1.158.
|
|
3
|
+
"version": "1.158.4",
|
|
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.2",
|
|
65
65
|
"tiny-invariant": "^1.3.3",
|
|
66
66
|
"@tanstack/history": "1.154.14",
|
|
67
|
-
"@tanstack/
|
|
68
|
-
"@tanstack/
|
|
69
|
-
"@tanstack/start-
|
|
67
|
+
"@tanstack/router-core": "1.158.4",
|
|
68
|
+
"@tanstack/start-client-core": "1.158.4",
|
|
69
|
+
"@tanstack/start-storage-context": "1.158.4"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@standard-schema/spec": "^1.0.0",
|
|
@@ -218,10 +218,16 @@ export function createStartHandler<TRegister = Register>(
|
|
|
218
218
|
|
|
219
219
|
try {
|
|
220
220
|
// normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.
|
|
221
|
-
|
|
221
|
+
// during normalization paths like '//posts' are flattened to '/posts'.
|
|
222
|
+
// in these cases we would prefer to redirect to the new path
|
|
223
|
+
const { url, handledProtocolRelativeURL } = getNormalizedURL(request.url)
|
|
222
224
|
const href = url.pathname + url.search + url.hash
|
|
223
225
|
const origin = getOrigin(request)
|
|
224
226
|
|
|
227
|
+
if (handledProtocolRelativeURL) {
|
|
228
|
+
return Response.redirect(url, 308)
|
|
229
|
+
}
|
|
230
|
+
|
|
225
231
|
const entries = await getEntries()
|
|
226
232
|
const startOptions: AnyStartInstanceOptions =
|
|
227
233
|
(await entries.startEntry.startInstance?.getOptions()) ||
|