* Find an available TCP port starting from `start`. * Tries by actually binding a socket (most reliable cross-platform approach).
(start = 8888, maxAttempts = 20)
| 387 | * Tries by actually binding a socket (most reliable cross-platform approach). |
| 388 | */ |
| 389 | function findAvailablePort(start = 8888, maxAttempts = 20) { |
| 390 | for (let port = start; port < start + maxAttempts; port++) { |
| 391 | try { |
| 392 | const server = createServer(); |
| 393 | // Use a synchronous-like approach: try to listen, then close immediately |
| 394 | const result = new Promise((resolve, reject) => { |
| 395 | server.once('error', reject); |
| 396 | server.listen(port, '127.0.0.1', () => { |
| 397 | server.close(() => resolve(port)); |
| 398 | }); |
| 399 | }); |
| 400 | // We cannot await here (sync context), so use the blocking approach: |
| 401 | // Try to bind synchronously using a different technique. |
| 402 | server.close(); |
| 403 | } catch { |
| 404 | // fall through |
| 405 | } |
| 406 | } |
| 407 | // Synchronous fallback: try to connect; if connection refused, port is free. |
| 408 | for (let port = start; port < start + maxAttempts; port++) { |
| 409 | try { |
| 410 | execFileSync(process.execPath, [ |
| 411 | '-e', |
| 412 | `const s=require("net").createServer();` + |
| 413 | `s.listen(${port},"127.0.0.1",()=>{s.close();process.exit(0)});` + |
| 414 | `s.on("error",()=>process.exit(1))`, |
| 415 | ], { timeout: 3000, stdio: 'pipe' }); |
| 416 | return port; |
| 417 | } catch { |
| 418 | continue; |
| 419 | } |
| 420 | } |
| 421 | die(`No available ports found in range ${start}-${start + maxAttempts - 1}`); |
| 422 | } |
| 423 | |
| 424 | // --------------------------------------------------------------------------- |
| 425 | // PID file management |
no test coverage detected