Add composeCtx and composeCtxAndArgs helpers#902
Add composeCtx and composeCtxAndArgs helpers#902SamHoque wants to merge 1 commit intoget-convex:mainfrom
Conversation
These composition helpers allow building middleware layers by: - composeCtx: adding context transformation to existing middleware - composeCtxAndArgs: extending middleware with additional args and transformation
📝 WalkthroughWalkthroughTwo new composition utilities, Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/convex-helpers/server/customFunctions.ts`:
- Around line 288-334: composeCtxAndArgs currently discards baseResult.onSuccess
so base finalizers never run; update the returned customization from
composeCtxAndArgs to preserve and compose onSuccess handlers: after awaiting
base.input and extension.transform, create a combined onSuccess that first
awaits baseResult.onSuccess (if present) with the original args/ctx, then calls
any extension or new onSuccess (if extension provides one), and return that
combined onSuccess along with ctx and args; locate composeCtxAndArgs,
base.input, baseResult, and extension.transform to implement this merge.
- Around line 235-256: composeCtx currently discards any onSuccess hook returned
by the base input; update the returned object from the input wrapper in
composeCtx to forward baseResult.onSuccess (e.g., include an onSuccess property
referencing baseResult.onSuccess) so base middleware post-success callbacks are
preserved; locate the composeCtx function and modify the return of the input
async function (where baseResult is computed) to also return onSuccess:
baseResult.onSuccess (handling the possibility it's undefined).
🧹 Nitpick comments (1)
packages/convex-helpers/README.md (1)
127-133: Clarify the source ofdevice/staffin the example.The example references
device/staffwithout showing their derivation; a short lookup line would make it clearer.💡 Suggested doc tweak
-const withDevice = customCtxAndArgs({ - args: { token: v.string() }, - input: async (ctx, { token }) => ({ - ctx: { device, staff: staff || null }, - args: {}, - }), -}); +const withDevice = customCtxAndArgs({ + args: { token: v.string() }, + input: async (ctx, { token }) => { + const { device, staff } = await getDeviceAndStaff(token); + return { ctx: { device, staff: staff ?? null }, args: {} }; + }, +});
| export function composeCtx< | ||
| Ctx extends Record<string, any>, | ||
| CustomArgsValidator extends PropertyValidators, | ||
| CustomCtx extends Record<string, any>, | ||
| CustomMadeArgs extends Record<string, any>, | ||
| ExtraArgs extends Record<string, any>, | ||
| TransformedCtx extends Record<string, any>, | ||
| >( | ||
| base: Customization<Ctx, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>, | ||
| transform: ( | ||
| ctx: Ctx & CustomCtx, | ||
| extra: ExtraArgs, | ||
| ) => TransformedCtx | Promise<TransformedCtx>, | ||
| ): Customization<Ctx, CustomArgsValidator, TransformedCtx, CustomMadeArgs, ExtraArgs> { | ||
| return { | ||
| args: base.args, | ||
| input: async (ctx, args, extra) => { | ||
| const baseResult = await base.input(ctx, args, extra); | ||
| const combinedCtx = { ...ctx, ...baseResult.ctx } as Ctx & CustomCtx; | ||
| const transformedCtx = await transform(combinedCtx, extra); | ||
| return { ctx: transformedCtx, args: baseResult.args }; | ||
| }, |
There was a problem hiding this comment.
Preserve base onSuccess callbacks during composition.
composeCtx drops baseResult.onSuccess, so any post-success hook from the base middleware is lost. Please forward it.
🐛 Proposed fix
- const transformedCtx = await transform(combinedCtx, extra);
- return { ctx: transformedCtx, args: baseResult.args };
+ const transformedCtx = await transform(combinedCtx, extra);
+ return {
+ ctx: transformedCtx,
+ args: baseResult.args,
+ onSuccess: baseResult.onSuccess,
+ };🤖 Prompt for AI Agents
In `@packages/convex-helpers/server/customFunctions.ts` around lines 235 - 256,
composeCtx currently discards any onSuccess hook returned by the base input;
update the returned object from the input wrapper in composeCtx to forward
baseResult.onSuccess (e.g., include an onSuccess property referencing
baseResult.onSuccess) so base middleware post-success callbacks are preserved;
locate the composeCtx function and modify the return of the input async function
(where baseResult is computed) to also return onSuccess: baseResult.onSuccess
(handling the possibility it's undefined).
| export function composeCtxAndArgs< | ||
| Ctx extends Record<string, any>, | ||
| CustomArgsValidator extends PropertyValidators, | ||
| CustomCtx extends Record<string, any>, | ||
| CustomMadeArgs extends Record<string, any>, | ||
| ExtraArgs extends Record<string, any>, | ||
| NewArgsValidator extends PropertyValidators, | ||
| TransformedCtx extends Record<string, any>, | ||
| >( | ||
| base: Customization<Ctx, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>, | ||
| extension: { | ||
| args: NewArgsValidator; | ||
| transform: ( | ||
| ctx: Ctx & CustomCtx, | ||
| args: ObjectType<NewArgsValidator>, | ||
| extra: ExtraArgs, | ||
| ) => TransformedCtx | Promise<TransformedCtx>; | ||
| }, | ||
| ): Customization< | ||
| Ctx, | ||
| CustomArgsValidator & NewArgsValidator, | ||
| TransformedCtx, | ||
| CustomMadeArgs, | ||
| ExtraArgs | ||
| > { | ||
| return { | ||
| args: { ...base.args, ...extension.args }, | ||
| input: async (ctx, args, extra) => { | ||
| const argsRecord = args as Record<string, any>; | ||
|
|
||
| // Extract base args | ||
| const baseArgKeys = Object.keys(base.args); | ||
| const baseArgs = Object.fromEntries( | ||
| baseArgKeys.map((k) => [k, argsRecord[k]]), | ||
| ) as ObjectType<CustomArgsValidator>; | ||
|
|
||
| // Extract extension args | ||
| const extArgKeys = Object.keys(extension.args); | ||
| const extArgs = Object.fromEntries( | ||
| extArgKeys.map((k) => [k, argsRecord[k]]), | ||
| ) as ObjectType<NewArgsValidator>; | ||
|
|
||
| const baseResult = await base.input(ctx, baseArgs, extra); | ||
| const combinedCtx = { ...ctx, ...baseResult.ctx } as Ctx & CustomCtx; | ||
| const transformedCtx = await extension.transform(combinedCtx, extArgs, extra); | ||
| return { ctx: transformedCtx, args: baseResult.args }; | ||
| }, |
There was a problem hiding this comment.
Preserve base onSuccess callbacks in composeCtxAndArgs.
Same issue here: baseResult.onSuccess is dropped, so base finalizers won’t run.
🐛 Proposed fix
- const transformedCtx = await extension.transform(combinedCtx, extArgs, extra);
- return { ctx: transformedCtx, args: baseResult.args };
+ const transformedCtx = await extension.transform(combinedCtx, extArgs, extra);
+ return {
+ ctx: transformedCtx,
+ args: baseResult.args,
+ onSuccess: baseResult.onSuccess,
+ };🤖 Prompt for AI Agents
In `@packages/convex-helpers/server/customFunctions.ts` around lines 288 - 334,
composeCtxAndArgs currently discards baseResult.onSuccess so base finalizers
never run; update the returned customization from composeCtxAndArgs to preserve
and compose onSuccess handlers: after awaiting base.input and
extension.transform, create a combined onSuccess that first awaits
baseResult.onSuccess (if present) with the original args/ctx, then calls any
extension or new onSuccess (if extension provides one), and return that combined
onSuccess along with ctx and args; locate composeCtxAndArgs, base.input,
baseResult, and extension.transform to implement this merge.
These composition helpers allow building middleware layers by:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.