import { readdirSync, readFileSync, watch } from 'node:fs';
import { join } from 'node:path';
const STALE_MS = 90_000;
const DEFAULT_REFRESH_MS = 15_000;
/**
 * Watches the dashboard-daemons descriptor directory and exposes the
 * currently-online daemons (filtered by 90s heartbeat staleness).
 */
export class DaemonRegistry {
    dir;
    items = new Map();
    listeners = new Set();
    watcher;
    poller;
    refreshIntervalMs;
    constructor(dir, options = {}) {
        this.dir = dir;
        this.refreshIntervalMs = options.refreshIntervalMs ?? DEFAULT_REFRESH_MS;
    }
    async start() {
        this.refresh();
        if (!this.poller && this.refreshIntervalMs > 0) {
            this.poller = setInterval(() => this.refresh(), this.refreshIntervalMs);
            this.poller.unref?.();
        }
        try {
            this.watcher = watch(this.dir, { persistent: true }, () => this.refresh());
        }
        catch {
            // Directory may not exist yet — caller is expected to ensure it exists
            // or the dashboard runs with an empty registry until the daemon writes.
        }
    }
    stop() {
        this.watcher?.close();
        this.watcher = undefined;
        if (this.poller) {
            clearInterval(this.poller);
            this.poller = undefined;
        }
    }
    list() {
        const now = Date.now();
        return [...this.items.values()].filter(d => now - d.lastHeartbeat <= STALE_MS);
    }
    getByAppId(id) {
        const d = this.items.get(id);
        if (!d)
            return undefined;
        return Date.now() - d.lastHeartbeat > STALE_MS ? undefined : d;
    }
    on(fn) {
        this.listeners.add(fn);
        return () => this.listeners.delete(fn);
    }
    refresh() {
        let names = [];
        try {
            names = readdirSync(this.dir);
        }
        catch {
            return;
        }
        const next = new Map();
        for (const n of names) {
            if (!n.endsWith('.json'))
                continue;
            try {
                const d = JSON.parse(readFileSync(join(this.dir, n), 'utf8'));
                next.set(d.larkAppId, d);
            }
            catch {
                // Skip malformed / partially-written files
            }
        }
        this.items = next;
        const online = this.list();
        for (const fn of this.listeners) {
            try {
                fn(online);
            }
            catch { /* swallow */ }
        }
    }
}
//# sourceMappingURL=registry.js.map