kitcn
Version:
kitcn - React Query integration and CLI tools for Convex
349 lines (346 loc) • 17.4 kB
JavaScript
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 };