convex-effect-workflows
v0.1.0
Published
Effect-native Convex workflow engine with durable observability
Maintainers
Readme
convex-effect-workflows
Effect-native durable workflows for Convex with first-class observability tables.
Current capabilities
- Convex component schema + storage APIs for:
- executions
- ordered journal steps
- spans
- logs
- deferreds
- payload offload
- activity completion idempotency
- Dashboard query endpoints:
listExecutionsgetExecutionlistExecutionStepslistExecutionSpanslistExecutionLogs
- Client-side runtime utilities:
ConvexWorkflowEngine(start/poll/resume/interrupt + dashboard reads)makeConvexEncoded+makeWorkflowEngineforWorkflowEngine.EncodeddefineWorkflowRunnerfor workflow-style one-tick runner mutationsdefineActivityWorker+ registry (registerActivity,registerWorkflow)
Workflow-style usage (current)
import * as Effect from "effect/Effect";
import * as Schema from "effect/Schema";
import * as Workflow from "@effect/workflow/Workflow";
import * as Activity from "@effect/workflow/Activity";
import { components } from "./_generated/api";
import {
ConvexWorkflowEngine,
defineWorkflowRunner,
defineActivityWorker,
registerActivity,
functionHandleForRunner,
} from "convex-effect-workflows";
const ProcessOrder = Workflow.make({
name: "processOrder",
payload: Schema.Struct({ orderId: Schema.String }),
success: Schema.Struct({ status: Schema.String }),
error: Schema.String,
idempotencyKey: (p) => p.orderId,
});
const validateOrder = Activity.make({
name: "validateOrder",
success: Schema.Boolean,
execute: Effect.succeed(true),
});
registerActivity(validateOrder);
const activityWorker = defineActivityWorker();
const runner = defineWorkflowRunner(
components.effectWorkflows,
ProcessOrder,
(payload) =>
Effect.gen(function*() {
const ok = yield* validateOrder;
if (!ok) return yield* Effect.fail("invalid");
return { status: "processed" };
}),
{ activityWorker: "myModule:activityWorker" },
);
export const startOrder = mutation({
args: { orderId: v.string() },
handler: async (ctx, args) => {
const engine = new ConvexWorkflowEngine(components.effectWorkflows);
const runnerHandle = await functionHandleForRunner("myModule:runner");
return await engine.start(ctx, {
workflowName: ProcessOrder.name,
executionId: await Effect.runPromise(ProcessOrder.executionId({ orderId: args.orderId })),
payload: { orderId: args.orderId },
runner: runnerHandle,
});
},
});Example host app
A host app scaffold lives at example/convex/.
For real convex dev / convex deploy from this repo, use
example/convex-deploy/ (wired via repo-root convex.json).
This is the deployment model for standalone components: the host app mounts
convex-effect-workflows/convex.config when published, or ../../src/component/convex.config.js
for local repo testing.
Development
bun install
bun run typecheck
bun run build
bun run test
convex codegen --component-dir ./src/componentrequires a configured Convex deployment (CONVEX_DEPLOYMENT).
