| 128 | const DEFAULT_MAX_EVENT_BUFFER = 1000; |
| 129 | |
| 130 | export class WsConnection { |
| 131 | public readonly id: string; |
| 132 | |
| 133 | public readonly subscriptions = new Set<string>(); |
| 134 | |
| 135 | /** Last cursor each subscribed session was synced from (client-claimed). */ |
| 136 | public readonly cursorsBySession = new Map<string, SessionCursor>(); |
| 137 | |
| 138 | /** ISO 8601 UTC timestamp the socket was accepted at. */ |
| 139 | public readonly connectedAt: string; |
| 140 | |
| 141 | /** Peer address from the upgrade socket. Null when unavailable. */ |
| 142 | public readonly remoteAddress: string | null; |
| 143 | |
| 144 | /** `User-Agent` header from the upgrade request. Null when absent. */ |
| 145 | public readonly userAgent: string | null; |
| 146 | |
| 147 | private readonly socket: WebSocket; |
| 148 | private readonly logger: ILogService; |
| 149 | private readonly sessionClients: ISessionClientsService; |
| 150 | private readonly wsBroadcast: BufferReplaySource; |
| 151 | private readonly abortHandler: AbortHandler | undefined; |
| 152 | private readonly fsWatchHandler: FsWatchHandler | undefined; |
| 153 | private readonly terminalHandler: TerminalHandler | undefined; |
| 154 | private readonly pingIntervalMs: number; |
| 155 | private readonly pongTimeoutMs: number; |
| 156 | private readonly maxEventBufferSize: number; |
| 157 | |
| 158 | private pingTimer?: NodeJS.Timeout; |
| 159 | private pongTimer?: NodeJS.Timeout; |
| 160 | private closed = false; |
| 161 | private gotClientHello = false; |
| 162 | |
| 163 | constructor(opts: WsConnectionOptions) { |
| 164 | this.id = `conn_${ulid()}`; |
| 165 | this.connectedAt = new Date().toISOString(); |
| 166 | this.remoteAddress = opts.remoteAddress ?? null; |
| 167 | this.userAgent = opts.userAgent ?? null; |
| 168 | this.socket = opts.socket; |
| 169 | this.logger = opts.logger.child({ connId: this.id }); |
| 170 | this.sessionClients = opts.sessionClients; |
| 171 | this.wsBroadcast = opts.wsBroadcast; |
| 172 | this.abortHandler = opts.abortHandler; |
| 173 | this.fsWatchHandler = opts.fsWatchHandler; |
| 174 | this.terminalHandler = opts.terminalHandler; |
| 175 | this.pingIntervalMs = opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS; |
| 176 | this.pongTimeoutMs = opts.pongTimeoutMs ?? DEFAULT_PONG_TIMEOUT_MS; |
| 177 | this.maxEventBufferSize = opts.maxEventBufferSize ?? DEFAULT_MAX_EVENT_BUFFER; |
| 178 | |
| 179 | this.send( |
| 180 | buildServerHello({ |
| 181 | ws_connection_id: this.id, |
| 182 | protocol_version: WS_PROTOCOL_VERSION, |
| 183 | heartbeat_ms: this.pingIntervalMs, |
| 184 | max_event_buffer_size: this.maxEventBufferSize, |
| 185 | capabilities: { event_batching: false, compression: false }, |
| 186 | }), |
| 187 | ); |
nothing calls this directly
no outgoing calls
no test coverage detected