Durable JavaScript workflows for .NET using Jint. Write long-running orchestration logic as async JavaScript, suspend execution at any await, serialize the state, and resume later — potentially days later, in a different process. Uses deterministic replay on top of Jint's public API, with zero engine modifications.
Influenced by Vercel's Workflow SDK, adapted for the .NET / Jint environment.
- 💾 Tiny, portable state — serialized state is a compact JSON journal of results; store it anywhere
- ⏳ Suspend for days — pause at any
await, persist, resume in a different process - 🔁 Deterministic replay —
Date.now(),new Date(),Math.random()overridden for stable replays - 🛡️ Journaled side effects — step functions execute once and replay from cache
- 🩹 Policy-based retries — attach Foundatio.Resilience policies for in-process retry, or throw
RetryableStepExceptionfor durable cross-restart retries - 🌐 Browser-compatible
fetch— opt-in WHATWGfetchwith.json(),.text(),.clone() - 📣 Named external events —
waitForEvent('name')single or multi-event, with timeout support - 🔀 Fan-out/fan-in —
Promise.allandPromise.raceover steps and suspends - ♾️ Continue As New — restart with a fresh journal to avoid unbounded growth
- 🛠️ Journal compatibility check — script drift fails fast with a clear diagnostic
dotnet add package Jint.Workflowsusing Jint.Workflows;
var workflow = new WorkflowEngine();
workflow.RegisterSuspendFunction("sleep", args => DurationParser.Parse(args[0]));
workflow.RegisterStepFunction("fetchOrder", args => orderService.GetOrder((string)args[0]!));
workflow.RegisterSuspendFunction("getApproval");
var script = """
async function processOrder(orderId) {
const order = await fetchOrder(orderId);
await sleep('3d');
const approved = await getApproval('manager', orderId);
return approved ? 'shipped' : 'cancelled';
}
""";
var result = workflow.RunWorkflow(script, "processOrder", "ORD-001");
// result.Status == Suspended, result.Suspension.ResumeAt ≈ 3 days from now
var json = result.State!.Serialize(); // persist anywhere
// ... 3 days later, in a different process
var resumed = workflow.ResumeWorkflow(script, json);👉 Getting Started Guide — step-by-step setup, step functions, suspend functions, and replay.
Want the latest CI build before it hits NuGet? Add the Feedz source (read-only public) and install the pre-release version:
dotnet nuget add source https://f.feedz.io/foundatio/foundatio/nuget -n foundatio-feedz
dotnet add package Jint.Workflows --prereleaseOr add to your NuGet.config:
<configuration>
<packageSources>
<add key="foundatio-feedz" value="https://f.feedz.io/foundatio/foundatio/nuget" />
</packageSources>
<packageSourceMapping>
<packageSource key="foundatio-feedz">
<package pattern="Foundatio.*" />
<package pattern="Jint.Workflows" />
</packageSource>
</packageSourceMapping>
</configuration>Contributions are welcome! See the documentation for how the engine works and what edits are safe across versions.
- Jint — the JavaScript interpreter this library is built on
- Vercel Workflow SDK — the primary source of inspiration for the replay-based durable workflow model
- Azure Durable Functions — canonical .NET implementation of the deterministic replay pattern
- Temporal / Restate — full workflow runtimes with the same replay model at a larger scope
Apache-2.0 License