@oxog/port-terminator
Version:
Cross-platform utility to terminate processes on ports with zero dependencies
148 lines • 6.62 kB
JavaScript
import { ProcessFinder } from './core/process-finder';
import { ProcessKiller } from './core/process-killer';
import { validatePort, validatePorts, validateTimeout } from './utils/validators';
import { TimeoutError } from './errors';
import { Logger } from './utils/logger';
export class PortTerminator {
constructor(options = {}) {
this.processFinder = new ProcessFinder();
this.processKiller = new ProcessKiller();
this.options = {
method: options.method || 'both',
timeout: options.timeout || 30000,
force: options.force || false,
silent: options.silent || false,
gracefulTimeout: options.gracefulTimeout || 5000,
};
this.logger = new Logger('info', this.options.silent);
}
async terminate(port) {
const ports = Array.isArray(port) ? port : [port];
const validatedPorts = validatePorts(ports);
this.logger.info(`Terminating processes on port${ports.length > 1 ? 's' : ''}: ${validatedPorts.join(', ')}`);
const results = await this.terminateMultiple(validatedPorts);
return Array.from(results.values()).every((success) => success);
}
async terminateMultiple(ports) {
const validatedPorts = validatePorts(ports);
const results = new Map();
const protocol = this.options.method;
await Promise.all(validatedPorts.map(async (port) => {
try {
this.logger.debug(`Finding processes on port ${port}`);
const processes = await this.processFinder.findByPort(port, protocol);
if (processes.length === 0) {
this.logger.warn(`No processes found on port ${port}`);
results.set(port, true);
return;
}
this.logger.info(`Found ${processes.length} process(es) on port ${port}`);
const killedProcesses = await this.processKiller.killProcessesByPort(port, this.options.force, this.options.gracefulTimeout, protocol);
const success = killedProcesses.length === processes.length;
results.set(port, success);
if (success) {
this.logger.info(`Successfully terminated ${killedProcesses.length} process(es) on port ${port}`);
}
else {
this.logger.error(`Failed to terminate some processes on port ${port}`);
}
}
catch (error) {
this.logger.error(`Error terminating processes on port ${port}: ${error instanceof Error ? error.message : 'Unknown error'}`);
results.set(port, false);
}
}));
return results;
}
async getProcesses(port) {
const validatedPort = validatePort(port);
const protocol = this.options.method;
this.logger.debug(`Getting processes on port ${validatedPort}`);
return this.processFinder.findByPort(validatedPort, protocol);
}
async isPortAvailable(port) {
const validatedPort = validatePort(port);
const protocol = this.options.method;
return this.processFinder.isPortAvailable(validatedPort, protocol);
}
async waitForPort(port, timeout) {
const validatedPort = validatePort(port);
const timeoutMs = timeout ? validateTimeout(timeout) : this.options.timeout;
const protocol = this.options.method;
this.logger.debug(`Waiting for port ${validatedPort} to become available (timeout: ${timeoutMs}ms)`);
const success = await this.processFinder.waitForPortToBeAvailable(validatedPort, timeoutMs, protocol);
if (!success) {
throw new TimeoutError(`waitForPort(${validatedPort})`, timeoutMs);
}
return success;
}
async terminateWithDetails(ports) {
const validatedPorts = validatePorts(ports);
const results = [];
const protocol = this.options.method;
for (const port of validatedPorts) {
try {
const processes = await this.processFinder.findByPort(port, protocol);
if (processes.length === 0) {
results.push({
port,
success: true,
processes: [],
});
continue;
}
const killedProcesses = await this.processKiller.killProcessesByPort(port, this.options.force, this.options.gracefulTimeout, protocol);
results.push({
port,
success: killedProcesses.length === processes.length,
processes: killedProcesses,
});
}
catch (error) {
results.push({
port,
success: false,
processes: [],
error: error instanceof Error ? error.message : 'Unknown error',
});
}
}
return results;
}
}
// Convenience functions
export async function killPort(port, options = {}) {
const terminator = new PortTerminator(options);
return terminator.terminate(port);
}
export async function killPorts(ports, options = {}) {
const terminator = new PortTerminator(options);
return terminator.terminateMultiple(ports);
}
export async function getProcessOnPort(port, options = {}) {
const terminator = new PortTerminator(options);
const processes = await terminator.getProcesses(port);
return processes.length > 0 ? processes[0] : null;
}
export async function getProcessesOnPort(port, options = {}) {
const terminator = new PortTerminator(options);
return terminator.getProcesses(port);
}
export async function isPortAvailable(port, options = {}) {
const terminator = new PortTerminator(options);
return terminator.isPortAvailable(port);
}
export async function waitForPort(port, timeout, options = {}) {
const terminator = new PortTerminator(options);
return terminator.waitForPort(port, timeout);
}
// Re-export types and errors
export * from './types';
export * from './errors';
export { ProcessFinder } from './core/process-finder';
export { ProcessKiller } from './core/process-killer';
export { PortScanner } from './core/port-scanner';
export { Logger } from './utils/logger';
export { validatePort, validatePorts, parsePortRange } from './utils/validators';
export { getPlatform, isWindows, isMacOS, isLinux } from './utils/platform';
//# sourceMappingURL=index.js.map