(server)
| 294 | } |
| 295 | |
| 296 | function probeServer(server) { |
| 297 | return new Promise(resolve => { |
| 298 | if (!server.wsUrl) { resolve({ ok: false, latencyMs: null }); return; } |
| 299 | let done = false; |
| 300 | const startedAt = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now(); |
| 301 | const finish = (ok) => { |
| 302 | if (done) return; |
| 303 | done = true; |
| 304 | const endedAt = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now(); |
| 305 | resolve({ |
| 306 | ok, |
| 307 | latencyMs: ok ? Math.max(1, Math.round(endedAt - startedAt)) : null, |
| 308 | }); |
| 309 | }; |
| 310 | try { |
| 311 | const ws = new WebSocket(server.wsUrl); |
| 312 | const timer = setTimeout(() => { try { ws.close(); } catch {} finish(false); }, HUB_PROBE_TIMEOUT_MS); |
| 313 | ws.onopen = () => { |
| 314 | try { |
| 315 | ws.send(JSON.stringify({ |
| 316 | type: 'hello', |
| 317 | clientInstanceId: `hub_probe_${Date.now().toString(36)}`, |
| 318 | token: server.token || '', |
| 319 | page: '/hub-probe', |
| 320 | userAgent: navigator.userAgent || '', |
| 321 | })); |
| 322 | } catch { |
| 323 | clearTimeout(timer); |
| 324 | finish(false); |
| 325 | } |
| 326 | }; |
| 327 | ws.onmessage = (event) => { |
| 328 | let msg; |
| 329 | try { msg = JSON.parse(event.data); } catch { return; } |
| 330 | if (!isAuthReadyMessage(msg)) return; |
| 331 | clearTimeout(timer); |
| 332 | try { ws.close(); } catch {} |
| 333 | finish(true); |
| 334 | }; |
| 335 | ws.onerror = () => { clearTimeout(timer); finish(false); }; |
| 336 | ws.onclose = () => { clearTimeout(timer); if (!done) finish(false); }; |
| 337 | } catch { finish(false); } |
| 338 | }); |
| 339 | } |
| 340 | |
| 341 | function applyHubProbeResult(server, result) { |
| 342 | const info = getHubProbeInfo(server.id); |
no test coverage detected