/** * 投递回执广播 —— 前端单一 SSE 连接的服务端一侧(编码规范 §9.2)。 * * 它**不是第二条投递链路**:投递五诺仍由 `sys.event_delivery` 承担。 * 这里只在投递已落库、已送达**之后**,把「哪个目标动了」这一事实告诉在线的浏览器, * 让前端失效对应缓存、回服务端重取投影 —— 前端零过程状态,推送里不带业务载荷。 */ /** 动了的目标:与信封 `correlation.target` 同构;`kind: 'system'` 表示治理面的全局变更 */ export type Touch = { readonly kind: 'task' | 'workflow' | 'staff' | 'system'; readonly id: string; /** 变更缘由,仅用于前端选择失效哪一组键 */ readonly reason: string; }; export type Broadcaster = { publish(touch: Touch): void; subscribe(listener: (touch: Touch) => void): () => void; readonly size: number; }; export function createBroadcaster(): Broadcaster { const listeners = new Set<(touch: Touch) => void>(); return { publish(touch) { // 广播失败不得反噬业务:任何一个订阅者抛错都只影响它自己 for (const listener of listeners) { try { listener(touch); } catch { listeners.delete(listener); } } }, subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); }, get size() { return listeners.size; }, }; }