Files
biblical-guide.com/lib/websocket/client.ts

120 lines
3.0 KiB
TypeScript

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<void> {
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<string, any>): 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 = []
}
}