{"version":3,"file":"migration-graph-BW4ty9WV.mjs","names":[],"sources":["../src/queue.ts","../src/graph-ops.ts","../src/migration-graph.ts"],"sourcesContent":["/**\n * FIFO queue with amortised O(1) push and shift.\n *\n * Uses a head-index cursor over a backing array rather than\n * `Array.prototype.shift()`, which is O(n) on V8. Intended for BFS-shaped\n * traversals where the queue is drained in a single pass — it does not\n * reclaim memory for already-shifted items, so it is not suitable for\n * long-lived queues with many push/shift cycles.\n */\nexport class Queue<T> {\n  private readonly items: T[];\n  private head = 0;\n\n  constructor(initial: Iterable<T> = []) {\n    this.items = [...initial];\n  }\n\n  push(item: T): void {\n    this.items.push(item);\n  }\n\n  /**\n   * Remove and return the next item. Caller must check `isEmpty` first —\n   * shifting an empty queue throws.\n   */\n  shift(): T {\n    if (this.head >= this.items.length) {\n      throw new Error('Queue.shift called on empty queue');\n    }\n    // biome-ignore lint/style/noNonNullAssertion: bounds-checked on the line above\n    return this.items[this.head++]!;\n  }\n\n  get isEmpty(): boolean {\n    return this.head >= this.items.length;\n  }\n}\n","import { Queue } from './queue';\n\n/**\n * One step of a BFS traversal.\n *\n * `parent` and `incomingEdge` are `null` for start states — they were not\n * reached via any edge. For every other state they record the predecessor\n * state and the edge by which this state was first reached.\n *\n * `state` is the BFS state, most often a string (graph node identifier) but\n * can be a composite object. The string overload keeps the common case\n * ergonomic; the generic overload accepts a caller-supplied `key` function\n * that produces a stable equality key for dedup.\n */\nexport interface BfsStep<S, E> {\n  readonly state: S;\n  readonly parent: S | null;\n  readonly incomingEdge: E | null;\n}\n\n/**\n * Generic breadth-first traversal.\n *\n * Direction (forward/reverse) is expressed by the caller's `neighbours`\n * closure: return `{ next, edge }` pairs where `next` is the state to visit\n * next and `edge` is the edge that connects them. Callers that don't need\n * path reconstruction can ignore the `parent`/`incomingEdge` fields of each\n * yielded step.\n *\n * Ordering — when the result needs to be deterministic (path-finding) the\n * caller is responsible for sorting inside `neighbours`; this generator\n * does not impose an ordering hook of its own. State-dependent orderings\n * have full access to the source state inside the closure.\n *\n * Stops are intrinsic — callers `break` out of the `for..of` loop when\n * they've found what they're looking for.\n */\nexport function bfs<E>(\n  starts: Iterable<string>,\n  neighbours: (state: string) => Iterable<{ next: string; edge: E }>,\n): Generator<BfsStep<string, E>>;\nexport function bfs<S, E>(\n  starts: Iterable<S>,\n  neighbours: (state: S) => Iterable<{ next: S; edge: E }>,\n  key: (state: S) => string,\n): Generator<BfsStep<S, E>>;\nexport function* bfs<S, E>(\n  starts: Iterable<S>,\n  neighbours: (state: S) => Iterable<{ next: S; edge: E }>,\n  // Identity default for the string overload. TypeScript can't express\n  // \"default applies only when S = string\", so this cast bridges the\n  // generic implementation signature to the public overloads — which\n  // guarantee `key` is omitted only when S = string at the call site.\n  key: (state: S) => string = (state) => state as unknown as string,\n): Generator<BfsStep<S, E>> {\n  // Queue entries carry the state alongside its key so we don't recompute\n  // key() twice per visit (once on dedup, once on parent lookup). Composite\n  // keys can be non-trivial to compute; string-overload callers pay nothing\n  // since key() is identity there.\n  interface Entry {\n    readonly state: S;\n    readonly key: string;\n  }\n  const visited = new Set<string>();\n  const parentMap = new Map<string, { parent: S; edge: E }>();\n  const queue = new Queue<Entry>();\n  for (const start of starts) {\n    const k = key(start);\n    if (!visited.has(k)) {\n      visited.add(k);\n      queue.push({ state: start, key: k });\n    }\n  }\n  while (!queue.isEmpty) {\n    const { state: current, key: curKey } = queue.shift();\n    const parentInfo = parentMap.get(curKey);\n    yield {\n      state: current,\n      parent: parentInfo?.parent ?? null,\n      incomingEdge: parentInfo?.edge ?? null,\n    };\n\n    for (const { next, edge } of neighbours(current)) {\n      const k = key(next);\n      if (!visited.has(k)) {\n        visited.add(k);\n        parentMap.set(k, { parent: current, edge });\n        queue.push({ state: next, key: k });\n      }\n    }\n  }\n}\n","import { ifDefined } from '@prisma-next/utils/defined';\nimport { EMPTY_CONTRACT_HASH } from './constants';\nimport { errorAmbiguousTarget, errorNoInitialMigration, errorNoTarget } from './errors';\nimport type { MigrationEdge, MigrationGraph } from './graph';\nimport { bfs } from './graph-ops';\nimport type { OnDiskMigrationPackage } from './package';\n\n/** Forward-edge neighbours: edge `e` from `n` visits `e.to` next. */\nfunction forwardNeighbours(graph: MigrationGraph, node: string) {\n  return (graph.forwardChain.get(node) ?? []).map((edge) => ({ next: edge.to, edge }));\n}\n\n/**\n * Forward-edge neighbours, sorted by the deterministic tie-break.\n * Used by path-finding so the resulting shortest path is stable across runs.\n */\nfunction sortedForwardNeighbours(graph: MigrationGraph, node: string) {\n  const edges = graph.forwardChain.get(node) ?? [];\n  return [...edges].sort(compareTieBreak).map((edge) => ({ next: edge.to, edge }));\n}\n\n/** Reverse-edge neighbours: edge `e` from `n` visits `e.from` next. */\nfunction reverseNeighbours(graph: MigrationGraph, node: string) {\n  return (graph.reverseChain.get(node) ?? []).map((edge) => ({ next: edge.from, edge }));\n}\n\nfunction appendEdge(map: Map<string, MigrationEdge[]>, key: string, entry: MigrationEdge): void {\n  const bucket = map.get(key);\n  if (bucket) bucket.push(entry);\n  else map.set(key, [entry]);\n}\n\nexport function reconstructGraph(packages: readonly OnDiskMigrationPackage[]): MigrationGraph {\n  const nodes = new Set<string>();\n  const forwardChain = new Map<string, MigrationEdge[]>();\n  const reverseChain = new Map<string, MigrationEdge[]>();\n  const migrationByHash = new Map<string, MigrationEdge>();\n\n  for (const pkg of packages) {\n    // Manifest `from` is `string | null` (null = baseline). The graph layer\n    // is the marker/path layer where \"no prior state\" is encoded as the\n    // EMPTY_CONTRACT_HASH sentinel; bridge here so pathfinding stays string-\n    // keyed.\n    const from = pkg.metadata.from ?? EMPTY_CONTRACT_HASH;\n    const { to } = pkg.metadata;\n\n    nodes.add(from);\n    nodes.add(to);\n\n    const migration: MigrationEdge = {\n      from,\n      to,\n      migrationHash: pkg.metadata.migrationHash,\n      dirName: pkg.dirName,\n      createdAt: pkg.metadata.createdAt,\n      invariants: pkg.metadata.providedInvariants,\n    };\n\n    if (!migrationByHash.has(migration.migrationHash)) {\n      migrationByHash.set(migration.migrationHash, migration);\n    }\n\n    appendEdge(forwardChain, from, migration);\n    appendEdge(reverseChain, to, migration);\n  }\n\n  return { nodes, forwardChain, reverseChain, migrationByHash };\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic tie-breaking for BFS neighbour order.\n// Used by path-finders only; not a general-purpose utility.\n// Ordering: createdAt → to → migrationHash.\n// ---------------------------------------------------------------------------\n\nfunction compareTieBreak(a: MigrationEdge, b: MigrationEdge): number {\n  const ca = a.createdAt.localeCompare(b.createdAt);\n  if (ca !== 0) return ca;\n  const tc = a.to.localeCompare(b.to);\n  if (tc !== 0) return tc;\n  return a.migrationHash.localeCompare(b.migrationHash);\n}\n\nfunction sortedNeighbors(edges: readonly MigrationEdge[]): readonly MigrationEdge[] {\n  return [...edges].sort(compareTieBreak);\n}\n\n/**\n * Find the shortest path from `fromHash` to `toHash` using BFS over the\n * contract-hash graph. Returns the ordered list of edges, or null if no path\n * exists. Returns an empty array when `fromHash === toHash` (no-op).\n *\n * Neighbor ordering is deterministic via the tie-break sort key:\n * createdAt → to → migrationHash.\n */\nexport function findPath(\n  graph: MigrationGraph,\n  fromHash: string,\n  toHash: string,\n): readonly MigrationEdge[] | null {\n  if (fromHash === toHash) return [];\n\n  const parents = new Map<string, { parent: string; edge: MigrationEdge }>();\n  for (const step of bfs([fromHash], (n) => sortedForwardNeighbours(graph, n))) {\n    if (step.parent !== null && step.incomingEdge !== null) {\n      parents.set(step.state, { parent: step.parent, edge: step.incomingEdge });\n    }\n    if (step.state === toHash) {\n      const path: MigrationEdge[] = [];\n      let cur = toHash;\n      let p = parents.get(cur);\n      while (p) {\n        path.push(p.edge);\n        cur = p.parent;\n        p = parents.get(cur);\n      }\n      path.reverse();\n      return path;\n    }\n  }\n\n  return null;\n}\n\n/**\n * Find the shortest path from `fromHash` to `toHash` whose edges collectively\n * cover every invariant in `required`. Returns `null` when no such path exists\n * (either `fromHash`→`toHash` is structurally unreachable, or every reachable\n * path leaves at least one required invariant uncovered). When `required` is\n * empty, delegates to `findPath` so the result is byte-identical for that case.\n *\n * Algorithm: BFS over `(node, coveredSubset)` states with state-level dedup.\n * The covered subset is a `Set<string>` of invariant ids; the state's dedup\n * key is `${node}\\0${[...covered].sort().join('\\0')}`. State keys distinguish\n * distinct `(node, covered)` tuples regardless of node-name length because\n * `\\0` cannot appear in any invariant id (validation rejects whitespace and\n * control chars at authoring time).\n *\n * Neighbour ordering when `required ≠ ∅`: edges covering ≥1 still-needed\n * invariant come first, with `createdAt → to → migrationHash` as the\n * secondary key. The heuristic steers BFS toward the satisfying path;\n * correctness (shortest, deterministic) does not depend on it.\n */\nexport function findPathWithInvariants(\n  graph: MigrationGraph,\n  fromHash: string,\n  toHash: string,\n  required: ReadonlySet<string>,\n): readonly MigrationEdge[] | null {\n  if (required.size === 0) {\n    return findPath(graph, fromHash, toHash);\n  }\n\n  interface InvState {\n    readonly node: string;\n    readonly covered: ReadonlySet<string>;\n  }\n  // `\\0` is a safe segment separator: `validateInvariantId` rejects any id\n  // containing whitespace or control characters (NUL is U+0000), and node\n  // hashes are hex strings. Distinct `(node, covered)` tuples therefore\n  // map to distinct strings. If `validateInvariantId` is ever relaxed,\n  // re-confirm dedup correctness here.\n  const stateKey = (s: InvState): string => {\n    if (s.covered.size === 0) return `${s.node}\\0`;\n    return `${s.node}\\0${[...s.covered].sort().join('\\0')}`;\n  };\n\n  const neighbours = (s: InvState): Iterable<{ next: InvState; edge: MigrationEdge }> => {\n    const outgoing = graph.forwardChain.get(s.node) ?? [];\n    if (outgoing.length === 0) return [];\n    return [...outgoing]\n      .map((edge) => {\n        let useful = false;\n        let next: Set<string> | null = null;\n        for (const inv of edge.invariants) {\n          if (required.has(inv) && !s.covered.has(inv)) {\n            if (next === null) next = new Set(s.covered);\n            next.add(inv);\n            useful = true;\n          }\n        }\n        return { edge, useful, nextCovered: next ?? s.covered };\n      })\n      .sort((a, b) => {\n        if (a.useful !== b.useful) return a.useful ? -1 : 1;\n        return compareTieBreak(a.edge, b.edge);\n      })\n      .map(({ edge, nextCovered }) => ({\n        next: { node: edge.to, covered: nextCovered },\n        edge,\n      }));\n  };\n\n  // Path reconstruction is consumer-side, keyed on stateKey, same shape as\n  // findPath's parents map.\n  const parents = new Map<string, { parentKey: string; edge: MigrationEdge }>();\n  for (const step of bfs<InvState, MigrationEdge>(\n    [{ node: fromHash, covered: new Set() }],\n    neighbours,\n    stateKey,\n  )) {\n    const curKey = stateKey(step.state);\n    if (step.parent !== null && step.incomingEdge !== null) {\n      parents.set(curKey, { parentKey: stateKey(step.parent), edge: step.incomingEdge });\n    }\n    if (step.state.node === toHash && step.state.covered.size === required.size) {\n      const path: MigrationEdge[] = [];\n      let cur: string | undefined = curKey;\n      while (cur !== undefined) {\n        const p = parents.get(cur);\n        if (!p) break;\n        path.push(p.edge);\n        cur = p.parentKey;\n      }\n      path.reverse();\n      return path;\n    }\n  }\n\n  return null;\n}\n\n/**\n * Reverse-BFS from `toHash` over `reverseChain` to collect every node from\n * which `toHash` is reachable (inclusive of `toHash` itself).\n */\nfunction collectNodesReachingTarget(graph: MigrationGraph, toHash: string): Set<string> {\n  const reached = new Set<string>();\n  for (const step of bfs([toHash], (n) => reverseNeighbours(graph, n))) {\n    reached.add(step.state);\n  }\n  return reached;\n}\n\nexport interface PathDecision {\n  readonly selectedPath: readonly MigrationEdge[];\n  readonly fromHash: string;\n  readonly toHash: string;\n  readonly alternativeCount: number;\n  readonly tieBreakReasons: readonly string[];\n  readonly refName?: string;\n  /** The caller-supplied required invariant set, sorted ascending. */\n  readonly requiredInvariants: readonly string[];\n  /**\n   * The subset of `requiredInvariants` actually covered by edges on\n   * `selectedPath`. Always a subset of `requiredInvariants` (when the path\n   * is satisfying, equal to it); always derived from `selectedPath`.\n   */\n  readonly satisfiedInvariants: readonly string[];\n}\n\n/**\n * Outcome of {@link findPathWithDecision}. The pathfinder distinguishes\n * three cases up front so callers don't re-derive structural reachability:\n *\n * - `ok` — a path covering `required` exists; `decision` carries the\n *   selection metadata and per-edge invariants.\n * - `unreachable` — `from`→`to` has no structural path. Mapped by callers\n *   to the existing no-path / `NO_TARGET` diagnostic.\n * - `unsatisfiable` — `from`→`to` is structurally reachable but no path\n *   covers every required invariant. `structuralPath` is the\n *   `findPath(graph, from, to)` result, included so callers don't have to\n *   recompute it when raising `MIGRATION.NO_INVARIANT_PATH`. `missing` is\n *   the subset of `required` that the structural path does *not* cover —\n *   correctly accounts for partial coverage when some required invariants\n *   are met by the fallback path. Only emitted when `required` is\n *   non-empty.\n */\nexport type FindPathOutcome =\n  | { readonly kind: 'ok'; readonly decision: PathDecision }\n  | { readonly kind: 'unreachable' }\n  | {\n      readonly kind: 'unsatisfiable';\n      readonly structuralPath: readonly MigrationEdge[];\n      readonly missing: readonly string[];\n    };\n\n/**\n * Routing context for {@link findPathWithDecision}. Both fields are optional;\n * `refName` is only used to decorate the resulting `PathDecision` for the\n * JSON envelope, and `required` defaults to an empty set (purely structural\n * routing). They are passed via a single options object so the call sites\n * cannot silently swap two adjacent string parameters.\n */\nexport interface FindPathWithDecisionOptions {\n  readonly refName?: string;\n  readonly required?: ReadonlySet<string>;\n}\n\n/**\n * Find the shortest path from `fromHash` to `toHash` and return structured\n * path-decision metadata for machine-readable output. When `required` is\n * non-empty, the returned path is the shortest one whose edges collectively\n * cover every required invariant.\n *\n * The discriminated return type tells the caller *why* a path could not be\n * found, so the CLI can pick the right structured error without re-running\n * a structural BFS.\n */\nexport function findPathWithDecision(\n  graph: MigrationGraph,\n  fromHash: string,\n  toHash: string,\n  options: FindPathWithDecisionOptions = {},\n): FindPathOutcome {\n  const { refName, required = new Set<string>() } = options;\n  const requiredInvariants = [...required].sort();\n\n  if (fromHash === toHash && required.size === 0) {\n    return {\n      kind: 'ok',\n      decision: {\n        selectedPath: [],\n        fromHash,\n        toHash,\n        alternativeCount: 0,\n        tieBreakReasons: [],\n        requiredInvariants,\n        satisfiedInvariants: [],\n        ...ifDefined('refName', refName),\n      },\n    };\n  }\n\n  const path = findPathWithInvariants(graph, fromHash, toHash, required);\n  if (!path) {\n    if (required.size === 0) {\n      return { kind: 'unreachable' };\n    }\n    const structural = findPath(graph, fromHash, toHash);\n    if (structural === null) {\n      return { kind: 'unreachable' };\n    }\n    const coveredByStructural = new Set<string>();\n    for (const edge of structural) {\n      for (const inv of edge.invariants) {\n        if (required.has(inv)) coveredByStructural.add(inv);\n      }\n    }\n    const missing = requiredInvariants.filter((id) => !coveredByStructural.has(id));\n    return { kind: 'unsatisfiable', structuralPath: structural, missing };\n  }\n\n  const satisfiedInvariants = computeSatisfiedInvariants(required, path);\n\n  // Single reverse BFS marks every node from which `toHash` is reachable.\n  // Replaces a per-edge `findPath(e.to, toHash)` call inside the loop below,\n  // which made the whole function O(|path| · (V + E)) instead of O(V + E).\n  const reachesTarget = collectNodesReachingTarget(graph, toHash);\n  const coveragePrefixes = requiredCoveragePrefixes(required, path);\n\n  const tieBreakReasons: string[] = [];\n  let alternativeCount = 0;\n\n  for (const [i, edge] of path.entries()) {\n    const outgoing = graph.forwardChain.get(edge.from);\n    if (!outgoing || outgoing.length <= 1) continue;\n    const reachable = outgoing.filter((e) => reachesTarget.has(e.to));\n    if (reachable.length <= 1) continue;\n\n    let comparisonPool: readonly MigrationEdge[] = reachable;\n    if (required.size > 0) {\n      // coveragePrefixes is built one-per-edge from path, so the index is\n      // always in range here; the explicit guard keeps the type narrowed\n      // without a non-null assertion.\n      const prefixSet = coveragePrefixes[i];\n      if (prefixSet === undefined) continue;\n      comparisonPool = invariantViableAlternativesAtStep(required, prefixSet, reachable);\n    }\n\n    alternativeCount += reachable.length - 1;\n    const sorted = sortedNeighbors(reachable);\n    if (sorted[0]?.migrationHash !== edge.migrationHash) continue;\n    if (!reachable.some((e) => e.migrationHash !== edge.migrationHash)) continue;\n\n    const sortedViable = sortedNeighbors(comparisonPool);\n    if (\n      sortedViable.length > 1 &&\n      sortedViable[0]?.migrationHash === edge.migrationHash &&\n      sortedViable.some((e) => e.migrationHash !== edge.migrationHash)\n    ) {\n      tieBreakReasons.push(\n        `at ${edge.from}: ${comparisonPool.length} candidates, selected by tie-break`,\n      );\n    }\n  }\n\n  return {\n    kind: 'ok',\n    decision: {\n      selectedPath: path,\n      fromHash,\n      toHash,\n      alternativeCount,\n      tieBreakReasons,\n      requiredInvariants,\n      satisfiedInvariants,\n      ...ifDefined('refName', refName),\n    },\n  };\n}\n\nfunction computeSatisfiedInvariants(\n  required: ReadonlySet<string>,\n  path: readonly MigrationEdge[],\n): readonly string[] {\n  if (required.size === 0) return [];\n  const covered = new Set<string>();\n  for (const edge of path) {\n    for (const inv of edge.invariants) {\n      if (required.has(inv)) covered.add(inv);\n    }\n  }\n  return [...covered].sort();\n}\n\n/**\n * For each edge on path, invariant coverage accumulated from earlier edges only —\n * `(required ∩ ∪_{j<i} path[j].invariants)` represented as cumulative set along `required`,\n * keyed as \"full set of required ids satisfied before taking path[i]\".\n */\nfunction requiredCoveragePrefixes(\n  required: ReadonlySet<string>,\n  path: readonly MigrationEdge[],\n): readonly ReadonlySet<string>[] {\n  const prefixes: ReadonlySet<string>[] = [];\n  const acc = new Set<string>();\n  for (const edge of path) {\n    prefixes.push(new Set(acc));\n    for (const inv of edge.invariants) {\n      if (required.has(inv)) acc.add(inv);\n    }\n  }\n  return prefixes;\n}\n\nfunction invariantViableAlternativesAtStep(\n  required: ReadonlySet<string>,\n  coverageBeforeTakingEdge: ReadonlySet<string>,\n  outgoing: readonly MigrationEdge[],\n): readonly MigrationEdge[] {\n  if (required.size === 0) return [...outgoing];\n  return outgoing.filter((e) =>\n    [...required].every((id) => coverageBeforeTakingEdge.has(id) || e.invariants.includes(id)),\n  );\n}\n\n/**\n * Walk ancestors of each branch tip back to find the last node\n * that appears on all paths. Returns `fromHash` if no shared ancestor is found.\n */\nfunction findDivergencePoint(\n  graph: MigrationGraph,\n  fromHash: string,\n  leaves: readonly string[],\n): string {\n  const ancestorSets = leaves.map((leaf) => {\n    const ancestors = new Set<string>();\n    for (const step of bfs([leaf], (n) => reverseNeighbours(graph, n))) {\n      ancestors.add(step.state);\n    }\n    return ancestors;\n  });\n\n  const commonAncestors = [...(ancestorSets[0] ?? [])].filter((node) =>\n    ancestorSets.every((s) => s.has(node)),\n  );\n\n  let deepest = fromHash;\n  let deepestDepth = -1;\n  for (const ancestor of commonAncestors) {\n    const path = findPath(graph, fromHash, ancestor);\n    const depth = path ? path.length : 0;\n    if (depth > deepestDepth) {\n      deepestDepth = depth;\n      deepest = ancestor;\n    }\n  }\n  return deepest;\n}\n\n/**\n * Find all branch tips (nodes with no outgoing edges) reachable from\n * `fromHash` via forward edges.\n */\nexport function findReachableLeaves(graph: MigrationGraph, fromHash: string): readonly string[] {\n  const leaves: string[] = [];\n  for (const step of bfs([fromHash], (n) => forwardNeighbours(graph, n))) {\n    if (!graph.forwardChain.get(step.state)?.length) {\n      leaves.push(step.state);\n    }\n  }\n  return leaves;\n}\n\n/**\n * Find the target contract hash of the migration graph reachable from\n * EMPTY_CONTRACT_HASH. Returns `null` for a graph that has no target\n * state (either empty, or containing only the root with no outgoing\n * edges). Throws NO_INITIAL_MIGRATION if the graph has nodes but none\n * originate from the empty hash, and AMBIGUOUS_TARGET if multiple\n * branch tips exist.\n */\nexport function findLeaf(graph: MigrationGraph): string | null {\n  if (graph.nodes.size === 0) {\n    return null;\n  }\n\n  if (!graph.nodes.has(EMPTY_CONTRACT_HASH)) {\n    throw errorNoInitialMigration([...graph.nodes]);\n  }\n\n  const leaves = findReachableLeaves(graph, EMPTY_CONTRACT_HASH);\n\n  if (leaves.length === 0) {\n    const reachable = [...graph.nodes].filter((n) => n !== EMPTY_CONTRACT_HASH);\n    if (reachable.length > 0) {\n      throw errorNoTarget(reachable);\n    }\n    return null;\n  }\n\n  if (leaves.length > 1) {\n    const divergencePoint = findDivergencePoint(graph, EMPTY_CONTRACT_HASH, leaves);\n    const branches = leaves.map((tip) => {\n      const path = findPath(graph, divergencePoint, tip);\n      return {\n        tip,\n        edges: (path ?? []).map((e) => ({ dirName: e.dirName, from: e.from, to: e.to })),\n      };\n    });\n    throw errorAmbiguousTarget(leaves, { divergencePoint, branches });\n  }\n\n  // biome-ignore lint/style/noNonNullAssertion: leaves.length is neither 0 nor >1 per the branches above, so exactly one leaf remains\n  return leaves[0]!;\n}\n\n/**\n * Find the latest migration entry by traversing from EMPTY_CONTRACT_HASH\n * to the single target. Returns null for an empty graph.\n * Throws AMBIGUOUS_TARGET if the graph has multiple branch tips.\n */\nexport function findLatestMigration(graph: MigrationGraph): MigrationEdge | null {\n  const leafHash = findLeaf(graph);\n  if (leafHash === null) return null;\n\n  const path = findPath(graph, EMPTY_CONTRACT_HASH, leafHash);\n  return path?.at(-1) ?? null;\n}\n\nexport function detectCycles(graph: MigrationGraph): readonly string[][] {\n  const WHITE = 0;\n  const GRAY = 1;\n  const BLACK = 2;\n\n  const color = new Map<string, number>();\n  const parentMap = new Map<string, string | null>();\n  const cycles: string[][] = [];\n\n  for (const node of graph.nodes) {\n    color.set(node, WHITE);\n  }\n\n  // Iterative three-color DFS. A frame is (node, outgoing edges, next-index).\n  interface Frame {\n    node: string;\n    outgoing: readonly MigrationEdge[];\n    index: number;\n  }\n  const stack: Frame[] = [];\n\n  function pushFrame(u: string): void {\n    color.set(u, GRAY);\n    stack.push({ node: u, outgoing: graph.forwardChain.get(u) ?? [], index: 0 });\n  }\n\n  for (const root of graph.nodes) {\n    if (color.get(root) !== WHITE) continue;\n    parentMap.set(root, null);\n    pushFrame(root);\n\n    while (stack.length > 0) {\n      // biome-ignore lint/style/noNonNullAssertion: stack.length > 0 should guarantee that this cannot be undefined\n      const frame = stack[stack.length - 1]!;\n      if (frame.index >= frame.outgoing.length) {\n        color.set(frame.node, BLACK);\n        stack.pop();\n        continue;\n      }\n      // biome-ignore lint/style/noNonNullAssertion: the early-continue above guarantees frame.index < frame.outgoing.length here, so this is defined\n      const edge = frame.outgoing[frame.index++]!;\n      const v = edge.to;\n      const vColor = color.get(v);\n      if (vColor === GRAY) {\n        const cycle: string[] = [v];\n        let cur = frame.node;\n        while (cur !== v) {\n          cycle.push(cur);\n          cur = parentMap.get(cur) ?? v;\n        }\n        cycle.reverse();\n        cycles.push(cycle);\n      } else if (vColor === WHITE) {\n        parentMap.set(v, frame.node);\n        pushFrame(v);\n      }\n    }\n  }\n\n  return cycles;\n}\n\nexport function detectOrphans(graph: MigrationGraph): readonly MigrationEdge[] {\n  if (graph.nodes.size === 0) return [];\n\n  const reachable = new Set<string>();\n  const startNodes: string[] = [];\n\n  if (graph.forwardChain.has(EMPTY_CONTRACT_HASH)) {\n    startNodes.push(EMPTY_CONTRACT_HASH);\n  } else {\n    const allTargets = new Set<string>();\n    for (const edges of graph.forwardChain.values()) {\n      for (const edge of edges) {\n        allTargets.add(edge.to);\n      }\n    }\n    for (const node of graph.nodes) {\n      if (!allTargets.has(node)) {\n        startNodes.push(node);\n      }\n    }\n  }\n\n  for (const step of bfs(startNodes, (n) => forwardNeighbours(graph, n))) {\n    reachable.add(step.state);\n  }\n\n  const orphans: MigrationEdge[] = [];\n  for (const [from, migrations] of graph.forwardChain) {\n    if (!reachable.has(from)) {\n      orphans.push(...migrations);\n    }\n  }\n\n  return orphans;\n}\n"],"mappings":"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"}