{"version":3,"file":"index.mjs","names":[],"sources":["../src/dependency-graph.ts"],"sourcesContent":["export interface DependencyGraph {\n  nodes: Set<string>;\n  edges: Map<string, Set<string>>;\n}\n\nexport interface CircularDependencyResult {\n  hasCircular: boolean;\n  cycle: string[] | null;\n}\n\nexport interface TopologicalOrderResult {\n  success: boolean;\n  order: string[];\n  error?: string;\n}\n\n/**\n * Build a dependency graph from a dependencies map\n *\n * @param dependencies - Map of node names to their dependencies\n * @returns Dependency graph with nodes and edges\n *\n * @example\n * const graph = buildDependencyGraph({\n *   tax: ['price'],\n *   total: ['price', 'tax']\n * });\n * // graph.edges.get('tax') = Set(['price'])\n * // graph.edges.get('total') = Set(['price', 'tax'])\n */\nexport function buildDependencyGraph(\n  dependencies: Record<string, string[]>,\n): DependencyGraph {\n  const nodes = new Set<string>();\n  const edges = new Map<string, Set<string>>();\n\n  for (const [node, deps] of Object.entries(dependencies)) {\n    nodes.add(node);\n    edges.set(node, new Set(deps));\n\n    for (const dep of deps) {\n      nodes.add(dep);\n    }\n  }\n\n  return { nodes, edges };\n}\n\n/**\n * Detect first circular dependency in a dependency graph\n *\n * @param graph - Dependency graph\n * @returns Result with detected cycle (null if no cycle)\n *\n * @example\n * detectCircularDependencies(graph)\n * // { hasCircular: true, cycle: ['a', 'b', 'c', 'a'] }\n */\nexport function detectCircularDependencies(\n  graph: DependencyGraph,\n): CircularDependencyResult {\n  const visited = new Set<string>();\n  const recursionStack = new Set<string>();\n  const path: string[] = [];\n\n  for (const node of graph.nodes) {\n    if (!visited.has(node)) {\n      const cycle = dfsVisit(node, graph, visited, recursionStack, path);\n      if (cycle) {\n        return { hasCircular: true, cycle };\n      }\n    }\n  }\n\n  return { hasCircular: false, cycle: null };\n}\n\nfunction dfsVisit(\n  node: string,\n  graph: DependencyGraph,\n  visited: Set<string>,\n  recursionStack: Set<string>,\n  path: string[],\n): string[] | null {\n  visited.add(node);\n  recursionStack.add(node);\n  path.push(node);\n\n  const deps = graph.edges.get(node);\n  if (deps) {\n    for (const dep of deps) {\n      if (!visited.has(dep)) {\n        const cycle = dfsVisit(dep, graph, visited, recursionStack, path);\n        if (cycle) {\n          return cycle;\n        }\n      } else if (recursionStack.has(dep)) {\n        const cycleStart = path.indexOf(dep);\n        return [...path.slice(cycleStart), dep];\n      }\n    }\n  }\n\n  path.pop();\n  recursionStack.delete(node);\n  return null;\n}\n\n/**\n * Get topological order for formula evaluation\n *\n * @param graph - Dependency graph\n * @returns Ordered list of field names for evaluation\n *\n * @example\n * getTopologicalOrder(graph)\n * // { success: true, order: ['price', 'tax', 'total'] }\n */\nexport function getTopologicalOrder(\n  graph: DependencyGraph,\n): TopologicalOrderResult {\n  const circularCheck = detectCircularDependencies(graph);\n  if (circularCheck.hasCircular && circularCheck.cycle) {\n    return {\n      success: false,\n      order: [],\n      error: `Circular dependency detected: ${circularCheck.cycle.join(' -> ')}`,\n    };\n  }\n\n  const inDegree = initializeInDegree(graph);\n  const queue = findZeroInDegreeNodes(inDegree);\n  const order = processQueue(queue, graph, inDegree);\n\n  order.reverse();\n  return { success: true, order };\n}\n\nfunction initializeInDegree(graph: DependencyGraph): Map<string, number> {\n  const inDegree = new Map<string, number>();\n\n  for (const node of graph.nodes) {\n    inDegree.set(node, 0);\n  }\n\n  for (const deps of graph.edges.values()) {\n    for (const dep of deps) {\n      inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);\n    }\n  }\n\n  return inDegree;\n}\n\nfunction findZeroInDegreeNodes(inDegree: Map<string, number>): string[] {\n  const result: string[] = [];\n  for (const [node, degree] of inDegree) {\n    if (degree === 0) {\n      result.push(node);\n    }\n  }\n  return result;\n}\n\nfunction processQueue(\n  queue: string[],\n  graph: DependencyGraph,\n  inDegree: Map<string, number>,\n): string[] {\n  const order: string[] = [];\n  let head = 0;\n\n  while (head < queue.length) {\n    const node = queue[head]!;\n    head++;\n    order.push(node);\n\n    const deps = graph.edges.get(node);\n    if (deps) {\n      for (const dep of deps) {\n        const newDegree = (inDegree.get(dep) ?? 0) - 1;\n        inDegree.set(dep, newDegree);\n        if (newDegree === 0) {\n          queue.push(dep);\n        }\n      }\n    }\n  }\n\n  return order;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,SAAgB,qBACd,cACiB;CACjB,MAAM,wBAAQ,IAAI,KAAa;CAC/B,MAAM,wBAAQ,IAAI,KAA0B;AAE5C,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,aAAa,EAAE;AACvD,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,MAAM,IAAI,IAAI,KAAK,CAAC;AAE9B,OAAK,MAAM,OAAO,KAChB,OAAM,IAAI,IAAI;;AAIlB,QAAO;EAAE;EAAO;EAAO;;;;;;;;;;;;AAazB,SAAgB,2BACd,OAC0B;CAC1B,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,iCAAiB,IAAI,KAAa;CACxC,MAAM,OAAiB,EAAE;AAEzB,MAAK,MAAM,QAAQ,MAAM,MACvB,KAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;EACtB,MAAM,QAAQ,SAAS,MAAM,OAAO,SAAS,gBAAgB,KAAK;AAClE,MAAI,MACF,QAAO;GAAE,aAAa;GAAM;GAAO;;AAKzC,QAAO;EAAE,aAAa;EAAO,OAAO;EAAM;;AAG5C,SAAS,SACP,MACA,OACA,SACA,gBACA,MACiB;AACjB,SAAQ,IAAI,KAAK;AACjB,gBAAe,IAAI,KAAK;AACxB,MAAK,KAAK,KAAK;CAEf,MAAM,OAAO,MAAM,MAAM,IAAI,KAAK;AAClC,KAAI,MACF;OAAK,MAAM,OAAO,KAChB,KAAI,CAAC,QAAQ,IAAI,IAAI,EAAE;GACrB,MAAM,QAAQ,SAAS,KAAK,OAAO,SAAS,gBAAgB,KAAK;AACjE,OAAI,MACF,QAAO;aAEA,eAAe,IAAI,IAAI,EAAE;GAClC,MAAM,aAAa,KAAK,QAAQ,IAAI;AACpC,UAAO,CAAC,GAAG,KAAK,MAAM,WAAW,EAAE,IAAI;;;AAK7C,MAAK,KAAK;AACV,gBAAe,OAAO,KAAK;AAC3B,QAAO;;;;;;;;;;;;AAaT,SAAgB,oBACd,OACwB;CACxB,MAAM,gBAAgB,2BAA2B,MAAM;AACvD,KAAI,cAAc,eAAe,cAAc,MAC7C,QAAO;EACL,SAAS;EACT,OAAO,EAAE;EACT,OAAO,iCAAiC,cAAc,MAAM,KAAK,OAAO;EACzE;CAGH,MAAM,WAAW,mBAAmB,MAAM;CAE1C,MAAM,QAAQ,aADA,sBAAsB,SAAS,EACX,OAAO,SAAS;AAElD,OAAM,SAAS;AACf,QAAO;EAAE,SAAS;EAAM;EAAO;;AAGjC,SAAS,mBAAmB,OAA6C;CACvE,MAAM,2BAAW,IAAI,KAAqB;AAE1C,MAAK,MAAM,QAAQ,MAAM,MACvB,UAAS,IAAI,MAAM,EAAE;AAGvB,MAAK,MAAM,QAAQ,MAAM,MAAM,QAAQ,CACrC,MAAK,MAAM,OAAO,KAChB,UAAS,IAAI,MAAM,SAAS,IAAI,IAAI,IAAI,KAAK,EAAE;AAInD,QAAO;;AAGT,SAAS,sBAAsB,UAAyC;CACtE,MAAM,SAAmB,EAAE;AAC3B,MAAK,MAAM,CAAC,MAAM,WAAW,SAC3B,KAAI,WAAW,EACb,QAAO,KAAK,KAAK;AAGrB,QAAO;;AAGT,SAAS,aACP,OACA,OACA,UACU;CACV,MAAM,QAAkB,EAAE;CAC1B,IAAI,OAAO;AAEX,QAAO,OAAO,MAAM,QAAQ;EAC1B,MAAM,OAAO,MAAM;AACnB;AACA,QAAM,KAAK,KAAK;EAEhB,MAAM,OAAO,MAAM,MAAM,IAAI,KAAK;AAClC,MAAI,KACF,MAAK,MAAM,OAAO,MAAM;GACtB,MAAM,aAAa,SAAS,IAAI,IAAI,IAAI,KAAK;AAC7C,YAAS,IAAI,KAAK,UAAU;AAC5B,OAAI,cAAc,EAChB,OAAM,KAAK,IAAI;;;AAMvB,QAAO"}