Workflows

A workflow is an async generator function that yields steps. YieldStar compiles it into a WorkflowGenerator — the internal representation the runtime uses to execute, persist, and resume the workflow across process boundaries.

Defining a workflow

import { workflow } from "yieldstar";

const myWorkflow = workflow(async function* (step, event, logger) {
  const a = yield* step.run(() => 1);
  const b = yield* step.run(() => a + 1);
  return b;
});

The function receives three arguments:

ArgumentTypeDescription
stepStepRunnerExposes run, delay, and poll primitives
eventWorkflowEvent<Params, Context>Contains workflowId, executionId, params, and context
loggerLogger (Pino)Structured logger scoped to the execution

workflow and createWorkflow are aliases. Both return a WorkflowGenerator.

Type parameters

Workflows accept type parameters for input params and return type:

const processOrder = workflow<{ orderId: string }, boolean>(
  async function* (step, event) {
    const order = yield* step.run(() => fetchOrder(event.params.orderId));
    return yield* step.run(() => processPayment(order));
  }
);

When registered in a router, these types flow through to the SDK so workflowId, params, and return types are enforced at the call site.

Execution model

Each time the runtime invokes a workflow, the generator replays from the beginning. Completed steps return their cached result from the heap without re-executing the step function. When the generator reaches an uncached step, it executes the function, persists the result, and continues. When it reaches a delay or a retryable error, the generator yields control back to the runtime, which schedules a wake-up and terminates the process.

On the next invocation, the generator replays all cached steps (fast, no side effects), then resumes from where it left off.

const w = workflow(async function* (step) {
  // Execution 1: runs, caches result
  // Execution 2: replays from cache
  const a = yield* step.run(() => expensiveComputation());

  // Execution 1: pauses here, schedules wake-up
  // Execution 2: elapsed, continues
  yield* step.delay(5000);

  // Execution 2: runs, caches result
  const b = yield* step.run(() => useResult(a));

  return b;
});

Registering workflows

Workflows are registered in a router, which maps string IDs to workflow generators:

import { createWorkflowRouter } from "yieldstar";

const router = createWorkflowRouter({
  "process-order": processOrder,
  "send-reminder": sendReminder,
});

export type Router = typeof router;

Export the Router type to pass it to SDK factories. This gives you type-safe workflowId and params at the call site.