@tanstack/start-server-core 1.167.10 → 1.167.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/createSsrRpc.d.ts +1 -3
- package/dist/esm/createSsrRpc.js +2 -2
- package/dist/esm/createSsrRpc.js.map +1 -1
- package/dist/esm/createStartHandler.js +23 -9
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/fake-start-server-fn-resolver.d.ts +3 -1
- package/dist/esm/fake-start-server-fn-resolver.js +1 -1
- package/dist/esm/fake-start-server-fn-resolver.js.map +1 -1
- package/dist/esm/frame-protocol.d.ts +14 -2
- package/dist/esm/frame-protocol.js +83 -70
- package/dist/esm/frame-protocol.js.map +1 -1
- package/dist/esm/serializer/ServerFunctionSerializationAdapter.js +1 -1
- package/dist/esm/serializer/ServerFunctionSerializationAdapter.js.map +1 -1
- package/dist/esm/server-functions-handler.js +46 -30
- package/dist/esm/server-functions-handler.js.map +1 -1
- package/dist/esm/transformAssetUrls.js +6 -8
- package/dist/esm/transformAssetUrls.js.map +1 -1
- package/dist/esm/virtual-modules.d.ts +1 -0
- package/dist/esm/virtual-modules.js +2 -1
- package/dist/esm/virtual-modules.js.map +1 -1
- package/package.json +2 -2
- package/src/createSsrRpc.ts +2 -9
- package/src/createStartHandler.ts +44 -14
- package/src/fake-start-server-fn-resolver.ts +4 -1
- package/src/frame-protocol.ts +131 -89
- package/src/serializer/ServerFunctionSerializationAdapter.ts +1 -1
- package/src/server-functions-handler.ts +104 -54
- package/src/tanstack-start.d.ts +3 -1
- package/src/transformAssetUrls.ts +13 -18
- package/src/virtual-modules.ts +1 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { TSS_SERVER_FUNCTION, ClientFnMeta } from '@tanstack/start-client-core';
|
|
2
|
-
|
|
3
|
-
export type SsrRpcImporter = () => Promise<ServerFn>;
|
|
4
|
-
export declare const createSsrRpc: (functionId: string, importer?: SsrRpcImporter) => ((...args: Array<any>) => Promise<any>) & {
|
|
2
|
+
export declare const createSsrRpc: (functionId: string) => ((...args: Array<any>) => Promise<any>) & {
|
|
5
3
|
url: string;
|
|
6
4
|
serverFnMeta: ClientFnMeta;
|
|
7
5
|
[TSS_SERVER_FUNCTION]: boolean;
|
package/dist/esm/createSsrRpc.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getServerFnById } from "./getServerFnById.js";
|
|
2
2
|
import { TSS_SERVER_FUNCTION } from "@tanstack/start-client-core";
|
|
3
3
|
//#region src/createSsrRpc.ts
|
|
4
|
-
var createSsrRpc = (functionId
|
|
4
|
+
var createSsrRpc = (functionId) => {
|
|
5
5
|
const url = process.env.TSS_SERVER_FN_BASE + functionId;
|
|
6
6
|
const serverFnMeta = { id: functionId };
|
|
7
7
|
const fn = async (...args) => {
|
|
8
|
-
return (
|
|
8
|
+
return (await getServerFnById(functionId, { origin: "server" }))(...args);
|
|
9
9
|
};
|
|
10
10
|
return Object.assign(fn, {
|
|
11
11
|
url,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSsrRpc.js","names":[],"sources":["../../src/createSsrRpc.ts"],"sourcesContent":["import { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\nimport { getServerFnById } from './getServerFnById'\nimport type { ClientFnMeta } from '@tanstack/start-client-core'\
|
|
1
|
+
{"version":3,"file":"createSsrRpc.js","names":[],"sources":["../../src/createSsrRpc.ts"],"sourcesContent":["import { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\nimport { getServerFnById } from './getServerFnById'\nimport type { ClientFnMeta } from '@tanstack/start-client-core'\n\nexport const createSsrRpc = (functionId: string) => {\n const url = process.env.TSS_SERVER_FN_BASE + functionId\n const serverFnMeta: ClientFnMeta = { id: functionId }\n\n const fn = async (...args: Array<any>): Promise<any> => {\n const serverFn = await getServerFnById(functionId, { origin: 'server' })\n return serverFn(...args)\n }\n\n return Object.assign(fn, {\n url,\n serverFnMeta,\n [TSS_SERVER_FUNCTION]: true,\n })\n}\n"],"mappings":";;;AAIA,IAAa,gBAAgB,eAAuB;CAClD,MAAM,MAAM,QAAQ,IAAI,qBAAqB;CAC7C,MAAM,eAA6B,EAAE,IAAI,YAAY;CAErD,MAAM,KAAK,OAAO,GAAG,SAAmC;AAEtD,UADiB,MAAM,gBAAgB,YAAY,EAAE,QAAQ,UAAU,CAAC,EACxD,GAAG,KAAK;;AAG1B,QAAO,OAAO,OAAO,IAAI;EACvB;EACA;GACC,sBAAsB;EACxB,CAAC"}
|
|
@@ -8,7 +8,7 @@ import { createMemoryHistory } from "@tanstack/history";
|
|
|
8
8
|
import { createNullProtoObject, flattenMiddlewares, mergeHeaders, safeObjectMerge } from "@tanstack/start-client-core";
|
|
9
9
|
import { executeRewriteInput, isRedirect, isResolvedRedirect } from "@tanstack/router-core";
|
|
10
10
|
import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin } from "@tanstack/router-core/ssr/server";
|
|
11
|
-
import { runWithStartContext } from "@tanstack/start-storage-context";
|
|
11
|
+
import { getStartContext, runWithStartContext } from "@tanstack/start-storage-context";
|
|
12
12
|
//#region src/createStartHandler.ts
|
|
13
13
|
function getStartResponseHeaders(opts) {
|
|
14
14
|
return mergeHeaders({ "Content-Type": "text/html; charset=utf-8" }, ...opts.router.stores.activeMatchesSnapshot.state.map((match) => {
|
|
@@ -23,10 +23,15 @@ var baseManifestPromise;
|
|
|
23
23
|
*/
|
|
24
24
|
var cachedFinalManifestPromise;
|
|
25
25
|
async function loadEntries() {
|
|
26
|
-
const routerEntry = await
|
|
26
|
+
const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
|
|
27
|
+
import("#tanstack-router-entry"),
|
|
28
|
+
import("#tanstack-start-entry"),
|
|
29
|
+
import("#tanstack-start-plugin-adapters")
|
|
30
|
+
]);
|
|
27
31
|
return {
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
routerEntry,
|
|
33
|
+
startEntry,
|
|
34
|
+
pluginAdapters
|
|
30
35
|
};
|
|
31
36
|
}
|
|
32
37
|
function getEntries() {
|
|
@@ -212,7 +217,12 @@ function createStartHandler(cbOrOptions) {
|
|
|
212
217
|
if (handledProtocolRelativeURL) return Response.redirect(url, 308);
|
|
213
218
|
const entries = await getEntries();
|
|
214
219
|
const startOptions = await entries.startEntry.startInstance?.getOptions() || {};
|
|
215
|
-
const
|
|
220
|
+
const { hasPluginAdapters, pluginSerializationAdapters } = entries.pluginAdapters;
|
|
221
|
+
const serializationAdapters = [
|
|
222
|
+
...startOptions.serializationAdapters || [],
|
|
223
|
+
...hasPluginAdapters ? pluginSerializationAdapters : [],
|
|
224
|
+
ServerFunctionSerializationAdapter
|
|
225
|
+
];
|
|
216
226
|
const requestStartOptions = {
|
|
217
227
|
...startOptions,
|
|
218
228
|
serializationAdapters
|
|
@@ -245,7 +255,8 @@ function createStartHandler(cbOrOptions) {
|
|
|
245
255
|
startOptions: requestStartOptions,
|
|
246
256
|
contextAfterGlobalMiddlewares: context,
|
|
247
257
|
request,
|
|
248
|
-
executedRequestMiddlewares
|
|
258
|
+
executedRequestMiddlewares,
|
|
259
|
+
handlerType: "serverFn"
|
|
249
260
|
}, () => handleServerAction({
|
|
250
261
|
request,
|
|
251
262
|
context: requestOpts?.context,
|
|
@@ -268,12 +279,14 @@ function createStartHandler(cbOrOptions) {
|
|
|
268
279
|
const routerInstance = await getRouter();
|
|
269
280
|
attachRouterServerSsrUtils({
|
|
270
281
|
router: routerInstance,
|
|
271
|
-
manifest
|
|
282
|
+
manifest,
|
|
283
|
+
getRequestAssets: () => getStartContext({ throwIfNotFound: false })?.requestAssets
|
|
272
284
|
});
|
|
273
285
|
routerInstance.update({ additionalContext: { serverContext } });
|
|
274
286
|
await routerInstance.load();
|
|
275
287
|
if (routerInstance.state.redirect) return routerInstance.state.redirect;
|
|
276
|
-
|
|
288
|
+
const ctx = getStartContext({ throwIfNotFound: false });
|
|
289
|
+
await routerInstance.serverSsr.dehydrate({ requestAssets: ctx?.requestAssets });
|
|
277
290
|
const responseHeaders = getStartResponseHeaders({ router: routerInstance });
|
|
278
291
|
cbWillCleanup = true;
|
|
279
292
|
return cb({
|
|
@@ -288,7 +301,8 @@ function createStartHandler(cbOrOptions) {
|
|
|
288
301
|
startOptions: requestStartOptions,
|
|
289
302
|
contextAfterGlobalMiddlewares: context,
|
|
290
303
|
request,
|
|
291
|
-
executedRequestMiddlewares
|
|
304
|
+
executedRequestMiddlewares,
|
|
305
|
+
handlerType: "router"
|
|
292
306
|
}, async () => {
|
|
293
307
|
try {
|
|
294
308
|
return await handleServerRoutes({
|
|
@@ -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 { runWithStartContext } 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 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.activeMatchesSnapshot.state.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 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() {\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\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 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 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 })\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 {\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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"}
|
|
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.activeMatchesSnapshot.state.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":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-start-server-fn-resolver.js","names":[],"sources":["../../src/fake-start-server-fn-resolver.ts"],"sourcesContent":["export async function getServerFnById(): Promise<any> {}\n"],"mappings":";AAAA,eAAsB,
|
|
1
|
+
{"version":3,"file":"fake-start-server-fn-resolver.js","names":[],"sources":["../../src/fake-start-server-fn-resolver.ts"],"sourcesContent":["export async function getServerFnById(\n _id: string,\n _access: { origin: 'client' | 'server' },\n): Promise<any> {}\n"],"mappings":";AAAA,eAAsB,gBACpB,KACA,SACc"}
|
|
@@ -20,13 +20,25 @@ export declare function encodeEndFrame(streamId: number): Uint8Array;
|
|
|
20
20
|
* Encodes a raw stream error frame.
|
|
21
21
|
*/
|
|
22
22
|
export declare function encodeErrorFrame(streamId: number, error: unknown): Uint8Array;
|
|
23
|
+
/**
|
|
24
|
+
* Late stream registration for RawStreams discovered after serialization starts.
|
|
25
|
+
* Used when Promise<RawStream> resolves after the initial synchronous pass.
|
|
26
|
+
*/
|
|
27
|
+
export interface LateStreamRegistration {
|
|
28
|
+
id: number;
|
|
29
|
+
stream: ReadableStream<Uint8Array>;
|
|
30
|
+
}
|
|
23
31
|
/**
|
|
24
32
|
* Creates a multiplexed ReadableStream from JSON stream and raw streams.
|
|
25
33
|
*
|
|
26
34
|
* The JSON stream emits NDJSON lines (from seroval's toCrossJSONStream).
|
|
27
35
|
* Raw streams are pumped concurrently, interleaved with JSON frames.
|
|
28
36
|
*
|
|
37
|
+
* Supports late stream registration for RawStreams discovered after initial
|
|
38
|
+
* serialization (e.g., from resolved Promises).
|
|
39
|
+
*
|
|
29
40
|
* @param jsonStream Stream of JSON strings (each string is one NDJSON line)
|
|
30
|
-
* @param rawStreams Map of stream IDs to raw binary streams
|
|
41
|
+
* @param rawStreams Map of stream IDs to raw binary streams (known at start)
|
|
42
|
+
* @param lateStreamSource Optional stream of late registrations for streams discovered later
|
|
31
43
|
*/
|
|
32
|
-
export declare function createMultiplexedStream(jsonStream: ReadableStream<string>, rawStreams: Map<number, ReadableStream<Uint8Array
|
|
44
|
+
export declare function createMultiplexedStream(jsonStream: ReadableStream<string>, rawStreams: Map<number, ReadableStream<Uint8Array>>, lateStreamSource?: ReadableStream<LateStreamRegistration>): ReadableStream<Uint8Array>;
|
|
@@ -61,89 +61,102 @@ function encodeErrorFrame(streamId, error) {
|
|
|
61
61
|
* The JSON stream emits NDJSON lines (from seroval's toCrossJSONStream).
|
|
62
62
|
* Raw streams are pumped concurrently, interleaved with JSON frames.
|
|
63
63
|
*
|
|
64
|
+
* Supports late stream registration for RawStreams discovered after initial
|
|
65
|
+
* serialization (e.g., from resolved Promises).
|
|
66
|
+
*
|
|
64
67
|
* @param jsonStream Stream of JSON strings (each string is one NDJSON line)
|
|
65
|
-
* @param rawStreams Map of stream IDs to raw binary streams
|
|
68
|
+
* @param rawStreams Map of stream IDs to raw binary streams (known at start)
|
|
69
|
+
* @param lateStreamSource Optional stream of late registrations for streams discovered later
|
|
66
70
|
*/
|
|
67
|
-
function createMultiplexedStream(jsonStream, rawStreams) {
|
|
68
|
-
let
|
|
69
|
-
let controllerRef = null;
|
|
71
|
+
function createMultiplexedStream(jsonStream, rawStreams, lateStreamSource) {
|
|
72
|
+
let controller;
|
|
70
73
|
let cancelled = false;
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
if (cancelled
|
|
74
|
+
const readers = [];
|
|
75
|
+
const enqueue = (frame) => {
|
|
76
|
+
if (cancelled) return false;
|
|
74
77
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
controller.enqueue(frame);
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
77
83
|
};
|
|
78
|
-
const
|
|
79
|
-
if (cancelled
|
|
84
|
+
const errorOutput = (error) => {
|
|
85
|
+
if (cancelled) return;
|
|
86
|
+
cancelled = true;
|
|
80
87
|
try {
|
|
81
|
-
|
|
88
|
+
controller.error(error);
|
|
82
89
|
} catch {}
|
|
90
|
+
for (const reader of readers) reader.cancel().catch(() => {});
|
|
83
91
|
};
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
async function pumpRawStream(streamId, stream) {
|
|
93
|
+
const reader = stream.getReader();
|
|
94
|
+
readers.push(reader);
|
|
86
95
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (activePumps === 0) safeClose();
|
|
93
|
-
};
|
|
94
|
-
return new ReadableStream({
|
|
95
|
-
start(controller) {
|
|
96
|
-
controllerRef = controller;
|
|
97
|
-
cancelReaders.length = 0;
|
|
98
|
-
const pumpJSON = async () => {
|
|
99
|
-
const reader = jsonStream.getReader();
|
|
100
|
-
cancelReaders.push(() => {
|
|
101
|
-
reader.cancel().catch(() => {});
|
|
102
|
-
});
|
|
103
|
-
try {
|
|
104
|
-
while (true) {
|
|
105
|
-
const { done, value } = await reader.read();
|
|
106
|
-
if (cancelled) break;
|
|
107
|
-
if (done) break;
|
|
108
|
-
safeEnqueue(encodeJSONFrame(value));
|
|
109
|
-
}
|
|
110
|
-
} catch (error) {
|
|
111
|
-
safeError(error);
|
|
112
|
-
} finally {
|
|
113
|
-
reader.releaseLock();
|
|
114
|
-
checkComplete();
|
|
96
|
+
while (!cancelled) {
|
|
97
|
+
const { done, value } = await reader.read();
|
|
98
|
+
if (done) {
|
|
99
|
+
enqueue(encodeEndFrame(streamId));
|
|
100
|
+
return;
|
|
115
101
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
102
|
+
if (!enqueue(encodeChunkFrame(streamId, value))) return;
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
enqueue(encodeErrorFrame(streamId, error));
|
|
106
|
+
} finally {
|
|
107
|
+
reader.releaseLock();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function pumpJSON() {
|
|
111
|
+
const reader = jsonStream.getReader();
|
|
112
|
+
readers.push(reader);
|
|
113
|
+
try {
|
|
114
|
+
while (!cancelled) {
|
|
115
|
+
const { done, value } = await reader.read();
|
|
116
|
+
if (done) return;
|
|
117
|
+
if (!enqueue(encodeJSONFrame(value))) return;
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
errorOutput(error);
|
|
121
|
+
throw error;
|
|
122
|
+
} finally {
|
|
123
|
+
reader.releaseLock();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function pumpLateStreams() {
|
|
127
|
+
if (!lateStreamSource) return [];
|
|
128
|
+
const lateStreamPumps = [];
|
|
129
|
+
const reader = lateStreamSource.getReader();
|
|
130
|
+
readers.push(reader);
|
|
131
|
+
try {
|
|
132
|
+
while (!cancelled) {
|
|
133
|
+
const { done, value } = await reader.read();
|
|
134
|
+
if (done) break;
|
|
135
|
+
lateStreamPumps.push(pumpRawStream(value.id, value.stream));
|
|
136
|
+
}
|
|
137
|
+
} finally {
|
|
138
|
+
reader.releaseLock();
|
|
139
|
+
}
|
|
140
|
+
return lateStreamPumps;
|
|
141
|
+
}
|
|
142
|
+
return new ReadableStream({
|
|
143
|
+
async start(ctrl) {
|
|
144
|
+
controller = ctrl;
|
|
145
|
+
const pumps = [pumpJSON()];
|
|
146
|
+
for (const [streamId, stream] of rawStreams) pumps.push(pumpRawStream(streamId, stream));
|
|
147
|
+
if (lateStreamSource) pumps.push(pumpLateStreams());
|
|
148
|
+
try {
|
|
149
|
+
const latePumps = (await Promise.all(pumps)).find(Array.isArray);
|
|
150
|
+
if (latePumps && latePumps.length > 0) await Promise.all(latePumps);
|
|
151
|
+
if (!cancelled) try {
|
|
152
|
+
controller.close();
|
|
153
|
+
} catch {}
|
|
154
|
+
} catch {}
|
|
141
155
|
},
|
|
142
156
|
cancel() {
|
|
143
157
|
cancelled = true;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
cancelReaders.length = 0;
|
|
158
|
+
for (const reader of readers) reader.cancel().catch(() => {});
|
|
159
|
+
readers.length = 0;
|
|
147
160
|
}
|
|
148
161
|
});
|
|
149
162
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-protocol.js","names":[],"sources":["../../src/frame-protocol.ts"],"sourcesContent":["/**\n * Binary frame protocol for multiplexing JSON and raw streams over HTTP.\n *\n * Frame format: [type:1][streamId:4][length:4][payload:length]\n * - type: 1 byte - frame type (JSON, CHUNK, END, ERROR)\n * - streamId: 4 bytes big-endian uint32 - stream identifier\n * - length: 4 bytes big-endian uint32 - payload length\n * - payload: variable length bytes\n */\n\n// Re-export constants from shared location\nimport { FRAME_HEADER_SIZE, FrameType } from '@tanstack/start-client-core'\n\nexport {\n FRAME_HEADER_SIZE,\n FrameType,\n TSS_CONTENT_TYPE_FRAMED,\n TSS_CONTENT_TYPE_FRAMED_VERSIONED,\n TSS_FRAMED_PROTOCOL_VERSION,\n} from '@tanstack/start-client-core'\n\n/** Cached TextEncoder for frame encoding */\nconst textEncoder = new TextEncoder()\n\n/** Shared empty payload for END frames - avoids allocation per call */\nconst EMPTY_PAYLOAD = new Uint8Array(0)\n\n/**\n * Encodes a single frame with header and payload.\n */\nexport function encodeFrame(\n type: FrameType,\n streamId: number,\n payload: Uint8Array,\n): Uint8Array {\n const frame = new Uint8Array(FRAME_HEADER_SIZE + payload.length)\n // Write header bytes directly to avoid DataView allocation per frame\n // Frame format: [type:1][streamId:4 BE][length:4 BE]\n frame[0] = type\n frame[1] = (streamId >>> 24) & 0xff\n frame[2] = (streamId >>> 16) & 0xff\n frame[3] = (streamId >>> 8) & 0xff\n frame[4] = streamId & 0xff\n frame[5] = (payload.length >>> 24) & 0xff\n frame[6] = (payload.length >>> 16) & 0xff\n frame[7] = (payload.length >>> 8) & 0xff\n frame[8] = payload.length & 0xff\n frame.set(payload, FRAME_HEADER_SIZE)\n return frame\n}\n\n/**\n * Encodes a JSON frame (type 0, streamId 0).\n */\nexport function encodeJSONFrame(json: string): Uint8Array {\n return encodeFrame(FrameType.JSON, 0, textEncoder.encode(json))\n}\n\n/**\n * Encodes a raw stream chunk frame.\n */\nexport function encodeChunkFrame(\n streamId: number,\n chunk: Uint8Array,\n): Uint8Array {\n return encodeFrame(FrameType.CHUNK, streamId, chunk)\n}\n\n/**\n * Encodes a raw stream end frame.\n */\nexport function encodeEndFrame(streamId: number): Uint8Array {\n return encodeFrame(FrameType.END, streamId, EMPTY_PAYLOAD)\n}\n\n/**\n * Encodes a raw stream error frame.\n */\nexport function encodeErrorFrame(streamId: number, error: unknown): Uint8Array {\n const message =\n error instanceof Error ? error.message : String(error ?? 'Unknown error')\n return encodeFrame(FrameType.ERROR, streamId, textEncoder.encode(message))\n}\n\n/**\n * Creates a multiplexed ReadableStream from JSON stream and raw streams.\n *\n * The JSON stream emits NDJSON lines (from seroval's toCrossJSONStream).\n * Raw streams are pumped concurrently, interleaved with JSON frames.\n *\n * @param jsonStream Stream of JSON strings (each string is one NDJSON line)\n * @param rawStreams Map of stream IDs to raw binary streams\n */\nexport function createMultiplexedStream(\n jsonStream: ReadableStream<string>,\n rawStreams: Map<number, ReadableStream<Uint8Array>>,\n): ReadableStream<Uint8Array> {\n // Track active pumps for completion\n let activePumps = 1 + rawStreams.size // 1 for JSON + raw streams\n let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null\n let cancelled = false as boolean\n const cancelReaders: Array<() => void> = []\n\n const safeEnqueue = (chunk: Uint8Array) => {\n if (cancelled || !controllerRef) return\n try {\n controllerRef.enqueue(chunk)\n } catch {\n // Ignore enqueue after close/cancel\n }\n }\n\n const safeError = (err: unknown) => {\n if (cancelled || !controllerRef) return\n try {\n controllerRef.error(err)\n } catch {\n // Ignore\n }\n }\n\n const safeClose = () => {\n if (cancelled || !controllerRef) return\n try {\n controllerRef.close()\n } catch {\n // Ignore\n }\n }\n\n const checkComplete = () => {\n activePumps--\n if (activePumps === 0) {\n safeClose()\n }\n }\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controllerRef = controller\n cancelReaders.length = 0\n\n // Pump JSON stream (streamId 0)\n const pumpJSON = async () => {\n const reader = jsonStream.getReader()\n cancelReaders.push(() => {\n // Catch async rejection - reader may already be released\n reader.cancel().catch(() => {})\n })\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const { done, value } = await reader.read()\n // Check cancelled after await - flag may have changed while waiting\n if (cancelled) break\n if (done) break\n safeEnqueue(encodeJSONFrame(value))\n }\n } catch (error) {\n // JSON stream error - fatal, error the whole response\n safeError(error)\n } finally {\n reader.releaseLock()\n checkComplete()\n }\n }\n\n // Pump a single raw stream with its streamId\n const pumpRawStream = async (\n streamId: number,\n stream: ReadableStream<Uint8Array>,\n ) => {\n const reader = stream.getReader()\n cancelReaders.push(() => {\n // Catch async rejection - reader may already be released\n reader.cancel().catch(() => {})\n })\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const { done, value } = await reader.read()\n // Check cancelled after await - flag may have changed while waiting\n if (cancelled) break\n if (done) {\n safeEnqueue(encodeEndFrame(streamId))\n break\n }\n safeEnqueue(encodeChunkFrame(streamId, value))\n }\n } catch (error) {\n // Stream error - send ERROR frame (non-fatal, other streams continue)\n safeEnqueue(encodeErrorFrame(streamId, error))\n } finally {\n reader.releaseLock()\n checkComplete()\n }\n }\n\n // Start all pumps concurrently\n pumpJSON()\n for (const [streamId, stream] of rawStreams) {\n pumpRawStream(streamId, stream)\n }\n },\n\n cancel() {\n cancelled = true\n controllerRef = null\n // Proactively cancel all underlying readers to stop work quickly.\n for (const cancelReader of cancelReaders) {\n cancelReader()\n }\n cancelReaders.length = 0\n },\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"frame-protocol.js","names":[],"sources":["../../src/frame-protocol.ts"],"sourcesContent":["/**\n * Binary frame protocol for multiplexing JSON and raw streams over HTTP.\n *\n * Frame format: [type:1][streamId:4][length:4][payload:length]\n * - type: 1 byte - frame type (JSON, CHUNK, END, ERROR)\n * - streamId: 4 bytes big-endian uint32 - stream identifier\n * - length: 4 bytes big-endian uint32 - payload length\n * - payload: variable length bytes\n */\n\n// Re-export constants from shared location\nimport { FRAME_HEADER_SIZE, FrameType } from '@tanstack/start-client-core'\n\nexport {\n FRAME_HEADER_SIZE,\n FrameType,\n TSS_CONTENT_TYPE_FRAMED,\n TSS_CONTENT_TYPE_FRAMED_VERSIONED,\n TSS_FRAMED_PROTOCOL_VERSION,\n} from '@tanstack/start-client-core'\n\n/** Cached TextEncoder for frame encoding */\nconst textEncoder = new TextEncoder()\n\n/** Shared empty payload for END frames - avoids allocation per call */\nconst EMPTY_PAYLOAD = new Uint8Array(0)\n\n/**\n * Encodes a single frame with header and payload.\n */\nexport function encodeFrame(\n type: FrameType,\n streamId: number,\n payload: Uint8Array,\n): Uint8Array {\n const frame = new Uint8Array(FRAME_HEADER_SIZE + payload.length)\n // Write header bytes directly to avoid DataView allocation per frame\n // Frame format: [type:1][streamId:4 BE][length:4 BE]\n frame[0] = type\n frame[1] = (streamId >>> 24) & 0xff\n frame[2] = (streamId >>> 16) & 0xff\n frame[3] = (streamId >>> 8) & 0xff\n frame[4] = streamId & 0xff\n frame[5] = (payload.length >>> 24) & 0xff\n frame[6] = (payload.length >>> 16) & 0xff\n frame[7] = (payload.length >>> 8) & 0xff\n frame[8] = payload.length & 0xff\n frame.set(payload, FRAME_HEADER_SIZE)\n return frame\n}\n\n/**\n * Encodes a JSON frame (type 0, streamId 0).\n */\nexport function encodeJSONFrame(json: string): Uint8Array {\n return encodeFrame(FrameType.JSON, 0, textEncoder.encode(json))\n}\n\n/**\n * Encodes a raw stream chunk frame.\n */\nexport function encodeChunkFrame(\n streamId: number,\n chunk: Uint8Array,\n): Uint8Array {\n return encodeFrame(FrameType.CHUNK, streamId, chunk)\n}\n\n/**\n * Encodes a raw stream end frame.\n */\nexport function encodeEndFrame(streamId: number): Uint8Array {\n return encodeFrame(FrameType.END, streamId, EMPTY_PAYLOAD)\n}\n\n/**\n * Encodes a raw stream error frame.\n */\nexport function encodeErrorFrame(streamId: number, error: unknown): Uint8Array {\n const message =\n error instanceof Error ? error.message : String(error ?? 'Unknown error')\n return encodeFrame(FrameType.ERROR, streamId, textEncoder.encode(message))\n}\n\n/**\n * Late stream registration for RawStreams discovered after serialization starts.\n * Used when Promise<RawStream> resolves after the initial synchronous pass.\n */\nexport interface LateStreamRegistration {\n id: number\n stream: ReadableStream<Uint8Array>\n}\n\n/**\n * Creates a multiplexed ReadableStream from JSON stream and raw streams.\n *\n * The JSON stream emits NDJSON lines (from seroval's toCrossJSONStream).\n * Raw streams are pumped concurrently, interleaved with JSON frames.\n *\n * Supports late stream registration for RawStreams discovered after initial\n * serialization (e.g., from resolved Promises).\n *\n * @param jsonStream Stream of JSON strings (each string is one NDJSON line)\n * @param rawStreams Map of stream IDs to raw binary streams (known at start)\n * @param lateStreamSource Optional stream of late registrations for streams discovered later\n */\nexport function createMultiplexedStream(\n jsonStream: ReadableStream<string>,\n rawStreams: Map<number, ReadableStream<Uint8Array>>,\n lateStreamSource?: ReadableStream<LateStreamRegistration>,\n): ReadableStream<Uint8Array> {\n // Shared state for the multiplexed stream\n let controller: ReadableStreamDefaultController<Uint8Array>\n let cancelled = false\n const readers: Array<ReadableStreamDefaultReader<any>> = []\n\n // Helper to enqueue a frame, ignoring errors if stream is closed/cancelled\n const enqueue = (frame: Uint8Array): boolean => {\n if (cancelled) return false\n try {\n controller.enqueue(frame)\n return true\n } catch {\n return false\n }\n }\n\n // Helper to error the output stream (for fatal errors like JSON stream failure)\n const errorOutput = (error: unknown): void => {\n if (cancelled) return\n cancelled = true\n try {\n controller.error(error)\n } catch {\n // Already errored\n }\n // Cancel all readers to stop other pumps\n for (const reader of readers) {\n reader.cancel().catch(() => {})\n }\n }\n\n // Pumps a raw stream, sending CHUNK frames and END/ERROR on completion\n async function pumpRawStream(\n streamId: number,\n stream: ReadableStream<Uint8Array>,\n ): Promise<void> {\n const reader = stream.getReader()\n readers.push(reader)\n try {\n while (!cancelled) {\n const { done, value } = await reader.read()\n if (done) {\n enqueue(encodeEndFrame(streamId))\n return\n }\n if (!enqueue(encodeChunkFrame(streamId, value))) return\n }\n } catch (error) {\n // Raw stream error - send ERROR frame, don't fail entire response\n enqueue(encodeErrorFrame(streamId, error))\n } finally {\n reader.releaseLock()\n }\n }\n\n // Pumps the JSON stream, sending JSON frames\n // JSON stream errors are fatal - they error the entire output\n async function pumpJSON(): Promise<void> {\n const reader = jsonStream.getReader()\n readers.push(reader)\n try {\n while (!cancelled) {\n const { done, value } = await reader.read()\n if (done) return\n if (!enqueue(encodeJSONFrame(value))) return\n }\n } catch (error) {\n // JSON stream error is fatal - error the entire output\n errorOutput(error)\n throw error // Re-throw to signal failure to Promise.all\n } finally {\n reader.releaseLock()\n }\n }\n\n // Pumps late stream registrations, spawning raw stream pumps as they arrive\n async function pumpLateStreams(): Promise<Array<Promise<void>>> {\n if (!lateStreamSource) return []\n\n const lateStreamPumps: Array<Promise<void>> = []\n const reader = lateStreamSource.getReader()\n readers.push(reader)\n try {\n while (!cancelled) {\n const { done, value } = await reader.read()\n if (done) break\n // Start pumping this late stream and track it\n lateStreamPumps.push(pumpRawStream(value.id, value.stream))\n }\n } finally {\n reader.releaseLock()\n }\n return lateStreamPumps\n }\n\n return new ReadableStream<Uint8Array>({\n async start(ctrl) {\n controller = ctrl\n\n // Collect all pump promises\n const pumps: Array<Promise<void | Array<Promise<void>>>> = [pumpJSON()]\n\n for (const [streamId, stream] of rawStreams) {\n pumps.push(pumpRawStream(streamId, stream))\n }\n\n // Add late stream pump (returns array of spawned pump promises)\n if (lateStreamSource) {\n pumps.push(pumpLateStreams())\n }\n\n try {\n // Wait for initial pumps to complete\n const results = await Promise.all(pumps)\n\n // Wait for any late stream pumps that were spawned\n const latePumps = results.find(Array.isArray) as\n | Array<Promise<void>>\n | undefined\n if (latePumps && latePumps.length > 0) {\n await Promise.all(latePumps)\n }\n\n // All pumps done - close the output stream\n if (!cancelled) {\n try {\n controller.close()\n } catch {\n // Already closed\n }\n }\n } catch {\n // Error already handled by errorOutput in pumpJSON\n // or was a raw stream error (non-fatal, already sent ERROR frame)\n }\n },\n\n cancel() {\n cancelled = true\n // Cancel all readers to stop pumps quickly\n for (const reader of readers) {\n reader.cancel().catch(() => {})\n }\n readers.length = 0\n },\n })\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAM,cAAc,IAAI,aAAa;;AAGrC,IAAM,gBAAgB,IAAI,WAAW,EAAE;;;;AAKvC,SAAgB,YACd,MACA,UACA,SACY;CACZ,MAAM,QAAQ,IAAI,WAAW,oBAAoB,QAAQ,OAAO;AAGhE,OAAM,KAAK;AACX,OAAM,KAAM,aAAa,KAAM;AAC/B,OAAM,KAAM,aAAa,KAAM;AAC/B,OAAM,KAAM,aAAa,IAAK;AAC9B,OAAM,KAAK,WAAW;AACtB,OAAM,KAAM,QAAQ,WAAW,KAAM;AACrC,OAAM,KAAM,QAAQ,WAAW,KAAM;AACrC,OAAM,KAAM,QAAQ,WAAW,IAAK;AACpC,OAAM,KAAK,QAAQ,SAAS;AAC5B,OAAM,IAAI,SAAS,kBAAkB;AACrC,QAAO;;;;;AAMT,SAAgB,gBAAgB,MAA0B;AACxD,QAAO,YAAY,UAAU,MAAM,GAAG,YAAY,OAAO,KAAK,CAAC;;;;;AAMjE,SAAgB,iBACd,UACA,OACY;AACZ,QAAO,YAAY,UAAU,OAAO,UAAU,MAAM;;;;;AAMtD,SAAgB,eAAe,UAA8B;AAC3D,QAAO,YAAY,UAAU,KAAK,UAAU,cAAc;;;;;AAM5D,SAAgB,iBAAiB,UAAkB,OAA4B;CAC7E,MAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,SAAS,gBAAgB;AAC3E,QAAO,YAAY,UAAU,OAAO,UAAU,YAAY,OAAO,QAAQ,CAAC;;;;;;;;;;;;;;;AAyB5E,SAAgB,wBACd,YACA,YACA,kBAC4B;CAE5B,IAAI;CACJ,IAAI,YAAY;CAChB,MAAM,UAAmD,EAAE;CAG3D,MAAM,WAAW,UAA+B;AAC9C,MAAI,UAAW,QAAO;AACtB,MAAI;AACF,cAAW,QAAQ,MAAM;AACzB,UAAO;UACD;AACN,UAAO;;;CAKX,MAAM,eAAe,UAAyB;AAC5C,MAAI,UAAW;AACf,cAAY;AACZ,MAAI;AACF,cAAW,MAAM,MAAM;UACjB;AAIR,OAAK,MAAM,UAAU,QACnB,QAAO,QAAQ,CAAC,YAAY,GAAG;;CAKnC,eAAe,cACb,UACA,QACe;EACf,MAAM,SAAS,OAAO,WAAW;AACjC,UAAQ,KAAK,OAAO;AACpB,MAAI;AACF,UAAO,CAAC,WAAW;IACjB,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,MAAM;AACR,aAAQ,eAAe,SAAS,CAAC;AACjC;;AAEF,QAAI,CAAC,QAAQ,iBAAiB,UAAU,MAAM,CAAC,CAAE;;WAE5C,OAAO;AAEd,WAAQ,iBAAiB,UAAU,MAAM,CAAC;YAClC;AACR,UAAO,aAAa;;;CAMxB,eAAe,WAA0B;EACvC,MAAM,SAAS,WAAW,WAAW;AACrC,UAAQ,KAAK,OAAO;AACpB,MAAI;AACF,UAAO,CAAC,WAAW;IACjB,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,KAAM;AACV,QAAI,CAAC,QAAQ,gBAAgB,MAAM,CAAC,CAAE;;WAEjC,OAAO;AAEd,eAAY,MAAM;AAClB,SAAM;YACE;AACR,UAAO,aAAa;;;CAKxB,eAAe,kBAAiD;AAC9D,MAAI,CAAC,iBAAkB,QAAO,EAAE;EAEhC,MAAM,kBAAwC,EAAE;EAChD,MAAM,SAAS,iBAAiB,WAAW;AAC3C,UAAQ,KAAK,OAAO;AACpB,MAAI;AACF,UAAO,CAAC,WAAW;IACjB,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,KAAM;AAEV,oBAAgB,KAAK,cAAc,MAAM,IAAI,MAAM,OAAO,CAAC;;YAErD;AACR,UAAO,aAAa;;AAEtB,SAAO;;AAGT,QAAO,IAAI,eAA2B;EACpC,MAAM,MAAM,MAAM;AAChB,gBAAa;GAGb,MAAM,QAAqD,CAAC,UAAU,CAAC;AAEvE,QAAK,MAAM,CAAC,UAAU,WAAW,WAC/B,OAAM,KAAK,cAAc,UAAU,OAAO,CAAC;AAI7C,OAAI,iBACF,OAAM,KAAK,iBAAiB,CAAC;AAG/B,OAAI;IAKF,MAAM,aAHU,MAAM,QAAQ,IAAI,MAAM,EAGd,KAAK,MAAM,QAAQ;AAG7C,QAAI,aAAa,UAAU,SAAS,EAClC,OAAM,QAAQ,IAAI,UAAU;AAI9B,QAAI,CAAC,UACH,KAAI;AACF,gBAAW,OAAO;YACZ;WAIJ;;EAMV,SAAS;AACP,eAAY;AAEZ,QAAK,MAAM,UAAU,QACnB,QAAO,QAAQ,CAAC,YAAY,GAAG;AAEjC,WAAQ,SAAS;;EAEpB,CAAC"}
|
|
@@ -12,7 +12,7 @@ var ServerFunctionSerializationAdapter = createSerializationAdapter({
|
|
|
12
12
|
toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),
|
|
13
13
|
fromSerializable: ({ functionId }) => {
|
|
14
14
|
const fn = async (opts, signal) => {
|
|
15
|
-
return (await (await getServerFnById(functionId, {
|
|
15
|
+
return (await (await getServerFnById(functionId, { origin: "client" }))(opts ?? {}, signal)).result;
|
|
16
16
|
};
|
|
17
17
|
return fn;
|
|
18
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerFunctionSerializationAdapter.js","names":[],"sources":["../../../src/serializer/ServerFunctionSerializationAdapter.ts"],"sourcesContent":["import { createSerializationAdapter } from '@tanstack/router-core'\nimport { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\nimport { getServerFnById } from '../getServerFnById'\n\nexport const ServerFunctionSerializationAdapter = createSerializationAdapter({\n key: '$TSS/serverfn',\n test: (v): v is { serverFnMeta: { id: string } } => {\n if (typeof v !== 'function') return false\n\n if (!(TSS_SERVER_FUNCTION in v)) return false\n\n return !!v[TSS_SERVER_FUNCTION]\n },\n toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),\n fromSerializable: ({ functionId }) => {\n const fn = async (opts: any, signal: any): Promise<any> => {\n // When a function ID is received through serialization (e.g., as a parameter\n // to another server function), it originates from the client and must be\n // validated the same way as direct HTTP calls to server functions.\n const serverFn = await getServerFnById(functionId, {
|
|
1
|
+
{"version":3,"file":"ServerFunctionSerializationAdapter.js","names":[],"sources":["../../../src/serializer/ServerFunctionSerializationAdapter.ts"],"sourcesContent":["import { createSerializationAdapter } from '@tanstack/router-core'\nimport { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\nimport { getServerFnById } from '../getServerFnById'\n\nexport const ServerFunctionSerializationAdapter = createSerializationAdapter({\n key: '$TSS/serverfn',\n test: (v): v is { serverFnMeta: { id: string } } => {\n if (typeof v !== 'function') return false\n\n if (!(TSS_SERVER_FUNCTION in v)) return false\n\n return !!v[TSS_SERVER_FUNCTION]\n },\n toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),\n fromSerializable: ({ functionId }) => {\n const fn = async (opts: any, signal: any): Promise<any> => {\n // When a function ID is received through serialization (e.g., as a parameter\n // to another server function), it originates from the client and must be\n // validated the same way as direct HTTP calls to server functions.\n const serverFn = await getServerFnById(functionId, { origin: 'client' })\n const result = await serverFn(opts ?? {}, signal)\n return result.result\n }\n return fn as never\n },\n})\n"],"mappings":";;;;AAIA,IAAa,qCAAqC,2BAA2B;CAC3E,KAAK;CACL,OAAO,MAA6C;AAClD,MAAI,OAAO,MAAM,WAAY,QAAO;AAEpC,MAAI,EAAE,uBAAuB,GAAI,QAAO;AAExC,SAAO,CAAC,CAAC,EAAE;;CAEb,iBAAiB,EAAE,oBAAoB,EAAE,YAAY,aAAa,IAAI;CACtE,mBAAmB,EAAE,iBAAiB;EACpC,MAAM,KAAK,OAAO,MAAW,WAA8B;AAMzD,WADe,OADE,MAAM,gBAAgB,YAAY,EAAE,QAAQ,UAAU,CAAC,EAC1C,QAAQ,EAAE,EAAE,OAAO,EACnC;;AAEhB,SAAO;;CAEV,CAAC"}
|