API Reference
Complete API documentation for the port-resolver library.
Types
Result<T, E>
All methods return a Result type for explicit error handling:
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };PortConfig
Configuration for port allocation:
interface PortConfig {
minPort?: number; // Minimum port (default: 10000)
maxPort?: number; // Maximum port (default: 65535)
tag?: string; // Optional identifier
}PortEntry
Registry entry for an allocated port:
interface PortEntry {
port: number; // The allocated port number
pid: number; // Process ID that owns this port
tag?: string; // Optional tag for grouping
allocatedAt: string; // ISO 8601 timestamp
}PortAllocation
Result of a successful port allocation:
interface PortAllocation {
port: number; // The allocated port
entry: PortEntry; // Full registry entry
}RegistryStatus
Statistics about the port registry:
interface RegistryStatus {
totalEntries: number; // All entries in registry
activeEntries: number; // Entries with live processes
staleEntries: number; // Entries from dead processes
registryPath: string; // Path to registry file
}PortRegistry
Internal registry structure:
interface PortRegistry {
version: number; // Registry format version
entries: PortEntry[]; // All port entries
}Class: PortResolver
Main class for port allocation and management.
Constructor
new PortResolver(options?: {
registryPath?: string;
minPort?: number;
maxPort?: number;
})Parameters:
registryPath- Path to the registry file (default:/tmp/tuulbelt-port-registry.json)minPort- Default minimum port for allocations (default:10000)maxPort- Default maximum port for allocations (default:65535)
Example:
const resolver = new PortResolver({
registryPath: '/tmp/my-test-ports.json',
minPort: 8000,
maxPort: 9000,
});Methods
get(config?)
Allocate a single port.
async get(config?: PortConfig): Promise<Result<PortAllocation>>Parameters:
config.minPort- Override minimum portconfig.maxPort- Override maximum portconfig.tag- Tag for grouping/identification
Returns: Result<PortAllocation> containing the allocated port and entry
Example:
const result = await resolver.get({ tag: 'api-server' });
if (result.ok) {
console.log(`Port: ${result.value.port}`);
}getMultiple(count, config?)
Allocate multiple ports at once.
async getMultiple(
count: number,
config?: PortConfig
): Promise<Result<PortAllocation[]>>Parameters:
count- Number of ports to allocate (max: 100)config- Same asget()
Returns: Result<PortAllocation[]> with all allocated ports
Example:
const result = await resolver.getMultiple(3, { tag: 'services' });
if (result.ok) {
const [apiPort, dbPort, cachePort] = result.value.map(a => a.port);
}release(port)
Release a single allocated port.
async release(port: number): Promise<Result<void>>Parameters:
port- Port number to release
Returns: Result<void> indicating success or failure
Note: Only the process that allocated the port can release it.
Example:
const released = await resolver.release(54321);
if (!released.ok) {
console.error('Could not release port');
}releaseAll(tag?)
Release multiple ports.
async releaseAll(tag?: string): Promise<Result<number>>Parameters:
tag- Optional: only release ports with this tag
Returns: Result<number> with count of released ports
Example:
// Release all ports owned by this process
await resolver.releaseAll();
// Release only ports with specific tag
await resolver.releaseAll('api-server');list()
List all allocated ports in the registry.
async list(): Promise<Result<PortEntry[]>>Returns: Result<PortEntry[]> with all registry entries
Example:
const result = await resolver.list();
if (result.ok) {
for (const entry of result.value) {
console.log(`${entry.port}: ${entry.tag} (PID ${entry.pid})`);
}
}clean()
Remove stale entries from crashed processes.
async clean(): Promise<Result<number>>Returns: Result<number> with count of removed entries
Example:
const result = await resolver.clean();
if (result.ok) {
console.log(`Removed ${result.value} stale entries`);
}status()
Get registry statistics.
async status(): Promise<Result<RegistryStatus>>Returns: Result<RegistryStatus> with registry information
Example:
const result = await resolver.status();
if (result.ok) {
console.log(`Active: ${result.value.activeEntries}`);
console.log(`Stale: ${result.value.staleEntries}`);
}clear()
Remove all entries from the registry.
async clear(): Promise<Result<void>>Returns: Result<void>
Warning: This removes ALL entries, not just yours.
Example:
await resolver.clear();Standalone Functions
isPortAvailable(port)
Check if a specific port is available for binding.
async function isPortAvailable(port: number): Promise<boolean>Parameters:
port- Port number to check
Returns: true if port can be bound, false otherwise
Example:
import { isPortAvailable } from '@tuulbelt/port-resolver';
if (await isPortAvailable(3000)) {
console.log('Port 3000 is free');
}findAvailablePort(min, max)
Find any available port in a range.
async function findAvailablePort(
min: number,
max: number
): Promise<Result<number>>Parameters:
min- Minimum port numbermax- Maximum port number
Returns: Result<number> with an available port
Algorithm:
- Try 10 random ports in range
- Fall back to sequential scan if all random attempts fail
Example:
import { findAvailablePort } from '@tuulbelt/port-resolver';
const result = await findAvailablePort(8000, 9000);
if (result.ok) {
console.log(`Found port: ${result.value}`);
}Constants
const DEFAULT_REGISTRY_PATH = '/tmp/tuulbelt-port-registry.json';
const DEFAULT_MIN_PORT = 10000;
const DEFAULT_MAX_PORT = 65535;
const REGISTRY_VERSION = 1;
const MAX_PORTS_PER_REQUEST = 100;
const MAX_REGISTRY_ENTRIES = 10000;Error Handling
Common error scenarios:
// No ports available in range
{ ok: false, error: Error('No available port found in range 8000-8010') }
// Port not found for release
{ ok: false, error: Error('Port 54321 not found in registry') }
// Port owned by different process
{ ok: false, error: Error('Port 54321 is owned by PID 12345, not 67890') }
// Registry too large
{ ok: false, error: Error('Registry has too many entries (10001 > 10000)') }
// Too many ports requested
{ ok: false, error: Error('Cannot allocate more than 100 ports at once') }Thread Safety
The PortResolver uses file-based-semaphore-ts (semats) for atomic registry access. This ensures that concurrent port allocations from multiple processes never corrupt the registry file.
// Semaphore is used internally - no configuration needed
const resolver = new PortResolver();
// All registry operations are protected by semats