UNPKG

@convex-dev/workflow

Version:

Convex component for durably executing workflows.

484 lines 20.9 kB
import type { WorkpoolOptions, WorkpoolRetryOptions } from "@convex-dev/workpool"; import { type FunctionArgs, type FunctionReference, type FunctionVisibility, type GenericActionCtx, type GenericDataModel, type GenericMutationCtx, type GenericQueryCtx, type PaginationOptions, type PaginationResult, type RegisteredMutation, type ReturnValueForOptionalValidator } from "convex/server"; import type { ObjectType, PropertyValidators, Validator } from "convex/values"; import type { Step } from "../component/schema.js"; import type { EventId, OnCompleteArgs, PublicWorkflow, WorkflowId, WorkflowStep } from "../types.js"; import type { IdsToStrings, WorkflowComponent } from "./types.js"; export type { WorkflowComponent } from "./types.js"; import type { WorkflowCtx } from "./workflowContext.js"; import { type WorkflowArgs } from "./workflowMutation.js"; export { vEventId, vWorkflowId, vWorkflowStep, type EventId, type WorkflowId, type WorkflowStep, } from "../types.js"; export type { RunOptions, WorkflowCtx } from "./workflowContext.js"; export type { WorkflowArgs } from "./workflowMutation.js"; export { vResultValidator } from "@convex-dev/workpool"; export type CallbackOptions<Context = unknown> = { /** * A mutation to run after the workflow succeeds, fails, or is canceled. * The context type is for your use, feel free to provide a validator for it. * * If you don't need `context`, you can set the validator to optional * with `v.optional(v.any())` and pass `context: undefined`. * * ```ts * export const completion = internalMutation({ * args: { * workflowId: vWorkflowId, * result: vResultValidator, * context: v.optional(v.any()), * }, * handler: async (ctx, args) => { * console.log(args.result, "Got Context back -> ", args.context); * }, * }); * ``` */ onComplete: FunctionReference<"mutation", FunctionVisibility, OnCompleteArgs<Context>>; /** * A context object to pass to the `onComplete` mutation. * Useful for passing data from the enqueue site to the onComplete site. */ context: Context; } | { onComplete?: undefined; context?: undefined; }; export type WorkflowDefinition<ArgsValidator extends PropertyValidators, ReturnsValidator extends Validator<any, "required", any> | void = any> = { args?: ArgsValidator; returns?: ReturnsValidator; workpoolOptions?: WorkpoolRetryOptions; }; export type WorkflowHandler<ArgsValidator extends PropertyValidators, ReturnsValidator extends Validator<any, "required", any> | void> = (step: WorkflowCtx, args: ObjectType<ArgsValidator>) => Promise<ReturnValueForOptionalValidator<ReturnsValidator>>; export type WorkflowStatus = { type: "inProgress"; running: IdsToStrings<Step>[]; } | { type: "completed"; result: unknown; } | { type: "canceled"; } | { type: "failed"; error: string; }; /** * Define a new workflow with typed args and optional return validator. * * @example * ```ts * export const myWorkflow = defineWorkflow(components.workflow, { * args: { amount: v.number() }, * returns: v.object({ total: v.number() }), * }).handler(async (step, args) => { * ...workflow implementation * }); * ``` * * Start the workflow from a mutation or action: * ```ts * const workflowId = await start(ctx, internal.myFile.myWorkflow, { amount: 42 }); * ``` * Or call it directly: * ```ts * const workflowId = await ctx.runMutation(internal.myFile.myWorkflow, { args: { ...myArgs } }); * ``` */ export declare function defineWorkflow<AV extends PropertyValidators, RV extends Validator<any, "required", any> | void = void>(component: WorkflowComponent, config: WorkflowDefinition<AV, RV>): { /** * Define the workflow handler function. * Returns a registered mutation to export from your Convex module. */ handler(fn: (step: WorkflowCtx, args: ObjectType<AV>) => Promise<ReturnValueForOptionalValidator<RV>>): RegisteredMutation<"internal", WorkflowArgs<AV>, WorkflowId>; }; type StartOptions<Context = unknown> = CallbackOptions<Context> & { /** * By default, during creation the workflow will be initiated immediately. * With `startAsync` set to true, the workflow will be created but will * start asynchronously via the internal workpool. * @default false */ startAsync?: boolean; }; /** * Start a workflow * * It will run asynchronously, returning a workflow ID to monitor the progress. * * By default it will start running the handler as part of "start" unless * `startAsync` is set to true. * * ```ts * const id = await start(ctx, internal.myFile.myWorkflow, { ...args }, { * onComplete: internal.myFile.handleComplete, * context: { ...passed through to onComplete }, * }); * ``` * * @param ctx - The Convex mutation or action context. * @param workflow - The workflow to start (e.g. `internal.myFile.myWorkflow`). * @param args - The workflow arguments. * @param options - Options like `onComplete`, `context`, `startAsync`. * @returns The workflow ID. */ export declare function start<Context = unknown, F extends FunctionReference<"mutation", "internal"> = FunctionReference<"mutation", "internal">>(ctx: MutationCtx | ActionCtx, workflow: F, args: FunctionArgs<F>["args"], options?: StartOptions<Context>): Promise<WorkflowId>; /** * Get a workflow's status. * * @param ctx - The Convex context. * @param component - The workflow component. * @param workflowId - The workflow ID. * @returns The workflow status. */ export declare function getStatus(ctx: QueryCtx | MutationCtx | ActionCtx, component: WorkflowComponent, workflowId: WorkflowId): Promise<WorkflowStatus>; /** * Cancel a running workflow. * * @param ctx - The Convex context. * @param component - The workflow component. * @param workflowId - The workflow ID. */ export declare function cancel(ctx: MutationCtx | ActionCtx, component: WorkflowComponent, workflowId: WorkflowId): Promise<void>; /** * Restart a previously-failed workflow. * * By default it will retry the handler using the existing history of steps. * To restart from the beginning, pass `{from: 0}`. * To restart from a named step or event: `{from: "myName"}`. * To restart from a function call: `{from: internal.foo.bar}`. * * If the function or name were called multiple times, it will restart from * the last invocation. * * @param ctx - The Convex context. * @param component - The workflow component. * @param workflowId - The workflow ID. * @param options - Options for the retry. * @param options.from - The step to retry from. Can be a step number, * a step name, or the function / workflow `internal.foo.bar`. * Steps from this point onwards will be deleted before restarting. * If not provided, the handler will be re-executed using the existing * history of steps. * @param options.startAsync - If true, the workflow will be enqueued * via the workpool instead of running immediately. */ export declare function restart(ctx: MutationCtx | ActionCtx, component: WorkflowComponent, workflowId: WorkflowId, options?: { from?: number | string | FunctionReference<any, any>; startAsync?: boolean; }): Promise<void>; /** * Send an event to a workflow. * * @param ctx - From a mutation, action or workflow step. * @param component - The workflow component. * @param args - Either send an event by its ID, or by name and workflow ID. * If you have a validator, you must provide a value. * If you provide an error string, awaiting the event will throw an error. */ export declare function sendEvent<T = null, Name extends string = string>(ctx: MutationCtx | ActionCtx, component: WorkflowComponent, args: ({ workflowId: WorkflowId; name: Name; id?: EventId<Name>; } | { workflowId?: undefined; name?: Name; id: EventId<Name>; }) & ({ validator?: undefined; value?: T; } | { validator: Validator<T, any, any>; value: T; } | { error: string; value?: undefined; })): Promise<EventId<Name>>; /** * Create an event ahead of time, enabling awaiting a specific event by ID. * @param ctx - From an action, mutation or workflow step. * @param component - The workflow component. * @param args - The name of the event and what workflow it belongs to. * @returns The event ID, which can be used to send the event or await it. */ export declare function createEvent<Name extends string>(ctx: MutationCtx | ActionCtx, component: WorkflowComponent, args: { name: Name; workflowId: WorkflowId; }): Promise<EventId<Name>>; /** * List workflows, including their name, args, return value etc. * * @param ctx - The Convex context from a query, mutation, or action. * @param component - The workflow component. * @param opts - How many workflows to fetch and in what order. * e.g. `{ order: "desc", paginationOpts: { cursor: null, numItems: 10 } }` * will get the last 10 workflows in descending order. * Defaults to 100 workflows in ascending order. * @returns The pagination result with per-workflow data. */ export declare function list(ctx: QueryCtx | MutationCtx | ActionCtx, component: WorkflowComponent, opts?: { order?: "asc" | "desc"; paginationOpts?: PaginationOptions; }): Promise<PaginationResult<PublicWorkflow>>; /** * List workflows matching a specific name, including their args, return value etc. * * @param ctx - The Convex context from a query, mutation, or action. * @param component - The workflow component. * @param name - The workflow name to filter by. * @param opts - How many workflows to fetch and in what order. * e.g. `{ order: "desc", paginationOpts: { cursor: null, numItems: 10 } }` * will get the last 10 workflows in descending order. * Defaults to 100 workflows in ascending order. * @returns The pagination result with per-workflow data. */ export declare function listByName(ctx: QueryCtx | MutationCtx | ActionCtx, component: WorkflowComponent, name: string, opts?: { order?: "asc" | "desc"; paginationOpts?: PaginationOptions; }): Promise<PaginationResult<PublicWorkflow>>; /** * List the steps in a workflow, including their name, args, return value etc. * * @param ctx - The Convex context from a query, mutation, or action. * @param component - The workflow component. * @param workflowId - The workflow ID. * @param opts - How many steps to fetch and in what order. * e.g. `{ order: "desc", paginationOpts: { cursor: null, numItems: 10 } }` * will get the last 10 steps in descending order. * Defaults to 100 steps in ascending order. * @returns The pagination result with per-step data. */ export declare function listSteps(ctx: QueryCtx | MutationCtx | ActionCtx, component: WorkflowComponent, workflowId: WorkflowId, opts?: { order?: "asc" | "desc"; paginationOpts?: PaginationOptions; }): Promise<PaginationResult<WorkflowStep>>; /** * Clean up a completed workflow's storage. * * @param ctx - The Convex context. * @param component - The workflow component. * @param workflowId - The workflow ID. * @returns - Whether the workflow's state was cleaned up. */ export declare function cleanup(ctx: MutationCtx | ActionCtx, component: WorkflowComponent, workflowId: WorkflowId): Promise<boolean>; export declare class WorkflowManager { component: WorkflowComponent; options?: { workpoolOptions: WorkpoolOptions; } | undefined; constructor(component: WorkflowComponent, options?: { workpoolOptions: WorkpoolOptions; } | undefined); /** * Define a new workflow. * * Start the workflow from a mutation or action: * ```ts * const workflowId = await start(ctx, internal.myFile.myWorkflow, { ...myArgs }); * ``` * Or call it directly: * ```ts * const workflowId = await ctx.runMutation(internal.myFile.myWorkflow, { args: { ...myArgs } }); * ``` * * @param workflow - The workflow definition. * @returns The workflow mutation. */ define<ArgsValidator extends PropertyValidators, ReturnsValidator extends Validator<unknown, "required", string> | void>(workflow: WorkflowDefinition<ArgsValidator, ReturnsValidator> & { handler: WorkflowHandler<ArgsValidator, ReturnsValidator>; }): RegisteredMutation<"internal", WorkflowArgs<ArgsValidator>, WorkflowId>; define<ArgsValidator extends PropertyValidators, ReturnsValidator extends Validator<unknown, "required", string> | void>(workflow: WorkflowDefinition<ArgsValidator, ReturnsValidator>): { /** * Define the workflow handler function. * Returns a registered mutation to export from your Convex module. */ handler(fn: (step: WorkflowCtx, args: ObjectType<ArgsValidator>) => Promise<ReturnValueForOptionalValidator<ReturnsValidator>>): RegisteredMutation<"internal", WorkflowArgs<ArgsValidator>, WorkflowId>; }; /** * Start a workflow. * * Alternative to `start` (`import { start } from "@convex-dev/workflow"`). * * This is slightly more efficient than calling `start` when passing * `startAsync: true`, and slightly less efficient in the default case. * * @param ctx - The Convex context. * @param workflow - The workflow to start (e.g. `internal.index.exampleWorkflow`). * @param args - The workflow arguments. * @returns The workflow ID. */ start<Context = unknown, F extends FunctionReference<"mutation", "internal"> = FunctionReference<"mutation", "internal">>(ctx: MutationCtx | ActionCtx, workflow: F, args: FunctionArgs<F>["args"], options?: CallbackOptions<Context> & { /** * By default, during creation the workflow will be initiated immediately. * The benefit is that you catch errors earlier (e.g. passing a bad * workflow reference or catch arg validation). * * With `startAsync` set to true, the workflow will be created but will * start asynchronously via the internal workpool. * You can use this to queue up a lot of work, * or make `start` return faster (you still get a workflowId back). * @default false */ startAsync?: boolean; }): Promise<WorkflowId>; /** * Get a workflow's status. * * @param ctx - The Convex context. * @param workflowId - The workflow ID. * @returns The workflow status. */ status(ctx: QueryCtx | MutationCtx | ActionCtx, workflowId: WorkflowId): Promise<WorkflowStatus>; /** * Restart a previously-failed workflow. * * By default it will retry the handler using the existing history of steps. * To restart from the beginning, pass `{from: 0}`. * To restart from a named step or event: `{from: "myName"}`. * To restart from a function call: `{from: internal.foo.bar}`. * * If the function or name were called multiple times, it will restart from * the last invocation. * * @param ctx - The Convex context. * @param workflowId - The workflow ID. * @param options - Options for the retry. * @param options.from - The step to retry from. Can be a step number, * a step name, or the function / workflow `internal.foo.bar`. * Steps from this point onwards will be deleted before restarting. * If not provided, the handler will be re-executed using the existing * history of steps. * @param options.startAsync - If true, the workflow will be enqueued * via the workpool instead of running immediately. */ restart(ctx: MutationCtx | ActionCtx, workflowId: WorkflowId, options?: { from?: number | string | FunctionReference<any, any>; startAsync?: boolean; }): Promise<void>; /** * Cancel a running workflow. * * @param ctx - The Convex context. * @param workflowId - The workflow ID. */ cancel(ctx: MutationCtx | ActionCtx, workflowId: WorkflowId): Promise<void>; /** * List workflows, including their name, args, return value etc. * * @param ctx - The Convex context from a query, mutation, or action. * @param opts - How many workflows to fetch and in what order. * e.g. `{ order: "desc", paginationOpts: { cursor: null, numItems: 10 } }` * will get the last 10 workflows in descending order. * Defaults to 100 workflows in ascending order. * @returns The pagination result with per-workflow data. */ list(ctx: QueryCtx | MutationCtx | ActionCtx, opts?: { order?: "asc" | "desc"; paginationOpts?: PaginationOptions; }): Promise<PaginationResult<PublicWorkflow>>; /** * List workflows matching a specific name, including their args, return value etc. * * @param ctx - The Convex context from a query, mutation, or action. * @param name - The workflow name to filter by. * @param opts - How many workflows to fetch and in what order. * e.g. `{ order: "desc", paginationOpts: { cursor: null, numItems: 10 } }` * will get the last 10 workflows in descending order. * Defaults to 100 workflows in ascending order. * @returns The pagination result with per-workflow data. */ listByName(ctx: QueryCtx | MutationCtx | ActionCtx, name: string, opts?: { order?: "asc" | "desc"; paginationOpts?: PaginationOptions; }): Promise<PaginationResult<PublicWorkflow>>; /** * List the steps in a workflow, including their name, args, return value etc. * * @param ctx - The Convex context from a query, mutation, or action. * @param workflowId - The workflow ID. * @param opts - How many steps to fetch and in what order. * e.g. `{ order: "desc", paginationOpts: { cursor: null, numItems: 10 } }` * will get the last 10 steps in descending order. * Defaults to 100 steps in ascending order. * @returns The pagination result with per-step data. */ listSteps(ctx: QueryCtx | MutationCtx | ActionCtx, workflowId: WorkflowId, opts?: { order?: "asc" | "desc"; paginationOpts?: PaginationOptions; }): Promise<PaginationResult<WorkflowStep>>; /** * Clean up a completed workflow's storage. * * @param ctx - The Convex context. * @param workflowId - The workflow ID. * @returns - Whether the workflow's state was cleaned up. */ cleanup(ctx: MutationCtx | ActionCtx, workflowId: WorkflowId): Promise<boolean>; /** * Send an event to a workflow. * * @param ctx - From a mutation, action or workflow step. * @param args - Either send an event by its ID, or by name and workflow ID. * If you have a validator, you must provide a value. * If you provide an error string, awaiting the event will throw an error. */ sendEvent<T = null, Name extends string = string>(ctx: MutationCtx | ActionCtx, args: ({ workflowId: WorkflowId; name: Name; id?: EventId<Name>; } | { workflowId?: undefined; name?: Name; id: EventId<Name>; }) & ({ validator?: undefined; value?: T; } | { validator: Validator<T, any, any>; value: T; } | { error: string; value?: undefined; })): Promise<EventId<Name>>; /** * Create an event ahead of time, enabling awaiting a specific event by ID. * @param ctx - From an action, mutation or workflow step. * @param args - The name of the event and what workflow it belongs to. * @returns The event ID, which can be used to send the event or await it. */ createEvent<Name extends string>(ctx: MutationCtx | ActionCtx, args: { name: Name; workflowId: WorkflowId; }): Promise<EventId<Name>>; } /** * Define an event specification: a name and a validator. * This helps share definitions between workflow.sendEvent and ctx.awaitEvent. * e.g. * ```ts * const approvalEvent = defineEvent({ * name: "approval", * validator: v.object({ approved: v.boolean() }), * }); * ``` * Then you can await it in a workflow: * ```ts * const result = await ctx.awaitEvent(approvalEvent); * ``` * And send from somewhere else: * ```ts * await workflow.sendEvent(ctx, { * ...approvalEvent, * workflowId, * value: { approved: true }, * }); * ``` */ export declare function defineEvent<Name extends string, V extends Validator<unknown, "required", string>>(spec: { name: Name; validator: V; }): { name: Name; validator: V; }; type QueryCtx = Pick<GenericQueryCtx<GenericDataModel>, "runQuery">; type MutationCtx = Pick<GenericMutationCtx<GenericDataModel>, "runQuery" | "runMutation">; type ActionCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation" | "runAction">; //# sourceMappingURL=index.d.ts.map