first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
# `graph/graph.ts`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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.