@tanstack/start-server-core 1.132.32 → 1.132.33
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/createServerRpc.js +1 -15
- package/dist/esm/createServerRpc.js.map +1 -1
- package/dist/esm/createStartHandler.js +6 -17
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/router-manifest.d.ts +1 -3
- package/dist/esm/router-manifest.js +1 -1
- package/dist/esm/router-manifest.js.map +1 -1
- package/dist/esm/server-functions-handler.js +4 -11
- package/dist/esm/server-functions-handler.js.map +1 -1
- package/package.json +4 -4
- package/src/createServerRpc.ts +1 -17
- package/src/createStartHandler.ts +4 -20
- package/src/global.d.ts +2 -2
- package/src/router-manifest.ts +1 -1
- package/src/server-functions-handler.ts +5 -12
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { TSS_SERVER_FUNCTION } from "@tanstack/start-client-core";
|
|
2
|
-
import invariant from "tiny-invariant";
|
|
3
|
-
let baseUrl;
|
|
4
|
-
function sanitizeBase(base) {
|
|
5
|
-
return base.replace(/^\/|\/$/g, "");
|
|
6
|
-
}
|
|
7
2
|
const createServerRpc = (functionId, splitImportFn) => {
|
|
8
|
-
|
|
9
|
-
const sanitizedAppBase = sanitizeBase(process.env.TSS_APP_BASE || "/");
|
|
10
|
-
const sanitizedServerBase = sanitizeBase(process.env.TSS_SERVER_FN_BASE);
|
|
11
|
-
baseUrl = `${sanitizedAppBase ? `/${sanitizedAppBase}` : ""}/${sanitizedServerBase}/`;
|
|
12
|
-
}
|
|
13
|
-
invariant(
|
|
14
|
-
splitImportFn,
|
|
15
|
-
"🚨splitImportFn required for the server functions server runtime, but was not provided."
|
|
16
|
-
);
|
|
17
|
-
const url = baseUrl + functionId;
|
|
3
|
+
const url = process.env.TSS_SERVER_FN_BASE + functionId;
|
|
18
4
|
return Object.assign(splitImportFn, {
|
|
19
5
|
url,
|
|
20
6
|
functionId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createServerRpc.js","sources":["../../src/createServerRpc.ts"],"sourcesContent":["import { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\
|
|
1
|
+
{"version":3,"file":"createServerRpc.js","sources":["../../src/createServerRpc.ts"],"sourcesContent":["import { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\n\nexport const createServerRpc = (\n functionId: string,\n splitImportFn: (...args: any) => any,\n) => {\n const url = process.env.TSS_SERVER_FN_BASE + functionId\n\n return Object.assign(splitImportFn, {\n url,\n functionId,\n [TSS_SERVER_FUNCTION]: true,\n })\n}\n"],"names":[],"mappings":";AAEO,MAAM,kBAAkB,CAC7B,YACA,kBACG;AACH,QAAM,MAAM,QAAQ,IAAI,qBAAqB;AAE7C,SAAO,OAAO,OAAO,eAAe;AAAA,IAClC;AAAA,IACA;AAAA,IACA,CAAC,mBAAmB,GAAG;AAAA,EAAA,CACxB;AACH;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createMemoryHistory } from "@tanstack/history";
|
|
2
2
|
import { flattenMiddlewares, json, mergeHeaders } from "@tanstack/start-client-core";
|
|
3
|
-
import {
|
|
3
|
+
import { isRedirect, isResolvedRedirect, executeRewriteInput } from "@tanstack/router-core";
|
|
4
4
|
import { getOrigin, attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
|
|
5
5
|
import { runWithStartContext } from "@tanstack/start-storage-context";
|
|
6
6
|
import { requestHandler } from "./request-response.js";
|
|
@@ -20,17 +20,7 @@ function getStartResponseHeaders(opts) {
|
|
|
20
20
|
return headers;
|
|
21
21
|
}
|
|
22
22
|
function createStartHandler(cb) {
|
|
23
|
-
|
|
24
|
-
throw new Error(
|
|
25
|
-
"tanstack/start-server-core: TSS_SERVER_FN_BASE must be defined in your environment for createStartHandler()"
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
const APP_BASE = process.env.TSS_APP_BASE || "/";
|
|
29
|
-
const serverFnBase = joinPaths([
|
|
30
|
-
APP_BASE,
|
|
31
|
-
trimPath(process.env.TSS_SERVER_FN_BASE),
|
|
32
|
-
"/"
|
|
33
|
-
]);
|
|
23
|
+
const ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || "/";
|
|
34
24
|
let startRoutesManifest = null;
|
|
35
25
|
let startEntry = null;
|
|
36
26
|
let routerEntry = null;
|
|
@@ -85,7 +75,8 @@ function createStartHandler(cb) {
|
|
|
85
75
|
...{
|
|
86
76
|
defaultSsr: startOptions.defaultSsr,
|
|
87
77
|
serializationAdapters: startOptions.serializationAdapters
|
|
88
|
-
}
|
|
78
|
+
},
|
|
79
|
+
basepath: ROUTER_BASEPATH
|
|
89
80
|
});
|
|
90
81
|
return router;
|
|
91
82
|
};
|
|
@@ -102,7 +93,7 @@ function createStartHandler(cb) {
|
|
|
102
93
|
},
|
|
103
94
|
async () => {
|
|
104
95
|
try {
|
|
105
|
-
if (href.startsWith(
|
|
96
|
+
if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {
|
|
106
97
|
return await handleServerAction({
|
|
107
98
|
request,
|
|
108
99
|
context: requestOpts?.context
|
|
@@ -130,9 +121,7 @@ function createStartHandler(cb) {
|
|
|
130
121
|
);
|
|
131
122
|
}
|
|
132
123
|
if (startRoutesManifest === null) {
|
|
133
|
-
startRoutesManifest = await getStartManifest(
|
|
134
|
-
basePath: APP_BASE
|
|
135
|
-
});
|
|
124
|
+
startRoutesManifest = await getStartManifest();
|
|
136
125
|
}
|
|
137
126
|
const router2 = await getRouter();
|
|
138
127
|
attachRouterServerSsrUtils({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStartHandler.js","sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n flattenMiddlewares,\n json,\n mergeHeaders,\n} from '@tanstack/start-client-core'\nimport {\n executeRewriteInput,\n isRedirect,\n isResolvedRedirect,\n joinPaths,\n trimPath,\n} from '@tanstack/router-core'\nimport {\n attachRouterServerSsrUtils,\n getOrigin,\n} from '@tanstack/router-core/ssr/server'\nimport { runWithStartContext } from '@tanstack/start-storage-context'\nimport { requestHandler } from './request-response'\nimport { getStartManifest } from './router-manifest'\nimport { handleServerAction } from './server-functions-handler'\n\nimport { HEADERS } from './constants'\nimport { ServerFunctionSerializationAdapter } from './serializer/ServerFunctionSerializationAdapter'\nimport type {\n AnyStartInstanceOptions,\n RouteMethod,\n RouteMethodHandlerFn,\n RouterEntry,\n StartEntry,\n} from '@tanstack/start-client-core'\nimport type { RequestHandler } from './request-handler'\nimport type {\n AnyRoute,\n AnyRouter,\n Awaitable,\n Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\n\ntype TODO = any\n\nfunction getStartResponseHeaders(opts: { router: AnyRouter }) {\n const headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=utf-8',\n },\n ...opts.router.state.matches.map((match) => {\n return match.headers\n }),\n )\n return headers\n}\n\nexport function createStartHandler<TRegister = Register>(\n cb: HandlerCallback<AnyRouter>,\n): RequestHandler<TRegister> {\n if (!process.env.TSS_SERVER_FN_BASE) {\n throw new Error(\n 'tanstack/start-server-core: TSS_SERVER_FN_BASE must be defined in your environment for createStartHandler()',\n )\n }\n // TODO do we remove this?\n const APP_BASE = process.env.TSS_APP_BASE || '/'\n // Add trailing slash to sanitise user defined TSS_SERVER_FN_BASE\n const serverFnBase = joinPaths([\n APP_BASE,\n trimPath(process.env.TSS_SERVER_FN_BASE),\n '/',\n ])\n let startRoutesManifest: Manifest | null = null\n let startEntry: StartEntry | null = null\n let routerEntry: RouterEntry | null = null\n const getEntries = async (): Promise<{\n startEntry: StartEntry\n routerEntry: RouterEntry\n }> => {\n if (routerEntry === null) {\n // @ts-ignore when building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n routerEntry = await import('#tanstack-router-entry')\n }\n if (startEntry === null) {\n // @ts-ignore when building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n startEntry = await import('#tanstack-start-entry')\n }\n return {\n startEntry: startEntry as unknown as StartEntry,\n routerEntry: routerEntry as unknown as RouterEntry,\n }\n }\n\n const originalFetch = globalThis.fetch\n\n const startRequestResolver: RequestHandler<Register> = async (\n request,\n requestOpts,\n ) => {\n const origin = getOrigin(request)\n\n // Patching fetch function to use our request resolver\n // if the input starts with `/` which is a common pattern for\n // client-side routing.\n // When we encounter similar requests, we can assume that the\n // user wants to use the same origin as the current request.\n globalThis.fetch = async function (input, init) {\n function resolve(url: URL, requestOptions: RequestInit | undefined) {\n const fetchRequest = new Request(url, requestOptions)\n return startRequestResolver(fetchRequest, requestOpts)\n }\n\n if (typeof input === 'string' && input.startsWith('/')) {\n // e.g: fetch('/api/data')\n const url = new URL(input, origin)\n return resolve(url, init)\n } else if (\n typeof input === 'object' &&\n 'url' in input &&\n typeof input.url === 'string' &&\n input.url.startsWith('/')\n ) {\n // e.g: fetch(new Request('/api/data'))\n const url = new URL(input.url, origin)\n return resolve(url, init)\n }\n\n // If not, it should just use the original fetch\n return originalFetch(input, init)\n } as typeof fetch\n\n const url = new URL(request.url)\n const href = url.href.replace(url.origin, '')\n\n let router: AnyRouter | null = null\n const getRouter = async () => {\n if (router) return router\n // TODO how does this work with base path? does the router need to be configured the same as APP_BASE?\n router = await (await getEntries()).routerEntry.getRouter()\n\n // Update the client-side router with the history\n const isPrerendering = process.env.TSS_PRERENDERING === 'true'\n // env var is set during dev is SPA mode is enabled\n let isShell = process.env.TSS_SHELL === 'true'\n if (isPrerendering && !isShell) {\n // only read the shell header if we are prerendering\n // to avoid runtime behavior changes by injecting this header\n // the header is set by the prerender plugin\n isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'\n }\n\n // Create a history for the client-side router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n router.update({\n history,\n isShell,\n isPrerendering,\n origin: router.options.origin ?? origin,\n ...{\n defaultSsr: startOptions.defaultSsr,\n serializationAdapters: startOptions.serializationAdapters,\n },\n })\n return router\n }\n\n const startOptions: AnyStartInstanceOptions =\n (await (await getEntries()).startEntry.startInstance?.getOptions()) ||\n ({} as AnyStartInstanceOptions)\n startOptions.serializationAdapters =\n startOptions.serializationAdapters || []\n // insert start specific default serialization adapters\n startOptions.serializationAdapters.push(ServerFunctionSerializationAdapter)\n\n const requestHandlerMiddleware = handlerToMiddleware(\n async ({ context }) => {\n const response = await runWithStartContext(\n {\n getRouter,\n startOptions,\n contextAfterGlobalMiddlewares: context,\n },\n async () => {\n try {\n // First, let's attempt to handle server functions\n if (href.startsWith(serverFnBase)) {\n return await handleServerAction({\n request,\n context: requestOpts?.context,\n })\n }\n\n const executeRouter = async ({\n serverContext,\n }: {\n serverContext: any\n }) => {\n const requestAcceptHeader =\n request.headers.get('Accept') || '*/*'\n const splitRequestAcceptHeader = requestAcceptHeader.split(',')\n\n const supportedMimeTypes = ['*/*', 'text/html']\n const isRouterAcceptSupported = supportedMimeTypes.some(\n (mimeType) =>\n splitRequestAcceptHeader.some((acceptedMimeType) =>\n acceptedMimeType.trim().startsWith(mimeType),\n ),\n )\n\n if (!isRouterAcceptSupported) {\n return json(\n {\n error: 'Only HTML requests are supported here',\n },\n {\n status: 500,\n },\n )\n }\n\n // if the startRoutesManifest is not loaded yet, load it once\n if (startRoutesManifest === null) {\n startRoutesManifest = await getStartManifest({\n basePath: APP_BASE,\n })\n }\n const router = await getRouter()\n attachRouterServerSsrUtils({\n router,\n manifest: startRoutesManifest,\n })\n\n router.update({ additionalContext: { serverContext } })\n await router.load()\n\n // If there was a redirect, skip rendering the page at all\n if (router.state.redirect) {\n return router.state.redirect\n }\n\n await router.serverSsr!.dehydrate()\n\n const responseHeaders = getStartResponseHeaders({ router })\n const response = await cb({\n request,\n router,\n responseHeaders,\n })\n\n return response\n }\n\n const response = await handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n })\n\n return response\n } catch (err) {\n if (err instanceof Response) {\n return err\n }\n\n throw err\n }\n },\n )\n return response\n },\n )\n\n const flattenedMiddlewares = startOptions.requestMiddleware\n ? flattenMiddlewares(startOptions.requestMiddleware)\n : []\n const middlewares = flattenedMiddlewares.map((d) => d.options.server)\n const ctx = await executeMiddleware(\n [...middlewares, requestHandlerMiddleware],\n {\n request,\n\n context: requestOpts?.context || {},\n },\n )\n\n const response: Response = ctx.response\n\n if (isRedirect(response)) {\n if (isResolvedRedirect(response)) {\n if (request.headers.get('x-tsr-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n return response\n }\n if (\n response.options.to &&\n typeof response.options.to === 'string' &&\n !response.options.to.startsWith('/')\n ) {\n throw new Error(\n `Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's \"to\" property accepts an internal path only. Use the \"href\" property to provide an external URL. Received: ${JSON.stringify(response.options)}`,\n )\n }\n\n if (\n ['params', 'search', 'hash'].some(\n (d) => typeof (response.options as any)[d] === 'function',\n )\n ) {\n throw new Error(\n `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(\n response.options,\n )\n .filter((d) => typeof (response.options as any)[d] === 'function')\n .map((d) => `\"${d}\"`)\n .join(', ')}`,\n )\n }\n\n const router = await getRouter()\n const redirect = router.resolveRedirect(response)\n\n if (request.headers.get('x-tsr-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n\n return redirect\n }\n\n return response\n }\n\n return requestHandler(startRequestResolver)\n}\n\nasync function handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n}: {\n getRouter: () => Awaitable<AnyRouter>\n request: Request\n executeRouter: ({\n serverContext,\n }: {\n serverContext: any\n }) => Promise<Response>\n}) {\n const router = await getRouter()\n let url = new URL(request.url)\n url = executeRewriteInput(router.rewrite, url)\n const pathname = url.pathname\n const { matchedRoutes, foundRoute, routeParams } = router.getMatchedRoutes(\n pathname,\n undefined,\n )\n\n // TODO: Error handling? What happens when its `throw redirect()` vs `throw new Error()`?\n\n const middlewares = flattenMiddlewares(\n matchedRoutes.flatMap((r) => r.options.server?.middleware).filter(Boolean),\n ).map((d) => d.options.server)\n\n const server = foundRoute?.options.server\n\n if (server) {\n if (server.handlers) {\n const handlers =\n typeof server.handlers === 'function'\n ? server.handlers({\n createHandlers: (d: any) => d,\n })\n : server.handlers\n\n const requestMethod = request.method.toLowerCase()\n\n // Attempt to find the method in the handlers\n let method = Object.keys(handlers).find(\n (method) => method.toLowerCase() === requestMethod,\n )\n\n // If no method is found, attempt to find the 'all' method\n if (!method) {\n method = Object.keys(handlers).find(\n (method) => method.toLowerCase() === 'all',\n )\n ? 'all'\n : undefined\n }\n\n // If a method is found, execute the handler\n if (method) {\n const handler = handlers[method as RouteMethod]\n if (handler) {\n const mayDefer = !!foundRoute.options.component\n if (typeof handler === 'function') {\n middlewares.push(handlerToMiddleware(handler, mayDefer))\n } else {\n const { middleware } = handler\n if (middleware && middleware.length) {\n middlewares.push(\n ...flattenMiddlewares(middleware).map((d) => d.options.server),\n )\n }\n if (handler.handler) {\n middlewares.push(handlerToMiddleware(handler.handler, mayDefer))\n }\n }\n }\n }\n }\n }\n\n // eventually, execute the router\n middlewares.push(\n handlerToMiddleware((ctx) => executeRouter({ serverContext: ctx.context })),\n )\n\n const ctx = await executeMiddleware(middlewares, {\n request,\n context: {},\n params: routeParams,\n pathname,\n })\n\n const response: Response = ctx.response\n\n return response\n}\n\nfunction throwRouteHandlerError() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\n `It looks like you forgot to return a response from your server route handler. If you want to defer to the app router, make sure to have a component set in this route.`,\n )\n }\n throw new Error('Internal Server Error')\n}\n\nfunction throwIfMayNotDefer() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\n `You cannot defer to the app router if there is no component defined on this route.`,\n )\n }\n throw new Error('Internal Server Error')\n}\nfunction handlerToMiddleware(\n handler: RouteMethodHandlerFn<any, AnyRoute, any, any, any, any>,\n mayDefer: boolean = false,\n) {\n if (mayDefer) {\n return handler as TODO\n }\n return async ({ next: _next, ...rest }: TODO) => {\n const response = await handler({ ...rest, next: throwIfMayNotDefer })\n if (!response) {\n throwRouteHandlerError()\n }\n return response\n }\n}\n\nfunction executeMiddleware(middlewares: TODO, ctx: TODO) {\n let index = -1\n\n const next = async (ctx: TODO) => {\n index++\n const middleware = middlewares[index]\n if (!middleware) return ctx\n\n let result\n try {\n result = await middleware({\n ...ctx,\n // Allow the middleware to call the next middleware in the chain\n next: async (nextCtx: TODO) => {\n // Allow the caller to extend the context for the next middleware\n const nextResult = await next({\n ...ctx,\n ...nextCtx,\n context: {\n ...ctx.context,\n ...(nextCtx?.context || {}),\n },\n })\n\n // Merge the result into the context\\\n return Object.assign(ctx, handleCtxResult(nextResult))\n },\n // Allow the middleware result to extend the return context\n })\n } catch (err: TODO) {\n if (isSpecialResponse(err)) {\n result = {\n response: err,\n }\n } else {\n throw err\n }\n }\n\n // Merge the middleware result into the context, just in case it\n // returns a partial context\n return Object.assign(ctx, handleCtxResult(result))\n }\n\n return handleCtxResult(next(ctx))\n}\n\nfunction handleCtxResult(result: TODO) {\n if (isSpecialResponse(result)) {\n return {\n response: result,\n }\n }\n\n return result\n}\n\nfunction isSpecialResponse(err: TODO) {\n return isResponse(err) || isRedirect(err)\n}\n\nfunction isResponse(response: Response): response is Response {\n return response instanceof Response\n}\n"],"names":["url","response","router","method","ctx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createStartHandler.js","sources":["../../src/createStartHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport {\n flattenMiddlewares,\n json,\n mergeHeaders,\n} from '@tanstack/start-client-core'\nimport {\n executeRewriteInput,\n isRedirect,\n isResolvedRedirect,\n} from '@tanstack/router-core'\nimport {\n attachRouterServerSsrUtils,\n getOrigin,\n} from '@tanstack/router-core/ssr/server'\nimport { runWithStartContext } from '@tanstack/start-storage-context'\nimport { requestHandler } from './request-response'\nimport { getStartManifest } from './router-manifest'\nimport { handleServerAction } from './server-functions-handler'\n\nimport { HEADERS } from './constants'\nimport { ServerFunctionSerializationAdapter } from './serializer/ServerFunctionSerializationAdapter'\nimport type {\n AnyStartInstanceOptions,\n RouteMethod,\n RouteMethodHandlerFn,\n RouterEntry,\n StartEntry,\n} from '@tanstack/start-client-core'\nimport type { RequestHandler } from './request-handler'\nimport type {\n AnyRoute,\n AnyRouter,\n Awaitable,\n Manifest,\n Register,\n} from '@tanstack/router-core'\nimport type { HandlerCallback } from '@tanstack/router-core/ssr/server'\n\ntype TODO = any\n\nfunction getStartResponseHeaders(opts: { router: AnyRouter }) {\n const headers = mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=utf-8',\n },\n ...opts.router.state.matches.map((match) => {\n return match.headers\n }),\n )\n return headers\n}\n\nexport function createStartHandler<TRegister = Register>(\n cb: HandlerCallback<AnyRouter>,\n): RequestHandler<TRegister> {\n const ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || '/'\n let startRoutesManifest: Manifest | null = null\n let startEntry: StartEntry | null = null\n let routerEntry: RouterEntry | null = null\n const getEntries = async (): Promise<{\n startEntry: StartEntry\n routerEntry: RouterEntry\n }> => {\n if (routerEntry === null) {\n // @ts-ignore when building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n routerEntry = await import('#tanstack-router-entry')\n }\n if (startEntry === null) {\n // @ts-ignore when building, we currently don't respect tsconfig.ts' `include` so we are not picking up the .d.ts from start-client-core\n startEntry = await import('#tanstack-start-entry')\n }\n return {\n startEntry: startEntry as unknown as StartEntry,\n routerEntry: routerEntry as unknown as RouterEntry,\n }\n }\n\n const originalFetch = globalThis.fetch\n\n const startRequestResolver: RequestHandler<Register> = async (\n request,\n requestOpts,\n ) => {\n const origin = getOrigin(request)\n\n // Patching fetch function to use our request resolver\n // if the input starts with `/` which is a common pattern for\n // client-side routing.\n // When we encounter similar requests, we can assume that the\n // user wants to use the same origin as the current request.\n globalThis.fetch = async function (input, init) {\n function resolve(url: URL, requestOptions: RequestInit | undefined) {\n const fetchRequest = new Request(url, requestOptions)\n return startRequestResolver(fetchRequest, requestOpts)\n }\n\n if (typeof input === 'string' && input.startsWith('/')) {\n // e.g: fetch('/api/data')\n const url = new URL(input, origin)\n return resolve(url, init)\n } else if (\n typeof input === 'object' &&\n 'url' in input &&\n typeof input.url === 'string' &&\n input.url.startsWith('/')\n ) {\n // e.g: fetch(new Request('/api/data'))\n const url = new URL(input.url, origin)\n return resolve(url, init)\n }\n\n // If not, it should just use the original fetch\n return originalFetch(input, init)\n } as typeof fetch\n\n const url = new URL(request.url)\n const href = url.href.replace(url.origin, '')\n\n let router: AnyRouter | null = null\n const getRouter = async () => {\n if (router) return router\n router = await (await getEntries()).routerEntry.getRouter()\n\n // Update the client-side router with the history\n const isPrerendering = process.env.TSS_PRERENDERING === 'true'\n // env var is set during dev is SPA mode is enabled\n let isShell = process.env.TSS_SHELL === 'true'\n if (isPrerendering && !isShell) {\n // only read the shell header if we are prerendering\n // to avoid runtime behavior changes by injecting this header\n // the header is set by the prerender plugin\n isShell = request.headers.get(HEADERS.TSS_SHELL) === 'true'\n }\n\n // Create a history for the client-side router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n router.update({\n history,\n isShell,\n isPrerendering,\n origin: router.options.origin ?? origin,\n ...{\n defaultSsr: startOptions.defaultSsr,\n serializationAdapters: startOptions.serializationAdapters,\n },\n basepath: ROUTER_BASEPATH,\n })\n return router\n }\n\n const startOptions: AnyStartInstanceOptions =\n (await (await getEntries()).startEntry.startInstance?.getOptions()) ||\n ({} as AnyStartInstanceOptions)\n startOptions.serializationAdapters =\n startOptions.serializationAdapters || []\n // insert start specific default serialization adapters\n startOptions.serializationAdapters.push(ServerFunctionSerializationAdapter)\n\n const requestHandlerMiddleware = handlerToMiddleware(\n async ({ context }) => {\n const response = await runWithStartContext(\n {\n getRouter,\n startOptions,\n contextAfterGlobalMiddlewares: context,\n },\n async () => {\n try {\n // First, let's attempt to handle server functions\n if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {\n return await handleServerAction({\n request,\n context: requestOpts?.context,\n })\n }\n\n const executeRouter = async ({\n serverContext,\n }: {\n serverContext: any\n }) => {\n const requestAcceptHeader =\n request.headers.get('Accept') || '*/*'\n const splitRequestAcceptHeader = requestAcceptHeader.split(',')\n\n const supportedMimeTypes = ['*/*', 'text/html']\n const isRouterAcceptSupported = supportedMimeTypes.some(\n (mimeType) =>\n splitRequestAcceptHeader.some((acceptedMimeType) =>\n acceptedMimeType.trim().startsWith(mimeType),\n ),\n )\n\n if (!isRouterAcceptSupported) {\n return json(\n {\n error: 'Only HTML requests are supported here',\n },\n {\n status: 500,\n },\n )\n }\n\n // if the startRoutesManifest is not loaded yet, load it once\n if (startRoutesManifest === null) {\n startRoutesManifest = await getStartManifest()\n }\n const router = await getRouter()\n attachRouterServerSsrUtils({\n router,\n manifest: startRoutesManifest,\n })\n\n router.update({ additionalContext: { serverContext } })\n await router.load()\n\n // If there was a redirect, skip rendering the page at all\n if (router.state.redirect) {\n return router.state.redirect\n }\n\n await router.serverSsr!.dehydrate()\n\n const responseHeaders = getStartResponseHeaders({ router })\n const response = await cb({\n request,\n router,\n responseHeaders,\n })\n\n return response\n }\n\n const response = await handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n })\n\n return response\n } catch (err) {\n if (err instanceof Response) {\n return err\n }\n\n throw err\n }\n },\n )\n return response\n },\n )\n\n const flattenedMiddlewares = startOptions.requestMiddleware\n ? flattenMiddlewares(startOptions.requestMiddleware)\n : []\n const middlewares = flattenedMiddlewares.map((d) => d.options.server)\n const ctx = await executeMiddleware(\n [...middlewares, requestHandlerMiddleware],\n {\n request,\n\n context: requestOpts?.context || {},\n },\n )\n\n const response: Response = ctx.response\n\n if (isRedirect(response)) {\n if (isResolvedRedirect(response)) {\n if (request.headers.get('x-tsr-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n return response\n }\n if (\n response.options.to &&\n typeof response.options.to === 'string' &&\n !response.options.to.startsWith('/')\n ) {\n throw new Error(\n `Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's \"to\" property accepts an internal path only. Use the \"href\" property to provide an external URL. Received: ${JSON.stringify(response.options)}`,\n )\n }\n\n if (\n ['params', 'search', 'hash'].some(\n (d) => typeof (response.options as any)[d] === 'function',\n )\n ) {\n throw new Error(\n `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(\n response.options,\n )\n .filter((d) => typeof (response.options as any)[d] === 'function')\n .map((d) => `\"${d}\"`)\n .join(', ')}`,\n )\n }\n\n const router = await getRouter()\n const redirect = router.resolveRedirect(response)\n\n if (request.headers.get('x-tsr-redirect') === 'manual') {\n return json(\n {\n ...response.options,\n isSerializedRedirect: true,\n },\n {\n headers: response.headers,\n },\n )\n }\n\n return redirect\n }\n\n return response\n }\n\n return requestHandler(startRequestResolver)\n}\n\nasync function handleServerRoutes({\n getRouter,\n request,\n executeRouter,\n}: {\n getRouter: () => Awaitable<AnyRouter>\n request: Request\n executeRouter: ({\n serverContext,\n }: {\n serverContext: any\n }) => Promise<Response>\n}) {\n const router = await getRouter()\n let url = new URL(request.url)\n url = executeRewriteInput(router.rewrite, url)\n const pathname = url.pathname\n const { matchedRoutes, foundRoute, routeParams } = router.getMatchedRoutes(\n pathname,\n undefined,\n )\n\n // TODO: Error handling? What happens when its `throw redirect()` vs `throw new Error()`?\n\n const middlewares = flattenMiddlewares(\n matchedRoutes.flatMap((r) => r.options.server?.middleware).filter(Boolean),\n ).map((d) => d.options.server)\n\n const server = foundRoute?.options.server\n\n if (server) {\n if (server.handlers) {\n const handlers =\n typeof server.handlers === 'function'\n ? server.handlers({\n createHandlers: (d: any) => d,\n })\n : server.handlers\n\n const requestMethod = request.method.toLowerCase()\n\n // Attempt to find the method in the handlers\n let method = Object.keys(handlers).find(\n (method) => method.toLowerCase() === requestMethod,\n )\n\n // If no method is found, attempt to find the 'all' method\n if (!method) {\n method = Object.keys(handlers).find(\n (method) => method.toLowerCase() === 'all',\n )\n ? 'all'\n : undefined\n }\n\n // If a method is found, execute the handler\n if (method) {\n const handler = handlers[method as RouteMethod]\n if (handler) {\n const mayDefer = !!foundRoute.options.component\n if (typeof handler === 'function') {\n middlewares.push(handlerToMiddleware(handler, mayDefer))\n } else {\n const { middleware } = handler\n if (middleware && middleware.length) {\n middlewares.push(\n ...flattenMiddlewares(middleware).map((d) => d.options.server),\n )\n }\n if (handler.handler) {\n middlewares.push(handlerToMiddleware(handler.handler, mayDefer))\n }\n }\n }\n }\n }\n }\n\n // eventually, execute the router\n middlewares.push(\n handlerToMiddleware((ctx) => executeRouter({ serverContext: ctx.context })),\n )\n\n const ctx = await executeMiddleware(middlewares, {\n request,\n context: {},\n params: routeParams,\n pathname,\n })\n\n const response: Response = ctx.response\n\n return response\n}\n\nfunction throwRouteHandlerError() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\n `It looks like you forgot to return a response from your server route handler. If you want to defer to the app router, make sure to have a component set in this route.`,\n )\n }\n throw new Error('Internal Server Error')\n}\n\nfunction throwIfMayNotDefer() {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(\n `You cannot defer to the app router if there is no component defined on this route.`,\n )\n }\n throw new Error('Internal Server Error')\n}\nfunction handlerToMiddleware(\n handler: RouteMethodHandlerFn<any, AnyRoute, any, any, any, any>,\n mayDefer: boolean = false,\n) {\n if (mayDefer) {\n return handler as TODO\n }\n return async ({ next: _next, ...rest }: TODO) => {\n const response = await handler({ ...rest, next: throwIfMayNotDefer })\n if (!response) {\n throwRouteHandlerError()\n }\n return response\n }\n}\n\nfunction executeMiddleware(middlewares: TODO, ctx: TODO) {\n let index = -1\n\n const next = async (ctx: TODO) => {\n index++\n const middleware = middlewares[index]\n if (!middleware) return ctx\n\n let result\n try {\n result = await middleware({\n ...ctx,\n // Allow the middleware to call the next middleware in the chain\n next: async (nextCtx: TODO) => {\n // Allow the caller to extend the context for the next middleware\n const nextResult = await next({\n ...ctx,\n ...nextCtx,\n context: {\n ...ctx.context,\n ...(nextCtx?.context || {}),\n },\n })\n\n // Merge the result into the context\\\n return Object.assign(ctx, handleCtxResult(nextResult))\n },\n // Allow the middleware result to extend the return context\n })\n } catch (err: TODO) {\n if (isSpecialResponse(err)) {\n result = {\n response: err,\n }\n } else {\n throw err\n }\n }\n\n // Merge the middleware result into the context, just in case it\n // returns a partial context\n return Object.assign(ctx, handleCtxResult(result))\n }\n\n return handleCtxResult(next(ctx))\n}\n\nfunction handleCtxResult(result: TODO) {\n if (isSpecialResponse(result)) {\n return {\n response: result,\n }\n }\n\n return result\n}\n\nfunction isSpecialResponse(err: TODO) {\n return isResponse(err) || isRedirect(err)\n}\n\nfunction isResponse(response: Response): response is Response {\n return response instanceof Response\n}\n"],"names":["url","response","router","method","ctx"],"mappings":"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"}
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
* special assets that are needed for the client. It does not include relationships
|
|
5
5
|
* between routes or any other data that is not needed for the client.
|
|
6
6
|
*/
|
|
7
|
-
export declare function getStartManifest(
|
|
8
|
-
basePath: string;
|
|
9
|
-
}): Promise<{
|
|
7
|
+
export declare function getStartManifest(): Promise<{
|
|
10
8
|
routes: {
|
|
11
9
|
[k: string]: {
|
|
12
10
|
preloads: string[] | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { rootRouteId } from "@tanstack/router-core";
|
|
2
2
|
import { VIRTUAL_MODULES } from "./virtual-modules.js";
|
|
3
3
|
import { loadVirtualModule } from "./loadVirtualModule.js";
|
|
4
|
-
async function getStartManifest(
|
|
4
|
+
async function getStartManifest() {
|
|
5
5
|
const { tsrStartManifest } = await loadVirtualModule(
|
|
6
6
|
VIRTUAL_MODULES.startManifest
|
|
7
7
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-manifest.js","sources":["../../src/router-manifest.ts"],"sourcesContent":["import { rootRouteId } from '@tanstack/router-core'\nimport { VIRTUAL_MODULES } from './virtual-modules'\nimport { loadVirtualModule } from './loadVirtualModule'\n\n/**\n * @description Returns the router manifest that should be sent to the client.\n * This includes only the assets and preloads for the current route and any\n * special assets that are needed for the client. It does not include relationships\n * between routes or any other data that is not needed for the client.\n */\nexport async function getStartManifest(
|
|
1
|
+
{"version":3,"file":"router-manifest.js","sources":["../../src/router-manifest.ts"],"sourcesContent":["import { rootRouteId } from '@tanstack/router-core'\nimport { VIRTUAL_MODULES } from './virtual-modules'\nimport { loadVirtualModule } from './loadVirtualModule'\n\n/**\n * @description Returns the router manifest that should be sent to the client.\n * This includes only the assets and preloads for the current route and any\n * special assets that are needed for the client. It does not include relationships\n * between routes or any other data that is not needed for the client.\n */\nexport async function getStartManifest() {\n const { tsrStartManifest } = await loadVirtualModule(\n VIRTUAL_MODULES.startManifest,\n )\n const startManifest = tsrStartManifest()\n\n const rootRoute = (startManifest.routes[rootRouteId] =\n startManifest.routes[rootRouteId] || {})\n\n rootRoute.assets = rootRoute.assets || []\n\n let script = `import('${startManifest.clientEntry}')`\n if (process.env.TSS_DEV_SERVER === 'true') {\n const { injectedHeadScripts } = await loadVirtualModule(\n VIRTUAL_MODULES.injectedHeadScripts,\n )\n if (injectedHeadScripts) {\n script = `${injectedHeadScripts + ';'}${script}`\n }\n }\n rootRoute.assets.push({\n tag: 'script',\n attrs: {\n type: 'module',\n suppressHydrationWarning: true,\n async: true,\n },\n children: script,\n })\n\n const manifest = {\n ...startManifest,\n routes: Object.fromEntries(\n Object.entries(startManifest.routes).map(([k, v]) => {\n const { preloads, assets } = v\n return [\n k,\n {\n preloads,\n assets,\n },\n ]\n }),\n ),\n }\n\n // Strip out anything that isn't needed for the client\n return manifest\n}\n"],"names":[],"mappings":";;;AAUA,eAAsB,mBAAmB;AACvC,QAAM,EAAE,iBAAA,IAAqB,MAAM;AAAA,IACjC,gBAAgB;AAAA,EAAA;AAElB,QAAM,gBAAgB,iBAAA;AAEtB,QAAM,YAAa,cAAc,OAAO,WAAW,IACjD,cAAc,OAAO,WAAW,KAAK,CAAA;AAEvC,YAAU,SAAS,UAAU,UAAU,CAAA;AAEvC,MAAI,SAAS,WAAW,cAAc,WAAW;AACjD,MAAI,QAAQ,IAAI,mBAAmB,QAAQ;AACzC,UAAM,EAAE,oBAAA,IAAwB,MAAM;AAAA,MACpC,gBAAgB;AAAA,IAAA;AAElB,QAAI,qBAAqB;AACvB,eAAS,GAAG,sBAAsB,GAAG,GAAG,MAAM;AAAA,IAChD;AAAA,EACF;AACA,YAAU,OAAO,KAAK;AAAA,IACpB,KAAK;AAAA,IACL,OAAO;AAAA,MACL,MAAM;AAAA,MACN,0BAA0B;AAAA,MAC1B,OAAO;AAAA,IAAA;AAAA,IAET,UAAU;AAAA,EAAA,CACX;AAED,QAAM,WAAW;AAAA,IACf,GAAG;AAAA,IACH,QAAQ,OAAO;AAAA,MACb,OAAO,QAAQ,cAAc,MAAM,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM;AACnD,cAAM,EAAE,UAAU,OAAA,IAAW;AAC7B,eAAO;AAAA,UACL;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,UAAA;AAAA,QACF;AAAA,MAEJ,CAAC;AAAA,IAAA;AAAA,EACH;AAIF,SAAO;AACT;"}
|
|
@@ -4,14 +4,7 @@ import { getDefaultSerovalPlugins, TSS_FORMDATA_CONTEXT, X_TSS_RAW_RESPONSE, X_T
|
|
|
4
4
|
import { toCrossJSONStream, toCrossJSONAsync, fromJSON } from "seroval";
|
|
5
5
|
import { getResponse } from "./request-response.js";
|
|
6
6
|
import { getServerFnById } from "./getServerFnById.js";
|
|
7
|
-
|
|
8
|
-
if (!base) {
|
|
9
|
-
throw new Error(
|
|
10
|
-
"🚨 process.env.TSS_SERVER_FN_BASE is required in start/server-handler/index"
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
return base.replace(/^\/|\/$/g, "");
|
|
14
|
-
}
|
|
7
|
+
let regex = void 0;
|
|
15
8
|
const handleServerAction = async ({
|
|
16
9
|
request,
|
|
17
10
|
context
|
|
@@ -20,11 +13,11 @@ const handleServerAction = async ({
|
|
|
20
13
|
const signal = controller.signal;
|
|
21
14
|
const abort = () => controller.abort();
|
|
22
15
|
request.signal.addEventListener("abort", abort);
|
|
16
|
+
if (regex === void 0) {
|
|
17
|
+
regex = new RegExp(`${process.env.TSS_SERVER_FN_BASE}([^/?#]+)`);
|
|
18
|
+
}
|
|
23
19
|
const method = request.method;
|
|
24
20
|
const url = new URL(request.url, "http://localhost:3000");
|
|
25
|
-
const regex = new RegExp(
|
|
26
|
-
`${sanitizeBase(process.env.TSS_SERVER_FN_BASE)}/([^/?#]+)`
|
|
27
|
-
);
|
|
28
21
|
const match = url.pathname.match(regex);
|
|
29
22
|
const serverFnId = match ? match[1] : null;
|
|
30
23
|
const search = Object.fromEntries(url.searchParams.entries());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-functions-handler.js","sources":["../../src/server-functions-handler.ts"],"sourcesContent":["import { isNotFound } from '@tanstack/router-core'\nimport invariant from 'tiny-invariant'\nimport {\n TSS_FORMDATA_CONTEXT,\n X_TSS_RAW_RESPONSE,\n X_TSS_SERIALIZED,\n getDefaultSerovalPlugins,\n} from '@tanstack/start-client-core'\nimport { fromJSON, toCrossJSONAsync, toCrossJSONStream } from 'seroval'\nimport { getResponse } from './request-response'\nimport { getServerFnById } from './getServerFnById'\n\nfunction sanitizeBase(base: string | undefined) {\n if (!base) {\n throw new Error(\n '🚨 process.env.TSS_SERVER_FN_BASE is required in start/server-handler/index',\n )\n }\n\n return base.replace(/^\\/|\\/$/g, '')\n}\n\nexport const handleServerAction = async ({\n request,\n context,\n}: {\n request: Request\n context: any\n}) => {\n const controller = new AbortController()\n const signal = controller.signal\n const abort = () => controller.abort()\n request.signal.addEventListener('abort', abort)\n\n const method = request.method\n const url = new URL(request.url, 'http://localhost:3000')\n // extract the serverFnId from the url as host/_serverFn/:serverFnId\n // Define a regex to match the path and extract the :thing part\n const regex = new RegExp(\n `${sanitizeBase(process.env.TSS_SERVER_FN_BASE)}/([^/?#]+)`,\n )\n\n // Execute the regex\n const match = url.pathname.match(regex)\n const serverFnId = match ? match[1] : null\n const search = Object.fromEntries(url.searchParams.entries()) as {\n payload?: any\n createServerFn?: boolean\n }\n\n const isCreateServerFn = 'createServerFn' in search\n\n if (typeof serverFnId !== 'string') {\n throw new Error('Invalid server action param for serverFnId: ' + serverFnId)\n }\n\n const action = await getServerFnById(serverFnId)\n\n // Known FormData 'Content-Type' header values\n const formDataContentTypes = [\n 'multipart/form-data',\n 'application/x-www-form-urlencoded',\n ]\n\n const contentType = request.headers.get('Content-Type')\n const serovalPlugins = getDefaultSerovalPlugins()\n\n function parsePayload(payload: any) {\n const parsedPayload = fromJSON(payload, { plugins: serovalPlugins })\n return parsedPayload as any\n }\n\n const response = await (async () => {\n try {\n let result = await (async () => {\n // FormData\n if (\n formDataContentTypes.some(\n (type) => contentType && contentType.includes(type),\n )\n ) {\n // We don't support GET requests with FormData payloads... that seems impossible\n invariant(\n method.toLowerCase() !== 'get',\n 'GET requests with FormData payloads are not supported',\n )\n const formData = await request.formData()\n const serializedContext = formData.get(TSS_FORMDATA_CONTEXT)\n formData.delete(TSS_FORMDATA_CONTEXT)\n\n const params = {\n context,\n data: formData,\n }\n if (typeof serializedContext === 'string') {\n try {\n const parsedContext = JSON.parse(serializedContext)\n if (typeof parsedContext === 'object' && parsedContext) {\n params.context = { ...context, ...parsedContext }\n }\n } catch {}\n }\n\n return await action(params, signal)\n }\n\n // Get requests use the query string\n if (method.toLowerCase() === 'get') {\n invariant(\n isCreateServerFn,\n 'expected GET request to originate from createServerFn',\n )\n // By default the payload is the search params\n let payload: any = search.payload\n // If there's a payload, we should try to parse it\n payload = payload ? parsePayload(JSON.parse(payload)) : payload\n payload.context = { ...context, ...payload.context }\n // Send it through!\n return await action(payload, signal)\n }\n\n if (method.toLowerCase() !== 'post') {\n throw new Error('expected POST method')\n }\n\n if (!contentType || !contentType.includes('application/json')) {\n throw new Error('expected application/json content type')\n }\n\n const jsonPayload = await request.json()\n\n // If this POST request was created by createServerFn,\n // its payload will be the only argument\n if (isCreateServerFn) {\n const payload = parsePayload(jsonPayload)\n payload.context = { ...payload.context, ...context }\n return await action(payload, signal)\n }\n\n // Otherwise, we'll spread the payload. Need to\n // support `use server` functions that take multiple\n // arguments.\n return await action(...jsonPayload)\n })()\n\n // Any time we get a Response back, we should just\n // return it immediately.\n if (result.result instanceof Response) {\n result.result.headers.set(X_TSS_RAW_RESPONSE, 'true')\n return result.result\n }\n\n // If this is a non createServerFn request, we need to\n // pull out the result from the result object\n if (!isCreateServerFn) {\n result = result.result\n\n // The result might again be a response,\n // and if it is, return it.\n if (result instanceof Response) {\n return result\n }\n }\n\n // TODO: RSCs Where are we getting this package?\n // if (isValidElement(result)) {\n // const { renderToPipeableStream } = await import(\n // // @ts-expect-error\n // 'react-server-dom/server'\n // )\n\n // const pipeableStream = renderToPipeableStream(result)\n\n // setHeaders(event, {\n // 'Content-Type': 'text/x-component',\n // } as any)\n\n // sendStream(event, response)\n // event._handled = true\n\n // return new Response(null, { status: 200 })\n // }\n\n if (isNotFound(result)) {\n return isNotFoundResponse(result)\n }\n\n const response = getResponse()\n let nonStreamingBody: any = undefined\n\n if (result !== undefined) {\n // first run without the stream in case `result` does not need streaming\n let done = false as boolean\n const callbacks: {\n onParse: (value: any) => void\n onDone: () => void\n onError: (error: any) => void\n } = {\n onParse: (value) => {\n nonStreamingBody = value\n },\n onDone: () => {\n done = true\n },\n onError: (error) => {\n throw error\n },\n }\n toCrossJSONStream(result, {\n refs: new Map(),\n plugins: serovalPlugins,\n onParse(value) {\n callbacks.onParse(value)\n },\n onDone() {\n callbacks.onDone()\n },\n onError: (error) => {\n callbacks.onError(error)\n },\n })\n if (done) {\n return new Response(\n nonStreamingBody ? JSON.stringify(nonStreamingBody) : undefined,\n {\n status: response?.status,\n statusText: response?.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n },\n )\n }\n\n // not done yet, we need to stream\n const stream = new ReadableStream({\n start(controller) {\n callbacks.onParse = (value) =>\n controller.enqueue(JSON.stringify(value) + '\\n')\n callbacks.onDone = () => {\n try {\n controller.close()\n } catch (error) {\n controller.error(error)\n }\n }\n callbacks.onError = (error) => controller.error(error)\n // stream the initial body\n if (nonStreamingBody !== undefined) {\n callbacks.onParse(nonStreamingBody)\n }\n },\n })\n return new Response(stream, {\n status: response?.status,\n statusText: response?.statusText,\n headers: {\n 'Content-Type': 'application/x-ndjson',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n\n return new Response(undefined, {\n status: response?.status,\n statusText: response?.statusText,\n })\n } catch (error: any) {\n if (error instanceof Response) {\n return error\n }\n // else if (\n // isPlainObject(error) &&\n // 'result' in error &&\n // error.result instanceof Response\n // ) {\n // return error.result\n // }\n\n // Currently this server-side context has no idea how to\n // build final URLs, so we need to defer that to the client.\n // The client will check for __redirect and __notFound keys,\n // and if they exist, it will handle them appropriately.\n\n if (isNotFound(error)) {\n return isNotFoundResponse(error)\n }\n\n console.info()\n console.info('Server Fn Error!')\n console.info()\n console.error(error)\n console.info()\n\n const serializedError = JSON.stringify(\n await Promise.resolve(\n toCrossJSONAsync(error, {\n refs: new Map(),\n plugins: serovalPlugins,\n }),\n ),\n )\n const response = getResponse()\n return new Response(serializedError, {\n status: response?.status ?? 500,\n statusText: response?.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n })()\n\n request.signal.removeEventListener('abort', abort)\n\n return response\n}\n\nfunction isNotFoundResponse(error: any) {\n const { headers, ...rest } = error\n\n return new Response(JSON.stringify(rest), {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n ...(headers || {}),\n },\n })\n}\n"],"names":["response","controller"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server-functions-handler.js","sources":["../../src/server-functions-handler.ts"],"sourcesContent":["import { isNotFound } from '@tanstack/router-core'\nimport invariant from 'tiny-invariant'\nimport {\n TSS_FORMDATA_CONTEXT,\n X_TSS_RAW_RESPONSE,\n X_TSS_SERIALIZED,\n getDefaultSerovalPlugins,\n} from '@tanstack/start-client-core'\nimport { fromJSON, toCrossJSONAsync, toCrossJSONStream } from 'seroval'\nimport { getResponse } from './request-response'\nimport { getServerFnById } from './getServerFnById'\n\nlet regex: RegExp | undefined = undefined\n\nexport const handleServerAction = async ({\n request,\n context,\n}: {\n request: Request\n context: any\n}) => {\n const controller = new AbortController()\n const signal = controller.signal\n const abort = () => controller.abort()\n request.signal.addEventListener('abort', abort)\n\n if (regex === undefined) {\n regex = new RegExp(`${process.env.TSS_SERVER_FN_BASE}([^/?#]+)`)\n }\n\n const method = request.method\n const url = new URL(request.url, 'http://localhost:3000')\n // extract the serverFnId from the url as host/_serverFn/:serverFnId\n // Define a regex to match the path and extract the :thing part\n\n // Execute the regex\n const match = url.pathname.match(regex)\n const serverFnId = match ? match[1] : null\n const search = Object.fromEntries(url.searchParams.entries()) as {\n payload?: any\n createServerFn?: boolean\n }\n\n const isCreateServerFn = 'createServerFn' in search\n\n if (typeof serverFnId !== 'string') {\n throw new Error('Invalid server action param for serverFnId: ' + serverFnId)\n }\n\n const action = await getServerFnById(serverFnId)\n\n // Known FormData 'Content-Type' header values\n const formDataContentTypes = [\n 'multipart/form-data',\n 'application/x-www-form-urlencoded',\n ]\n\n const contentType = request.headers.get('Content-Type')\n const serovalPlugins = getDefaultSerovalPlugins()\n\n function parsePayload(payload: any) {\n const parsedPayload = fromJSON(payload, { plugins: serovalPlugins })\n return parsedPayload as any\n }\n\n const response = await (async () => {\n try {\n let result = await (async () => {\n // FormData\n if (\n formDataContentTypes.some(\n (type) => contentType && contentType.includes(type),\n )\n ) {\n // We don't support GET requests with FormData payloads... that seems impossible\n invariant(\n method.toLowerCase() !== 'get',\n 'GET requests with FormData payloads are not supported',\n )\n const formData = await request.formData()\n const serializedContext = formData.get(TSS_FORMDATA_CONTEXT)\n formData.delete(TSS_FORMDATA_CONTEXT)\n\n const params = {\n context,\n data: formData,\n }\n if (typeof serializedContext === 'string') {\n try {\n const parsedContext = JSON.parse(serializedContext)\n if (typeof parsedContext === 'object' && parsedContext) {\n params.context = { ...context, ...parsedContext }\n }\n } catch {}\n }\n\n return await action(params, signal)\n }\n\n // Get requests use the query string\n if (method.toLowerCase() === 'get') {\n invariant(\n isCreateServerFn,\n 'expected GET request to originate from createServerFn',\n )\n // By default the payload is the search params\n let payload: any = search.payload\n // If there's a payload, we should try to parse it\n payload = payload ? parsePayload(JSON.parse(payload)) : payload\n payload.context = { ...context, ...payload.context }\n // Send it through!\n return await action(payload, signal)\n }\n\n if (method.toLowerCase() !== 'post') {\n throw new Error('expected POST method')\n }\n\n if (!contentType || !contentType.includes('application/json')) {\n throw new Error('expected application/json content type')\n }\n\n const jsonPayload = await request.json()\n\n // If this POST request was created by createServerFn,\n // its payload will be the only argument\n if (isCreateServerFn) {\n const payload = parsePayload(jsonPayload)\n payload.context = { ...payload.context, ...context }\n return await action(payload, signal)\n }\n\n // Otherwise, we'll spread the payload. Need to\n // support `use server` functions that take multiple\n // arguments.\n return await action(...jsonPayload)\n })()\n\n // Any time we get a Response back, we should just\n // return it immediately.\n if (result.result instanceof Response) {\n result.result.headers.set(X_TSS_RAW_RESPONSE, 'true')\n return result.result\n }\n\n // If this is a non createServerFn request, we need to\n // pull out the result from the result object\n if (!isCreateServerFn) {\n result = result.result\n\n // The result might again be a response,\n // and if it is, return it.\n if (result instanceof Response) {\n return result\n }\n }\n\n // TODO: RSCs Where are we getting this package?\n // if (isValidElement(result)) {\n // const { renderToPipeableStream } = await import(\n // // @ts-expect-error\n // 'react-server-dom/server'\n // )\n\n // const pipeableStream = renderToPipeableStream(result)\n\n // setHeaders(event, {\n // 'Content-Type': 'text/x-component',\n // } as any)\n\n // sendStream(event, response)\n // event._handled = true\n\n // return new Response(null, { status: 200 })\n // }\n\n if (isNotFound(result)) {\n return isNotFoundResponse(result)\n }\n\n const response = getResponse()\n let nonStreamingBody: any = undefined\n\n if (result !== undefined) {\n // first run without the stream in case `result` does not need streaming\n let done = false as boolean\n const callbacks: {\n onParse: (value: any) => void\n onDone: () => void\n onError: (error: any) => void\n } = {\n onParse: (value) => {\n nonStreamingBody = value\n },\n onDone: () => {\n done = true\n },\n onError: (error) => {\n throw error\n },\n }\n toCrossJSONStream(result, {\n refs: new Map(),\n plugins: serovalPlugins,\n onParse(value) {\n callbacks.onParse(value)\n },\n onDone() {\n callbacks.onDone()\n },\n onError: (error) => {\n callbacks.onError(error)\n },\n })\n if (done) {\n return new Response(\n nonStreamingBody ? JSON.stringify(nonStreamingBody) : undefined,\n {\n status: response?.status,\n statusText: response?.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n },\n )\n }\n\n // not done yet, we need to stream\n const stream = new ReadableStream({\n start(controller) {\n callbacks.onParse = (value) =>\n controller.enqueue(JSON.stringify(value) + '\\n')\n callbacks.onDone = () => {\n try {\n controller.close()\n } catch (error) {\n controller.error(error)\n }\n }\n callbacks.onError = (error) => controller.error(error)\n // stream the initial body\n if (nonStreamingBody !== undefined) {\n callbacks.onParse(nonStreamingBody)\n }\n },\n })\n return new Response(stream, {\n status: response?.status,\n statusText: response?.statusText,\n headers: {\n 'Content-Type': 'application/x-ndjson',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n\n return new Response(undefined, {\n status: response?.status,\n statusText: response?.statusText,\n })\n } catch (error: any) {\n if (error instanceof Response) {\n return error\n }\n // else if (\n // isPlainObject(error) &&\n // 'result' in error &&\n // error.result instanceof Response\n // ) {\n // return error.result\n // }\n\n // Currently this server-side context has no idea how to\n // build final URLs, so we need to defer that to the client.\n // The client will check for __redirect and __notFound keys,\n // and if they exist, it will handle them appropriately.\n\n if (isNotFound(error)) {\n return isNotFoundResponse(error)\n }\n\n console.info()\n console.info('Server Fn Error!')\n console.info()\n console.error(error)\n console.info()\n\n const serializedError = JSON.stringify(\n await Promise.resolve(\n toCrossJSONAsync(error, {\n refs: new Map(),\n plugins: serovalPlugins,\n }),\n ),\n )\n const response = getResponse()\n return new Response(serializedError, {\n status: response?.status ?? 500,\n statusText: response?.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n })()\n\n request.signal.removeEventListener('abort', abort)\n\n return response\n}\n\nfunction isNotFoundResponse(error: any) {\n const { headers, ...rest } = error\n\n return new Response(JSON.stringify(rest), {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n ...(headers || {}),\n },\n })\n}\n"],"names":["response","controller"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/start-server-core",
|
|
3
|
-
"version": "1.132.
|
|
3
|
+
"version": "1.132.33",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"seroval": "^1.3.2",
|
|
54
54
|
"tiny-invariant": "^1.3.3",
|
|
55
55
|
"@tanstack/history": "1.132.31",
|
|
56
|
-
"@tanstack/
|
|
57
|
-
"@tanstack/start-client-core": "1.132.
|
|
58
|
-
"@tanstack/
|
|
56
|
+
"@tanstack/router-core": "1.132.33",
|
|
57
|
+
"@tanstack/start-client-core": "1.132.33",
|
|
58
|
+
"@tanstack/start-storage-context": "1.132.33"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@standard-schema/spec": "^1.0.0",
|
package/src/createServerRpc.ts
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'
|
|
2
|
-
import invariant from 'tiny-invariant'
|
|
3
|
-
|
|
4
|
-
let baseUrl: string
|
|
5
|
-
function sanitizeBase(base: string) {
|
|
6
|
-
return base.replace(/^\/|\/$/g, '')
|
|
7
|
-
}
|
|
8
2
|
|
|
9
3
|
export const createServerRpc = (
|
|
10
4
|
functionId: string,
|
|
11
5
|
splitImportFn: (...args: any) => any,
|
|
12
6
|
) => {
|
|
13
|
-
|
|
14
|
-
const sanitizedAppBase = sanitizeBase(process.env.TSS_APP_BASE || '/')
|
|
15
|
-
const sanitizedServerBase = sanitizeBase(process.env.TSS_SERVER_FN_BASE!)
|
|
16
|
-
baseUrl = `${sanitizedAppBase ? `/${sanitizedAppBase}` : ''}/${sanitizedServerBase}/`
|
|
17
|
-
}
|
|
18
|
-
invariant(
|
|
19
|
-
splitImportFn,
|
|
20
|
-
'🚨splitImportFn required for the server functions server runtime, but was not provided.',
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
const url = baseUrl + functionId
|
|
7
|
+
const url = process.env.TSS_SERVER_FN_BASE + functionId
|
|
24
8
|
|
|
25
9
|
return Object.assign(splitImportFn, {
|
|
26
10
|
url,
|
|
@@ -8,8 +8,6 @@ import {
|
|
|
8
8
|
executeRewriteInput,
|
|
9
9
|
isRedirect,
|
|
10
10
|
isResolvedRedirect,
|
|
11
|
-
joinPaths,
|
|
12
|
-
trimPath,
|
|
13
11
|
} from '@tanstack/router-core'
|
|
14
12
|
import {
|
|
15
13
|
attachRouterServerSsrUtils,
|
|
@@ -56,19 +54,7 @@ function getStartResponseHeaders(opts: { router: AnyRouter }) {
|
|
|
56
54
|
export function createStartHandler<TRegister = Register>(
|
|
57
55
|
cb: HandlerCallback<AnyRouter>,
|
|
58
56
|
): RequestHandler<TRegister> {
|
|
59
|
-
|
|
60
|
-
throw new Error(
|
|
61
|
-
'tanstack/start-server-core: TSS_SERVER_FN_BASE must be defined in your environment for createStartHandler()',
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
// TODO do we remove this?
|
|
65
|
-
const APP_BASE = process.env.TSS_APP_BASE || '/'
|
|
66
|
-
// Add trailing slash to sanitise user defined TSS_SERVER_FN_BASE
|
|
67
|
-
const serverFnBase = joinPaths([
|
|
68
|
-
APP_BASE,
|
|
69
|
-
trimPath(process.env.TSS_SERVER_FN_BASE),
|
|
70
|
-
'/',
|
|
71
|
-
])
|
|
57
|
+
const ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH || '/'
|
|
72
58
|
let startRoutesManifest: Manifest | null = null
|
|
73
59
|
let startEntry: StartEntry | null = null
|
|
74
60
|
let routerEntry: RouterEntry | null = null
|
|
@@ -134,7 +120,6 @@ export function createStartHandler<TRegister = Register>(
|
|
|
134
120
|
let router: AnyRouter | null = null
|
|
135
121
|
const getRouter = async () => {
|
|
136
122
|
if (router) return router
|
|
137
|
-
// TODO how does this work with base path? does the router need to be configured the same as APP_BASE?
|
|
138
123
|
router = await (await getEntries()).routerEntry.getRouter()
|
|
139
124
|
|
|
140
125
|
// Update the client-side router with the history
|
|
@@ -162,6 +147,7 @@ export function createStartHandler<TRegister = Register>(
|
|
|
162
147
|
defaultSsr: startOptions.defaultSsr,
|
|
163
148
|
serializationAdapters: startOptions.serializationAdapters,
|
|
164
149
|
},
|
|
150
|
+
basepath: ROUTER_BASEPATH,
|
|
165
151
|
})
|
|
166
152
|
return router
|
|
167
153
|
}
|
|
@@ -185,7 +171,7 @@ export function createStartHandler<TRegister = Register>(
|
|
|
185
171
|
async () => {
|
|
186
172
|
try {
|
|
187
173
|
// First, let's attempt to handle server functions
|
|
188
|
-
if (href.startsWith(
|
|
174
|
+
if (href.startsWith(process.env.TSS_SERVER_FN_BASE)) {
|
|
189
175
|
return await handleServerAction({
|
|
190
176
|
request,
|
|
191
177
|
context: requestOpts?.context,
|
|
@@ -222,9 +208,7 @@ export function createStartHandler<TRegister = Register>(
|
|
|
222
208
|
|
|
223
209
|
// if the startRoutesManifest is not loaded yet, load it once
|
|
224
210
|
if (startRoutesManifest === null) {
|
|
225
|
-
startRoutesManifest = await getStartManifest(
|
|
226
|
-
basePath: APP_BASE,
|
|
227
|
-
})
|
|
211
|
+
startRoutesManifest = await getStartManifest()
|
|
228
212
|
}
|
|
229
213
|
const router = await getRouter()
|
|
230
214
|
attachRouterServerSsrUtils({
|
package/src/global.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
namespace NodeJS {
|
|
3
3
|
interface ProcessEnv {
|
|
4
|
-
|
|
5
|
-
TSS_SERVER_FN_BASE
|
|
4
|
+
TSS_ROUTER_BASEPATH: string
|
|
5
|
+
TSS_SERVER_FN_BASE: string
|
|
6
6
|
TSS_CLIENT_OUTPUT_DIR?: string
|
|
7
7
|
TSS_SHELL?: 'true' | 'false'
|
|
8
8
|
TSS_PRERENDERING?: 'true' | 'false'
|
package/src/router-manifest.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { loadVirtualModule } from './loadVirtualModule'
|
|
|
8
8
|
* special assets that are needed for the client. It does not include relationships
|
|
9
9
|
* between routes or any other data that is not needed for the client.
|
|
10
10
|
*/
|
|
11
|
-
export async function getStartManifest(
|
|
11
|
+
export async function getStartManifest() {
|
|
12
12
|
const { tsrStartManifest } = await loadVirtualModule(
|
|
13
13
|
VIRTUAL_MODULES.startManifest,
|
|
14
14
|
)
|
|
@@ -10,15 +10,7 @@ import { fromJSON, toCrossJSONAsync, toCrossJSONStream } from 'seroval'
|
|
|
10
10
|
import { getResponse } from './request-response'
|
|
11
11
|
import { getServerFnById } from './getServerFnById'
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
if (!base) {
|
|
15
|
-
throw new Error(
|
|
16
|
-
'🚨 process.env.TSS_SERVER_FN_BASE is required in start/server-handler/index',
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return base.replace(/^\/|\/$/g, '')
|
|
21
|
-
}
|
|
13
|
+
let regex: RegExp | undefined = undefined
|
|
22
14
|
|
|
23
15
|
export const handleServerAction = async ({
|
|
24
16
|
request,
|
|
@@ -32,13 +24,14 @@ export const handleServerAction = async ({
|
|
|
32
24
|
const abort = () => controller.abort()
|
|
33
25
|
request.signal.addEventListener('abort', abort)
|
|
34
26
|
|
|
27
|
+
if (regex === undefined) {
|
|
28
|
+
regex = new RegExp(`${process.env.TSS_SERVER_FN_BASE}([^/?#]+)`)
|
|
29
|
+
}
|
|
30
|
+
|
|
35
31
|
const method = request.method
|
|
36
32
|
const url = new URL(request.url, 'http://localhost:3000')
|
|
37
33
|
// extract the serverFnId from the url as host/_serverFn/:serverFnId
|
|
38
34
|
// Define a regex to match the path and extract the :thing part
|
|
39
|
-
const regex = new RegExp(
|
|
40
|
-
`${sanitizeBase(process.env.TSS_SERVER_FN_BASE)}/([^/?#]+)`,
|
|
41
|
-
)
|
|
42
35
|
|
|
43
36
|
// Execute the regex
|
|
44
37
|
const match = url.pathname.match(regex)
|