import { EventEmitter } from 'events' import { WebSocketMessage, WebSocketMessageType } from './types' export class WebSocketClient extends EventEmitter { private url: string private clientId: string = `client-${Math.random().toString(36).substr(2, 9)}` private userId: string | null = null private connected: boolean = false private messageQueue: WebSocketMessage[] = [] private ws: WebSocket | null = null private reconnectAttempts: number = 0 private maxReconnectAttempts: number = 5 private reconnectDelay: number = 1000 constructor(url: string) { super() this.url = url } getClientId(): string { return this.clientId } isConnected(): boolean { return this.connected && this.ws !== null && this.ws.readyState === WebSocket.OPEN } getQueueLength(): number { return this.messageQueue.length } async connect(userId: string): Promise { this.userId = userId return new Promise((resolve, reject) => { try { this.ws = new WebSocket(this.url) this.ws.onopen = () => { this.connected = true this.reconnectAttempts = 0 this.emit('connected') this.flushMessageQueue() resolve() } this.ws.onmessage = (event) => { try { const message: WebSocketMessage = JSON.parse(event.data) this.emit(message.type, message.payload) this.emit('message', message) } catch (error) { console.error('Failed to parse message:', error) } } this.ws.onerror = (error) => { console.error('WebSocket error:', error) this.emit('error', error) reject(error) } this.ws.onclose = () => { this.connected = false this.emit('disconnected') this.attemptReconnect() } } catch (error) { reject(error) } }) } send(type: WebSocketMessageType, payload: Record): void { const message: WebSocketMessage = { type, payload, timestamp: Date.now(), clientId: this.clientId } if (this.isConnected() && this.ws) { this.ws.send(JSON.stringify(message)) } else { this.messageQueue.push(message) } } private flushMessageQueue(): void { while (this.messageQueue.length > 0) { const message = this.messageQueue.shift() if (message && this.ws) { this.ws.send(JSON.stringify(message)) } } } private attemptReconnect(): void { if (this.reconnectAttempts < this.maxReconnectAttempts) { this.reconnectAttempts++ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1) setTimeout(() => { if (this.userId) { this.connect(this.userId).catch(() => { // Retry will happen in onclose }) } }, delay) } } disconnect(): void { if (this.ws) { this.ws.close() } this.connected = false this.messageQueue = [] } }