85 lines
2.2 KiB
TypeScript
85 lines
2.2 KiB
TypeScript
import { log } from '@janhq/core/node'
|
|
import { spawn, ChildProcess } from 'child_process'
|
|
import { EventEmitter } from 'events'
|
|
|
|
interface WatchdogOptions {
|
|
cwd?: string
|
|
restartDelay?: number
|
|
maxRestarts?: number
|
|
env?: NodeJS.ProcessEnv
|
|
}
|
|
|
|
export class ProcessWatchdog extends EventEmitter {
|
|
private command: string
|
|
private args: string[]
|
|
private options: WatchdogOptions
|
|
private process: ChildProcess | null
|
|
private restartDelay: number
|
|
private maxRestarts: number
|
|
private restartCount: number
|
|
private isTerminating: boolean
|
|
|
|
constructor(command: string, args: string[], options: WatchdogOptions = {}) {
|
|
super()
|
|
this.command = command
|
|
this.args = args
|
|
this.options = options
|
|
this.process = null
|
|
this.restartDelay = options.restartDelay || 5000
|
|
this.maxRestarts = options.maxRestarts || 5
|
|
this.restartCount = 0
|
|
this.isTerminating = false
|
|
}
|
|
|
|
start(): void {
|
|
this.spawnProcess()
|
|
}
|
|
|
|
private spawnProcess(): void {
|
|
if (this.isTerminating) return
|
|
|
|
log(`Starting process: ${this.command} ${this.args.join(' ')}`)
|
|
this.process = spawn(this.command, this.args, this.options)
|
|
|
|
this.process.stdout?.on('data', (data: Buffer) => {
|
|
log(`Process output: ${data}`)
|
|
this.emit('output', data.toString())
|
|
})
|
|
|
|
this.process.stderr?.on('data', (data: Buffer) => {
|
|
log(`Process error: ${data}`)
|
|
this.emit('error', data.toString())
|
|
})
|
|
|
|
this.process.on('close', (code: number | null) => {
|
|
log(`Process exited with code ${code}`)
|
|
this.emit('close', code)
|
|
if (!this.isTerminating) {
|
|
this.restartProcess()
|
|
}
|
|
})
|
|
}
|
|
|
|
private restartProcess(): void {
|
|
if (this.restartCount < this.maxRestarts) {
|
|
this.restartCount++
|
|
log(
|
|
`Restarting process in ${this.restartDelay}ms (Attempt ${this.restartCount}/${this.maxRestarts})`
|
|
)
|
|
setTimeout(() => this.spawnProcess(), this.restartDelay)
|
|
} else {
|
|
log('Max restart attempts reached. Exiting watchdog.')
|
|
this.emit('maxRestartsReached')
|
|
}
|
|
}
|
|
|
|
terminate(): void {
|
|
this.isTerminating = true
|
|
if (this.process) {
|
|
log('Terminating watched process...')
|
|
this.process.kill()
|
|
}
|
|
this.emit('terminated')
|
|
}
|
|
}
|