* Open the WS socket, wait for `server_hello`, send `client_hello`, await * the ack. Returns the server's hello payload (heartbeat config, etc.).
()
| 370 | * the ack. Returns the server's hello payload (heartbeat config, etc.). |
| 371 | */ |
| 372 | async connect(): Promise<ServerHelloMessage['payload']> { |
| 373 | if (this._serverHello) return this._serverHello; |
| 374 | const wsUrl = `${this.baseUrl.replace(/^http/, 'ws')}${this.apiPrefix}/ws`; |
| 375 | const ws = new WsClient({ |
| 376 | url: wsUrl, |
| 377 | wsImpl: this._wsImpl, |
| 378 | logger: this._logger, |
| 379 | reportDir: this._reportDir, |
| 380 | }); |
| 381 | this._ws = ws; |
| 382 | await ws.open(); |
| 383 | |
| 384 | const helloFrame = await ws.waitForFrame( |
| 385 | (f) => f.type === 'server_hello', |
| 386 | this._controlAckTimeoutMs, |
| 387 | ); |
| 388 | const helloPayload = helloFrame.payload as ServerHelloMessage['payload']; |
| 389 | this._serverHello = helloPayload; |
| 390 | |
| 391 | const helloId = `hello-${ulid()}`; |
| 392 | const ack = await ws.sendAndAwaitAck( |
| 393 | { |
| 394 | type: 'client_hello', |
| 395 | id: helloId, |
| 396 | payload: { client_id: this.clientId, subscriptions: [] }, |
| 397 | }, |
| 398 | this._controlAckTimeoutMs, |
| 399 | ); |
| 400 | if (ack.code !== 0) { |
| 401 | throw new Error(`client_hello rejected (code=${ack.code}): ${ack.msg ?? 'no message'}`); |
| 402 | } |
| 403 | this._logger('debug', 'ws: handshake complete', { |
| 404 | wsConnectionId: helloPayload.ws_connection_id, |
| 405 | clientId: this.clientId, |
| 406 | }); |
| 407 | return helloPayload; |
| 408 | } |
| 409 | |
| 410 | /** Send `subscribe` and await its ack. Tracks the session for `close()`. */ |
| 411 | async subscribe(sid: string): Promise<void> { |