3.1 KiB
conditional-step
(no submodules)
Purpose
ConditionalStep is the branching primitive in @suaveplan/workflow. It
evaluates a GuardFn against the context and dispatches to one of two
Step instances (onTrue or onFalse). When the false branch is omitted
and the condition resolves falsy, the step is recorded as "skipped" —
giving callers a way to express "do this only if the predicate holds"
without filling in a no-op alternative.
This class is the workflow analogue of a ternary expression: it never
declares its own retry policy or compensation directly; both branches are
fully-fledged Step instances whose own configuration governs those
concerns. compensate rolls back only the branch that actually ran.
Features
onTrue/ optionalonFalsebranches.lastBranchaccessor reflecting which path executed.- Skipped branch records a
"skipped"StepResultand emits an OTel"skipped"counter increment (the duration histogram is intentionally skipped — seeotel/instruments). compensate(ctx)rolls back only the branch that ran.- Injectable
Clock.
Usage: Basic Example
import { createConditionalStep, createStep } from "@suaveplan/workflow";
const branch = createConditionalStep<{ premium: boolean }>({
id: "discount",
name: "Discount",
condition: (ctx) => ctx.premium,
onTrue: createStep({
id: "premium",
name: "Premium discount",
execute: () => ({ discountPct: 30 }),
}),
onFalse: createStep({
id: "standard",
name: "Standard discount",
execute: () => ({ discountPct: 5 }),
}),
});
const result = await branch.run({ premium: true });
Usage: Advanced Example
import { createConditionalStep, createStep } from "@suaveplan/workflow";
const ship = createStep<{ shipped: boolean }>({
id: "ship",
name: "Ship",
execute: async (ctx) => {
await dispatch(ctx);
},
compensate: async (ctx) => {
await recall(ctx);
},
});
const guard = createConditionalStep<{ ready: boolean }>({
id: "maybe-ship",
name: "Maybe ship",
condition: (ctx) => ctx.ready,
onTrue: ship,
});
await guard.run({ ready: true });
// Later, on outer-step rollback:
await guard.compensate({ ready: true });
API Reference
ConditionalStepOptions<TCtx>—{ id; name; condition; onTrue; onFalse?; clock?; }.ConditionalStep<TCtx>—trueBranch,falseBranch?,lastBranch?run(context): Promise<StepResult>compensate(context): Promise<void>
createConditionalStep<TCtx>(options): ConditionalStep<TCtx>.
Implementation Notes
lastBranch is set before the branch runs so callers that observe the
step (e.g. via hooks) can attribute downstream side-effects to the chosen
arm. compensate checks both the branch identity and the underlying
step's hasCompensation flag, so a guard whose onTrue step has no
compensation is a no-op rather than a thrown error.
recordStepExecuted(stepId, "skipped", 0) is emitted on the no-false-branch
fallthrough so the metric for skipped steps lines up with what
WorkflowEngine emits when a top-level Step.shouldExecute returns false.