Repository Public
1/** In-process fan-out of `ServerEvent`s to the authenticated `/api/v1/events` WebSockets. */2import type WebSocket from 'ws';3import type { ServerEvent } from '@cr/shared';45const SLOW_CONSUMER_BYTES = 1024 * 1024;67interface Client {8 socket: WebSocket;9 userId: string;10}1112export class EventHub {13 private readonly clients = new Set<Client>();1415 add(socket: WebSocket, userId: string): () => void {16 const c: Client = { socket, userId };17 this.clients.add(c);18 return () => this.clients.delete(c);19 }20 get size(): number {21 return this.clients.size;22 }23 /** Sends to every socket of the user. Consumers that cannot keep up are dropped, not buffered. */24 publish(userId: string, event: ServerEvent): void {25 const text = JSON.stringify(event);26 for (const c of this.clients) {27 if (c.userId !== userId || c.socket.readyState !== c.socket.OPEN) continue;28 if (c.socket.bufferedAmount > SLOW_CONSUMER_BYTES) {29 c.socket.terminate();30 this.clients.delete(c);31 continue;32 }33 c.socket.send(text);34 }35 }36}37