find-available-port-lite
v0.1.3
Published
Encuentra un puerto libre con opciones (host, reintentos, rango, timeout, abort).
Maintainers
Readme
FIND FREE PORT
Encuentra rápido un puerto libre para tu servidor de desarrollo en Node.js. Minimalista pero robusto: soporta host, reintentos, rango de puertos, timeout y cancelación con AbortSignal. ESM por defecto. CLI opcional.
✨ Características
✅ ESM nativo ("type":"module")
🔁 Reintentos automáticos (retries)
🎯 Fallback a puerto aleatorio (randomOnConflict)
🌐 Host configurable (127.0.0.1, ::, 0.0.0.0, etc.)
📏 Rango de búsqueda (range: { min, max })
⏱️ Timeout (timeoutMs)
🛑 Cancelación con AbortSignal
🧪 Tipos (.d.ts) opcionales
🚀 Instalación npm install find-available-port-lite
o
pnpm add find-available-port-lite
o
yarn add find-available-port-lite
🧩 Uso (ESM) // index.mjs / index.js con "type":"module" import { findAvailablePort } from 'find-available-port-lite';
const port = await findAvailablePort(3000, { host: '127.0.0.1', retries: 3, randomOnConflict: true, timeoutMs: 3000 });
console.log(Usaremos el puerto: ${port});
Integración con un server HTTP simple:
import http from 'node:http'; import { findAvailablePort } from 'find-available-port-lite';
const server = http.createServer((req, res) => res.end('OK'));
const port = await findAvailablePort(3000);
server.listen(port, () => {
console.log(Servidor en http://localhost:${port});
});
🛠️ API findAvailablePort(desiredPort: number, opts?: FindPortOptions): Promise
interface FindPortOptions { host?: string; // por defecto '127.0.0.1' retries?: number; // por defecto 1 randomOnConflict?: boolean; // por defecto true (usa 0 si hay conflicto) range?: { min: number; max: number } // busca dentro de un rango si desiredPort falla timeoutMs?: number | null; // por defecto null (sin timeout) signal?: AbortSignal; // permite cancelar la operación }
Notas:
Si randomOnConflict es true, tras EADDRINUSE intenta con 0 (el SO elige un puerto libre).
Si definís range, primero intentará recorrer el rango.
host explícito ayuda a evitar sorpresas (IPv6, exposición externa, etc.).
⏯️ Ejemplos adicionales
Búsqueda en rango const port = await findAvailablePort(3000, { range: { min: 3000, max: 3100 }, retries: 0, // no hace falta reintentar si hay rango randomOnConflict: false });
Timeout + AbortSignal const ac = new AbortController();
const p = findAvailablePort(3000, { timeoutMs: 2000, signal: ac.signal });
// Si querés cancelar manualmente: // ac.abort();
const port = await p; // lanza con 'ETIMEDOUT' o 'EABORT' si corresponde
- Respetar process.env.PORT primero const desired = Number(process.env.PORT) || 3000; const port = await findAvailablePort(desired);
🖥️ CLI (opcional)
Instalación global o vía npx:
ejecución directa sin instalar globalmente
npx find-available-port-lite 3000
salida: imprime el puerto disponible (solo el número)
Ejemplos:
PORT=$(npx find-available-port-lite 3000) echo "Puerto elegido: $PORT"
⚠️ Errores comunes
EADDRINUSE: el puerto objetivo está en uso. El paquete reintenta según tu config.
ETIMEDOUT: no se pudo encontrar/abrir un puerto dentro del tiempo dado.
EABORT: cancelado por AbortSignal.
Manejálos con try/catch:
try { const port = await findAvailablePort(3000, { timeoutMs: 1500 }); } catch (err) { if (err.code === 'ETIMEDOUT') { // manejar timeout } throw err; }
📦 package.json sugerido (extracto) { "name": "find-available-port-lite", "version": "0.1.0", "type": "module", "exports": { ".": { "import": "./src/index.mjs" } }, "main": "./src/index.mjs", "types": "./types/index.d.ts", "bin": { "find-port": "./bin/find-port.mjs" }, "engines": { "node": ">=18" } }
Si querés soporte CJS, podés agregar un build .cjs y mapear en exports la entrada "require".
🧪 Tipos (TypeScript / JSDoc)
Incluí types/index.d.ts o anotá JSDoc en el código fuente. Ejemplo de types/index.d.ts:
export interface FindPortOptions { host?: string; retries?: number; randomOnConflict?: boolean; range?: { min: number; max: number }; timeoutMs?: number | null; signal?: AbortSignal; }
export function findAvailablePort(desiredPort: number, opts?: FindPortOptions): Promise;
🔬 Tests (idea mínima)
Simular EADDRINUSE: abre un server en 3000 y pedí 3000 → debe retornar otro puerto.
Probar range, timeoutMs y signal.
import test from 'node:test'; import assert from 'node:assert'; import http from 'node:http'; import { findAvailablePort } from 'find-available-port-lite';
test('devuelve otro puerto si 3000 está ocupado', async () => { const s = http.createServer().listen(3000); const port = await findAvailablePort(3000); s.close(); assert.notStrictEqual(port, 3000); });
📝 Licencia
MIT © Juan Pablo Maletti
🤝 Contribuir
PRs y issues bienvenidos. Ideas que podés sumar:
Backoff exponencial entre reintentos
Soporte dual ESM/CJS out-of-the-box
Métricas o callback onAttempt
Tests de estrés y CI
📣 Changelog
0.1.0 – Primera versión pública.
