86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
import {ChildProcess} from 'node:child_process';
|
|
import type {IProcessManager, ProcessManagerEvents} from '../interfaces/IProcessManager.js';
|
|
import type {IProcessSpawner} from '../interfaces/IProcessSpawner.js';
|
|
import type {ILogger} from '../interfaces/ILogger.js';
|
|
import {EventEmitter} from 'node:events';
|
|
|
|
/**
|
|
* Manages process lifecycle
|
|
* Single Responsibility: Process management only
|
|
* Open/Closed: Can be extended without modification
|
|
*/
|
|
export class ProcessManager extends EventEmitter implements IProcessManager {
|
|
private activeProcess: ChildProcess | null = null;
|
|
|
|
constructor(
|
|
private readonly processSpawner: IProcessSpawner,
|
|
private readonly logger: ILogger
|
|
) {
|
|
super();
|
|
}
|
|
|
|
start(command: string, args: string[]): void {
|
|
if (this.isRunning()) {
|
|
throw new Error('Process is already running');
|
|
}
|
|
|
|
if (!command) {
|
|
throw new Error('Invalid command: command cannot be empty');
|
|
}
|
|
|
|
try {
|
|
this.activeProcess = this.processSpawner.spawn(command, args);
|
|
|
|
if (!this.activeProcess) {
|
|
throw new Error('Failed to spawn process');
|
|
}
|
|
|
|
this.setupEventHandlers();
|
|
} catch (error) {
|
|
this.logger.error('Failed to start process:', error);
|
|
this.activeProcess = null;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
stop(): void {
|
|
if (this.activeProcess) {
|
|
this.activeProcess.kill();
|
|
this.activeProcess = null;
|
|
}
|
|
}
|
|
|
|
isRunning(): boolean {
|
|
return this.activeProcess !== null && !this.activeProcess.killed;
|
|
}
|
|
|
|
private setupEventHandlers(): void {
|
|
if (!this.activeProcess) {
|
|
return;
|
|
}
|
|
|
|
this.activeProcess.stdout?.on('data', (data: Buffer) => {
|
|
this.logger.log(`stdout: ${data}`);
|
|
this.emit('data', data);
|
|
});
|
|
|
|
this.activeProcess.stderr?.on('data', (data: Buffer) => {
|
|
this.logger.log(`stderr: ${data}`);
|
|
this.emit('data', data);
|
|
});
|
|
|
|
this.activeProcess.on('close', (code: number | null) => {
|
|
this.logger.log(`child process exited with code ${code}`);
|
|
this.activeProcess = null;
|
|
this.emit('close', code);
|
|
});
|
|
|
|
this.activeProcess.on('error', (error: Error) => {
|
|
this.logger.error('Process error:', error);
|
|
this.activeProcess = null;
|
|
this.emit('error', error);
|
|
});
|
|
}
|
|
}
|
|
|