/** * Project cycle detection for the compile-time dependency graph. * * The architecture graph is a BUILD graph: a cycle in it is not a style problem, it is a graph that * cannot be built in any order, and the level numbers every rendered row is keyed on are meaningless * the moment one exists. * * `sortGraphTopologically` already refuses to stratify a cyclic graph, but it reports exactly ONE * cycle plus an undifferentiated "among: a, b, c, d, e" list — so a repo with three independent * cycles fixes one, re-runs, finds another, three times. This enumerates ALL of them up front, each * as a concrete `a -> b -> c -> a` chain of PROJECT KEYS, so one run names everything to break. * * IT OPERATES ON PROJECT KEYS, NEVER ON DISPLAY NAMES. A workspace may hold both * `@scope/public-api` and `public-api`; running detection on scope-stripped names would fuse them * and report their perfectly legal L6 → L0 edge as a self-loop cycle. See graph-names.ts. * * Self-loops count: a project listing itself in `dependsOn` is the degenerate cycle and fails too. */ /** A directed dependency graph keyed on PROJECT KEYS: `graph[project]` is what `project` depends on. */ export type ProjectAdjacency = Record; /** * The one field the level assertion reads off a project entry. * * Spelled here rather than importing `EnhancedGraph` from graph-sorter on purpose: graph-sorter * imports THIS module (it reports its Kahn stall through the detector), and even a type-only import * back closes a file-import cycle the build refuses. `EnhancedGraph` satisfies this structurally. */ export declare class ProjectLevel { readonly level: number; constructor(level: number); } /** Any project map carrying levels — what {@link ProjectCycleDetector.levelsOf} reads. */ export type LevelledProjects = Record; /** One cycle, as the ordered chain of project keys that closes it (first key repeated at the end). */ export declare class ProjectCycle { readonly path: string[]; constructor(path: string[]); /** `a -> b -> c -> a`, the form a reader can act on directly. */ describe(): string; } /** One edge whose direction contradicts the levels assigned to its endpoints. */ export declare class LevelViolation { readonly from: string; readonly fromLevel: number; readonly to: string; readonly toLevel: number; constructor(from: string, fromLevel: number, to: string, toLevel: number); describe(): string; } /** Thrown when the compile-time project graph is not a DAG. */ export declare class CircularProjectDependencyError extends Error { constructor(message: string); } /** Thrown when a freshly stratified graph contains an edge that its own levels forbid. */ export declare class LevelViolationError extends Error { constructor(message: string); } export declare class ProjectCycleDetector { /** * Every cycle in the graph, deterministically ordered. * * Tarjan (shared with the runtime graph's detector — one SCC implementation, two callers) gives * the strongly connected components; each component is then walked to recover an actual ordered * path, because "these five projects are tangled" is not something a reader can act on and * `a -> b -> c -> a` is. */ find(graph: ProjectAdjacency): ProjectCycle[]; /** * Fail the generation on any cycle, naming ALL of them. * * @param source what the graph was read from, so the message says WHERE to go fix it. */ assertAcyclic(graph: ProjectAdjacency, source: string): void; /** * The level each project was stratified onto — the input {@link assertLevelsDescend} checks * against. Only `level` is read off each entry. */ levelsOf(graph: LevelledProjects): Map; /** * Edges that contradict the levels assigned to their endpoints. * * `sortGraphTopologically` stratifies so that every dependency sits STRICTLY below its * dependent, so on a freshly sorted graph this can only fire if the stratification itself broke * — it is an assertion about the sorter, not a rule about repos. Callers must therefore only run * it against a graph they just sorted, never against a possibly-stale committed file. */ findLevelViolations(graph: ProjectAdjacency, levelOf: Map): LevelViolation[]; /** Fail loudly when a just-sorted graph disagrees with its own levels. */ assertLevelsDescend(graph: ProjectAdjacency, levelOf: Map, source: string): void; /** * Recover one ordered cycle from a strongly connected component: walk forward inside the * component until a node repeats, then return the closed chain from that repeat onward. A * one-node component only reaches here when it carries a self-edge, which yields `x -> x`. */ private pathWithin; }