Declare the bad timeline
Duplicate IDs, delayed retries, and concurrent groups are readable data—not bespoke sleep-and-curl scripts.
Local proof harness / npm
Rehearse duplicate, delayed, and out-of-order webhooks before they charge, email, or provision twice.
Zero runtime dependencies · No real provider calls · No telemetry
01Declare a sequence
→02Swap safe adapters
→03Assert one effect
Interactive trace
This browser demo mirrors the library’s report model. Choose a handler, then inspect every delivery and business effect.
key: order_demo_042
queued
key: order_demo_042
+250 ms
No effects recorded yet.
Start the rehearsal to populate this trace.Five-minute setup
Use the in-process recorder for unit tests or point the CLI at a running local handler. Both produce the same secret-safe report.
import { defineScenario, runScenario, assertIdempotent }
from 'idempotency-rehearsal';
const report = await runScenario({
scenario: defineScenario({
name: 'duplicate after retry',
deliveries: [original, { ...duplicate, afterMs: 250 }],
expect: [{ adapter: 'payment', action: 'capture', idempotencyKey: 'order_demo_042', count: 1 }],
}),
handler: handleWebhook,
});
assertIdempotent(report); // exits loudly on the second effect
The safety contract
The harness exercises timing and ordering without turning your test into a payment or email cannon.
Duplicate IDs, delayed retries, and concurrent groups are readable data—not bespoke sleep-and-curl scripts.
Payment and email adapters record intent only. HTTP collection is loopback-only and test IDs are validated.
Exact expectations and stable JSON output make the scenario a regression test your CI can keep.
CLI mode
The runner posts your declared sequence and opens a temporary loopback collector for test-only effect adapters.
$ npx idempotency-rehearsal run duplicate.json \
--target http://127.0.0.1:3000/webhooks/orders
PASS delayed duplicate
2 deliveries · 1 effect · 264ms
✓ one logical effect per idempotency key
Before the next retry