@tanstack/start-server-core
Version:
Modern and scalable routing for React applications
619 lines (612 loc) • 26.2 kB
JavaScript
import { createFinalizedResponse, finalizeResponse, reconcileResponse, restoreResponseProtocol, transferResponseMetadata, transferResponseProtocol, withStartRequest } from "./internal-request-response.js";
import { getStartManifest } from "./router-manifest.js";
import { DeferredResponse, createServerFnErrorResponse, handleServerAction, toThrownServerFnResponse } from "./server-functions-handler.js";
import { createEarlyHintsCollector } from "./early-hints.js";
import { createCachedBaseManifestLoader, createFinalManifestResolver } from "./finalManifest.js";
import { HEADERS } from "./constants.js";
import { ServerFunctionSerializationAdapter } from "./serializer/ServerFunctionSerializationAdapter.js";
import { createServerHistory } from "@tanstack/history";
import { X_TSS_RAW_RESPONSE, X_TSS_SERIALIZED, createCsrfMiddleware, createNullProtoObject, csrfSymbol, flattenMiddlewares, mergeHeaders, safeObjectMerge } from "@tanstack/start-client-core";
import { _getRenderedMatches, isDangerousProtocol, isPromise, isRedirect } from "@tanstack/router-core";
import { attachRouterServerSsrUtils, bindSsrResponseToRequest, disposeSsrResponse, getNormalizedURL, isSsrResponse, normalizeSsrResponse, stripSsrResponseBody, waitForRequest } from "@tanstack/router-core/ssr/server";
import { getStartContext, runWithStartContext } from "@tanstack/start-storage-context";
//#region src/createStartHandler.ts
function getStartResponseHeaders(router) {
const headers = new Headers({ "content-type": "text/html; charset=utf-8" });
const matches = _getRenderedMatches(router.stores.matches.get());
return matches.some((match) => match.headers) ? mergeHeaders(headers, ...matches.map((match) => match.headers)) : headers;
}
var cachedEntries;
var hasWarnedMissingCsrfMiddleware = false;
var defaultCsrfMiddleware = createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === "serverFn" });
var getCachedBaseManifest = createCachedBaseManifestLoader(() => getStartManifest());
var getProdBaseManifest = () => getCachedBaseManifest();
var getBaseManifest = process.env.TSS_DEV_SERVER === "true" ? getStartManifest : getProdBaseManifest;
var createEarlyHintsForRequest = process.env.TSS_DEV_SERVER === "true" ? () => void 0 : createEarlyHintsCollector;
async function loadEntries() {
const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
import("#tanstack-router-entry"),
import("#tanstack-start-entry"),
import("#tanstack-start-plugin-adapters")
]);
return {
routerEntry,
startEntry,
pluginAdapters
};
}
function getEntries() {
if (!cachedEntries) cachedEntries = loadEntries().then((entries) => {
cachedEntries = entries;
return entries;
});
return cachedEntries;
}
function hasCsrfMiddleware(middlewares) {
return middlewares.some((middleware) => csrfSymbol in middleware);
}
function warnMissingCsrfMiddlewareOnce() {
if (hasWarnedMissingCsrfMiddleware) return;
hasWarnedMissingCsrfMiddleware = true;
console.warn(`TanStack Start server functions are not protected by the CSRF middleware.
Server functions are same-origin RPC endpoints and should be protected from cross-site requests.
Add the CSRF middleware in src/start.ts:
const csrfMiddleware = createCsrfMiddleware({
filter: (ctx) => ctx.handlerType === 'serverFn',
})
export const startInstance = createStart(() => ({
requestMiddleware: [csrfMiddleware],
}))
If you intentionally handle CSRF another way, disable this warning:
tanstackStart({
serverFns: {
disableCsrfMiddlewareWarning: true,
},
})`);
}
var ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || "/";
var SERVER_FN_BASE = process.env.TSS_SERVER_FN_BASE;
var IS_PRERENDERING = process.env.TSS_PRERENDERING === "true";
var IS_SHELL_ENV = process.env.TSS_SHELL === "true";
var IS_DEV = process.env.NODE_ENV === "development";
var ERR_NO_RESPONSE = IS_DEV ? `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.` : "Internal Server Error";
var ERR_NO_DEFER = IS_DEV ? `You cannot defer to the app router if there is no component defined on this route.` : "Internal Server Error";
function throwRouteHandlerError() {
throw new Error(ERR_NO_RESPONSE);
}
function throwIfMayNotDefer() {
throw new Error(ERR_NO_DEFER);
}
function getResponseFromResult(result) {
return isSsrResponse(result) || result instanceof Response ? result : result?.response;
}
function disposeResponseResult(result, reason) {
const response = getResponseFromResult(result);
if (isSsrResponse(response) || response instanceof Response) disposeSsrResponse(response, reason);
}
/**
* A new body continues the owned one when it is the same stream, or when the
* owned body is already being read, as by a pipe, a tee, or a `Response` that
* copies a body by reading it (Bun, or a wrapped global `Response`). Start
* cannot tell such a reader from one that stops early, so it disposes only an
* unread body. A stream whose reader stops early ends with the request's abort
* or the SSR stream's lifetime limit.
*/
function inheritsResponseOwnership(ownership, body) {
const ownedBody = ownership.response.body;
return body === ownedBody || ownedBody.locked;
}
function disposeResponseOwnership(ownership, reason) {
const { response, sourceBody, streamResponse } = ownership;
streamResponse?.dispose(reason);
if (!streamResponse || response.body !== sourceBody) response.body.cancel(reason).catch(() => {});
}
function getOwnedResponse(ownership) {
const { response, sourceBody, streamResponse } = ownership;
if (!streamResponse) return response;
if (streamResponse.response === response && response.body === sourceBody) return streamResponse;
if (response.body === sourceBody) return {
...streamResponse,
response
};
return {
...streamResponse,
response,
dispose(reason) {
disposeResponseOwnership(ownership, reason);
}
};
}
function createLateResponseDisposer(signal) {
return (result) => disposeResponseResult(result, signal.reason);
}
/**
* Compose middleware around a terminal response handler. With no middleware
* the terminal runs directly.
*/
async function executeMiddleware(middlewares, terminal, ctx, signal, event, terminalNext) {
let index = -1;
let responseOwnership;
let returnedDepth = Infinity;
const disposeAbandonedResult = createLateResponseDisposer(signal);
const adoptResponse = (exposed, streamResponse) => {
const current = responseOwnership;
const body = exposed instanceof Response ? exposed.body : null;
const response = exposed;
if (streamResponse && !body && streamResponse !== current?.streamResponse) streamResponse.dispose("Response body dropped by Start reconciliation");
if (current && current.response === exposed) current.streamResponse ??= streamResponse;
else if (current && body && inheritsResponseOwnership(current, body)) {
if (body === current.response.body) transferResponseProtocol(current.response, response);
current.response = response;
current.streamResponse ??= streamResponse;
} else {
if (current) {
responseOwnership = void 0;
disposeResponseOwnership(current, "middleware response replaced");
}
responseOwnership = body ? {
response,
sourceBody: body,
streamResponse
} : void 0;
}
ctx.response = exposed;
};
const disposeCurrentBody = (reason) => {
const current = responseOwnership;
responseOwnership = void 0;
if (current) disposeResponseOwnership(current, reason);
};
const setResponse = (response, caller) => {
const owned = response !== void 0 && response === responseOwnership?.response;
const ssrResponse = !owned && isSsrResponse(response) ? response : void 0;
const exposed = ssrResponse ? ssrResponse.response : response;
if (returnedDepth <= caller) {
if (exposed !== ctx.response) disposeResponseResult(response, "late middleware response");
return;
}
if (!owned) adoptResponse(exposed, ssrResponse?.serverSsrCleanup === "stream" ? ssrResponse : void 0);
if (exposed instanceof Response) {
const reconciled = reconcileResponse(exposed, event, disposeCurrentBody);
if (reconciled !== exposed) adoptRebuilt(exposed, reconciled);
}
};
const adoptRebuilt = (exposed, rebuilt) => {
const current = responseOwnership;
if (current?.response === exposed && rebuilt.body) {
current.response = rebuilt;
ctx.response = rebuilt;
} else adoptResponse(rebuilt);
};
let nextPromise;
function next(nextCtx) {
const result = runNext(index, nextCtx);
nextPromise = result;
return result;
}
async function runNext(caller, nextCtx) {
signal.throwIfAborted();
if (nextCtx) {
if (nextCtx.context) ctx.context = safeObjectMerge(ctx.context, nextCtx.context);
const keys = Object.keys(nextCtx);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key === "response") setResponse(nextCtx.response, caller);
else if (key !== "context") ctx[key] = nextCtx[key];
}
}
const depth = ++index;
const isTerminal = depth === middlewares.length;
const middleware = depth < middlewares.length ? middlewares[depth] : isTerminal ? terminal : void 0;
const middlewareNext = isTerminal && terminalNext ? terminalNext : next;
if (!middleware) return ctx;
let result;
try {
const pending = middleware({
...ctx,
next: middlewareNext
});
if (nextPromise && pending === nextPromise) {
nextPromise = void 0;
result = await pending;
signal.throwIfAborted();
} else if (!isPromise(pending)) {
result = pending;
signal.throwIfAborted();
} else result = await waitForRequest(pending, signal, disposeAbandonedResult, disposeAbandonedResult);
} catch (err) {
if (depth < returnedDepth) returnedDepth = depth;
if (returnedDepth > caller) adoptResponse(ctx.response);
if (signal.aborted) {
if (result !== void 0) disposeAbandonedResult(result);
if (err !== signal.reason) disposeAbandonedResult(err);
throw signal.reason;
}
if (err instanceof Response) {
setResponse(ctx.handlerType === "serverFn" ? toThrownServerFnResponse(err) : err, caller);
return ctx;
}
throw err;
}
if (depth < returnedDepth) returnedDepth = depth;
if (isTerminal && terminalNext && !result) throwRouteHandlerError();
if (isTerminal && !terminalNext && result instanceof DeferredResponse) {
if (returnedDepth > caller) {
adoptResponse(void 0);
adoptResponse(result.createResponse(event));
}
return ctx;
}
if (result && result !== ctx) {
const response = getResponseFromResult(result);
if (response !== void 0) {
if (ctx.response !== responseOwnership?.response && ctx.response !== response && returnedDepth > caller) adoptResponse(ctx.response);
setResponse(response, caller);
} else setResponse(ctx.response, caller);
if (returnedDepth > caller && response !== result && result.context && result.context !== ctx.context) ctx.context = safeObjectMerge(ctx.context, result.context);
} else setResponse(ctx.response, caller);
return ctx;
}
try {
await runNext(-1);
let response = ctx.response;
if (!response) throwRouteHandlerError();
if (signal.aborted) throw signal.reason;
returnedDepth = -1;
if (middlewares.length && ctx.handlerType === "serverFn") {
const restored = restoreResponseProtocol(response, event);
if (restored !== response) {
adoptRebuilt(response, restored);
response = restored;
}
}
return responseOwnership ? getOwnedResponse(responseOwnership) : response;
} catch (err) {
returnedDepth = -1;
if (responseOwnership) disposeResponseOwnership(responseOwnership, signal.aborted ? signal.reason : err);
throw err;
}
}
/**
* Creates the TanStack Start request handler.
*
* @example Backwards-compatible usage (handler callback only):
* ```ts
* export default createStartHandler(defaultStreamHandler)
* ```
*
* @example With CDN URL rewriting:
* ```ts
* export default createStartHandler({
* handler: defaultStreamHandler,
* transformAssets: 'https://cdn.example.com',
* })
* ```
*
* @example With per-request URL rewriting:
* ```ts
* export default createStartHandler({
* handler: defaultStreamHandler,
* transformAssets: {
* transform: ({ url }) => {
* const cdnBase = getRequest().headers.get('x-cdn-base') || ''
* return { href: `${cdnBase}${url}` }
* },
* cache: false,
* },
* })
* ```
*/
function createStartHandler(cbOrOptions) {
const handlerOptions = typeof cbOrOptions === "function" ? {} : cbOrOptions;
const cb = typeof cbOrOptions === "function" ? cbOrOptions : cbOrOptions.handler;
const finalManifestResolver = createFinalManifestResolver({
...handlerOptions,
cacheCreateTransform: process.env.TSS_DEV_SERVER !== "true"
});
const resolveManifestForRequest = process.env.TSS_DEV_SERVER === "true" ? finalManifestResolver.resolveUncached : finalManifestResolver.resolveCached;
if (process.env.TSS_DEV_SERVER !== "true") finalManifestResolver.warmup({ getBaseManifest: () => getBaseManifest(void 0) });
const startRequestResolver = async (request, requestOpts, event) => {
const signal = request.signal;
let router;
let routerPromise;
let responseOwnsCleanup = false;
let response;
try {
signal.throwIfAborted();
const requestUrl = event.requestUrl;
let url = requestUrl;
const pathname = url.pathname;
if (!SERVER_FN_BASE || !pathname.startsWith(SERVER_FN_BASE) || pathname.includes("%") || pathname.startsWith("//")) {
const normalized = getNormalizedURL(requestUrl);
url = normalized.url;
if (normalized.handledProtocolRelativeURL) return finalizeResponse(Response.redirect(url, 308), event);
}
const pendingEntries = getEntries();
const entries = isPromise(pendingEntries) ? await waitForRequest(pendingEntries, signal) : pendingEntries;
const isServerFnRequest = !!SERVER_FN_BASE && url.pathname.startsWith(SERVER_FN_BASE);
const startInstance = entries.startEntry.startInstance;
let startOptions;
if (startInstance) {
const pendingStartOptions = startInstance.getOptions();
startOptions = isPromise(pendingStartOptions) ? await waitForRequest(pendingStartOptions, signal) : pendingStartOptions;
signal.throwIfAborted();
} else startOptions = {};
const { hasPluginAdapters, pluginSerializationAdapters } = entries.pluginAdapters;
const serializationAdapters = [
...startOptions.serializationAdapters || [],
...hasPluginAdapters ? pluginSerializationAdapters : [],
ServerFunctionSerializationAdapter
];
const requestStartOptions = {
...startOptions,
requestMiddleware: startInstance ? startOptions.requestMiddleware : isServerFnRequest ? [defaultCsrfMiddleware] : void 0,
serializationAdapters
};
const requestMiddleware = requestStartOptions.requestMiddleware;
let executedRequestMiddlewares;
let flattenedRequestMiddlewares = [];
if (requestMiddleware?.length) {
executedRequestMiddlewares = /* @__PURE__ */ new Set();
flattenedRequestMiddlewares = flattenMiddlewares(requestMiddleware, void 0, executedRequestMiddlewares);
}
const getRouter = () => {
routerPromise ??= (async () => {
signal.throwIfAborted();
const routerUrl = url === requestUrl ? getNormalizedURL(url).url : url;
const pendingRouter = entries.routerEntry.getRouter();
const requestRouter = isPromise(pendingRouter) ? await waitForRequest(pendingRouter, signal) : pendingRouter;
let isShell = IS_SHELL_ENV;
if (IS_PRERENDERING && !isShell) isShell = request.headers.get(HEADERS.TSS_SHELL) === "true";
const history = createServerHistory(routerUrl.pathname + routerUrl.search + routerUrl.hash);
requestRouter.update({
history,
isShell,
isPrerendering: IS_PRERENDERING,
origin: requestRouter.options.origin ?? routerUrl.origin,
defaultSsr: requestStartOptions.defaultSsr,
serializationAdapters: requestRouter.options.serializationAdapters?.length ? [...requestStartOptions.serializationAdapters, ...requestRouter.options.serializationAdapters] : requestStartOptions.serializationAdapters,
basepath: ROUTER_BASEPATH
});
router = requestRouter;
return requestRouter;
})();
return routerPromise;
};
const handlerType = isServerFnRequest ? "serverFn" : "router";
const startContext = {
getRouter,
startOptions: requestStartOptions,
request,
executedRequestMiddlewares,
handlerType
};
let terminal;
if (isServerFnRequest) {
if (process.env.NODE_ENV !== "production" && process.env.TSS_DISABLE_CSRF_MIDDLEWARE_WARNING !== "true" && !hasCsrfMiddleware(flattenedRequestMiddlewares)) warnMissingCsrfMiddlewareOnce();
const serverFnId = url.pathname.slice(SERVER_FN_BASE.length).split("/")[0];
if (!serverFnId) throw new Error("Invalid server action param for serverFnId");
terminal = ({ context }) => runWithStartContext({
...startContext,
contextAfterGlobalMiddlewares: context
}, () => handleServerAction({
request,
serverFnId
}));
} else {
const executeRouter = async (serverContext, matchedRoutes, routerInstance) => {
if (!/(^|,)\s*(\*\/\*|text\/html)/.test(request.headers.get("Accept") || "*/*")) return normalizeSsrResponse(Response.json({ error: "Only HTML requests are supported here" }, { status: 406 }));
const pendingManifest = resolveManifestForRequest({
request,
requestInlineCss: requestOpts?.inlineCss,
getBaseManifest: () => getBaseManifest(matchedRoutes)
});
const manifest = isPromise(pendingManifest) ? await waitForRequest(pendingManifest, signal) : pendingManifest;
const earlyHints = createEarlyHintsForRequest(requestOpts);
earlyHints?.collectStatic({
manifest,
matchedRoutes
});
const storedStartContext = getStartContext({ throwIfNotFound: false });
attachRouterServerSsrUtils({
router: routerInstance,
manifest,
getRequestAssets: () => storedStartContext?.requestAssets
});
routerInstance.options.additionalContext = { serverContext };
await routerInstance.load({
_signal: signal,
_skipLocationUpdate: true
});
signal.throwIfAborted();
if (routerInstance._serverResult?.type === "redirect") return normalizeSsrResponse(routerInstance._serverResult.redirect);
earlyHints?.collectDynamic(_getRenderedMatches(routerInstance.stores.matches.get()));
await routerInstance.serverSsr.dehydrate({
requestAssets: storedStartContext?.requestAssets,
signal
});
signal.throwIfAborted();
const responseHeaders = getStartResponseHeaders(routerInstance);
earlyHints?.appendResponseHeaders(responseHeaders);
signal.throwIfAborted();
return normalizeSsrResponse(await cb({
request,
router: routerInstance,
responseHeaders
}));
};
terminal = ({ context }) => runWithStartContext({
...startContext,
contextAfterGlobalMiddlewares: context
}, () => handleServerRoutes({
getRouter,
request,
executeRouter,
context,
executedRequestMiddlewares,
signal,
event
}));
}
const middlewareCtx = {
request,
pathname: url.pathname,
handlerType,
context: createNullProtoObject(requestOpts?.context)
};
let middlewareResponse;
try {
if (!isServerFnRequest && flattenedRequestMiddlewares.length === 0) middlewareResponse = await terminal(middlewareCtx);
else middlewareResponse = await executeMiddleware(flattenedRequestMiddlewares.map((d) => d.options.server), terminal, middlewareCtx, signal, event);
} catch (error) {
if (signal.aborted) throw error;
if (error instanceof Response) middlewareResponse = reconcileResponse(error, event);
else if (!isServerFnRequest) throw error;
else {
const disposeLate = createLateResponseDisposer(signal);
middlewareResponse = await waitForRequest(runWithStartContext({
...startContext,
contextAfterGlobalMiddlewares: middlewareCtx.context
}, () => createServerFnErrorResponse(error)), signal, disposeLate, disposeLate);
middlewareResponse = reconcileResponse(middlewareResponse, event);
}
}
let result;
try {
result = normalizeSsrResponse(middlewareResponse);
if (isRedirect(result.response)) result = await handleRedirectResponse(result, getRouter, signal, isServerFnRequest && request.headers.get("x-tsr-serverFn") === "true", event);
if (request.method === "HEAD" && result.response.body !== null) {
const source = result.response;
result = stripSsrResponseBody(result, "HEAD body stripped");
transferResponseMetadata(source, result.response);
}
} catch (error) {
disposeResponseResult(middlewareResponse, signal.aborted ? signal.reason : error);
throw error;
}
bindSsrResponseToRequest(router, result, signal);
signal.throwIfAborted();
responseOwnsCleanup = result.serverSsrCleanup === "stream";
response = result.response;
} finally {
if (router?.serverSsr && !responseOwnsCleanup) {
router.serverSsr.cleanup();
if (response) response = finalizeResponse(response, event);
}
}
return response;
};
return withStartRequest(startRequestResolver);
}
var relativeRedirectProtocols = /* @__PURE__ */ new Set();
var SERIALIZED_REDIRECT_HEADERS = /* @__PURE__ */ new Map([
["content-type", "application/json"],
["location", null],
[X_TSS_RAW_RESPONSE, null],
[X_TSS_SERIALIZED, null]
]);
async function handleRedirectResponse(ssrResponse, getRouter, signal, serializeRedirect, event) {
signal.throwIfAborted();
const redirect = ssrResponse.response;
if (!isRedirect(redirect)) return ssrResponse;
const opts = redirect.options;
const href = redirect.headers.get("Location") || opts.href;
if (!href && opts.to && typeof opts.to === "string" && !opts.to.startsWith("/")) throw new Error(`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)}`);
if (!href && [
"params",
"search",
"hash"
].some((d) => typeof opts[d] === "function")) throw new Error(`Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(opts).filter((d) => typeof opts[d] === "function").map((d) => `"${d}"`).join(", ")}`);
signal.throwIfAborted();
if (href && !isDangerousProtocol(href, relativeRedirectProtocols)) {
opts.href = href;
redirect.headers.set("Location", href);
} else {
const router = await getRouter();
signal.throwIfAborted();
router.resolveRedirect(redirect);
}
if (serializeRedirect) {
const redirectOptions = { ...opts };
delete redirectOptions.headers;
redirectOptions.isSerializedRedirect = true;
const responseHeaders = new Headers(redirect.headers);
responseHeaders.set("content-type", "application/json");
responseHeaders.delete("location");
responseHeaders.delete(X_TSS_RAW_RESPONSE);
responseHeaders.delete(X_TSS_SERIALIZED);
const body = JSON.stringify(redirectOptions);
if (body === void 0) throw new TypeError("Value is not JSON serializable");
disposeSsrResponse(ssrResponse, "redirect response replaced");
return {
response: createFinalizedResponse(body, responseHeaders, SERIALIZED_REDIRECT_HEADERS, event),
serverSsrCleanup: "none"
};
}
ssrResponse.response = finalizeResponse(redirect, event);
return ssrResponse;
}
function withParsedParams(handler, matchedRoutes) {
if (!matchedRoutes.some((route) => route.options.params?.parse ?? route.options.parseParams)) return handler;
return (ctx) => {
const params = Object.assign(Object.create(null), ctx.params);
for (const route of matchedRoutes) {
const parse = route.options.params?.parse ?? route.options.parseParams;
if (parse) Object.assign(params, parse(params));
}
return handler({
...ctx,
params
});
};
}
async function handleServerRoutes({ getRouter, request, executeRouter, context, executedRequestMiddlewares, signal, event }) {
const router = await getRouter();
const { pathname } = router.latestLocation;
const [matchedRoutes, rawParams, foundRoute] = router.getMatchedRoutes(pathname);
const isExactMatch = foundRoute && rawParams["**"] === void 0;
const routeMiddlewares = [];
let terminalHandler = (ctx) => executeRouter(ctx.context, matchedRoutes, router);
let terminalNext;
for (const route of matchedRoutes) {
const serverMiddleware = route.options.server?.middleware;
if (serverMiddleware) {
const flattened = flattenMiddlewares(serverMiddleware);
for (const m of flattened) if (!executedRequestMiddlewares?.has(m)) routeMiddlewares.push(m.options.server);
}
}
const server = foundRoute?.options.server;
if (server?.handlers && isExactMatch) {
const handlers = typeof server.handlers === "function" ? server.handlers({ createHandlers: (d) => d }) : server.handlers;
const requestMethod = request.method.toUpperCase();
const handler = requestMethod === "HEAD" ? handlers["HEAD"] ?? handlers["GET"] ?? handlers["ANY"] : handlers[requestMethod] ?? handlers["ANY"];
if (handler) {
const mayDefer = !!foundRoute.options.component;
if (typeof handler !== "function") {
if (handler.middleware?.length) {
const handlerMiddlewares = flattenMiddlewares(handler.middleware);
for (const m of handlerMiddlewares) routeMiddlewares.push(m.options.server);
}
}
const routeHandler = typeof handler === "function" ? handler : handler.handler;
if (routeHandler) {
const parsedHandler = withParsedParams(routeHandler, matchedRoutes);
if (!mayDefer) {
terminalHandler = parsedHandler;
terminalNext = throwIfMayNotDefer;
} else routeMiddlewares.push(parsedHandler);
}
}
}
const response = await executeMiddleware(routeMiddlewares, terminalHandler, {
request,
context,
params: rawParams,
pathname,
handlerType: "router"
}, signal, event, terminalNext);
return normalizeSsrResponse(response);
}
//#endregion
export { createStartHandler };
//# sourceMappingURL=createStartHandler.js.map