kitcn
Version:
kitcn - React Query integration and CLI tools for Convex
145 lines (143 loc) • 4.08 kB
JavaScript
import { i as pick, r as omit } from "./upstream-BR6sBLg3.js";
import { t as addFieldsToValidator } from "./validators-C7LelqTN.js";
//#region src/internal/upstream/server/customFunctions.ts
/**
* A helper for defining a Customization when your mod doesn't need to add or remove
* anything from args.
* @param modifyCtx A function that defines how to modify the ctx.
* @returns A ctx delta to be applied to the original ctx.
*/
function customCtx(modifyCtx) {
return {
args: {},
input: async (ctx, _, extra) => ({
ctx: await modifyCtx(ctx, extra),
args: {}
})
};
}
/**
* A Customization that doesn't add or remove any context or args.
*/
const NoOp = {
args: {},
input() {
return {
args: {},
ctx: {}
};
}
};
/**
* customMutation helps define custom behavior on top of `mutation`
* or `internalMutation` by passing a function that modifies the ctx and args.
*
* Example usage:
* ```js
* const myMutationBuilder = customMutation(mutation, {
* args: { sessionId: v.id("sessions") },
* input: async (ctx, args) => {
* const user = await getUserOrNull(ctx);
* const session = await db.get(sessionId);
* const db = wrapDatabaseReader({ user }, ctx.db, rlsRules);
* return {
* ctx: { db, user, session },
* args: {},
* onSuccess: ({ result }) => {
* // Optional callback that runs after the function executes
* // Has access to resources created during input processing
* console.log(`User ${user.name} returned:`, result);
* }
* };
* },
* });
*
* // Using the custom builder
* export const setSomeData = myMutationBuilder({
* args: { someArg: v.string() },
* handler: async (ctx, args) => {
* const { db, user, session, scheduler } = ctx;
* const { someArg } = args;
* // ...
* }
* });
* ```
*
* Simple usage only modifying ctx:
* ```js
* const myUserMutation = customMutation(
* mutation,
* customCtx(async (ctx) => {
* return {
* // Throws an exception if the user isn't logged in
* user: await getUserByTokenIdentifier(ctx),
* };
* })
* );
*
* // Using it
* export const setMyName = myUserMutation({
* args: { name: v.string() },
* handler: async (ctx, args) => {
* await ctx.db.patch(ctx.user._id, { name: args.name });
* },
* });
*
* @param mutation The mutation to be modified. Usually `mutation` or `internalMutation`
* from `_generated/server`.
* @param customization The modifier to be applied to the mutation, changing ctx and args.
* @returns A new mutation builder to define queries with modified ctx and args.
*/
function customMutation(mutation, customization) {
return customFnBuilder(mutation, customization);
}
function customFnBuilder(builder, customization) {
const customInput = customization.input ?? NoOp.input;
const inputArgs = customization.args ?? NoOp.args;
return function customBuilder(fn) {
const { args, handler = fn, returns, ...extra } = fn;
if (args) return builder({
args: addFieldsToValidator(args, inputArgs),
returns,
handler: async (ctx, allArgs) => {
const added = await customInput(ctx, pick(allArgs, Object.keys(inputArgs)), extra);
const args = omit(allArgs, Object.keys(inputArgs));
const result = await handler({
...ctx,
...added.ctx
}, {
...args,
...added.args
});
if (added.onSuccess) await added.onSuccess({
ctx,
args,
result
});
return result;
}
});
if (Object.keys(inputArgs).length > 0) throw new Error("If you're using a custom function with arguments for the input customization, you must declare the arguments for the function too.");
return builder({
returns: fn.returns,
handler: async (ctx, args) => {
const added = await customInput(ctx, args, extra);
const result = await handler({
...ctx,
...added.ctx
}, {
...args,
...added.args
});
if (added.onSuccess) await added.onSuccess({
ctx,
args,
result
});
return result;
}
});
};
}
//#endregion
export { customCtx as n, customMutation as r, NoOp as t };