UNPKG

@tanstack/start-client-core

Version:

Modern and scalable routing for React applications

200 lines (199 loc) 6.78 kB
import { TSS_SERVER_FUNCTION_FACTORY } from "./constants.js"; import { getStartOptions } from "./getStartOptions.js"; import { getStartContextServerOnly } from "./getStartContextServerOnly.js"; import { createNullProtoObject, safeObjectMerge } from "./safeObjectMerge.js"; import { mergeHeaders } from "@tanstack/router-core/ssr/client"; import { isRedirect, parseRedirect } from "@tanstack/router-core"; //#region src/createServerFn.ts var createServerFn = (options, __opts) => { const resolvedOptions = __opts || options || {}; if (typeof resolvedOptions.method === "undefined") resolvedOptions.method = "GET"; const res = { options: resolvedOptions, middleware: (middleware) => { const newMiddleware = [...resolvedOptions.middleware || []]; middleware.map((m) => { if (TSS_SERVER_FUNCTION_FACTORY in m) { if (m.options.middleware) newMiddleware.push(...m.options.middleware); } else newMiddleware.push(m); }); const res = createServerFn(void 0, { ...resolvedOptions, middleware: newMiddleware }); res[TSS_SERVER_FUNCTION_FACTORY] = true; return res; }, inputValidator: (inputValidator) => { return createServerFn(void 0, { ...resolvedOptions, inputValidator }); }, handler: (...args) => { const [extractedFn, serverFn] = args; const newOptions = { ...resolvedOptions, extractedFn, serverFn }; const resolvedMiddleware = [...newOptions.middleware || [], serverFnBaseToMiddleware(newOptions)]; extractedFn.method = resolvedOptions.method; return Object.assign(async (opts) => { const result = await executeMiddleware(resolvedMiddleware, "client", { ...extractedFn, ...newOptions, data: opts?.data, headers: opts?.headers, signal: opts?.signal, fetch: opts?.fetch, context: createNullProtoObject() }); const redirect = parseRedirect(result.error); if (redirect) throw redirect; if (result.error) throw result.error; return result.result; }, { ...extractedFn, method: resolvedOptions.method, __executeServer: async (opts) => { const startContext = getStartContextServerOnly(); const serverContextAfterGlobalMiddlewares = startContext.contextAfterGlobalMiddlewares; return await executeMiddleware(resolvedMiddleware, "server", { ...extractedFn, ...opts, serverFnMeta: extractedFn.serverFnMeta, context: safeObjectMerge(serverContextAfterGlobalMiddlewares, opts.context), request: startContext.request }).then((d) => ({ result: d.result, error: d.error, context: d.sendContext })); } }); } }; const fun = (options) => { return createServerFn(void 0, { ...resolvedOptions, ...options }); }; return Object.assign(fun, res); }; async function executeMiddleware(middlewares, env, opts) { let flattenedMiddlewares = flattenMiddlewares([...getStartOptions()?.functionMiddleware || [], ...middlewares]); if (env === "server") { const startContext = getStartContextServerOnly({ throwIfNotFound: false }); if (startContext?.executedRequestMiddlewares) flattenedMiddlewares = flattenedMiddlewares.filter((m) => !startContext.executedRequestMiddlewares.has(m)); } const callNextMiddleware = async (ctx) => { const nextMiddleware = flattenedMiddlewares.shift(); if (!nextMiddleware) return ctx; try { if ("inputValidator" in nextMiddleware.options && nextMiddleware.options.inputValidator && env === "server") ctx.data = await execValidator(nextMiddleware.options.inputValidator, ctx.data); let middlewareFn = void 0; if (env === "client") { if ("client" in nextMiddleware.options) middlewareFn = nextMiddleware.options.client; } else if ("server" in nextMiddleware.options) middlewareFn = nextMiddleware.options.server; if (middlewareFn) { const userNext = async (userCtx = {}) => { const result = await callNextMiddleware({ ...ctx, ...userCtx, context: safeObjectMerge(ctx.context, userCtx.context), sendContext: safeObjectMerge(ctx.sendContext, userCtx.sendContext), headers: mergeHeaders(ctx.headers, userCtx.headers), _callSiteFetch: ctx._callSiteFetch, fetch: ctx._callSiteFetch ?? userCtx.fetch ?? ctx.fetch, result: userCtx.result !== void 0 ? userCtx.result : userCtx instanceof Response ? userCtx : ctx.result, error: userCtx.error ?? ctx.error }); if (result.error) throw result.error; return result; }; const result = await middlewareFn({ ...ctx, next: userNext }); if (isRedirect(result)) return { ...ctx, error: result }; if (result instanceof Response) return { ...ctx, result }; if (!result) throw new Error("User middleware returned undefined. You must call next() or return a result in your middlewares."); return result; } return callNextMiddleware(ctx); } catch (error) { return { ...ctx, error }; } }; return callNextMiddleware({ ...opts, headers: opts.headers || {}, sendContext: opts.sendContext || {}, context: opts.context || createNullProtoObject(), _callSiteFetch: opts.fetch }); } function flattenMiddlewares(middlewares, maxDepth = 100) { const seen = /* @__PURE__ */ new Set(); const flattened = []; const recurse = (middleware, depth) => { if (depth > maxDepth) throw new Error(`Middleware nesting depth exceeded maximum of ${maxDepth}. Check for circular references.`); middleware.forEach((m) => { if (m.options.middleware) recurse(m.options.middleware, depth + 1); if (!seen.has(m)) { seen.add(m); flattened.push(m); } }); }; recurse(middlewares, 0); return flattened; } async function execValidator(validator, input) { if (validator == null) return {}; if ("~standard" in validator) { const result = await validator["~standard"].validate(input); if (result.issues) throw new Error(JSON.stringify(result.issues, void 0, 2)); return result.value; } if ("parse" in validator) return validator.parse(input); if (typeof validator === "function") return validator(input); throw new Error("Invalid validator type!"); } function serverFnBaseToMiddleware(options) { return { "~types": void 0, options: { inputValidator: options.inputValidator, client: async ({ next, sendContext, fetch, ...ctx }) => { const payload = { ...ctx, context: sendContext, fetch }; return next(await options.extractedFn?.(payload)); }, server: async ({ next, ...ctx }) => { const result = await options.serverFn?.(ctx); return next({ ...ctx, result }); } } }; } //#endregion export { createServerFn, execValidator, executeMiddleware, flattenMiddlewares }; //# sourceMappingURL=createServerFn.js.map