Skip to content

Commit 63b147f

Browse files
committed
use a pattern to break type dependency cycles
1 parent ee5e919 commit 63b147f

File tree

4 files changed

+65
-52
lines changed

4 files changed

+65
-52
lines changed

example/convex/_generated/api.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import type * as admin from "../admin.js";
1212
import type * as example from "../example.js";
1313
import type * as transcription from "../transcription.js";
14-
import type * as userConfirmation from "../userConfirmation.js";
14+
import type * as userConfirmation_steps from "../userConfirmation/steps.js";
15+
import type * as userConfirmation_workflow from "../userConfirmation/workflow.js";
1516

1617
import type {
1718
ApiFromModules,
@@ -31,7 +32,8 @@ declare const fullApi: ApiFromModules<{
3132
admin: typeof admin;
3233
example: typeof example;
3334
transcription: typeof transcription;
34-
userConfirmation: typeof userConfirmation;
35+
"userConfirmation/steps": typeof userConfirmation_steps;
36+
"userConfirmation/workflow": typeof userConfirmation_workflow;
3537
}>;
3638
declare const fullApiWithMounts: typeof fullApi;
3739

example/convex/userConfirmation.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { v } from "convex/values";
2+
import { internalAction, internalMutation } from "../_generated/server";
3+
import { workflow } from "../example";
4+
import { vWorkflowId, defineEvent } from "@convex-dev/workflow";
5+
6+
export const approvalEvent = defineEvent({
7+
name: "approval",
8+
validator: v.union(
9+
v.object({ approved: v.literal(true), choice: v.number() }),
10+
v.object({ approved: v.literal(false), reason: v.string() }),
11+
),
12+
});
13+
14+
export const generateProposals = internalAction({
15+
args: { prompt: v.string() },
16+
handler: async (_ctx, _args) => {
17+
// imagine this is a call to an LLM
18+
return ["proposal1", "proposal2", "proposal3"];
19+
},
20+
});
21+
22+
export const chooseProposal = internalMutation({
23+
args: { workflowId: vWorkflowId, choice: v.number() },
24+
handler: async (ctx, args) => {
25+
await workflow.sendEvent(ctx, args.workflowId, approvalEvent, {
26+
approved: true,
27+
choice: args.choice,
28+
});
29+
return true;
30+
},
31+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { v } from "convex/values";
2+
import { anyApi, type ApiFromModules } from "convex/server";
3+
import { workflow } from "../example";
4+
import { approvalEvent } from "./steps";
5+
6+
// This allows us to scope an `internal` object to just the steps file,
7+
// breaking circular dependency issues.
8+
const steps = (
9+
anyApi as unknown as ApiFromModules<{
10+
"userConfirmation/steps": typeof import("./steps");
11+
}>
12+
).userConfirmation.steps;
13+
14+
export const confirmationWorkflow = workflow.define({
15+
args: { prompt: v.string() },
16+
returns: v.string(),
17+
handler: async (ctx, args) => {
18+
const proposals = await ctx.runAction(
19+
steps.generateProposals,
20+
{ prompt: args.prompt },
21+
{ retry: true },
22+
);
23+
const approval = await ctx.awaitEvent(approvalEvent);
24+
if (!approval.approved) {
25+
return "rejected: " + approval.reason;
26+
}
27+
const choice = proposals[approval.choice];
28+
return choice;
29+
},
30+
});

0 commit comments

Comments
 (0)