UNPKG

@bigdigital/kiosk-content-sdk

Version:

A Firebase-powered Content Management System SDK optimized for kiosks with offline support, template management, and real-time connection monitoring

163 lines 5.88 kB
export class KioskConnection { constructor(options) { this.options = options; this.ws = null; this.isConnected = false; this.eventHandlers = new Map(); this.retryCount = 0; this.retryTimeout = null; this.isShuttingDown = false; this.maxRetries = options.maxRetries ?? 5; this.initialRetryDelay = options.retryDelay ?? 1000; this.debug = options.debug ?? false; } log(...args) { if (this.debug && !this.isShuttingDown) { console.log(...args); } } logError(...args) { if (this.debug && !this.isShuttingDown) { console.error(...args); } } connect() { if (this.ws) { this.disconnect(); } try { this.log('Attempting to connect to:', this.options.url); const wsUrl = new URL(this.options.url); wsUrl.searchParams.set('kioskId', this.options.kioskId); this.ws = new WebSocket(wsUrl.toString()); this.ws.onopen = () => { this.log('WebSocket connection established'); this.isConnected = true; this.retryCount = 0; this.options.onConnectionUpdate?.(true); this.emit('connected'); }; this.ws.onclose = (event) => { this.log(`WebSocket closed with code ${event.code}`); this.handleDisconnect('Connection closed', event); }; this.ws.onerror = (error) => { this.logError('WebSocket error:', error); this.handleDisconnect('Connection error', error); }; this.ws.onmessage = (event) => { if (event.data === 'ping') { if (this.ws?.readyState === WebSocket.OPEN) { this.ws.send('pong'); this.emit('ping'); } } else { try { const data = JSON.parse(event.data.toString()); this.emit('message', data); } catch (error) { this.logError('Error parsing message:', error); } } }; } catch (error) { this.logError('Error creating WebSocket:', error); this.handleDisconnect('Failed to create connection', error); } } handleDisconnect(reason, error) { if (!this.isConnected && this.retryCount > 0) return; this.isConnected = false; if (!this.isShuttingDown) { this.options.onConnectionUpdate?.(false); this.emit('disconnected', { reason, error }); } if (this.retryTimeout) { clearTimeout(this.retryTimeout); this.retryTimeout = null; } if (!this.isShuttingDown && this.retryCount < this.maxRetries) { const delay = this.initialRetryDelay * Math.pow(2, this.retryCount); this.log(`Attempting to reconnect in ${delay}ms (attempt ${this.retryCount + 1}/${this.maxRetries})`); this.retryTimeout = setTimeout(() => { this.retryCount++; this.connect(); }, delay); } else if (!this.isShuttingDown) { this.logError('Max retry attempts reached'); this.emit('maxRetriesExceeded'); } } disconnect() { return new Promise((resolve) => { this.isShuttingDown = true; // Clear any pending retry attempts if (this.retryTimeout) { clearTimeout(this.retryTimeout); this.retryTimeout = null; } // If there's no WebSocket or it's already closed, cleanup and resolve immediately if (!this.ws || this.ws.readyState === WebSocket.CLOSED) { this.cleanup(); resolve(); return; } // Set up one-time close handler for cleanup and resolution const handleClose = () => { this.cleanup(); resolve(); }; const currentWs = this.ws; currentWs.addEventListener('close', handleClose, { once: true }); // If the socket is open or connecting, close it if (currentWs.readyState === WebSocket.OPEN || currentWs.readyState === WebSocket.CONNECTING) { currentWs.close(); } }); } cleanup() { if (this.ws) { // Remove all event listeners this.ws.onopen = null; this.ws.onclose = null; this.ws.onerror = null; this.ws.onmessage = null; } this.ws = null; this.isConnected = false; this.retryCount = 0; if (!this.isShuttingDown) { this.options.onConnectionUpdate?.(false); this.emit('disconnected', { reason: 'Manual disconnect' }); } } on(event, callback) { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, new Set()); } this.eventHandlers.get(event)?.add(callback); return this; } emit(event, ...args) { const handlers = this.eventHandlers.get(event); if (!handlers || this.isShuttingDown) return; handlers.forEach(callback => { try { callback(...args); } catch (error) { this.logError('Error in event handler:', error); } }); } isConnectionOpen() { return this.isConnected && this.ws?.readyState === WebSocket.OPEN; } } //# sourceMappingURL=KioskConnection.js.map