/** * B3(壳侧 [ref]② 取证):SIGHUP 的 hup-pending 空闲窗。 * * SIGHUP(壳窗口关闭打到整个进程组)≠「没人在用这只引擎」:共享引擎形态(同 config 多壳会话,引擎按 * T11 防孤儿拍骑在首壳进程组里)下 peer 会话还活着——无条件 drain 会把 peer mid-turn 斩掉(499)且窗内 * 新提交 503。这里改为:SIGHUP 不翻 draining(peer 零感知),armed 一只空闲窗——`inflight()`(bg/resume * legs + live streams)连续 idleGraceMs 为 0 才 shutdown,任何在飞 leg 重置窗口。真孤儿(最后一窗关掉) * 有界自灭=T11 保留;第二个 SIGHUP 升级 escalate(既有 drain 路径,操作员硬梯子)。 * * 诚实边界:idle-but-live 的 peer(两 turn 之间静默超过窗口)会碰到引擎已退→壳 dead-respawn 1-2s 自愈 * (伤害面从 mid-turn 斩降级为冷启动)。若壳侧确认每活会话对引擎持长连,可把连接数并入 inflight 判据。 */ export interface SighupIdleDeps { /** This replica's in-flight legs (drainState.inflight). */ inflight: () => number; /** MONOTONIC-clock ms of the last non-probe HTTP request (drainState.lastActivityAt). Covers what inflight * can't: admitted-but-not-yet-registered requests (auth/prepare/claim phases) and sub-tick short requests — * and keeps an idle-but-live peer (still listing/browsing) alive. Absent ⇒ inflight-only semantics. * ⚠️ 时基契约(三路复审修3,seam 对齐 D 组):lastActivityAt() 必须与本模块的 `now` 同为 * performance.now() 单调域(server.ts 的 lastActivityAt 由 D 组切 performance.now())——混用 Date.now() * 域会让 `now() - lastActivity` 差出 epoch 量级,活性判据恒真/恒假。 */ lastActivityAt?: () => number; /** True once another shutdown owner is active (closing || draining) — the watch stands down. */ isStopped: () => boolean; /** Second SIGHUP → the normal drain path. */ escalate: () => void; /** Idle window expired → hard shutdown. */ shutdown: () => void; idleGraceMs: number; log: (event: string, fields: Record) => void; /** Watch poll period (test seam). Default 1s. */ tickMs?: number; /** 时钟 seam(三路复审修3):armedAt/idleSince/去抖判定全走它,缺省 `() => performance.now()`(单调)。 * 之前用 Date.now():NTP/手动前跳 > grace 会让 idle-elapsed 与 activity-quiet 两个检查同 tick 通过 → * 过早关停 peer;后跳让窗口等墙钟追回才到期;回拨还可能把操作员真第二信号误判进 1s 去抖窗。 * 本文件不对外报告 epoch 时间戳,故 Date.now() 一律不再出现。 */ now?: () => number; } /** * SIGHUP 处理器 + 它自己那只空闲窗的**停表口**。 * * 🔴 `stop()` 不是可选装饰(codex 交叉复审 F2,已复现):armed 之后这只 interval **刻意不 unref** * (孤儿形下它就是关停驱动),而它握着的 `shutdown`/`escalate` 是**装配那一刻**的闭包。只摘 SIGHUP 监听、 * 不停表 = 一个已经退役的装配仍会在 grace 到期时按旧 ctx 关停进程(实测:arm 后立刻摘监听,1.1s 后旧 * ctx 的 `server.close` 照样被调)。注册/摘除配对必须覆盖它。 */ export interface SighupIdleHandler { /** 信号处理函数本体:每次 SIGHUP 调一次(首次 arm 空闲窗,第二次 escalate)。 */ (): void; /** 停掉已 armed 的空闲窗(幂等;没 arm 过时无操作)。 */ stop(): void; } /** Returns the SIGHUP handler. Call it on every SIGHUP; it arms the idle watch on the first signal and * escalates on the second. The interval is deliberately NOT unref'd — in the orphan case it IS the * shutdown driver (same posture as the drain tick); {@link SighupIdleHandler.stop} is the退役口. */ export declare function createSighupIdleHandler(deps: SighupIdleDeps): SighupIdleHandler; //# sourceMappingURL=sighup-idle.d.ts.map