UNPKG

@raphab3/hermes-notifier

Version:

JavaScript/React plugin for Hermes notifications system with SSE support

244 lines (240 loc) 8.58 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * Hermes Notifier - JavaScript/React Plugin * * A JavaScript library for integrating Hermes notifications into web applications * using Server-Sent Events (SSE) for real-time notifications. */ /** * Main HermesNotifier class for managing SSE connections and notifications */ class HermesNotifier { constructor(config) { this.eventSource = null; this.notificationHandlers = new Set(); this.connectionHandlers = new Set(); this.errorHandlers = new Set(); this.reconnectAttempts = 0; this.reconnectTimeout = null; this.connectionStatus = { connected: false, reconnecting: false }; this.config = { reconnectDelay: 5000, maxReconnectAttempts: 10, ...config, }; } /** * Connect to the Hermes notifications SSE stream */ async connect() { if (this.eventSource) { this.disconnect(); } try { const sseUrl = `${this.config.baseUrl}/sse/notifications/${encodeURIComponent(this.config.userId)}/`; this.eventSource = new EventSource(sseUrl); this.eventSource.onopen = () => { this.reconnectAttempts = 0; this.updateConnectionStatus({ connected: true, reconnecting: false }); }; this.eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); // Handle different message types if (data.type === 'notification' || data.type === 'new_notification') { const notification = data.notification || data; this.notifyHandlers(notification); } else if (data.type === 'heartbeat') { // Heartbeat - connection is alive console.debug('Hermes: Heartbeat received'); } } catch (error) { console.warn('Hermes: Failed to parse notification data:', error); } }; this.eventSource.onerror = (event) => { console.error('Hermes: SSE connection error:', event); this.updateConnectionStatus({ connected: false, reconnecting: this.reconnectAttempts < this.config.maxReconnectAttempts, error: 'Connection error' }); this.handleConnectionError(); }; } catch (error) { this.handleError(new Error(`Failed to connect: ${error}`)); } } /** * Disconnect from the SSE stream */ disconnect() { if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = null; } if (this.eventSource) { this.eventSource.close(); this.eventSource = null; } this.updateConnectionStatus({ connected: false, reconnecting: false }); } /** * Check if currently connected */ isConnected() { return this.connectionStatus.connected; } /** * Get current connection status */ getConnectionStatus() { return { ...this.connectionStatus }; } /** * Subscribe to notification events */ onNotification(handler) { this.notificationHandlers.add(handler); return () => this.notificationHandlers.delete(handler); } /** * Subscribe to connection status changes */ onConnectionChange(handler) { this.connectionHandlers.add(handler); return () => this.connectionHandlers.delete(handler); } /** * Subscribe to error events */ onError(handler) { this.errorHandlers.add(handler); return () => this.errorHandlers.delete(handler); } /** * Send a test notification (for development) */ async sendTestNotification(title, body) { try { const response = await fetch(`${this.config.baseUrl}/api/v1/notifications/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.config.token}`, }, body: JSON.stringify({ user_id: this.config.userId, title, body, source_system: 'hermes-notifier-plugin', priority: 'normal', channels: ['in_app'], }), }); if (!response.ok) { throw new Error(`Failed to send test notification: ${response.statusText}`); } } catch (error) { this.handleError(new Error(`Failed to send test notification: ${error}`)); } } /** * Mark a notification as read */ async markAsRead(notificationId) { try { const response = await fetch(`${this.config.baseUrl}/api/v1/notifications/${notificationId}/mark_as_read/`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.config.token}`, }, }); if (!response.ok) { throw new Error(`Failed to mark notification as read: ${response.statusText}`); } } catch (error) { this.handleError(new Error(`Failed to mark notification as read: ${error}`)); } } /** * Get unread notification count */ async getUnreadCount() { try { const response = await fetch(`${this.config.baseUrl}/api/v1/notifications/unread_count/?user_id=${encodeURIComponent(this.config.userId)}`, { headers: { 'Authorization': `Bearer ${this.config.token}`, }, }); if (!response.ok) { throw new Error(`Failed to get unread count: ${response.statusText}`); } const data = await response.json(); return data.unread_count || 0; } catch (error) { this.handleError(new Error(`Failed to get unread count: ${error}`)); return 0; } } notifyHandlers(notification) { this.notificationHandlers.forEach(handler => { try { handler(notification); } catch (error) { console.error('Hermes: Error in notification handler:', error); } }); } updateConnectionStatus(status) { this.connectionStatus = { ...this.connectionStatus, ...status }; this.connectionHandlers.forEach(handler => { try { handler(this.connectionStatus); } catch (error) { console.error('Hermes: Error in connection handler:', error); } }); } handleConnectionError() { if (this.reconnectAttempts < this.config.maxReconnectAttempts) { this.reconnectAttempts++; const delay = this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); console.log(`Hermes: Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`); this.reconnectTimeout = window.setTimeout(() => { this.connect(); }, delay); } else { this.handleError(new Error('Max reconnection attempts reached')); this.updateConnectionStatus({ connected: false, reconnecting: false, error: 'Max reconnection attempts reached' }); } } handleError(error) { console.error('Hermes:', error); this.errorHandlers.forEach(handler => { try { handler(error); } catch (handlerError) { console.error('Hermes: Error in error handler:', handlerError); } }); } } exports.HermesNotifier = HermesNotifier; exports.default = HermesNotifier; //# sourceMappingURL=index.js.map