// engine/workers/worker-heartbeat.ts — Heartbeat monitor for background workers (SPEC-137)
import { EventEmitter } from 'node:events';
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const DEFAULT_INTERVAL_MS = 30_000; // 30 seconds
const DEFAULT_MAX_SILENCE_MS = 90_000; // 3 x interval
// ---------------------------------------------------------------------------
// WorkerHeartbeat
// ---------------------------------------------------------------------------
/**
 * Monitors a single worker via periodic ping checks.
 * Emits 'worker-dead' when the worker has been silent beyond maxSilenceMs.
 * Uses setTimeout loops (not setInterval) for clean teardown.
 */
export class WorkerHeartbeat extends EventEmitter {
    workerName;
    intervalMs;
    maxSilenceMs;
    lastPing = null;
    timer = null;
    running = false;
    constructor(workerName, options) {
        super();
        this.workerName = workerName;
        this.intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;
        this.maxSilenceMs = options?.maxSilenceMs ?? DEFAULT_MAX_SILENCE_MS;
    }
    /** Start the heartbeat monitoring loop. Idempotent. */
    start() {
        if (this.running) {
            return;
        }
        this.running = true;
        this.scheduleCheck();
    }
    /** Stop the heartbeat loop. Safe to call multiple times. */
    stop() {
        this.running = false;
        if (this.timer !== null) {
            clearTimeout(this.timer);
            this.timer = null;
        }
    }
    /**
     * Called by the worker process to signal it is alive.
     * Resets the silence counter.
     */
    ping() {
        this.lastPing = new Date();
    }
    /** Returns true if the worker pinged recently (within maxSilenceMs). */
    isAlive() {
        if (this.lastPing === null) {
            return false;
        }
        return Date.now() - this.lastPing.getTime() < this.maxSilenceMs;
    }
    /** Returns the timestamp of the last ping, or null if never pinged. */
    getLastPing() {
        return this.lastPing;
    }
    // ---------------------------------------------------------------------------
    // Private
    // ---------------------------------------------------------------------------
    scheduleCheck() {
        if (!this.running) {
            return;
        }
        this.timer = setTimeout(() => {
            this.check();
        }, this.intervalMs);
    }
    check() {
        if (!this.running) {
            return;
        }
        if (this.lastPing !== null && !this.isAlive()) {
            this.emit('worker-dead', this.workerName);
        }
        this.scheduleCheck();
    }
}
//# sourceMappingURL=worker-heartbeat.js.map