Skip to content

Dagonizer

Orchestrate work as a DAG of typed nodes.

A TypeScript framework for directed-acyclic flows with a state machine lifecycle. Compose. Observe. Resume. No external runtime required.

Dagonizer

Type-Safe Nodes

Output types narrow the routing map at compile time. An unwired output is a TypeScript error before registerDAG confirms it at runtime.

Abortable Execution

Pass a caller-controlled AbortSignal or a deadlineMs hard limit. The dispatcher composes them and propagates cancellation through every in-flight operation and deep-DAG nesting level.

Deterministic Resume

Snapshot a paused DAG at its cursor. Serialize to JSON, store anywhere, restore and resume with a new Execution that picks up exactly where it left off.

Deep-DAG Composition

Invoke a registered DAG as a nested node. State maps in before the child runs and out after it returns. Errors and warnings bubble up automatically.

Parallel & Fan-Out

Run independent nodes concurrently with parallel groups. Apply one node to every item in a collection with fan-out and configurable concurrency.

Retry Policies

RetryPolicy provides constant, linear, exponential, and decorrelated-jitter strategies. Filter by error type; cooperates with the abort signal so retries stop immediately on cancellation.

JSON-LD Canonical Wire Format

DAG definitions are validated against DAGSchema (Ajv 2020-12) at the ingest boundary. Dagonizer.load is the single entry point for external JSON — everything inside is fully typed.

Observability Hooks

Subclass Dagonizer and override onFlowStart, onFlowEnd, onNodeStart, onNodeEnd, and onError for structured metrics, tracing, and audit trails.

Deterministic Testing

VirtualClockProvider and VirtualScheduler replace platform timers in tests. Step through retry delays and deadlines with scheduler.advance(ms).

Contract-derived Flows

Declare what each operation `produces` and `hardRequired`s; DAGDeriver builds the topology automatically by matching the data graph. Multi-port routing and sub-DAG composition via annotations — adding an operation is one contract, the flow rewires itself.

⦿ What it is

A node is a typed, stateless unit of work that receives shared state and a context (including an AbortSignal) and returns a named output. The dispatcher routes on that output to the next node. Four placement kinds cover the full composition space:

KindWhat it does
singleOne node; output name selects the next vertex
parallelMultiple independent nodes run concurrently; combine strategy reduces to one route
fan-outOne node per item in a state array; fan-in strategy merges results
deep-dagA registered DAG invoked as a nested call; state mapped in and out

⦿ FSM-driven lifecycle

Every execution runs through DAGLifecycleMachine: pending → running → completed | failed | cancelled | timed_out. Terminal states are sticky. Every transition is timestamped with monotonic milliseconds. The lifecycle state travels on NodeStateInterface through every node in the graph.

pending ──start──▶ running ──succeed──▶ completed

                      ├──fail(error)──▶ failed
                      ├──cancel(reason)▶ cancelled
                      └──timeout──────▶ timed_out

⦿ No external runtime

Dagonizer runs in-process. No worker pool, no external state store, no IPC. DAG definitions are plain JSON objects — store them in files, databases, or configuration services and load them at runtime via Dagonizer.load. The framework is browser-runnable: no Node.js-only primitives in the core engine.

⦿ See it in action

The Archivist is an end-to-end in-browser demo built entirely on Dagonizer — a bibliographic-assistant pipeline that exercises linear intake, fan-out, deep-DAG composition, cancellation, retry, checkpoint, and visualization in a single runnable flow.

Watched over by the Order of Dagon.