{"version":3,"file":"circular-detector.mjs","names":["isProduction","process","env","NODE_ENV","CircularDetector","detectCycle","waiterName","targetName","getHolder","visited","Set","queue","name","path","length","currentName","shift","has","add","holder","waitingForName","waitingFor","push","formatCycle","cycle","join"],"sources":["../../../../src/internal/lifecycle/circular-detector.mts"],"sourcesContent":["import type { InstanceHolder } from '../holder/instance-holder.mjs'\n\n/**\n * Whether we're running in production mode.\n * In production, circular dependency detection is skipped for performance.\n */\nconst isProduction = process.env.NODE_ENV === 'production'\n\n/**\n * Detects circular dependencies by analyzing the waitingFor relationships\n * between service holders.\n *\n * Uses BFS to traverse the waitingFor graph starting from a target holder\n * and checks if following the chain leads back to the waiter, indicating a circular dependency.\n *\n * Note: In production (NODE_ENV === 'production'), detection is skipped for performance.\n */\nexport class CircularDetector {\n  /**\n   * Detects if waiting for `targetName` from `waiterName` would create a cycle.\n   *\n   * This works by checking if `targetName` (or any holder in its waitingFor chain)\n   * is currently waiting for `waiterName`. If so, waiting would create a deadlock.\n   *\n   * In production mode, this always returns null to skip the BFS traversal overhead.\n   *\n   * @param waiterName The name of the holder that wants to wait\n   * @param targetName The name of the holder being waited on\n   * @param getHolder Function to retrieve a holder by name\n   * @returns The cycle path if a cycle is detected, null otherwise\n   */\n  static detectCycle(\n    waiterName: string,\n    targetName: string,\n    getHolder: (name: string) => InstanceHolder | undefined,\n  ): string[] | null {\n    // Skip circular dependency detection in production for performance\n    if (isProduction) {\n      return null\n    }\n\n    // Use BFS to find if there's a path from targetName back to waiterName\n    const visited = new Set<string>()\n    const queue: Array<{ name: string; path: string[] }> = [\n      { name: targetName, path: [waiterName, targetName] },\n    ]\n\n    while (queue.length > 0) {\n      const { name: currentName, path } = queue.shift()!\n\n      // If we've reached back to the waiter, we have a cycle\n      if (currentName === waiterName) {\n        return path\n      }\n\n      // Skip if already visited\n      if (visited.has(currentName)) {\n        continue\n      }\n      visited.add(currentName)\n\n      // Get the holder and check what it's waiting for\n      const holder = getHolder(currentName)\n      if (!holder) {\n        continue\n      }\n\n      // Add all services this holder is waiting for to the queue\n      for (const waitingForName of holder.waitingFor) {\n        if (!visited.has(waitingForName)) {\n          queue.push({\n            name: waitingForName,\n            path: [...path, waitingForName],\n          })\n        }\n      }\n    }\n\n    // No path found from target back to waiter, no cycle\n    return null\n  }\n\n  /**\n   * Formats a cycle path into a human-readable string.\n   *\n   * @param cycle The cycle path (array of service names)\n   * @returns Formatted string like \"ServiceA -> ServiceB -> ServiceA\"\n   */\n  static formatCycle(cycle: string[]): string {\n    return cycle.join(' -> ')\n  }\n}\n\n"],"mappings":";;;;;;;;;GAiBA,IAAaI,mBAAb,MAAaA;;;;;;;;;;;;;IAcX,OAAOC,YACLC,YACAC,YACAC,WACiB;EAOjB,MAAMC,0BAAU,IAAIC,KAAAA;EACpB,MAAMC,QAAiD,CACrD;GAAEC,MAAML;GAAYM,MAAM,CAACP,YAAYC,WAAW;GAAC,CACpD;AAED,SAAOI,MAAMG,SAAS,GAAG;GACvB,MAAM,EAAEF,MAAMG,aAAaF,SAASF,MAAMK,OAAK;AAG/C,OAAID,gBAAgBT,WAClB,QAAOO;AAIT,OAAIJ,QAAQQ,IAAIF,YAAAA,CACd;AAEFN,WAAQS,IAAIH,YAAAA;GAGZ,MAAMI,SAASX,UAAUO,YAAAA;AACzB,OAAI,CAACI,OACH;AAIF,QAAK,MAAMC,kBAAkBD,OAAOE,WAClC,KAAI,CAACZ,QAAQQ,IAAIG,eAAAA,CACfT,OAAMW,KAAK;IACTV,MAAMQ;IACNP,MAAM,IAAIA,MAAMO,eAAe;IACjC,CAAA;;AAMN,SAAO;;;;;;;IAST,OAAOG,YAAYC,OAAyB;AAC1C,SAAOA,MAAMC,KAAK,OAAA"}