export interface WorkerLifecycleDependencies { pollIntervalMs: number; runTick: () => Promise; closePool: () => Promise; onTickFailure: (cause: unknown) => void; graceMs?: number; } /** Owns worker tick scheduling and makes shutdown single-flight and bounded. */ export class WorkerLifecycle { private stopped = false; private timer: NodeJS.Timeout | undefined; private activeTick: Promise | undefined; private shutdownPromise: Promise | undefined; private readonly graceMs: number; constructor(private readonly deps: WorkerLifecycleDependencies) { this.graceMs = deps.graceMs ?? 25_000; } start(): void { if (!this.activeTick && !this.stopped) this.activeTick = this.tick(); } shutdown(): Promise { if (!this.shutdownPromise) this.shutdownPromise = this.stop(); return this.shutdownPromise; } private async tick(): Promise { try { await this.deps.runTick(); } catch (cause) { this.deps.onTickFailure(cause); } finally { if (!this.stopped) this.timer = setTimeout(() => { this.activeTick = this.tick(); }, this.deps.pollIntervalMs); } } private async stop(): Promise { this.stopped = true; if (this.timer) clearTimeout(this.timer); const work = (async () => { try { if (this.activeTick) await this.activeTick; await this.deps.closePool(); return 0; } catch { return 1; } })(); let timeout: NodeJS.Timeout | undefined; const timedOut = new Promise((resolve) => { timeout = setTimeout(() => resolve(1), this.graceMs); }); const result = await Promise.race([work, timedOut]); if (timeout) clearTimeout(timeout); return result; } }