Skip to content

effect-temporalDurable Effect workflows on Temporal.

Author workflows with Effect — schemas, typed errors, composition — and run them on Temporal's retries, timers, signals, and history.

The whole idea, in one file ​

ts
import { Effect, Exit, Schema } from "effect";
import * as Workflow from "effect/workflow/Workflow";
import { defineActivity, defineDeferred, sleep } from "@springbird/effect-temporal/definition";
import { workflowBundle } from "@springbird/effect-temporal/bundle";
import { WorkflowClient } from "@springbird/effect-temporal/client";

// 1. Declare once: shared by the workflow bundle, the worker, and every client.
const OrderFlow = Workflow.make("orderFlow", {
  payload: { orderId: Schema.String },
  idempotencyKey: ({ orderId }) => orderId,
  success: Schema.String,
});
const Charge = defineActivity("charge", {
  payload: { orderId: Schema.String },
  success: Schema.String,
});
const ManagerApproval = defineDeferred("manager-approval", {
  success: Schema.String,
});

// 2. Author the body — call the declarations directly. The handler needs
//    only WorkflowOps: workflowBundle provides Temporal's; the testing
//    module provides an in-memory one for plain unit tests.
const OrderFlowLive = OrderFlow.toLayer((payload) =>
  Effect.gen(function* () {
    const paid = yield* Charge({ orderId: payload.orderId });
    yield* sleep({ name: "cooling-off", duration: "3 days" });
    const approver = yield* ManagerApproval.await;
    return `${paid}:approved-by:${approver}`;
  }),
);
export default workflowBundle(OrderFlowLive);

// 3. Drive it from ordinary Node — typed success, typed failure, idempotent;
//    client-side ops take the declaration itself.
const wf = yield* WorkflowClient;
yield* wf.completeDeferred(ManagerApproval, workflowId, Exit.succeed("ben"));
const result = yield* wf.execute(OrderFlow, { orderId: "ord_123" });
sh
pnpm add @springbird/effect-temporal   # or npm / yarn / bun