* Send event to all clients of a project
(projectId: string, event: RealtimeEvent)
| 56 | * Send event to all clients of a project |
| 57 | */ |
| 58 | public publish(projectId: string, event: RealtimeEvent): void { |
| 59 | websocketManager.broadcast(projectId, event); |
| 60 | |
| 61 | const projectStreams = this.streams.get(projectId); |
| 62 | if (!projectStreams || projectStreams.size === 0) { |
| 63 | return; |
| 64 | } |
| 65 | const message = `data: ${JSON.stringify(event)}\n\n`; |
| 66 | const encoder = new TextEncoder(); |
| 67 | const encodedMessage = encoder.encode(message); |
| 68 | |
| 69 | const deadControllers: ReadableStreamDefaultController[] = []; |
| 70 | |
| 71 | projectStreams.forEach((controller) => { |
| 72 | try { |
| 73 | controller.enqueue(encodedMessage); |
| 74 | } catch (error) { |
| 75 | console.error(`[StreamManager] Failed to send message:`, error); |
| 76 | // Mark for removal after iteration |
| 77 | deadControllers.push(controller); |
| 78 | } |
| 79 | }); |
| 80 | |
| 81 | // Remove dead connections after iteration |
| 82 | deadControllers.forEach((controller) => { |
| 83 | this.removeStream(projectId, controller); |
| 84 | }); |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * Return number of connected streams for a project |
no test coverage detected