""" WebSocketStream — legacy bridge between the EventBus and WebSocket clients. NOTE: The real broadcasting is now done in backend/main.py via `_ws_broadcast` which properly awaits the async send. This class is kept for backward compatibility with the api.py facade, but its `attach_bus` method is a no-op (the actual wiring happens in main.py). F-15 remediation: the shared ``clients`` list is now guarded by a lock so concurrent register/unregister calls from multiple WS handlers can't race. """ from __future__ import annotations import threading from typing import Any, List from backend.events.bus import EventBus class WebSocketStream: def __init__(self) -> None: self._lock = threading.Lock() self.clients: List[Any] = [] # shared with main.py's hub.clients def attach_bus(self, bus: EventBus) -> None: """No-op — broadcasting is wired in backend/main.py via _ws_broadcast. Kept for backward compat with api.py's attach_ws_stream(). """ pass def register_client(self, client: Any) -> None: with self._lock: self.clients.append(client) def unregister_client(self, client: Any) -> None: with self._lock: try: self.clients.remove(client) except ValueError: pass def snapshot_clients(self) -> List[Any]: """Return a snapshot copy of the client list (safe for iteration).""" with self._lock: return list(self.clients)