# `graph/graph.ts` _(no submodules)_ ## Purpose Implements the graph utilities that operate on a `WorkspaceGraph` built from a list of `PackageRecord`s. Covers the analyses every project tool eventually needs: build a forward / reverse adjacency map, compute a topological dependency-first order, detect cycles, gather transitive deps or dependents, and identify graph roots (no deps) and leaves (no dependents). Edges where the target is not also a node are silently dropped at build time. This is the same posture as `scripts/folder-reorg/generate-manifest.ts` — necessary because Suaveplan migrates in waves and partial-manifest snapshots are routine. ## Features - `buildGraph` — populates forward and reverse adjacency in one pass; emits `overview.graph_builds_total` - `topologicalOrder` — Kahn's algorithm with stable lexicographic tie-breaking; throws `OVERVIEW.CYCLE_DETECTED` on a cycle (error carries `context.cycle`) - `detectCycles` — Tarjan's SCC, returns every cycle as a sorted `string[]` - `transitiveDependencies` / `transitiveDependents` — BFS over the appropriate direction; throws `OVERVIEW.UNKNOWN_PACKAGE` for missing inputs - `leafPackages` — no dependents (no incoming edges) - `rootPackages` — no deps (no outgoing edges) ## Usage: Basic ```ts import { buildGraph, topologicalOrder } from "@suaveplan/overview"; const graph = buildGraph(records); const order = topologicalOrder(graph); // `order[0]` has no workspace deps; the last entry depends on everything. ``` ## Usage: Advanced ```ts import { buildGraph, detectCycles, transitiveDependencies, transitiveDependents, leafPackages, rootPackages, } from "@suaveplan/overview"; const graph = buildGraph(records); const cycles = detectCycles(graph); if (cycles.length > 0) { console.error("Cycles detected:", cycles); } const blastRadius = transitiveDependents(graph, "@suaveplan/error"); const supplyChain = transitiveDependencies(graph, "@suaveplan/api"); const leaves = leafPackages(graph); const roots = rootPackages(graph); ``` `topologicalOrder` is deterministic — two callers with the same graph see the same order — so it can drive reproducible build plans and CI fan-outs. ## API Reference - `buildGraph(records): WorkspaceGraph` - `topologicalOrder(graph): string[]` — throws `OVERVIEW.CYCLE_DETECTED` - `detectCycles(graph): string[][]` - `transitiveDependencies(graph, pkg): string[]` — throws `OVERVIEW.UNKNOWN_PACKAGE` - `transitiveDependents(graph, pkg): string[]` — throws `OVERVIEW.UNKNOWN_PACKAGE` - `leafPackages(graph): string[]` - `rootPackages(graph): string[]` ## Implementation Notes `topologicalOrder` initialises a remaining-out-degree counter per node and drains nodes with zero out-degree first; the queue is kept sorted on insertion so the output is stable across runs. `detectCycles` uses a recursive Tarjan implementation — the workspace size is bounded (hundreds of packages), so the stack-depth budget is comfortable. BFS in `transitiveDependencies` / `transitiveDependents` uses an array-based queue with `Array.prototype.shift` — acceptable at workspace scale; if the package ever has to handle tens of thousands of nodes the queue can be swapped for a head/tail index pair.