UNPKG

kitcn

Version:

kitcn - React Query integration and CLI tools for Convex

349 lines (346 loc) 17.4 kB
import { i as decodeWire, o as encodeWire } from "./transformer-C6pGVHqx.js"; import { _ as CRPCError } from "./builder-DBgto1yn.js"; import { z } from "zod"; //#region src/server/env.ts function createEnv(options) { const { schema, runtimeEnv, cache = true, codegenFallback = false } = options; let cached; return () => { if (cache && cached) return cached; const isCodegenParse = globalThis.__KITCN_CODEGEN__ === true || codegenFallback; const runtimeEnvSource = runtimeEnv ?? process.env; const runtimeEnvSnapshot = {}; for (const [key, zodType] of Object.entries(schema.shape)) { const undefinedParse = zodType.safeParse(void 0); if (undefinedParse.success) { if (Object.hasOwn(runtimeEnvSource, key) || Object.getOwnPropertyDescriptor(runtimeEnvSource, key) !== void 0 || key in runtimeEnvSource) runtimeEnvSnapshot[key] = runtimeEnvSource[key]; else if (!isCodegenParse && undefinedParse.data !== void 0) runtimeEnvSnapshot[key] = runtimeEnvSource[key]; continue; } runtimeEnvSnapshot[key] = runtimeEnvSource[key]; } const envForParse = isCodegenParse ? { ...Object.fromEntries(Object.entries(schema.shape).map(([key, zodType]) => { const result = zodType.safeParse(void 0); if (!result.success) { if (zodType instanceof z.ZodEnum && Array.isArray(zodType.options) && zodType.options.length > 0) return [key, zodType.options[0]]; return [key, ""]; } return [key, typeof result.data === "string" ? result.data : void 0]; })), ...Object.fromEntries(Object.entries(runtimeEnvSnapshot).filter(([, value]) => value !== void 0)) } : runtimeEnvSnapshot; const parsed = schema.safeParse(envForParse); if (!parsed.success) throw new CRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid environment variables" }); if (cache) cached = parsed.data; return parsed.data; }; } //#endregion //#region src/server/procedure-caller.ts const FUNCTION_REFERENCE_METADATA_KEY = "__kitcnFunctionReference"; function typedProcedureResolver(functionReference, resolver) { const typedResolver = resolver; typedResolver[FUNCTION_REFERENCE_METADATA_KEY] = functionReference; return typedResolver; } function getGeneratedFunctionReference(functionReference) { return functionReference; } function isRecord(value) { return typeof value === "object" && value !== null; } function isProcedureType(value) { return value === "query" || value === "mutation" || value === "action"; } function isProcedureDefinition(value) { return isRecord(value) && isProcedureType(value._type); } function getProcedureTypeFromExport(value) { const crpcType = value._crpcMeta?.type; if (isProcedureType(crpcType)) return crpcType; if (value.isQuery === true) return "query"; if (value.isMutation === true) return "mutation"; if (value.isAction === true) return "action"; return null; } function isProcedureExport(value) { if (value === null || typeof value !== "object" && typeof value !== "function") return false; const exportValue = value; if (typeof exportValue._handler !== "function") return false; return getProcedureTypeFromExport(exportValue) !== null; } function decodeProcedureResult(procedure, value) { return decodeWire(value, procedure.__kitcnTransformer); } function encodeProcedureInput(procedure, value) { return encodeWire(value, procedure.__kitcnTransformer); } async function executeProcedure(procedure, mode, pathString, ctx, input) { if (mode === "handler") { if (typeof procedure.__kitcnRawHandler === "function") return procedure.__kitcnRawHandler({ ctx, input }); if (typeof procedure._handler === "function") return procedure._handler(ctx, input); throw new Error(`[kitcn] Resolved procedure does not expose a raw handler: "${pathString}".`); } return decodeProcedureResult(procedure, await procedure._handler?.(ctx, input)); } function getNodeAtPath(api, path) { let current = api; for (const segment of path) { if (!isRecord(current)) return; current = current[segment]; if (current === void 0) return; } return current; } function getContextType(ctx) { if (!isRecord(ctx)) return "unknown"; const hasDb = "db" in ctx; const hasRunQuery = typeof ctx.runQuery === "function"; const hasRunMutation = typeof ctx.runMutation === "function"; const hasRunAction = typeof ctx.runAction === "function"; if (!hasDb && (hasRunQuery || hasRunMutation || hasRunAction)) return "action"; if (hasRunMutation) return "mutation"; if (hasDb) return "query"; return "unknown"; } function assertCanInvoke(ctxType, procedureType, pathString, opts = {}) { if (ctxType === "action") { if (!opts.supportsActionContext) throw new Error(`[kitcn] Action context is not supported by createCaller(ctx): "${pathString}".`); if (procedureType === "action") throw new Error(`[kitcn] Cannot call action procedures from action context: "${pathString}".`); return; } if (ctxType !== "query" && ctxType !== "mutation") throw new Error(`[kitcn] Unsupported context for createCaller(ctx): "${pathString}".`); if (ctxType === "query" && procedureType !== "query") throw new Error(`[kitcn] Cannot call ${procedureType} procedures from query context: "${pathString}".`); if (ctxType === "mutation" && procedureType === "action") throw new Error(`[kitcn] Mutation context cannot call action procedures: "${pathString}".`); } function createRecursiveProxy(path, ctx, opts, mode) { return new Proxy(() => {}, { get(_target, prop) { if (typeof prop === "symbol") return; if (prop === "then") return; return createRecursiveProxy([...path, prop], ctx, opts, mode); }, async apply(_target, _thisArg, argsList) { const pathString = path.join("."); const node = getNodeAtPath(opts.api, path); if (node === void 0) throw new Error(`[kitcn] Invalid procedure path: "${pathString}".`); if (!isProcedureDefinition(node)) throw new Error(`[kitcn] Path does not resolve to a procedure: "${pathString}".`); assertCanInvoke(getContextType(ctx), node._type, pathString); const resolved = await opts.resolver(path); if (!isProcedureExport(resolved)) throw new Error(`[kitcn] Resolved value is not a cRPC procedure: "${pathString}".`); const resolvedType = getProcedureTypeFromExport(resolved); if (resolvedType !== node._type) throw new Error(`[kitcn] Procedure type mismatch at "${pathString}". Expected "${node._type}" but got "${resolvedType ?? "unknown"}".`); return executeProcedure(resolved, mode, pathString, ctx, argsList[0] ?? {}); } }); } function hasProcedurePrefix(registry, key) { const prefix = `${key}.`; for (const path in registry) if (path.startsWith(prefix)) return true; return false; } function getResolverFunctionReference(resolver) { return resolver[FUNCTION_REFERENCE_METADATA_KEY]; } async function executeActionContextProcedure(ctx, procedureType, procedure, pathString, resolver, input) { const ctxValue = ctx; const functionReference = getResolverFunctionReference(resolver); if (!functionReference) throw new Error(`[kitcn] Missing function reference metadata for action context dispatch: "${pathString}".`); const encodedInput = procedure ? encodeProcedureInput(procedure, input) : input; const runner = procedureType === "query" ? ctxValue.runQuery : ctxValue.runMutation; if (typeof runner !== "function") { const runnerName = procedureType === "query" ? "runQuery" : "runMutation"; throw new Error(`[kitcn] Action context is missing ctx.${runnerName} for "${pathString}".`); } const result = await runner(functionReference, encodedInput); return procedure ? decodeProcedureResult(procedure, result) : result; } async function executeActionContextActionProcedure(ctx, procedure, pathString, resolver, input) { const ctxValue = ctx; const functionReference = getResolverFunctionReference(resolver); if (!functionReference) throw new Error(`[kitcn] Missing function reference metadata for action context dispatch: "${pathString}".`); if (typeof ctxValue.runAction !== "function") throw new Error(`[kitcn] Action context is missing ctx.runAction for "${pathString}".`); const encodedInput = procedure ? encodeProcedureInput(procedure, input) : input; const result = await ctxValue.runAction(functionReference, encodedInput); return procedure ? decodeProcedureResult(procedure, result) : result; } function getScheduleRunner(ctx, pathString, mode) { const scheduler = ctx.scheduler; if (!scheduler || typeof scheduler !== "object") throw new Error(`[kitcn] Context is missing ctx.scheduler for "${pathString}".`); if (mode.type === "after") { if (typeof scheduler.runAfter !== "function") throw new Error(`[kitcn] Context is missing ctx.scheduler.runAfter for "${pathString}".`); return (functionReference, args) => scheduler.runAfter?.(mode.delayMs, functionReference, args); } if (typeof scheduler.runAt !== "function") throw new Error(`[kitcn] Context is missing ctx.scheduler.runAt for "${pathString}".`); return (functionReference, args) => scheduler.runAt?.(mode.timestamp, functionReference, args); } async function executeScheduledProcedure(ctx, mode, procedure, pathString, resolver, input) { const functionReference = getResolverFunctionReference(resolver); if (!functionReference) throw new Error(`[kitcn] Missing function reference metadata for schedule dispatch: "${pathString}".`); return getScheduleRunner(ctx, pathString, mode)(functionReference, procedure ? encodeProcedureInput(procedure, input) : input); } function createActionsRegistryProxy(path, ctx, registry) { return new Proxy(() => {}, { get(_target, prop) { if (typeof prop === "symbol") return; if (prop === "then") return; return createActionsRegistryProxy([...path, prop], ctx, registry); }, async apply(_target, _thisArg, argsList) { const pathString = path.join("."); const entry = registry[pathString]; if (!entry) { if (hasProcedurePrefix(registry, pathString)) throw new Error(`[kitcn] Path does not resolve to an action procedure: "${pathString}".`); throw new Error(`[kitcn] Invalid procedure path: "${pathString}".`); } const [procedureType, resolveProcedure] = entry; if (procedureType !== "action") throw new Error(`[kitcn] Path does not resolve to an action procedure: "${pathString}".`); if (getContextType(ctx) !== "action") throw new Error(`[kitcn] Action procedures require action context: "${pathString}".`); const resolved = await resolveProcedure(); const procedure = isProcedureExport(resolved) ? resolved : null; const canDispatchDirectly = !!getResolverFunctionReference(resolveProcedure); if (!procedure && !canDispatchDirectly) throw new Error(`[kitcn] Resolved value is not a cRPC procedure: "${pathString}".`); const resolvedType = procedure ? getProcedureTypeFromExport(procedure) : null; if (procedure && resolvedType !== procedureType) throw new Error(`[kitcn] Procedure type mismatch at "${pathString}". Expected "${procedureType}" but got "${resolvedType ?? "unknown"}".`); return executeActionContextActionProcedure(ctx, procedure, pathString, resolveProcedure, argsList[0] ?? {}); } }); } function createScheduledRegistryProxy(path, ctx, registry, mode) { return new Proxy(() => {}, { get(_target, prop) { if (typeof prop === "symbol") return; if (prop === "then") return; return createScheduledRegistryProxy([...path, prop], ctx, registry, mode); }, async apply(_target, _thisArg, argsList) { const pathString = path.join("."); const entry = registry[pathString]; if (!entry) { if (hasProcedurePrefix(registry, pathString)) throw new Error(`[kitcn] Path does not resolve to a schedulable procedure: "${pathString}".`); throw new Error(`[kitcn] Invalid procedure path: "${pathString}".`); } const [procedureType, resolveProcedure] = entry; if (procedureType === "query") throw new Error(`[kitcn] Cannot schedule query procedures: "${pathString}".`); const ctxType = getContextType(ctx); if (ctxType !== "mutation" && ctxType !== "action") throw new Error(`[kitcn] Scheduling requires mutation or action context: "${pathString}".`); const resolved = await resolveProcedure(); const procedure = isProcedureExport(resolved) ? resolved : null; const canDispatchDirectly = !!getResolverFunctionReference(resolveProcedure); if (!procedure && !canDispatchDirectly) throw new Error(`[kitcn] Resolved value is not a cRPC procedure: "${pathString}".`); const resolvedType = procedure ? getProcedureTypeFromExport(procedure) : null; if (procedure && resolvedType !== procedureType) throw new Error(`[kitcn] Procedure type mismatch at "${pathString}". Expected "${procedureType}" but got "${resolvedType ?? "unknown"}".`); return executeScheduledProcedure(ctx, mode, procedure, pathString, resolveProcedure, argsList[0] ?? {}); } }); } function createScheduleNamespace(ctx, registry) { return { after: (delayMs) => createScheduledRegistryProxy([], ctx, registry, { type: "after", delayMs }), at: (timestamp) => createScheduledRegistryProxy([], ctx, registry, { type: "at", timestamp }), now: createScheduledRegistryProxy([], ctx, registry, { type: "after", delayMs: 0 }), cancel: async (id) => { const scheduler = ctx.scheduler; if (!scheduler || typeof scheduler !== "object") throw new Error("[kitcn] Context is missing ctx.scheduler for \"schedule.cancel\"."); if (typeof scheduler.cancel !== "function") throw new Error("[kitcn] Context is missing ctx.scheduler.cancel for \"schedule.cancel\"."); return scheduler.cancel(id); } }; } function createRegistryProxy(path, ctx, registry, mode, supportsActionContext) { return new Proxy(() => {}, { get(_target, prop) { if (typeof prop === "symbol") return; if (prop === "then") return; if (mode === "caller" && path.length === 0 && prop === "actions") return createActionsRegistryProxy([], ctx, registry); if (mode === "caller" && path.length === 0 && prop === "schedule") return createScheduleNamespace(ctx, registry); return createRegistryProxy([...path, prop], ctx, registry, mode, supportsActionContext); }, async apply(_target, _thisArg, argsList) { const pathString = path.join("."); const entry = registry[pathString]; if (!entry) { if (hasProcedurePrefix(registry, pathString)) throw new Error(`[kitcn] Path does not resolve to a procedure: "${pathString}".`); throw new Error(`[kitcn] Invalid procedure path: "${pathString}".`); } const [procedureType, resolveProcedure] = entry; const ctxType = getContextType(ctx); assertCanInvoke(ctxType, procedureType, pathString, { supportsActionContext }); const resolved = await resolveProcedure(); const procedure = isProcedureExport(resolved) ? resolved : null; const canDispatchDirectly = mode === "caller" && ctxType === "action" && !!getResolverFunctionReference(resolveProcedure); if (!procedure && !canDispatchDirectly) throw new Error(`[kitcn] Resolved value is not a cRPC procedure: "${pathString}".`); const resolvedType = procedure ? getProcedureTypeFromExport(procedure) : null; if (procedure && resolvedType !== procedureType) throw new Error(`[kitcn] Procedure type mismatch at "${pathString}". Expected "${procedureType}" but got "${resolvedType ?? "unknown"}".`); const input = argsList[0] ?? {}; if (ctxType === "action" && mode === "caller") { if (procedureType === "action") throw new Error(`[kitcn] Cannot call action procedures from action context: "${pathString}".`); return executeActionContextProcedure(ctx, procedureType, procedure, pathString, resolveProcedure, input); } if (!procedure) throw new Error(`[kitcn] Resolved value is not a cRPC procedure: "${pathString}".`); return executeProcedure(procedure, mode, pathString, ctx, input); } }); } function defineProcedure(type) { return { _type: type }; } function createProcedureCallerFactory(opts) { return function createCaller(ctx) { return createRecursiveProxy([], ctx, opts, "caller"); }; } function createProcedureHandlerFactory(opts) { return function createHandler(ctx) { return createRecursiveProxy([], ctx, opts, "handler"); }; } function createGenericCallerFactory(registry) { return function createCaller(ctx) { return createRegistryProxy([], ctx, registry, "caller", true); }; } function createGenericHandlerFactory(registry) { return function createHandler(ctx) { return createRegistryProxy([], ctx, registry, "handler", false); }; } function createGeneratedRegistryRuntime(createRegistryOrRegistry) { let cachedRegistry; let cachedCallerFactory; let cachedHandlerFactory; const getRegistry = () => { cachedRegistry ??= typeof createRegistryOrRegistry === "function" ? createRegistryOrRegistry() : createRegistryOrRegistry; return cachedRegistry; }; const getCallerFactory = () => { cachedCallerFactory ??= createGenericCallerFactory(getRegistry().procedureRegistry); return cachedCallerFactory; }; const getHandlerFactory = () => { cachedHandlerFactory ??= createGenericHandlerFactory(getRegistry().handlerRegistry); return cachedHandlerFactory; }; return { getCallerFactory, getHandlerFactory }; } //#endregion export { createProcedureHandlerFactory as a, typedProcedureResolver as c, createProcedureCallerFactory as i, createEnv as l, createGenericCallerFactory as n, defineProcedure as o, createGenericHandlerFactory as r, getGeneratedFunctionReference as s, createGeneratedRegistryRuntime as t };