Derive
Contract-derived flow generation. Ships through @noocodex/dagonizer/derive.
import { DAGDeriver } from '@noocodex/dagonizer/derive';
import type {
DAGDeriverAnnotations,
DAGDeriverFanOut,
DAGDeriverTerminal,
DAGDeriverOptions,
OperationContract,
} from '@noocodex/dagonizer/derive';DAGDeriver
Static class.
class DAGDeriver {
static derive(opts: DAGDeriverOptions): DAG;
static edges(contracts: readonly OperationContract[]): ReadonlyMap<string, ReadonlySet<string>>;
static depthBuckets(
contracts: readonly OperationContract[],
edges: ReadonlyMap<string, ReadonlySet<string>>,
): readonly (readonly string[])[];
}derive(opts)
Build a DAG from a contract registry plus declared annotations.
interface DAGDeriverOptions {
readonly name: string;
readonly version: string;
readonly entrypoint: string;
readonly contracts: readonly OperationContract[];
readonly annotations?: DAGDeriverAnnotations;
}Operations sharing a topological depth auto-group into a ParallelNode with combine: 'collect'; use annotations.parallels to override the grouping or pick a different combine strategy. Each port in contract.outputs routes to the first successor at the next depth; annotations.terminals overrides individual ports. When annotations.fanouts.<name>.strategy === 'custom', the referenced fanInOperation is emitted as a registered single-node placement alongside the fan-out so the dispatcher's custom fan-in reducer can resolve it.
Throws DAGError when contracts is empty, when a terminal references a port outside the contract's outputs, when a partition outcome isn't in outcomes, when a parallel member appears in multiple groups, or when an operation appears in more than one of fanouts / subDAGs / parallels.
edges(contracts)
Adjacency map. An entry A → B exists iff some path in A.produces appears in B.hardRequired. Useful for tooling that wants to inspect the data graph before deriving a DAG.
depthBuckets(contracts, edges)
Topological depth buckets. Operations sharing a depth share a bucket. Same data the renderer uses to decide which placements to wrap in a parallel.
DAGDeriverAnnotations
interface DAGDeriverAnnotations {
readonly terminals?: Readonly<Record<string, readonly DAGDeriverTerminal[]>>;
readonly fanouts?: Readonly<Record<string, DAGDeriverFanOut>>;
readonly subDAGs?: Readonly<Record<string, DAGDeriverSubDAG>>;
readonly parallels?: Readonly<Record<string, DAGDeriverParallel>>;
}
interface DAGDeriverTerminal {
readonly outcome: string;
readonly target: string | null;
}
// Fan-out is a discriminated union over the fan-in strategy.
type DAGDeriverFanOut = {
readonly source: string;
readonly itemKey: string;
readonly node: string;
readonly concurrency?: number;
readonly outcomes: readonly string[];
} & (
| { readonly strategy: 'custom'; readonly fanInOperation: string }
| { readonly strategy: 'partition'; readonly partitions: Readonly<Record<string, string>> }
| { readonly strategy: 'append'; readonly target: string }
);
interface DAGDeriverSubDAG {
readonly dag: string;
readonly stateMapping?: {
readonly input?: Readonly<Record<string, string>>;
readonly output?: Readonly<Record<string, string>>;
};
readonly outputs: readonly string[];
}
interface DAGDeriverParallel {
readonly members: readonly string[];
readonly combine: 'all-success' | 'any-success' | 'collect';
}terminals— per-operation alternate exits (route tonullto terminate, or to a named operation).fanouts— per-operation fan-out wrapping.sourceis the dotted state-array path;itemKeyis the metadata key the worker reads;nodeis the per-item registered node;strategydiscriminates which fan-in shape gets emitted (custom+fanInOperation,partition+partitions, orappend+target);outcomeslists the fan-out outcome names. Partition keys must appear inoutcomes— out-of-band keys throwDAGErrorat derive time.subDAGs— per-operation sub-DAG composition. Swaps the rendered placement fromSingleNodetoDeepDAGNodewhile preserving the contract's role in topology derivation.dagis the registered child DAG name;outputsis the port set the deep-DAG can route on (auto-wired to the next derived stage, withterminalsoverriding);stateMappingis forwarded verbatim to the rendered placement.parallels— explicitParallelNodegrouping with chosen combine strategy. Without it, same-topological-depth operations auto-group withcombine: 'collect'. With it, the named group forces members into oneParallelNodewith the consumer's chosen combine. Membership is exclusive across groups.- An operation cannot appear in more than one of
fanouts/subDAGs/parallels— placement kind must be unambiguous.
OperationContract
interface OperationContract {
readonly name: string;
readonly hardRequired: readonly string[];
readonly produces: readonly string[];
readonly outputs: readonly string[];
}Defined in @noocodex/dagonizer/contracts; re-exported from @noocodex/dagonizer/derive for convenience.
outputs declares every port the node can emit. DAGDeriver auto-wires each port to the next derived stage; DAGDeriverAnnotations.terminals[name] overrides individual ports per-operation. Terminals declaring a port not in the contract's outputs throw DAGError at derive time.
Related guides
- Contract-derived flows
- Authoring DAGs — when to use DAGDeriver vs DAGBuilder vs raw DAG literals
- DAGBuilder — imperative authoring for deterministic / ETL workflows
- Visualization