()
| 242 | } |
| 243 | |
| 244 | async #connect(): Promise<void> { |
| 245 | let retries = 0; |
| 246 | do { |
| 247 | try { |
| 248 | const connectStartTime = performance.now(); |
| 249 | this.#socket = await this.#createSocket(); |
| 250 | this.emit('connect'); |
| 251 | |
| 252 | try { |
| 253 | await this.#initiator(); |
| 254 | |
| 255 | // Check if socket was closed/destroyed during initiator execution |
| 256 | if (!this.#socket || this.#socket.destroyed || !this.#socket.readable || !this.#socket.writable) { |
| 257 | const retryIn = this.#shouldReconnect(retries++, new SocketClosedUnexpectedlyError()); |
| 258 | if (typeof retryIn !== 'number') { throw retryIn; } |
| 259 | await setTimeout(retryIn); |
| 260 | this.emit('reconnecting'); |
| 261 | continue; |
| 262 | } |
| 263 | } catch (err) { |
| 264 | this.#socket.destroy(); |
| 265 | this.#socket = undefined; |
| 266 | throw err; |
| 267 | } |
| 268 | this.#isReady = true; |
| 269 | this.#socketEpoch++; |
| 270 | publish(CHANNELS.CONNECTION_READY, () => ({ |
| 271 | clientId: this.#clientId, |
| 272 | serverAddress: this.host, |
| 273 | serverPort: this.port, |
| 274 | createTimeMs: performance.now() - connectStartTime, |
| 275 | })); |
| 276 | this.emit('ready'); |
| 277 | } catch (err) { |
| 278 | const retryIn = this.#shouldReconnect(retries++, err as Error); |
| 279 | if (typeof retryIn !== 'number') { |
| 280 | throw retryIn; |
| 281 | } |
| 282 | |
| 283 | publish(CHANNELS.ERROR, () => ({ |
| 284 | error: err as Error, |
| 285 | origin: 'client', |
| 286 | internal: false, |
| 287 | clientId: this.#clientId |
| 288 | })); |
| 289 | this.emit('error', err); |
| 290 | await setTimeout(retryIn); |
| 291 | this.emit('reconnecting'); |
| 292 | } |
| 293 | } while (this.#isOpen && !this.#isReady); |
| 294 | } |
| 295 | |
| 296 | setMaintenanceTimeout(ms?: number) { |
| 297 | dbgMaintenance(`Set socket timeout to ${ms}`); |
no test coverage detected