import type { VisitNode } from "../../model/types"; /** Flatten a visible forest into an id → node index. */ export function indexNodes(roots: VisitNode[]): Map { const map = new Map(); const walk = (n: VisitNode) => { map.set(n.id, n); n.children.forEach(walk); }; roots.forEach(walk); return map; } /** For each node, the latest timestamp anywhere in its subtree (inclusive). */ export function subtreeMaxTs(roots: VisitNode[]): Map { const max = new Map(); const visit = (n: VisitNode): number => { let m = n.ts; for (const c of n.children) m = Math.max(m, visit(c)); max.set(n.id, m); return m; }; roots.forEach(visit); return max; } /** * The "mainline" child of a fork: the most-recently-active branch, i.e. the * child whose subtree contains the latest visit. This is the branch the lane * continues through and the one kept when a fork is collapsed — they agree by * construction. */ export function mainlineChild( children: VisitNode[], maxTs: Map, ): VisitNode | undefined { if (children.length === 0) return undefined; return children.reduce((best, c) => (maxTs.get(c.id) ?? c.ts) > (maxTs.get(best.id) ?? best.ts) ? c : best, ); } /** Depth (distance from a root) for every node, following parentId. */ export function depthMap(index: Map): Map { const depth = new Map(); const get = (n: VisitNode): number => { const cached = depth.get(n.id); if (cached !== undefined) return cached; const parent = n.parentId ? index.get(n.parentId) : undefined; const d = parent ? get(parent) + 1 : 0; depth.set(n.id, d); return d; }; for (const n of index.values()) get(n); return depth; }