confect-workflow
v0.0.0-alpha.3
Published
`confect-workflow` integrates `@convex-dev/workflow` with Confect and Effect.
Readme
confect-workflow
confect-workflow integrates @convex-dev/workflow with Confect and Effect.
It keeps workflows inside the normal Confect spec/impl pipeline:
- define workflows with Effect handlers
- use
effect/Schemafor workflow args and returns - register workflows as normal Confect internal mutations
- start and manage workflows from normal Confect functions
Requirements
@convex-dev/workflowinstalled in your Convex app- a Confect project using the usual spec/impl structure
- workflow args and returns defined with
effect/Schema
Enable the Convex workflow component in convex/convex.config.ts:
import workflow from "@convex-dev/workflow/convex.config.js";
import { defineApp } from "convex/server";
const app = defineApp();
app.use(workflow);
export default app;Entry Points
The package mirrors Confect's split between spec and implementation code.
confect-workflow/spec
Use this only from *.spec.ts files.
- depends on spec-side Confect concepts
- keeps workflow registration in the normal
GroupSpecflow - does not expose runtime services
Exports:
workflowSpec({ name, args, returns })WorkflowFunctionSpectypeWorkflowMutationtype
confect-workflow/server
Use this from workflow definitions and *.impl.ts files.
- defines workflows
- exposes runtime services for workflow handlers
- exposes workflow manager services for query and mutation call sites
Exports:
defineWorkflow(...)WorkflowContextWorkflowManagerRequiresMutationWorkflowManagerRequiresQuerymakeWorkflowManagerLayers(...)
Usage
1. Define the workflow
import { Effect, Schema } from "effect";
import { api, components } from "../convex/_generated/api";
import { defineWorkflow, WorkflowContext } from "confect-workflow/server";
import { generateTaggedNote } from "./workflows.spec";
export const generateTaggedNoteWorkflow = defineWorkflow(components.workflow, generateTaggedNote, {
handler: ({ text }) =>
Effect.gen(function* () {
const ctx = yield* WorkflowContext;
yield* ctx.runMutation(api.notesAndRandom.notes.insert, { text });
yield* ctx.awaitEvent({ name: "approval" });
return null;
}),
});defineWorkflow(...) creates the upstream workflow mutation from a workflow spec and preserves schema-aware behavior at the wrapper boundary.
2. Add it to your Confect spec
import { GroupSpec } from "@confect/core";
import { Schema } from "effect";
import { workflowSpec } from "confect-workflow/spec";
export const generateTaggedNote = workflowSpec({
name: "generateTaggedNote",
args: Schema.Struct({ text: Schema.String }),
returns: Schema.Null,
});
export const workflows = GroupSpec.make("workflows").addFunction(generateTaggedNote);workflowSpec(...) is the spec-side source of truth. It keeps workflow registration aligned with normal Confect spec composition without importing runtime workflow code into the client-visible spec.
3. Register it and start it from server code
import { FunctionImpl } from "@confect/server";
import { Effect } from "effect";
import { components } from "../convex/_generated/api";
import api from "./_generated/api";
import refs from "./_generated/refs";
import {
makeWorkflowManagerLayers,
WorkflowManagerRequiresMutation,
} from "confect-workflow/server";
import { generateTaggedNoteWorkflow } from "./workflows";
const workflowManagerLayers = makeWorkflowManagerLayers(components.workflow);
export const generateTaggedNoteImpl = FunctionImpl.make(
api,
"workflows",
"generateTaggedNote",
generateTaggedNoteWorkflow,
);
export const startGenerateTaggedNote = FunctionImpl.make(
api,
"workflows",
"startGenerateTaggedNote",
({ text }) =>
Effect.gen(function* () {
const workflowManager = yield* WorkflowManagerRequiresMutation;
return yield* workflowManager.start(refs.internal.workflows.generateTaggedNote, { text });
}).pipe(Effect.provide(workflowManagerLayers.mutationLayer), Effect.orDie),
);Runtime Services
WorkflowContext is available inside workflow handlers and exposes:
workflowIdrunQueryrunMutationrunActionrunWorkflowawaitEvent
WorkflowManagerRequiresMutation exposes:
startrestartcleanupsendEventcreateEvent
WorkflowManagerRequiresQuery exposes:
statuslistlistByNamelistSteps
The wrapper encodes and decodes workflow args and returns with Effect Schema at the workflow boundary.
Structure
Recommended layout:
confect/workflows.spec.tsforworkflowSpec(...)confect/workflows.tsfor workflow definitions that consume the specconfect/workflows.impl.tsforFunctionImpl.make(...)and workflow manager usage
This is the same pattern used in the example app:
- apps/example/confect/workflows.ts
- apps/example/confect/workflows.spec.ts
- apps/example/confect/workflows.impl.ts
Development
vp install
vp test
vp pack