()
| 236 | }; |
| 237 | |
| 238 | function connect() { |
| 239 | reconnectTimer = null; |
| 240 | if (disposed) { |
| 241 | return; |
| 242 | } |
| 243 | if (activeSocket) { |
| 244 | activeSocket.close(); |
| 245 | } |
| 246 | |
| 247 | const socket = connectFn(); |
| 248 | activeSocket = socket; |
| 249 | |
| 250 | const handleOpen = () => { |
| 251 | // Connection succeeded — reset backoff. |
| 252 | lastReconnectAttempt = 0; |
| 253 | onOpen?.(socket); |
| 254 | }; |
| 255 | |
| 256 | const handleDisconnect = () => { |
| 257 | // Guard against duplicate calls: browsers fire both "error" |
| 258 | // and "close" on a failed WebSocket, so we only process the |
| 259 | // first event per socket instance. |
| 260 | if (activeSocket !== socket || disposed) { |
| 261 | return; |
| 262 | } |
| 263 | activeSocket = null; |
| 264 | const reconnect = getReconnectSchedule({ |
| 265 | attempt: lastReconnectAttempt + 1, |
| 266 | baseMs, |
| 267 | maxMs, |
| 268 | factor, |
| 269 | jitter, |
| 270 | random, |
| 271 | }); |
| 272 | onDisconnect?.(reconnect); |
| 273 | scheduleReconnect(reconnect); |
| 274 | }; |
| 275 | |
| 276 | socket.addEventListener("open", handleOpen); |
| 277 | socket.addEventListener("error", handleDisconnect); |
| 278 | socket.addEventListener("close", handleDisconnect); |
| 279 | } |
| 280 | |
| 281 | // Kick off the first connection. |
| 282 | connect(); |
no test coverage detected