()
| 345 | } |
| 346 | |
| 347 | private async open(): Promise<void> { |
| 348 | const headers: Record<string, string> = { ...(this.args.headers ?? {}) } |
| 349 | if (this.args.authToken) { |
| 350 | headers.Authorization = `Bearer ${this.args.authToken}` |
| 351 | } |
| 352 | const ws = new WS(this.args.websocketUrl, { headers }) |
| 353 | this.ws = ws |
| 354 | |
| 355 | await timeoutPromise( |
| 356 | new Promise<void>((resolve, reject) => { |
| 357 | ws.once('open', () => resolve()) |
| 358 | ws.once('error', reject) |
| 359 | ws.once('close', () => |
| 360 | reject( |
| 361 | new Error( |
| 362 | `remote app server at ${this.args.websocketUrl} closed before open`, |
| 363 | ), |
| 364 | ), |
| 365 | ) |
| 366 | }), |
| 367 | this.args.connectTimeoutMs, |
| 368 | `timed out connecting to remote app server at ${this.args.websocketUrl}`, |
| 369 | ) |
| 370 | |
| 371 | const pendingEvents = await this.initialize(ws) |
| 372 | ws.on('message', data => this.handleRawMessage(String(data))) |
| 373 | ws.on('close', (_code, reason) => { |
| 374 | const text = reason?.toString() || 'connection closed' |
| 375 | this.close(`remote app server at ${this.args.websocketUrl} disconnected: ${text}`) |
| 376 | }) |
| 377 | ws.on('error', error => { |
| 378 | this.close( |
| 379 | `remote app server at ${this.args.websocketUrl} transport failed: ${error.message}`, |
| 380 | ) |
| 381 | }) |
| 382 | ws.send(jsonStringify({ method: 'initialized' } satisfies ClientNotification)) |
| 383 | |
| 384 | for (const event of pendingEvents) { |
| 385 | this.enqueueEvent(event) |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | private async initialize(ws: WS): Promise<RemoteAppServerEvent[]> { |
| 390 | const pendingEvents: RemoteAppServerEvent[] = [] |
no test coverage detected