(
candidates: string[],
listen: (socketPath: string) => Promise<net.Server>,
opts: { onRelocate?: (from: string, to: string, code: string) => void } = {},
)
| 671 | * direct mode (#974). Exported for testing. |
| 672 | */ |
| 673 | export async function bindFirstUsableSocket( |
| 674 | candidates: string[], |
| 675 | listen: (socketPath: string) => Promise<net.Server>, |
| 676 | opts: { onRelocate?: (from: string, to: string, code: string) => void } = {}, |
| 677 | ): Promise<{ server: net.Server; socketPath: string }> { |
| 678 | let lastErr: unknown; |
| 679 | for (let i = 0; i < candidates.length; i++) { |
| 680 | const socketPath = candidates[i]!; // i < length, so always defined |
| 681 | const isLast = i === candidates.length - 1; |
| 682 | try { |
| 683 | const server = await listen(socketPath); |
| 684 | return { server, socketPath }; |
| 685 | } catch (err) { |
| 686 | lastErr = err; |
| 687 | const code = (err as NodeJS.ErrnoException).code; |
| 688 | if (!isLast && code !== SOCKET_BIND_CONFLICT_CODE) { |
| 689 | opts.onRelocate?.(socketPath, candidates[i + 1]!, code ?? ''); // !isLast ⇒ i+1 in range |
| 690 | continue; |
| 691 | } |
| 692 | throw err; |
| 693 | } |
| 694 | } |
| 695 | // Only reachable with an empty candidate list — a programmer error. |
| 696 | throw lastErr ?? new Error('no socket candidates to bind'); |
| 697 | } |
| 698 | |
| 699 | function resolveIdleTimeoutMs(): number { |
| 700 | const raw = process.env.CODEGRAPH_DAEMON_IDLE_TIMEOUT_MS; |
no outgoing calls
no test coverage detected