Skip to content

withDryRun

Skips all step bodies while still firing beforeStep and afterStep hooks. Use in tests and CI to validate hook wiring and observability pipelines without executing real side effects.

Setup

typescript
import { FlowBuilder } from "flowneer";
import { withDryRun } from "flowneer/plugins/dev";

const AppFlow = FlowBuilder.extend([withDryRun]);

Usage

typescript
const events: string[] = [];

const flow = new AppFlow<State>()
  .withDryRun()
  .withCallbacks({
    onChainStart: (meta) => events.push(`start:${meta.index}`),
    onChainEnd: (meta) => events.push(`end:${meta.index}`),
  })
  .startWith(async (s) => {
    await callExternalApi(s); /* never runs */
  })
  .then(async (s) => {
    await saveToDb(s); /* never runs */
  });

await flow.run({});
console.log(events);
// ["start:0", "end:0", "start:1", "end:1"]
// Step bodies didn't run, but hooks fired correctly

How It Works

Registers a wrapStep hook that does nothing — next() is never called, so the step body is skipped. Because hooks registered before withDryRun run in their own wrapStep layers, those hooks still fire.

Use Cases

  • Validate that your observability and callback wiring is correct without real I/O.
  • Benchmark hook overhead in isolation.
  • Snapshot-test step sequences in unit tests.

Notes

  • withDryRun affects all steps, including those inside .loop(), .batch(), and .parallel() bodies.
  • State remains unchanged (since step bodies don't run), so hooks that read from shared will see the initial values.
  • Combine with withMocks if you want some steps to execute with fake implementations.