@rivium/push-web 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +15 -2
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * RiviumPush Web SDK\n * Push notifications for browsers - Firebase alternative\n *\n * Features:\n * - Web Push API for background notifications\n * - MQTT over WebSocket for real-time foreground messages\n * - Service Worker integration\n * - Rich notifications with images, action buttons, and localization\n * - Analytics event tracking\n * - Detailed error codes and handling\n * - Network and app state monitoring\n * - Works without Firebase\n *\n * @packageDocumentation\n */\n\nimport {\n PNSocket,\n PNConfigBuilder,\n PNAuthFactory,\n PNState,\n PNDeliveryMode,\n PNMessage,\n PNError as PNProtocolError,\n PNConnectionListener,\n} from '@rivium/pn-protocol';\n\n// ============================================================================\n// Error Codes (matching Flutter SDK)\n// ============================================================================\n\n/**\n * Standardized error codes for RiviumPush SDK.\n * These codes help developers identify and handle specific error scenarios.\n */\nexport enum RiviumPushErrorCode {\n // Connection errors (1000-1099)\n CONNECTION_FAILED = 1000,\n CONNECTION_TIMEOUT = 1001,\n CONNECTION_LOST = 1002,\n CONNECTION_REFUSED = 1003,\n AUTHENTICATION_FAILED = 1004,\n SSL_ERROR = 1005,\n BROKER_UNAVAILABLE = 1006,\n\n // Subscription errors (1100-1199)\n SUBSCRIPTION_FAILED = 1100,\n UNSUBSCRIPTION_FAILED = 1101,\n INVALID_TOPIC = 1102,\n\n // Message errors (1200-1299)\n MESSAGE_DELIVERY_FAILED = 1200,\n MESSAGE_PARSE_ERROR = 1201,\n MESSAGE_TIMEOUT = 1202,\n\n // Configuration errors (1300-1399)\n INVALID_CONFIG = 1300,\n MISSING_API_KEY = 1301,\n /** @deprecated No longer used - server URL is internal */\n MISSING_SERVER_URL = 1302,\n INVALID_CREDENTIALS = 1303,\n\n // Registration errors (1400-1499)\n REGISTRATION_FAILED = 1400,\n DEVICE_ID_GENERATION_FAILED = 1401,\n SERVER_ERROR = 1402,\n NETWORK_ERROR = 1403,\n\n // State errors (1500-1599)\n NOT_INITIALIZED = 1500,\n NOT_CONNECTED = 1501,\n ALREADY_CONNECTED = 1502,\n SERVICE_NOT_RUNNING = 1503,\n\n // Permission errors (1600-1699)\n PERMISSION_DENIED = 1600,\n PERMISSION_DISMISSED = 1601,\n\n // Unknown error\n UNKNOWN_ERROR = 9999,\n}\n\n/**\n * Error code messages mapping\n */\nconst ERROR_MESSAGES: Record<RiviumPushErrorCode, string> = {\n [RiviumPushErrorCode.CONNECTION_FAILED]: 'Failed to connect to MQTT broker',\n [RiviumPushErrorCode.CONNECTION_TIMEOUT]: 'Connection timed out',\n [RiviumPushErrorCode.CONNECTION_LOST]: 'Connection to server was lost',\n [RiviumPushErrorCode.CONNECTION_REFUSED]: 'Connection was refused by server',\n [RiviumPushErrorCode.AUTHENTICATION_FAILED]: 'Authentication failed - invalid credentials',\n [RiviumPushErrorCode.SSL_ERROR]: 'SSL/TLS handshake failed',\n [RiviumPushErrorCode.BROKER_UNAVAILABLE]: 'MQTT broker is unavailable',\n [RiviumPushErrorCode.SUBSCRIPTION_FAILED]: 'Failed to subscribe to topic',\n [RiviumPushErrorCode.UNSUBSCRIPTION_FAILED]: 'Failed to unsubscribe from topic',\n [RiviumPushErrorCode.INVALID_TOPIC]: 'Invalid topic format',\n [RiviumPushErrorCode.MESSAGE_DELIVERY_FAILED]: 'Failed to deliver message',\n [RiviumPushErrorCode.MESSAGE_PARSE_ERROR]: 'Failed to parse message payload',\n [RiviumPushErrorCode.MESSAGE_TIMEOUT]: 'Message delivery timed out',\n [RiviumPushErrorCode.INVALID_CONFIG]: 'Invalid configuration',\n [RiviumPushErrorCode.MISSING_API_KEY]: 'API key is missing',\n [RiviumPushErrorCode.MISSING_SERVER_URL]: 'Server URL is missing',\n [RiviumPushErrorCode.INVALID_CREDENTIALS]: 'Invalid MQTT credentials',\n [RiviumPushErrorCode.REGISTRATION_FAILED]: 'Device registration failed',\n [RiviumPushErrorCode.DEVICE_ID_GENERATION_FAILED]: 'Failed to generate device ID',\n [RiviumPushErrorCode.SERVER_ERROR]: 'Server returned an error',\n [RiviumPushErrorCode.NETWORK_ERROR]: 'Network request failed',\n [RiviumPushErrorCode.NOT_INITIALIZED]: 'SDK is not initialized',\n [RiviumPushErrorCode.NOT_CONNECTED]: 'Not connected to server',\n [RiviumPushErrorCode.ALREADY_CONNECTED]: 'Already connected to server',\n [RiviumPushErrorCode.SERVICE_NOT_RUNNING]: 'Service worker is not running',\n [RiviumPushErrorCode.PERMISSION_DENIED]: 'Notification permission denied',\n [RiviumPushErrorCode.PERMISSION_DISMISSED]: 'Notification permission dismissed',\n [RiviumPushErrorCode.UNKNOWN_ERROR]: 'An unknown error occurred',\n};\n\n/**\n * Represents a RiviumPush error with code and additional details\n */\nexport class RiviumPushError extends Error {\n /** The error code */\n readonly code: RiviumPushErrorCode;\n /** Additional details about the error */\n readonly details?: string;\n\n constructor(code: RiviumPushErrorCode, details?: string) {\n super(ERROR_MESSAGES[code] || 'Unknown error');\n this.name = 'RiviumPushError';\n this.code = code;\n this.details = details;\n }\n\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n details: this.details,\n };\n }\n}\n\n// ============================================================================\n// Analytics Events (matching Flutter SDK)\n// ============================================================================\n\n/**\n * Analytics event types for tracking SDK usage.\n * Use with setAnalyticsHandler to track SDK events.\n */\nexport enum RiviumPushAnalyticsEvent {\n /** SDK was initialized */\n SDK_INITIALIZED = 'sdkInitialized',\n /** Device was registered */\n DEVICE_REGISTERED = 'deviceRegistered',\n /** Device was unregistered */\n DEVICE_UNREGISTERED = 'deviceUnregistered',\n /** Push message was received */\n MESSAGE_RECEIVED = 'messageReceived',\n /** Push message was displayed as notification */\n MESSAGE_DISPLAYED = 'messageDisplayed',\n /** Notification was clicked */\n NOTIFICATION_CLICKED = 'notificationClicked',\n /** Action button was clicked */\n ACTION_CLICKED = 'actionClicked',\n /** MQTT connected successfully */\n CONNECTED = 'connected',\n /** MQTT disconnected */\n DISCONNECTED = 'disconnected',\n /** Connection error occurred */\n CONNECTION_ERROR = 'connectionError',\n /** Retry attempt started (during exponential backoff) */\n RETRY_STARTED = 'retryStarted',\n /** Topic subscribed */\n TOPIC_SUBSCRIBED = 'topicSubscribed',\n /** Topic unsubscribed */\n TOPIC_UNSUBSCRIBED = 'topicUnsubscribed',\n /** Network state changed */\n NETWORK_STATE_CHANGED = 'networkStateChanged',\n /** App state changed (visible/hidden) */\n APP_STATE_CHANGED = 'appStateChanged',\n /** Permission requested */\n PERMISSION_REQUESTED = 'permissionRequested',\n /** Permission granted */\n PERMISSION_GRANTED = 'permissionGranted',\n /** Permission denied */\n PERMISSION_DENIED = 'permissionDenied',\n}\n\n// ============================================================================\n// Log Levels\n// ============================================================================\n\n/**\n * Log levels for the RiviumPush SDK.\n * Controls verbosity of logging output.\n */\nexport enum RiviumPushLogLevel {\n /** No logging at all (for production) */\n NONE = 0,\n /** Only errors */\n ERROR = 1,\n /** Errors and warnings */\n WARNING = 2,\n /** Errors, warnings, and info messages */\n INFO = 3,\n /** All messages including debug output (default for development) */\n DEBUG = 4,\n /** Everything including very detailed traces */\n VERBOSE = 5,\n}\n\n// ============================================================================\n// State Types\n// ============================================================================\n\n/**\n * Network type enumeration\n */\nexport enum NetworkType {\n WIFI = 'wifi',\n CELLULAR = 'cellular',\n ETHERNET = 'ethernet',\n NONE = 'none',\n UNKNOWN = 'unknown',\n}\n\n/**\n * Represents the current network state\n */\nexport interface NetworkState {\n /** Whether network is currently available */\n isAvailable: boolean;\n /** The type of network connection */\n networkType: NetworkType;\n /** Effective connection type (4g, 3g, 2g, slow-2g) */\n effectiveType?: string;\n /** Downlink speed in Mbps */\n downlink?: number;\n /** Round-trip time in ms */\n rtt?: number;\n}\n\n/**\n * Represents the app's visibility state\n */\nexport interface AppState {\n /** Whether the page is currently visible */\n isVisible: boolean;\n /** Visibility state: visible, hidden, prerender */\n visibilityState: DocumentVisibilityState;\n}\n\n/**\n * Represents the reconnection state during automatic retry\n */\nexport interface ReconnectionState {\n /** Current retry attempt number (0-based) */\n retryAttempt: number;\n /** Time in milliseconds until next retry */\n nextRetryMs: number;\n /** Maximum retry attempts */\n maxRetryAttempts: number;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for initializing RiviumPush Web SDK\n *\n * `apiKey` is required.\n * MQTT configuration is automatically fetched from the server during initialization.\n */\nexport interface RiviumPushConfig {\n /** Your RiviumPush API key (starts with rv_live_) - REQUIRED */\n apiKey: string;\n /** Path to RiviumPush service worker file */\n serviceWorkerPath?: string;\n /** VAPID public key for Web Push */\n vapidPublicKey?: string;\n /** Auto-register service worker (default: true) */\n autoRegisterServiceWorker?: boolean;\n /** MQTT QoS level (default: 1) */\n mqttQos?: 0 | 1 | 2;\n /** Maximum reconnect attempts (default: 10) */\n maxReconnectAttempts?: number;\n /** Initial log level (default: DEBUG in dev, ERROR in prod) */\n logLevel?: RiviumPushLogLevel;\n}\n\n/**\n * Internal MQTT configuration fetched from server\n */\ninterface MqttConfigInternal {\n host: string;\n wsHost?: string; // WebSocket host (for Cloudflare proxy)\n port: number;\n wsPort: number;\n // JWT token for authentication (provided at registration)\n token?: string;\n}\n\n/**\n * Notification action button\n */\nexport interface NotificationAction {\n /** Unique action identifier */\n id: string;\n /** Button display text */\n title: string;\n /** URL to open when action is clicked */\n action?: string;\n /** Icon for the action button */\n icon?: string;\n /** If true, action is marked as destructive */\n destructive?: boolean;\n /** If true, requires authentication */\n authRequired?: boolean;\n}\n\n/**\n * Localized content for i18n support\n */\nexport interface LocalizedContent {\n /** Locale code (e.g., 'en', 'fr', 'de') */\n locale: string;\n /** Localized title */\n title: string;\n /** Localized body */\n body: string;\n}\n\n/**\n * Push notification message with rich features\n */\nexport interface RiviumPushMessage {\n /** Notification title */\n title: string;\n /** Notification body */\n body: string;\n /** Custom data payload */\n data?: Record<string, any>;\n /** If true, message is delivered silently */\n silent?: boolean;\n // Rich notification features\n /** Large image URL */\n imageUrl?: string;\n /** Icon/avatar URL */\n iconUrl?: string;\n /** Action buttons (max 2 in browsers) */\n actions?: NotificationAction[];\n /** Deep link URL */\n deepLink?: string;\n // Badge management\n /** Badge count */\n badge?: number;\n /** Badge action: set, increment, decrement, clear */\n badgeAction?: 'set' | 'increment' | 'decrement' | 'clear';\n // Sound and grouping\n /** Custom sound name */\n sound?: string;\n /** Thread ID for grouping */\n threadId?: string;\n /** Collapse key for replacing notifications */\n collapseKey?: string;\n /** Category for filtering */\n category?: string;\n // Priority and TTL\n /** Priority: default, high, low */\n priority?: 'default' | 'high' | 'low';\n /** Time to live in seconds */\n ttl?: number;\n // Localization\n /** Localized content variations */\n localizations?: LocalizedContent[];\n /** Target timezone */\n timezone?: string;\n // Tracking\n /** Unique message ID */\n messageId?: string;\n /** Campaign ID for analytics */\n campaignId?: string;\n\n // Legacy fields for backwards compatibility\n /** @deprecated Use iconUrl instead */\n icon?: string;\n /** @deprecated Use imageUrl instead */\n image?: string;\n /** Notification tag for grouping (legacy) */\n tag?: string;\n}\n\n/**\n * Device registration options\n */\nexport interface RegisterOptions {\n /** User identifier */\n userId?: string;\n /** Additional metadata */\n metadata?: Record<string, string>;\n}\n\n/**\n * Connection state\n */\nexport type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error';\n\n// ============================================================================\n// Callback Types\n// ============================================================================\n\nexport type OnMessageCallback = (message: RiviumPushMessage) => void;\nexport type OnConnectionStateCallback = (state: ConnectionState) => void;\nexport type OnRegisteredCallback = (deviceId: string) => void;\nexport type OnErrorCallback = (error: Error) => void;\nexport type OnDetailedErrorCallback = (error: RiviumPushError) => void;\nexport type OnNotificationClickCallback = (message: RiviumPushMessage, action?: string) => void;\nexport type OnActionClickedCallback = (actionId: string, message: RiviumPushMessage) => void;\nexport type OnReconnectingCallback = (state: ReconnectionState) => void;\nexport type OnNetworkStateCallback = (state: NetworkState) => void;\nexport type OnAppStateCallback = (state: AppState) => void;\nexport type RiviumPushAnalyticsCallback = (\n event: RiviumPushAnalyticsEvent,\n properties?: Record<string, any>\n) => void;\n\n// ============================================================================\n// Internal Constants\n// ============================================================================\n\n/** Internal server URL - not configurable by users */\nconst RIVIUM_PUSH_SERVER_URL = 'https://push-api.rivium.co';\n\n// ============================================================================\n// RiviumPush Web SDK Class\n// ============================================================================\n\n/**\n * RiviumPush Web SDK - Push notifications for browsers\n *\n * @example\n * ```typescript\n * import RiviumPush from '@rivium/push-web';\n *\n * // Initialize with API key\n * // MQTT config is auto-fetched from server\n * const riviumPush = new RiviumPush({\n * apiKey: 'rv_live_your_api_key', // Get from Rivium Console\n * });\n *\n * // Set up analytics tracking\n * riviumPush.setAnalyticsHandler((event, properties) => {\n * console.log('Analytics:', event, properties);\n * });\n *\n * // Set up error handling\n * riviumPush.onDetailedError((error) => {\n * console.error('Error:', error.code, error.message, error.details);\n * });\n *\n * // Set up message handling\n * riviumPush.onMessage((message) => {\n * console.log('Received:', message.title);\n * });\n *\n * // Register device\n * await riviumPush.register({ userId: 'user123' });\n * ```\n */\nclass RiviumPush {\n private config: Required<Pick<RiviumPushConfig, 'apiKey'>> & RiviumPushConfig;\n private deviceId: string | null = null;\n private pnSocket: PNSocket | null = null;\n private serviceWorkerRegistration: ServiceWorkerRegistration | null = null;\n private pushSubscription: PushSubscription | null = null;\n private connectionState: ConnectionState = 'disconnected';\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 10;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private subscribedTopics: Set<string> = new Set();\n private badgeCount = 0;\n private initialized = false;\n private initialMessage: RiviumPushMessage | null = null;\n\n // MQTT configuration fetched from server\n private mqttConfig: MqttConfigInternal | null = null;\n private mqttConfigFetched = false;\n private appId: string | null = null; // For MQTT topics and token refresh\n private appIdentifier: string | null = null; // For per-app message routing\n\n // VAPID public key fetched from server (for Web Push background notifications)\n private vapidPublicKey: string | null = null;\n\n // Log level\n private logLevel: RiviumPushLogLevel = RiviumPushLogLevel.DEBUG;\n\n // Analytics\n private analyticsCallback: RiviumPushAnalyticsCallback | null = null;\n private analyticsEnabled = false;\n\n // Callbacks\n private onMessageCallback: OnMessageCallback | null = null;\n private onConnectionStateCallback: OnConnectionStateCallback | null = null;\n private onRegisteredCallback: OnRegisteredCallback | null = null;\n private onErrorCallback: OnErrorCallback | null = null;\n private onDetailedErrorCallback: OnDetailedErrorCallback | null = null;\n private onNotificationClickCallback: OnNotificationClickCallback | null = null;\n private onActionClickedCallback: OnActionClickedCallback | null = null;\n private onReconnectingCallback: OnReconnectingCallback | null = null;\n private onNetworkStateCallback: OnNetworkStateCallback | null = null;\n private onAppStateCallback: OnAppStateCallback | null = null;\n\n constructor(config: RiviumPushConfig) {\n if (!config.apiKey) {\n throw new Error('RiviumPush: apiKey is required');\n }\n this.config = {\n serviceWorkerPath: '/rivium-push-sw.js',\n autoRegisterServiceWorker: true,\n mqttQos: 1,\n maxReconnectAttempts: 10,\n logLevel: RiviumPushLogLevel.ERROR,\n ...config,\n };\n\n this.maxReconnectAttempts = this.config.maxReconnectAttempts!;\n this.logLevel = this.config.logLevel!;\n\n if (typeof window === 'undefined') {\n // SSR environment (Next.js server-side) - skip browser-only initialization\n this.initialized = true;\n return;\n }\n\n this.deviceId = this.getOrCreateDeviceId();\n this.badgeCount = parseInt(localStorage.getItem('rivium_push_badge_count') || '0', 10);\n\n // Check for initial message (from notification click that opened the page)\n this.checkInitialMessage();\n\n // Set up event listeners\n // Listen for messages from service worker\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerMessage.bind(this));\n }\n\n // Listen for visibility changes (app state)\n document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));\n\n // Listen for network changes\n if ('connection' in navigator) {\n (navigator as any).connection?.addEventListener('change', this.handleNetworkChange.bind(this));\n }\n window.addEventListener('online', this.handleOnline.bind(this));\n window.addEventListener('offline', this.handleOffline.bind(this));\n\n this.initialized = true;\n this.trackEvent(RiviumPushAnalyticsEvent.SDK_INITIALIZED);\n\n this.log(RiviumPushLogLevel.INFO, 'RiviumPush SDK initialized');\n\n // Fetch MQTT config from server\n this.fetchMqttConfig();\n }\n\n /**\n * Fetch MQTT and VAPID configuration from server\n */\n private async fetchMqttConfig(): Promise<void> {\n try {\n this.log(RiviumPushLogLevel.DEBUG, 'Fetching config from server...');\n\n const response = await fetch(`${RIVIUM_PUSH_SERVER_URL}/devices/config`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n this.mqttConfig = data.mqtt;\n this.mqttConfigFetched = true;\n\n // Store VAPID public key for Web Push\n if (data.vapidPublicKey) {\n this.vapidPublicKey = data.vapidPublicKey;\n this.log(RiviumPushLogLevel.DEBUG, 'VAPID public key received from server');\n }\n\n this.log(RiviumPushLogLevel.INFO, `Config fetched successfully, vapid=${!!this.vapidPublicKey}`);\n } catch (error) {\n this.log(RiviumPushLogLevel.ERROR, 'Failed to fetch config:', error);\n this.emitError(RiviumPushErrorCode.INVALID_CONFIG, `Failed to fetch config: ${(error as Error).message}`);\n }\n }\n\n // ==========================================================================\n // Logging\n // ==========================================================================\n\n private log(level: RiviumPushLogLevel, message: string, ...args: any[]): void {\n if (level > this.logLevel) return;\n\n const prefix = '[RiviumPush]';\n switch (level) {\n case RiviumPushLogLevel.ERROR:\n console.error(prefix, message, ...args);\n break;\n case RiviumPushLogLevel.WARNING:\n console.warn(prefix, message, ...args);\n break;\n case RiviumPushLogLevel.INFO:\n console.info(prefix, message, ...args);\n break;\n case RiviumPushLogLevel.DEBUG:\n case RiviumPushLogLevel.VERBOSE:\n console.log(prefix, message, ...args);\n break;\n }\n }\n\n /**\n * Set the log level for SDK logging.\n *\n * @example\n * ```typescript\n * // In production, reduce logging\n * riviumPush.setLogLevel(RiviumPushLogLevel.ERROR);\n * ```\n */\n setLogLevel(level: RiviumPushLogLevel): void {\n this.logLevel = level;\n this.log(RiviumPushLogLevel.INFO, `Log level set to ${RiviumPushLogLevel[level]}`);\n }\n\n /**\n * Get current log level\n */\n getLogLevel(): RiviumPushLogLevel {\n return this.logLevel;\n }\n\n // ==========================================================================\n // Analytics\n // ==========================================================================\n\n private trackEvent(event: RiviumPushAnalyticsEvent, properties?: Record<string, any>): void {\n if (this.analyticsEnabled && this.analyticsCallback) {\n try {\n this.analyticsCallback(event, properties);\n } catch (e) {\n this.log(RiviumPushLogLevel.ERROR, 'Analytics callback error:', e);\n }\n }\n this.log(RiviumPushLogLevel.VERBOSE, `Analytics event: ${event}`, properties);\n }\n\n /**\n * Enable analytics tracking with a custom handler.\n *\n * @example\n * ```typescript\n * riviumPush.setAnalyticsHandler((event, properties) => {\n * // Send to your analytics service\n * analytics.track(`rivium_push_${event}`, properties);\n * });\n * ```\n */\n setAnalyticsHandler(callback: RiviumPushAnalyticsCallback): void {\n this.analyticsCallback = callback;\n this.analyticsEnabled = true;\n this.log(RiviumPushLogLevel.INFO, 'Analytics handler set');\n }\n\n /**\n * Disable analytics tracking\n */\n disableAnalytics(): void {\n this.analyticsCallback = null;\n this.analyticsEnabled = false;\n this.log(RiviumPushLogLevel.INFO, 'Analytics disabled');\n }\n\n /**\n * Check if analytics tracking is enabled\n */\n isAnalyticsEnabled(): boolean {\n return this.analyticsEnabled;\n }\n\n // ==========================================================================\n // Error Handling\n // ==========================================================================\n\n private emitError(code: RiviumPushErrorCode, details?: string): void {\n const error = new RiviumPushError(code, details);\n this.log(RiviumPushLogLevel.ERROR, `Error [${code}]: ${error.message}`, details);\n\n if (this.onDetailedErrorCallback) {\n this.onDetailedErrorCallback(error);\n }\n if (this.onErrorCallback) {\n this.onErrorCallback(error);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.CONNECTION_ERROR, {\n errorCode: code,\n errorMessage: error.message,\n details,\n });\n }\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n /**\n * Register device for push notifications\n */\n async register(options?: RegisterOptions): Promise<string> {\n try {\n // Wait for config to be fetched (includes VAPID key)\n if (!this.mqttConfigFetched) {\n this.log(RiviumPushLogLevel.DEBUG, 'Waiting for server config...');\n await this.waitForConfig();\n }\n\n // Register service worker if enabled\n if (this.config.autoRegisterServiceWorker) {\n await this.registerServiceWorker();\n }\n\n // Request notification permission\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_REQUESTED);\n const permission = await this.requestNotificationPermission();\n\n if (permission === 'denied') {\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_DENIED);\n this.emitError(RiviumPushErrorCode.PERMISSION_DENIED);\n throw new RiviumPushError(RiviumPushErrorCode.PERMISSION_DENIED);\n }\n\n if (permission === 'default') {\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_DENIED);\n this.emitError(RiviumPushErrorCode.PERMISSION_DISMISSED, 'User dismissed the permission prompt');\n throw new RiviumPushError(RiviumPushErrorCode.PERMISSION_DISMISSED);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_GRANTED);\n\n // Get Web Push subscription for background notifications\n // Use VAPID key from server (preferred) or from config\n const vapidKey = this.vapidPublicKey || this.config.vapidPublicKey;\n if (vapidKey && this.serviceWorkerRegistration) {\n try {\n this.pushSubscription = await this.subscribeToPush(vapidKey);\n this.log(RiviumPushLogLevel.INFO, 'Web Push subscription created for background notifications');\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Web Push subscription failed (will use MQTT only):', e);\n }\n }\n\n // Register with backend\n const response = await this.registerDevice(options);\n this.deviceId = response.deviceId;\n\n // Connect MQTT for real-time messages\n this.connectToGateway();\n\n if (this.onRegisteredCallback) {\n this.onRegisteredCallback(response.deviceId);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.DEVICE_REGISTERED, {\n deviceId: response.deviceId,\n userId: options?.userId,\n hasWebPush: !!this.pushSubscription,\n });\n\n this.log(RiviumPushLogLevel.INFO, 'Registered with device ID:', response.deviceId, 'Web Push:', !!this.pushSubscription);\n return response.deviceId;\n } catch (error) {\n if (error instanceof RiviumPushError) {\n throw error;\n }\n this.log(RiviumPushLogLevel.ERROR, 'Registration failed:', error);\n this.emitError(RiviumPushErrorCode.REGISTRATION_FAILED, (error as Error).message);\n throw error;\n }\n }\n\n /**\n * Wait for config to be fetched from server\n */\n private async waitForConfig(timeoutMs = 5000): Promise<void> {\n const startTime = Date.now();\n while (!this.mqttConfigFetched && Date.now() - startTime < timeoutMs) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n if (!this.mqttConfigFetched) {\n this.log(RiviumPushLogLevel.WARNING, 'Config fetch timed out, continuing without it');\n }\n }\n\n /**\n * Unregister device and disconnect\n */\n async unregister(): Promise<void> {\n this.disconnectFromGateway();\n\n if (this.pushSubscription) {\n await this.pushSubscription.unsubscribe();\n this.pushSubscription = null;\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.DEVICE_UNREGISTERED, {\n deviceId: this.deviceId,\n });\n\n this.log(RiviumPushLogLevel.INFO, 'Unregistered');\n }\n\n /**\n * Subscribe to a topic\n */\n async subscribeTopic(topic: string): Promise<void> {\n if (!topic || topic.trim() === '') {\n this.emitError(RiviumPushErrorCode.INVALID_TOPIC, 'Topic cannot be empty');\n return;\n }\n\n this.subscribedTopics.add(topic);\n\n // Register topic subscription on server (for Web Push delivery via sendToTopic)\n if (this.deviceId) {\n try {\n await fetch(`${RIVIUM_PUSH_SERVER_URL}/topics/subscribe`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n body: JSON.stringify({ deviceId: this.deviceId, topic }),\n });\n } catch (err) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to register topic on server:', err);\n }\n }\n\n // Also subscribe via MQTT for real-time foreground messages\n if (this.pnSocket && this.pnSocket.isConnected()) {\n const appId = this.config.apiKey.substring(0, 16);\n const channel = `rivium_push/${appId}/topic/${topic}`;\n this.pnSocket.stream(channel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.INFO, 'Subscribed to topic:', topic);\n this.trackEvent(RiviumPushAnalyticsEvent.TOPIC_SUBSCRIBED, { topic });\n }\n }\n\n /**\n * Unsubscribe from a topic\n */\n async unsubscribeTopic(topic: string): Promise<void> {\n this.subscribedTopics.delete(topic);\n\n // Unregister topic on server\n if (this.deviceId) {\n try {\n await fetch(`${RIVIUM_PUSH_SERVER_URL}/topics/unsubscribe`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n body: JSON.stringify({ deviceId: this.deviceId, topic }),\n });\n } catch (err) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to unregister topic on server:', err);\n }\n }\n\n if (this.pnSocket && this.pnSocket.isConnected()) {\n const appId = this.config.apiKey.substring(0, 16);\n const channel = `rivium_push/${appId}/topic/${topic}`;\n this.pnSocket.detach(channel);\n this.log(RiviumPushLogLevel.INFO, 'Unsubscribed from topic:', topic);\n this.trackEvent(RiviumPushAnalyticsEvent.TOPIC_UNSUBSCRIBED, { topic });\n }\n }\n\n /**\n * Check if connected to MQTT broker\n */\n isConnected(): boolean {\n return this.connectionState === 'connected';\n }\n\n /**\n * Get current device ID\n */\n getDeviceId(): string | null {\n return this.deviceId;\n }\n\n /**\n * Set user ID\n */\n async setUserId(userId: string): Promise<void> {\n localStorage.setItem('rivium_push_user_id', userId);\n\n // Re-register with new user ID\n await this.registerDevice({ userId });\n this.log(RiviumPushLogLevel.INFO, 'User ID set:', userId);\n }\n\n /**\n * Clear user ID\n */\n clearUserId(): void {\n localStorage.removeItem('rivium_push_user_id');\n this.log(RiviumPushLogLevel.INFO, 'User ID cleared');\n }\n\n /**\n * Get the message that launched/opened the app (when user tapped a notification)\n * Returns null if the app was not opened from a notification tap\n */\n getInitialMessage(): RiviumPushMessage | null {\n return this.initialMessage;\n }\n\n /**\n * Get current badge count\n */\n getBadgeCount(): number {\n return this.badgeCount;\n }\n\n /**\n * Set badge count\n */\n setBadgeCount(count: number): void {\n this.badgeCount = Math.max(0, count);\n localStorage.setItem('rivium_push_badge_count', this.badgeCount.toString());\n\n // Update favicon badge\n this.updateFaviconBadge(this.badgeCount);\n\n // Use Badge API if available\n if ('setAppBadge' in navigator) {\n if (this.badgeCount > 0) {\n (navigator as any).setAppBadge(this.badgeCount);\n } else {\n (navigator as any).clearAppBadge();\n }\n }\n }\n\n /**\n * Clear badge\n */\n clearBadge(): void {\n this.setBadgeCount(0);\n }\n\n /**\n * Refresh MQTT JWT token (called automatically when token expires)\n * Can also be called manually if needed\n */\n async refreshMqttToken(): Promise<void> {\n if (!this.deviceId) {\n throw new RiviumPushError(RiviumPushErrorCode.NOT_INITIALIZED, 'Device not registered');\n }\n\n try {\n const response = await fetch(`${RIVIUM_PUSH_SERVER_URL}/devices/${this.deviceId}/mqtt-token/refresh`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new RiviumPushError(\n RiviumPushErrorCode.SERVER_ERROR,\n errorData.message || `HTTP ${response.status}`\n );\n }\n\n const data = await response.json();\n\n if (data.token && this.mqttConfig) {\n this.mqttConfig.token = data.token;\n this.log(RiviumPushLogLevel.INFO, 'MQTT token refreshed successfully');\n }\n } catch (error) {\n if (error instanceof RiviumPushError) throw error;\n throw new RiviumPushError(RiviumPushErrorCode.NETWORK_ERROR, (error as Error).message);\n }\n }\n\n /**\n * Get current network state\n */\n getNetworkState(): NetworkState {\n const connection = (navigator as any).connection;\n return {\n isAvailable: navigator.onLine,\n networkType: this.detectNetworkType(),\n effectiveType: connection?.effectiveType,\n downlink: connection?.downlink,\n rtt: connection?.rtt,\n };\n }\n\n /**\n * Get current app (visibility) state\n */\n getAppState(): AppState {\n return {\n isVisible: document.visibilityState === 'visible',\n visibilityState: document.visibilityState,\n };\n }\n\n /**\n * Check if notifications are supported\n */\n static isSupported(): boolean {\n return (\n typeof window !== 'undefined' &&\n 'Notification' in window &&\n 'serviceWorker' in navigator\n );\n }\n\n /**\n * Get current notification permission status\n */\n static getPermissionStatus(): NotificationPermission {\n if (typeof Notification === 'undefined') {\n return 'denied';\n }\n return Notification.permission;\n }\n\n // ==========================================================================\n // Event Listeners\n // ==========================================================================\n\n /**\n * Set callback for receiving messages\n */\n onMessage(callback: OnMessageCallback): () => void {\n this.onMessageCallback = callback;\n return () => {\n this.onMessageCallback = null;\n };\n }\n\n /**\n * Set callback for connection state changes\n */\n onConnectionState(callback: OnConnectionStateCallback): () => void {\n this.onConnectionStateCallback = callback;\n return () => {\n this.onConnectionStateCallback = null;\n };\n }\n\n /**\n * Set callback for registration success\n */\n onRegistered(callback: OnRegisteredCallback): () => void {\n this.onRegisteredCallback = callback;\n return () => {\n this.onRegisteredCallback = null;\n };\n }\n\n /**\n * Set callback for errors (simple)\n */\n onError(callback: OnErrorCallback): () => void {\n this.onErrorCallback = callback;\n return () => {\n this.onErrorCallback = null;\n };\n }\n\n /**\n * Set callback for detailed errors with error codes\n */\n onDetailedError(callback: OnDetailedErrorCallback): () => void {\n this.onDetailedErrorCallback = callback;\n return () => {\n this.onDetailedErrorCallback = null;\n };\n }\n\n /**\n * Set callback for notification clicks\n */\n onNotificationClick(callback: OnNotificationClickCallback): () => void {\n this.onNotificationClickCallback = callback;\n return () => {\n this.onNotificationClickCallback = null;\n };\n }\n\n /**\n * Set callback for action button clicks\n */\n onActionClicked(callback: OnActionClickedCallback): () => void {\n this.onActionClickedCallback = callback;\n return () => {\n this.onActionClickedCallback = null;\n };\n }\n\n /**\n * Set callback for reconnection state changes\n */\n onReconnecting(callback: OnReconnectingCallback): () => void {\n this.onReconnectingCallback = callback;\n return () => {\n this.onReconnectingCallback = null;\n };\n }\n\n /**\n * Set callback for network state changes\n */\n onNetworkState(callback: OnNetworkStateCallback): () => void {\n this.onNetworkStateCallback = callback;\n return () => {\n this.onNetworkStateCallback = null;\n };\n }\n\n /**\n * Set callback for app state changes (visibility)\n */\n onAppState(callback: OnAppStateCallback): () => void {\n this.onAppStateCallback = callback;\n return () => {\n this.onAppStateCallback = null;\n };\n }\n\n // ==========================================================================\n // Private Methods - Network & App State\n // ==========================================================================\n\n private detectNetworkType(): NetworkType {\n const connection = (navigator as any).connection;\n if (!connection) return NetworkType.UNKNOWN;\n\n const type = connection.type;\n switch (type) {\n case 'wifi':\n return NetworkType.WIFI;\n case 'cellular':\n return NetworkType.CELLULAR;\n case 'ethernet':\n return NetworkType.ETHERNET;\n case 'none':\n return NetworkType.NONE;\n default:\n return NetworkType.UNKNOWN;\n }\n }\n\n private handleNetworkChange(): void {\n const state = this.getNetworkState();\n this.log(RiviumPushLogLevel.DEBUG, 'Network state changed:', state);\n\n if (this.onNetworkStateCallback) {\n this.onNetworkStateCallback(state);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.NETWORK_STATE_CHANGED, {\n isAvailable: state.isAvailable,\n networkType: state.networkType,\n effectiveType: state.effectiveType,\n });\n }\n\n private handleOnline(): void {\n this.log(RiviumPushLogLevel.INFO, 'Network online');\n this.handleNetworkChange();\n\n // Reconnect if disconnected\n if (this.connectionState === 'disconnected' && this.deviceId) {\n this.log(RiviumPushLogLevel.INFO, 'Reconnecting after network restored');\n this.connectToGateway();\n }\n }\n\n private handleOffline(): void {\n this.log(RiviumPushLogLevel.INFO, 'Network offline');\n this.handleNetworkChange();\n }\n\n private handleVisibilityChange(): void {\n const state = this.getAppState();\n this.log(RiviumPushLogLevel.DEBUG, 'App state changed:', state);\n\n if (this.onAppStateCallback) {\n this.onAppStateCallback(state);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.APP_STATE_CHANGED, {\n isVisible: state.isVisible,\n visibilityState: state.visibilityState,\n });\n\n // Reconnect when becoming visible if disconnected\n if (state.isVisible && this.connectionState === 'disconnected' && this.deviceId) {\n this.log(RiviumPushLogLevel.INFO, 'Reconnecting after becoming visible');\n this.connectToGateway();\n }\n }\n\n // ==========================================================================\n // Private Methods - Initial Message\n // ==========================================================================\n\n private checkInitialMessage(): void {\n // Check URL parameters for notification data\n if (typeof window !== 'undefined') {\n const urlParams = new URLSearchParams(window.location.search);\n const notificationData = urlParams.get('rivium_push_notification');\n\n if (notificationData) {\n try {\n this.initialMessage = JSON.parse(decodeURIComponent(notificationData));\n this.log(RiviumPushLogLevel.INFO, 'Initial message found:', this.initialMessage);\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to parse initial message:', e);\n }\n }\n\n // Also check sessionStorage (set by service worker)\n const storedMessage = sessionStorage.getItem('rivium_push_initial_message');\n if (storedMessage && !this.initialMessage) {\n try {\n this.initialMessage = JSON.parse(storedMessage);\n sessionStorage.removeItem('rivium_push_initial_message');\n this.log(RiviumPushLogLevel.INFO, 'Initial message from session:', this.initialMessage);\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to parse stored message:', e);\n }\n }\n }\n }\n\n // ==========================================================================\n // Private Methods - Service Worker & Push\n // ==========================================================================\n\n private async registerServiceWorker(): Promise<void> {\n if (!('serviceWorker' in navigator)) {\n throw new RiviumPushError(RiviumPushErrorCode.SERVICE_NOT_RUNNING, 'Service Workers not supported');\n }\n\n try {\n this.serviceWorkerRegistration = await navigator.serviceWorker.register(\n this.config.serviceWorkerPath!,\n { scope: '/' }\n );\n this.log(RiviumPushLogLevel.INFO, 'Service Worker registered');\n } catch (error) {\n this.log(RiviumPushLogLevel.ERROR, 'Service Worker registration failed:', error);\n throw new RiviumPushError(RiviumPushErrorCode.SERVICE_NOT_RUNNING, (error as Error).message);\n }\n }\n\n private async requestNotificationPermission(): Promise<NotificationPermission> {\n if (typeof Notification === 'undefined') {\n return 'denied';\n }\n\n if (Notification.permission === 'granted') {\n return 'granted';\n }\n\n return await Notification.requestPermission();\n }\n\n private async subscribeToPush(vapidPublicKey: string): Promise<PushSubscription> {\n if (!this.serviceWorkerRegistration) {\n throw new RiviumPushError(RiviumPushErrorCode.SERVICE_NOT_RUNNING, 'Service Worker not registered');\n }\n\n // Check if there's an existing subscription\n const existingSubscription = await this.serviceWorkerRegistration.pushManager.getSubscription();\n if (existingSubscription) {\n this.log(RiviumPushLogLevel.DEBUG, 'Using existing Push subscription');\n return existingSubscription;\n }\n\n const subscription = await this.serviceWorkerRegistration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: this.urlBase64ToUint8Array(vapidPublicKey) as BufferSource,\n });\n\n this.log(RiviumPushLogLevel.INFO, 'Push subscription created');\n return subscription;\n }\n\n private async registerDevice(options?: RegisterOptions): Promise<{ deviceId: string; mqtt?: { token?: string } }> {\n try {\n // Build request body (use window.location.origin as appIdentifier for per-app isolation)\n const requestBody: Record<string, any> = {\n deviceId: this.deviceId,\n platform: 'web',\n userId: options?.userId,\n appIdentifier: typeof window !== 'undefined' ? window.location.origin : undefined,\n metadata: {\n ...options?.metadata,\n userAgent: navigator.userAgent,\n language: navigator.language,\n url: window.location.origin,\n },\n };\n\n // Add Web Push subscription if available (for background notifications)\n if (this.pushSubscription) {\n const subscriptionJson = this.pushSubscription.toJSON();\n requestBody.webPushSubscription = {\n endpoint: subscriptionJson.endpoint,\n keys: {\n p256dh: subscriptionJson.keys?.p256dh || '',\n auth: subscriptionJson.keys?.auth || '',\n },\n };\n this.log(RiviumPushLogLevel.DEBUG, 'Sending Web Push subscription to server');\n }\n\n const response = await fetch(`${RIVIUM_PUSH_SERVER_URL}/devices/register`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new RiviumPushError(\n RiviumPushErrorCode.SERVER_ERROR,\n errorData.message || `HTTP ${response.status}`\n );\n }\n\n const data = await response.json();\n\n // Store appId for topic subscriptions\n if (data.appId) {\n this.appId = data.appId;\n }\n\n // Store appIdentifier for per-app message routing\n if (data.appIdentifier) {\n this.appIdentifier = data.appIdentifier;\n }\n\n // Store connection token from registration response\n if (data.mqtt?.token && this.mqttConfig) {\n this.mqttConfig.token = data.mqtt.token;\n this.log(RiviumPushLogLevel.DEBUG, 'Connection token received from registration');\n }\n\n return data;\n } catch (error) {\n if (error instanceof RiviumPushError) throw error;\n throw new RiviumPushError(RiviumPushErrorCode.NETWORK_ERROR, (error as Error).message);\n }\n }\n\n // ==========================================================================\n // Private Methods - PN Protocol Connection\n // ==========================================================================\n\n private connectToGateway(): void {\n if (this.pnSocket) {\n this.pnSocket.close();\n }\n\n // Clear any pending reconnect timer\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n // Check if config is available\n if (!this.mqttConfig) {\n this.log(RiviumPushLogLevel.WARNING, 'Gateway config not available, retrying in 2s...');\n setTimeout(() => this.connectToGateway(), 2000);\n return;\n }\n\n // Check if we have JWT token for authentication\n if (!this.mqttConfig.token) {\n this.log(RiviumPushLogLevel.ERROR, 'Connection token not available. Device must be registered first.');\n this.emitError(RiviumPushErrorCode.AUTHENTICATION_FAILED, 'Connection token not available');\n return;\n }\n\n this.setConnectionState('connecting');\n\n // Use wsHost for WebSocket connections (via Cloudflare), fallback to host\n const gateway = this.mqttConfig.wsHost || this.mqttConfig.host;\n const port = this.mqttConfig.wsPort;\n\n // Determine if secure connection is needed\n const isSecurePage = typeof window !== 'undefined' && window.location.protocol === 'https:';\n const isSecurePort = port === 443;\n const secure = isSecurePage || isSecurePort;\n\n this.log(RiviumPushLogLevel.DEBUG, `Connecting to gateway (secure: ${secure})`);\n\n const clientId = `rivium_push_${this.appId}_${this.deviceId}`;\n\n // Build PNConfig using the protocol's builder\n const pnConfig = new PNConfigBuilder()\n .gateway(gateway)\n .port(port)\n .clientId(clientId)\n .auth(PNAuthFactory.token(this.mqttConfig.token))\n .secure(secure)\n .freshStart(false)\n .autoReconnect(false) // We handle reconnection ourselves\n .connectionTimeout(10)\n .build();\n\n this.pnSocket = new PNSocket(pnConfig);\n\n // Set up connection listener\n const connectionListener: PNConnectionListener = {\n onStateChanged: (state: PNState) => {\n this.log(RiviumPushLogLevel.DEBUG, `Connection state changed: ${state}`);\n },\n onConnected: () => {\n this.log(RiviumPushLogLevel.INFO, 'Connected to gateway');\n this.setConnectionState('connected');\n this.reconnectAttempts = 0;\n this.trackEvent(RiviumPushAnalyticsEvent.CONNECTED);\n\n // Stream device-specific channel (includes appIdentifier for app isolation)\n const appId = this.config.apiKey.substring(0, 16);\n const appIdentifier = this.appIdentifier || (typeof window !== 'undefined' ? window.location.origin : '_default');\n const deviceChannel = `rivium_push/${appId}/${this.deviceId}/${appIdentifier}`;\n this.pnSocket!.stream(deviceChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.DEBUG, 'Streaming from device channel');\n\n // Stream broadcast channel\n const broadcastChannel = `rivium_push/${appId}/broadcast`;\n this.pnSocket!.stream(broadcastChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.DEBUG, 'Streaming from broadcast channel');\n\n // Resubscribe to custom topics\n this.subscribedTopics.forEach((topic) => {\n const topicChannel = `rivium_push/${appId}/topic/${topic}`;\n this.pnSocket!.stream(topicChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n });\n },\n onDisconnected: (reason?: string) => {\n this.log(RiviumPushLogLevel.INFO, 'Disconnected from gateway', reason || '');\n this.setConnectionState('disconnected');\n this.trackEvent(RiviumPushAnalyticsEvent.DISCONNECTED);\n this.scheduleReconnect();\n },\n onReconnecting: (attempt: number, nextRetryMs: number) => {\n this.log(RiviumPushLogLevel.INFO, `Reconnecting attempt ${attempt} in ${nextRetryMs}ms`);\n },\n };\n\n this.pnSocket.addConnectionListener(connectionListener);\n\n // Set up error listener\n this.pnSocket.addErrorListener((error: PNProtocolError) => {\n this.log(RiviumPushLogLevel.ERROR, 'Gateway error:', error.message);\n this.setConnectionState('error');\n\n // Map PNProtocolError to RiviumPushErrorCode\n let errorCode = RiviumPushErrorCode.CONNECTION_FAILED;\n const errorMessage = error.message.toLowerCase();\n\n if (errorMessage.includes('timeout')) {\n errorCode = RiviumPushErrorCode.CONNECTION_TIMEOUT;\n } else if (errorMessage.includes('refused') || errorMessage.includes('not authorized')) {\n errorCode = RiviumPushErrorCode.CONNECTION_REFUSED;\n // Token might be expired - try to refresh it\n if (errorMessage.includes('not authorized')) {\n this.log(RiviumPushLogLevel.INFO, 'Token may be expired, attempting refresh...');\n this.refreshMqttToken().then(() => {\n this.log(RiviumPushLogLevel.INFO, 'Token refreshed, reconnecting...');\n this.connectToGateway();\n }).catch((refreshError) => {\n this.log(RiviumPushLogLevel.ERROR, 'Token refresh failed:', refreshError);\n this.emitError(RiviumPushErrorCode.AUTHENTICATION_FAILED, 'Token expired and refresh failed');\n });\n return;\n }\n } else if (errorMessage.includes('auth') || errorMessage.includes('credential')) {\n errorCode = RiviumPushErrorCode.AUTHENTICATION_FAILED;\n } else if (errorMessage.includes('ssl') || errorMessage.includes('tls')) {\n errorCode = RiviumPushErrorCode.SSL_ERROR;\n }\n\n this.emitError(errorCode, error.message);\n });\n\n // Open connection\n this.log(RiviumPushLogLevel.DEBUG, 'Opening connection to gateway...');\n this.pnSocket.open();\n }\n\n /**\n * Handle incoming PNMessage from the protocol layer\n */\n private handlePNMessage(message: PNMessage): void {\n try {\n const data = message.payloadAsJson();\n this.handleMqttMessage(message.channel, data);\n } catch (error) {\n this.log(RiviumPushLogLevel.ERROR, 'Message parse error:', error);\n this.emitError(RiviumPushErrorCode.MESSAGE_PARSE_ERROR, (error as Error).message);\n }\n }\n\n private disconnectFromGateway(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.pnSocket) {\n this.pnSocket.close();\n this.pnSocket = null;\n }\n\n this.setConnectionState('disconnected');\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log(RiviumPushLogLevel.WARNING, 'Max reconnect attempts reached');\n this.emitError(RiviumPushErrorCode.CONNECTION_FAILED, 'Max reconnect attempts reached');\n return;\n }\n\n // Check if we should reconnect (only if online and visible)\n if (!navigator.onLine) {\n this.log(RiviumPushLogLevel.DEBUG, 'Offline, skipping reconnect');\n return;\n }\n\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n this.reconnectAttempts++;\n\n this.log(RiviumPushLogLevel.INFO, `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);\n\n // Emit reconnecting state\n const reconnectionState: ReconnectionState = {\n retryAttempt: this.reconnectAttempts,\n nextRetryMs: delay,\n maxRetryAttempts: this.maxReconnectAttempts,\n };\n\n if (this.onReconnectingCallback) {\n this.onReconnectingCallback(reconnectionState);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.RETRY_STARTED, {\n retryAttempt: this.reconnectAttempts,\n nextRetryMs: delay,\n });\n\n this.reconnectTimer = setTimeout(() => {\n this.connectToGateway();\n }, delay);\n }\n\n private handleMqttMessage(topic: string, data: any): void {\n const message = this.normalizeMessage(data);\n\n this.log(RiviumPushLogLevel.DEBUG, 'Message received:', message.title);\n\n this.trackEvent(RiviumPushAnalyticsEvent.MESSAGE_RECEIVED, {\n messageId: message.messageId,\n title: message.title,\n silent: message.silent,\n hasImage: !!message.imageUrl,\n hasActions: !!message.actions?.length,\n });\n\n // Handle badge\n this.handleBadge(message);\n\n // Show notification if not silent and page is not visible\n if (!message.silent && document.visibilityState !== 'visible') {\n this.showRichNotification(message);\n this.trackEvent(RiviumPushAnalyticsEvent.MESSAGE_DISPLAYED, {\n messageId: message.messageId,\n title: message.title,\n });\n }\n\n if (this.onMessageCallback) {\n this.onMessageCallback(message);\n }\n }\n\n private normalizeMessage(data: any): RiviumPushMessage {\n // Get localized content\n const localizedTitle = this.getLocalizedContent(data, 'title');\n const localizedBody = this.getLocalizedContent(data, 'body');\n\n return {\n title: localizedTitle || data.title || '',\n body: localizedBody || data.body || '',\n data: data.data,\n silent: data.silent,\n // Rich features\n imageUrl: data.imageUrl || data.image,\n iconUrl: data.iconUrl || data.icon,\n actions: data.actions,\n deepLink: data.deepLink,\n // Badge\n badge: data.badge,\n badgeAction: data.badgeAction,\n // Sound and grouping\n sound: data.sound,\n threadId: data.threadId,\n collapseKey: data.collapseKey,\n category: data.category,\n // Priority\n priority: data.priority,\n ttl: data.ttl,\n // Localization\n localizations: data.localizations,\n timezone: data.timezone,\n // Tracking\n messageId: data.messageId,\n campaignId: data.campaignId,\n // Legacy fields\n icon: data.iconUrl || data.icon,\n image: data.imageUrl || data.image,\n tag: data.tag || data.collapseKey || data.threadId,\n };\n }\n\n private getLocalizedContent(data: any, field: 'title' | 'body'): string | null {\n if (!data.localizations || !Array.isArray(data.localizations)) {\n return null;\n }\n\n const deviceLocale = navigator.language.split('-')[0].toLowerCase();\n\n const localized = data.localizations.find((loc: LocalizedContent) =>\n loc.locale.toLowerCase().startsWith(deviceLocale)\n );\n\n return localized ? localized[field] : null;\n }\n\n private handleBadge(message: RiviumPushMessage): void {\n if (message.badge === undefined && !message.badgeAction) return;\n\n const action = message.badgeAction || 'set';\n let newBadge = message.badge || 0;\n\n switch (action) {\n case 'set':\n newBadge = message.badge || 0;\n break;\n case 'increment':\n newBadge = this.badgeCount + (message.badge || 1);\n break;\n case 'decrement':\n newBadge = Math.max(0, this.badgeCount - (message.badge || 1));\n break;\n case 'clear':\n newBadge = 0;\n break;\n }\n\n this.setBadgeCount(newBadge);\n }\n\n private handleServiceWorkerMessage(event: MessageEvent): void {\n const data = event.data;\n\n if (data.type === 'push-message') {\n // Message forwarded from service worker (when tab is visible)\n this.log(RiviumPushLogLevel.DEBUG, 'Push message received from SW:', data.message?.title);\n const message = this.normalizeMessage(data.message);\n\n this.trackEvent(RiviumPushAnalyticsEvent.MESSAGE_RECEIVED, {\n messageId: message.messageId,\n title: message.title,\n source: 'web-push',\n });\n\n // Handle badge\n this.handleBadge(message);\n\n // Notify callback\n if (this.onMessageCallback) {\n this.onMessageCallback(message);\n }\n } else if (data.type === 'notification-click') {\n this.log(RiviumPushLogLevel.INFO, 'Notification clicked from SW');\n const message = this.normalizeMessage(data.message || {});\n\n this.trackEvent(RiviumPushAnalyticsEvent.NOTIFICATION_CLICKED, {\n messageId: message.messageId,\n title: message.title,\n action: data.action,\n });\n\n if (this.onNotificationClickCallback) {\n this.onNotificationClickCallback(message, data.action);\n }\n } else if (data.type === 'action-clicked') {\n this.log(RiviumPushLogLevel.INFO, 'Action clicked from SW:', data.actionId);\n const message = this.normalizeMessage(data.message || {});\n\n this.trackEvent(RiviumPushAnalyticsEvent.ACTION_CLICKED, {\n actionId: data.actionId,\n messageId: message.messageId,\n title: message.title,\n });\n\n if (this.onActionClickedCallback) {\n this.onActionClickedCallback(data.actionId, message);\n }\n } else if (data.type === 'initial-message') {\n // Store initial message from service worker (when app opened via notification)\n this.log(RiviumPushLogLevel.INFO, 'Initial message received from SW');\n this.initialMessage = this.normalizeMessage(data.message || {});\n\n // Also trigger the notification click callback for initial messages\n if (this.onNotificationClickCallback && this.initialMessage) {\n this.onNotificationClickCallback(this.initialMessage, undefined);\n }\n } else if (data.type === 'navigate') {\n // Navigate to URL requested by service worker\n this.log(RiviumPushLogLevel.INFO, 'Navigating to:', data.url);\n if (data.url && typeof window !== 'undefined') {\n window.location.href = data.url;\n }\n }\n }\n\n private showRichNotification(message: RiviumPushMessage): void {\n if (Notification.permission !== 'granted') {\n return;\n }\n\n const options: NotificationOptions & { image?: string } = {\n body: message.body,\n icon: message.iconUrl || message.icon,\n badge: message.iconUrl || message.icon,\n image: message.imageUrl || message.image,\n tag: message.tag || message.collapseKey || message.threadId,\n data: {\n ...message.data,\n deepLink: message.deepLink,\n messageId: message.messageId,\n campaignId: message.campaignId,\n riviumPushMessage: message,\n },\n requireInteraction: message.priority === 'high',\n silent: message.sound === 'none',\n };\n\n // Add action buttons if supported\n if (message.actions && message.actions.length > 0) {\n const notificationActions = message.actions.slice(0, 2).map((action) => ({\n action: action.id,\n title: action.title,\n icon: action.icon,\n }));\n\n if (this.serviceWorkerRegistration) {\n (options as any).actions = notificationActions;\n }\n }\n\n if (this.serviceWorkerRegistration) {\n this.serviceWorkerRegistration.showNotification(message.title, options);\n } else {\n const notification = new Notification(message.title, options);\n\n notification.onclick = () => {\n window.focus();\n if (message.deepLink) {\n window.location.href = message.deepLink;\n }\n if (this.onNotificationClickCallback) {\n this.onNotificationClickCallback(message);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.NOTIFICATION_CLICKED, {\n messageId: message.messageId,\n title: message.title,\n });\n\n notification.close();\n };\n }\n }\n\n private updateFaviconBadge(count: number): void {\n try {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const size = 32;\n canvas.width = size;\n canvas.height = size;\n\n const existingFavicon = document.querySelector('link[rel=\"icon\"]') as HTMLLinkElement;\n const faviconUrl = existingFavicon?.href || '/favicon.ico';\n\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n ctx.drawImage(img, 0, 0, size, size);\n\n if (count > 0) {\n const badgeSize = 14;\n const x = size - badgeSize / 2;\n const y = badgeSize / 2;\n\n ctx.beginPath();\n ctx.arc(x, y, badgeSize / 2 + 1, 0, 2 * Math.PI);\n ctx.fillStyle = '#ef4444';\n ctx.fill();\n\n ctx.fillStyle = '#ffffff';\n ctx.font = 'bold 10px sans-serif';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(count > 99 ? '99+' : count.toString(), x, y);\n }\n\n const newFavicon = document.createElement('link');\n newFavicon.rel = 'icon';\n newFavicon.href = canvas.toDataURL('image/png');\n\n if (existingFavicon) {\n existingFavicon.remove();\n }\n document.head.appendChild(newFavicon);\n };\n img.src = faviconUrl;\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Could not update favicon badge:', e);\n }\n }\n\n private setConnectionState(state: ConnectionState): void {\n this.connectionState = state;\n if (this.onConnectionStateCallback) {\n this.onConnectionStateCallback(state);\n }\n }\n\n private getOrCreateDeviceId(): string {\n const key = 'rivium_push_device_id';\n let deviceId = localStorage.getItem(key);\n\n if (!deviceId) {\n deviceId = 'web_' + this.generateUUID();\n localStorage.setItem(key, deviceId);\n }\n\n return deviceId;\n }\n\n private generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n const rawData = window.atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n\n return outputArray;\n }\n}\n\nexport default RiviumPush;\nexport { RiviumPush };\n"],"names":["RiviumPushErrorCode","ERROR_MESSAGES","CONNECTION_FAILED","CONNECTION_TIMEOUT","CONNECTION_LOST","CONNECTION_REFUSED","AUTHENTICATION_FAILED","SSL_ERROR","BROKER_UNAVAILABLE","SUBSCRIPTION_FAILED","UNSUBSCRIPTION_FAILED","INVALID_TOPIC","MESSAGE_DELIVERY_FAILED","MESSAGE_PARSE_ERROR","MESSAGE_TIMEOUT","INVALID_CONFIG","MISSING_API_KEY","MISSING_SERVER_URL","INVALID_CREDENTIALS","REGISTRATION_FAILED","DEVICE_ID_GENERATION_FAILED","SERVER_ERROR","NETWORK_ERROR","NOT_INITIALIZED","NOT_CONNECTED","ALREADY_CONNECTED","SERVICE_NOT_RUNNING","PERMISSION_DENIED","PERMISSION_DISMISSED","UNKNOWN_ERROR","RiviumPushError","Error","constructor","code","details","super","this","name","toJSON","message","RiviumPushAnalyticsEvent","RiviumPushLogLevel","NetworkType","RIVIUM_PUSH_SERVER_URL","RiviumPush","config","deviceId","pnSocket","serviceWorkerRegistration","pushSubscription","connectionState","reconnectAttempts","maxReconnectAttempts","reconnectTimer","subscribedTopics","Set","badgeCount","initialized","initialMessage","mqttConfig","mqttConfigFetched","appId","appIdentifier","vapidPublicKey","logLevel","DEBUG","analyticsCallback","analyticsEnabled","onMessageCallback","onConnectionStateCallback","onRegisteredCallback","onErrorCallback","onDetailedErrorCallback","onNotificationClickCallback","onActionClickedCallback","onReconnectingCallback","onNetworkStateCallback","onAppStateCallback","apiKey","serviceWorkerPath","autoRegisterServiceWorker","mqttQos","ERROR","window","getOrCreateDeviceId","parseInt","localStorage","getItem","checkInitialMessage","navigator","serviceWorker","addEventListener","handleServiceWorkerMessage","bind","document","handleVisibilityChange","_a","connection","handleNetworkChange","handleOnline","handleOffline","trackEvent","SDK_INITIALIZED","log","INFO","fetchMqttConfig","response","fetch","method","headers","ok","status","data","json","mqtt","error","emitError","level","args","prefix","console","WARNING","warn","info","VERBOSE","setLogLevel","getLogLevel","event","properties","e","setAnalyticsHandler","callback","disableAnalytics","isAnalyticsEnabled","CONNECTION_ERROR","errorCode","errorMessage","register","options","waitForConfig","registerServiceWorker","PERMISSION_REQUESTED","permission","requestNotificationPermission","PERMISSION_GRANTED","vapidKey","subscribeToPush","registerDevice","connectToGateway","DEVICE_REGISTERED","userId","hasWebPush","timeoutMs","startTime","Date","now","Promise","resolve","setTimeout","unregister","disconnectFromGateway","unsubscribe","DEVICE_UNREGISTERED","subscribeTopic","topic","trim","add","body","JSON","stringify","err","isConnected","channel","substring","stream","handlePNMessage","TOPIC_SUBSCRIBED","unsubscribeTopic","delete","detach","TOPIC_UNSUBSCRIBED","getDeviceId","setUserId","setItem","clearUserId","removeItem","getInitialMessage","getBadgeCount","setBadgeCount","count","Math","max","toString","updateFaviconBadge","setAppBadge","clearAppBadge","clearBadge","refreshMqttToken","errorData","catch","token","getNetworkState","isAvailable","onLine","networkType","detectNetworkType","effectiveType","downlink","rtt","getAppState","isVisible","visibilityState","isSupported","getPermissionStatus","Notification","onMessage","onConnectionState","onRegistered","onError","onDetailedError","onNotificationClick","onActionClicked","onReconnecting","onNetworkState","onAppState","UNKNOWN","type","WIFI","CELLULAR","ETHERNET","NONE","state","NETWORK_STATE_CHANGED","APP_STATE_CHANGED","notificationData","URLSearchParams","location","search","get","parse","decodeURIComponent","storedMessage","sessionStorage","scope","requestPermission","existingSubscription","pushManager","getSubscription","subscription","subscribe","userVisibleOnly","applicationServerKey","urlBase64ToUint8Array","requestBody","platform","origin","undefined","metadata","userAgent","language","url","subscriptionJson","webPushSubscription","endpoint","keys","p256dh","auth","_b","_c","close","clearTimeout","setConnectionState","gateway","wsHost","host","port","wsPort","secure","protocol","clientId","pnConfig","PNConfigBuilder","PNAuthFactory","freshStart","autoReconnect","connectionTimeout","build","PNSocket","connectionListener","onStateChanged","onConnected","CONNECTED","deviceChannel","broadcastChannel","forEach","topicChannel","onDisconnected","reason","DISCONNECTED","scheduleReconnect","attempt","nextRetryMs","addConnectionListener","addErrorListener","toLowerCase","includes","then","refreshError","open","payloadAsJson","handleMqttMessage","delay","min","pow","reconnectionState","retryAttempt","maxRetryAttempts","RETRY_STARTED","normalizeMessage","title","MESSAGE_RECEIVED","messageId","silent","hasImage","imageUrl","hasActions","actions","length","handleBadge","showRichNotification","MESSAGE_DISPLAYED","localizedTitle","getLocalizedContent","localizedBody","image","iconUrl","icon","deepLink","badge","badgeAction","sound","threadId","collapseKey","category","priority","ttl","localizations","timezone","campaignId","tag","field","Array","isArray","deviceLocale","split","localized","find","loc","locale","startsWith","action","newBadge","source","NOTIFICATION_CLICKED","actionId","ACTION_CLICKED","href","riviumPushMessage","requireInteraction","notificationActions","slice","map","id","showNotification","notification","onclick","focus","canvas","createElement","ctx","getContext","size","width","height","existingFavicon","querySelector","faviconUrl","img","Image","crossOrigin","onload","drawImage","badgeSize","x","y","beginPath","arc","PI","fillStyle","fill","font","textAlign","textBaseline","fillText","newFavicon","rel","toDataURL","remove","head","appendChild","src","key","generateUUID","crypto","randomUUID","replace","c","r","random","base64String","base64","repeat","rawData","atob","outputArray","Uint8Array","i","charCodeAt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * RiviumPush Web SDK\n * Push notifications for browsers - Firebase alternative\n *\n * Features:\n * - Web Push API for background notifications\n * - MQTT over WebSocket for real-time foreground messages\n * - Service Worker integration\n * - Rich notifications with images, action buttons, and localization\n * - Analytics event tracking\n * - Detailed error codes and handling\n * - Network and app state monitoring\n * - Works without Firebase\n *\n * @packageDocumentation\n */\n\nimport {\n PNSocket,\n PNConfigBuilder,\n PNAuthFactory,\n PNState,\n PNDeliveryMode,\n PNMessage,\n PNError as PNProtocolError,\n PNConnectionListener,\n} from '@rivium/pn-protocol';\n\n// ============================================================================\n// Error Codes (matching Flutter SDK)\n// ============================================================================\n\n/**\n * Standardized error codes for RiviumPush SDK.\n * These codes help developers identify and handle specific error scenarios.\n */\nexport enum RiviumPushErrorCode {\n // Connection errors (1000-1099)\n CONNECTION_FAILED = 1000,\n CONNECTION_TIMEOUT = 1001,\n CONNECTION_LOST = 1002,\n CONNECTION_REFUSED = 1003,\n AUTHENTICATION_FAILED = 1004,\n SSL_ERROR = 1005,\n BROKER_UNAVAILABLE = 1006,\n\n // Subscription errors (1100-1199)\n SUBSCRIPTION_FAILED = 1100,\n UNSUBSCRIPTION_FAILED = 1101,\n INVALID_TOPIC = 1102,\n\n // Message errors (1200-1299)\n MESSAGE_DELIVERY_FAILED = 1200,\n MESSAGE_PARSE_ERROR = 1201,\n MESSAGE_TIMEOUT = 1202,\n\n // Configuration errors (1300-1399)\n INVALID_CONFIG = 1300,\n MISSING_API_KEY = 1301,\n /** @deprecated No longer used - server URL is internal */\n MISSING_SERVER_URL = 1302,\n INVALID_CREDENTIALS = 1303,\n\n // Registration errors (1400-1499)\n REGISTRATION_FAILED = 1400,\n DEVICE_ID_GENERATION_FAILED = 1401,\n SERVER_ERROR = 1402,\n NETWORK_ERROR = 1403,\n\n // State errors (1500-1599)\n NOT_INITIALIZED = 1500,\n NOT_CONNECTED = 1501,\n ALREADY_CONNECTED = 1502,\n SERVICE_NOT_RUNNING = 1503,\n\n // Permission errors (1600-1699)\n PERMISSION_DENIED = 1600,\n PERMISSION_DISMISSED = 1601,\n\n // Unknown error\n UNKNOWN_ERROR = 9999,\n}\n\n/**\n * Error code messages mapping\n */\nconst ERROR_MESSAGES: Record<RiviumPushErrorCode, string> = {\n [RiviumPushErrorCode.CONNECTION_FAILED]: 'Failed to connect to MQTT broker',\n [RiviumPushErrorCode.CONNECTION_TIMEOUT]: 'Connection timed out',\n [RiviumPushErrorCode.CONNECTION_LOST]: 'Connection to server was lost',\n [RiviumPushErrorCode.CONNECTION_REFUSED]: 'Connection was refused by server',\n [RiviumPushErrorCode.AUTHENTICATION_FAILED]: 'Authentication failed - invalid credentials',\n [RiviumPushErrorCode.SSL_ERROR]: 'SSL/TLS handshake failed',\n [RiviumPushErrorCode.BROKER_UNAVAILABLE]: 'MQTT broker is unavailable',\n [RiviumPushErrorCode.SUBSCRIPTION_FAILED]: 'Failed to subscribe to topic',\n [RiviumPushErrorCode.UNSUBSCRIPTION_FAILED]: 'Failed to unsubscribe from topic',\n [RiviumPushErrorCode.INVALID_TOPIC]: 'Invalid topic format',\n [RiviumPushErrorCode.MESSAGE_DELIVERY_FAILED]: 'Failed to deliver message',\n [RiviumPushErrorCode.MESSAGE_PARSE_ERROR]: 'Failed to parse message payload',\n [RiviumPushErrorCode.MESSAGE_TIMEOUT]: 'Message delivery timed out',\n [RiviumPushErrorCode.INVALID_CONFIG]: 'Invalid configuration',\n [RiviumPushErrorCode.MISSING_API_KEY]: 'API key is missing',\n [RiviumPushErrorCode.MISSING_SERVER_URL]: 'Server URL is missing',\n [RiviumPushErrorCode.INVALID_CREDENTIALS]: 'Invalid MQTT credentials',\n [RiviumPushErrorCode.REGISTRATION_FAILED]: 'Device registration failed',\n [RiviumPushErrorCode.DEVICE_ID_GENERATION_FAILED]: 'Failed to generate device ID',\n [RiviumPushErrorCode.SERVER_ERROR]: 'Server returned an error',\n [RiviumPushErrorCode.NETWORK_ERROR]: 'Network request failed',\n [RiviumPushErrorCode.NOT_INITIALIZED]: 'SDK is not initialized',\n [RiviumPushErrorCode.NOT_CONNECTED]: 'Not connected to server',\n [RiviumPushErrorCode.ALREADY_CONNECTED]: 'Already connected to server',\n [RiviumPushErrorCode.SERVICE_NOT_RUNNING]: 'Service worker is not running',\n [RiviumPushErrorCode.PERMISSION_DENIED]: 'Notification permission denied',\n [RiviumPushErrorCode.PERMISSION_DISMISSED]: 'Notification permission dismissed',\n [RiviumPushErrorCode.UNKNOWN_ERROR]: 'An unknown error occurred',\n};\n\n/**\n * Represents a RiviumPush error with code and additional details\n */\nexport class RiviumPushError extends Error {\n /** The error code */\n readonly code: RiviumPushErrorCode;\n /** Additional details about the error */\n readonly details?: string;\n\n constructor(code: RiviumPushErrorCode, details?: string) {\n super(ERROR_MESSAGES[code] || 'Unknown error');\n this.name = 'RiviumPushError';\n this.code = code;\n this.details = details;\n }\n\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n details: this.details,\n };\n }\n}\n\n// ============================================================================\n// Analytics Events (matching Flutter SDK)\n// ============================================================================\n\n/**\n * Analytics event types for tracking SDK usage.\n * Use with setAnalyticsHandler to track SDK events.\n */\nexport enum RiviumPushAnalyticsEvent {\n /** SDK was initialized */\n SDK_INITIALIZED = 'sdkInitialized',\n /** Device was registered */\n DEVICE_REGISTERED = 'deviceRegistered',\n /** Device was unregistered */\n DEVICE_UNREGISTERED = 'deviceUnregistered',\n /** Push message was received */\n MESSAGE_RECEIVED = 'messageReceived',\n /** Push message was displayed as notification */\n MESSAGE_DISPLAYED = 'messageDisplayed',\n /** Notification was clicked */\n NOTIFICATION_CLICKED = 'notificationClicked',\n /** Action button was clicked */\n ACTION_CLICKED = 'actionClicked',\n /** MQTT connected successfully */\n CONNECTED = 'connected',\n /** MQTT disconnected */\n DISCONNECTED = 'disconnected',\n /** Connection error occurred */\n CONNECTION_ERROR = 'connectionError',\n /** Retry attempt started (during exponential backoff) */\n RETRY_STARTED = 'retryStarted',\n /** Topic subscribed */\n TOPIC_SUBSCRIBED = 'topicSubscribed',\n /** Topic unsubscribed */\n TOPIC_UNSUBSCRIBED = 'topicUnsubscribed',\n /** Network state changed */\n NETWORK_STATE_CHANGED = 'networkStateChanged',\n /** App state changed (visible/hidden) */\n APP_STATE_CHANGED = 'appStateChanged',\n /** Permission requested */\n PERMISSION_REQUESTED = 'permissionRequested',\n /** Permission granted */\n PERMISSION_GRANTED = 'permissionGranted',\n /** Permission denied */\n PERMISSION_DENIED = 'permissionDenied',\n}\n\n// ============================================================================\n// Log Levels\n// ============================================================================\n\n/**\n * Log levels for the RiviumPush SDK.\n * Controls verbosity of logging output.\n */\nexport enum RiviumPushLogLevel {\n /** No logging at all (for production) */\n NONE = 0,\n /** Only errors */\n ERROR = 1,\n /** Errors and warnings */\n WARNING = 2,\n /** Errors, warnings, and info messages */\n INFO = 3,\n /** All messages including debug output (default for development) */\n DEBUG = 4,\n /** Everything including very detailed traces */\n VERBOSE = 5,\n}\n\n// ============================================================================\n// State Types\n// ============================================================================\n\n/**\n * Network type enumeration\n */\nexport enum NetworkType {\n WIFI = 'wifi',\n CELLULAR = 'cellular',\n ETHERNET = 'ethernet',\n NONE = 'none',\n UNKNOWN = 'unknown',\n}\n\n/**\n * Represents the current network state\n */\nexport interface NetworkState {\n /** Whether network is currently available */\n isAvailable: boolean;\n /** The type of network connection */\n networkType: NetworkType;\n /** Effective connection type (4g, 3g, 2g, slow-2g) */\n effectiveType?: string;\n /** Downlink speed in Mbps */\n downlink?: number;\n /** Round-trip time in ms */\n rtt?: number;\n}\n\n/**\n * Represents the app's visibility state\n */\nexport interface AppState {\n /** Whether the page is currently visible */\n isVisible: boolean;\n /** Visibility state: visible, hidden, prerender */\n visibilityState: DocumentVisibilityState;\n}\n\n/**\n * Represents the reconnection state during automatic retry\n */\nexport interface ReconnectionState {\n /** Current retry attempt number (0-based) */\n retryAttempt: number;\n /** Time in milliseconds until next retry */\n nextRetryMs: number;\n /** Maximum retry attempts */\n maxRetryAttempts: number;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for initializing RiviumPush Web SDK\n *\n * `apiKey` is required.\n * MQTT configuration is automatically fetched from the server during initialization.\n */\nexport interface RiviumPushConfig {\n /** Your RiviumPush API key (starts with rv_live_) - REQUIRED */\n apiKey: string;\n /** Path to RiviumPush service worker file */\n serviceWorkerPath?: string;\n /** VAPID public key for Web Push */\n vapidPublicKey?: string;\n /** Auto-register service worker (default: true) */\n autoRegisterServiceWorker?: boolean;\n /** MQTT QoS level (default: 1) */\n mqttQos?: 0 | 1 | 2;\n /** Maximum reconnect attempts (default: 10) */\n maxReconnectAttempts?: number;\n /** Initial log level (default: DEBUG in dev, ERROR in prod) */\n logLevel?: RiviumPushLogLevel;\n}\n\n/**\n * Internal MQTT configuration fetched from server\n */\ninterface MqttConfigInternal {\n host: string;\n wsHost?: string; // WebSocket host (for Cloudflare proxy)\n port: number;\n wsPort: number;\n // JWT token for authentication (provided at registration)\n token?: string;\n}\n\n/**\n * Notification action button\n */\nexport interface NotificationAction {\n /** Unique action identifier */\n id: string;\n /** Button display text */\n title: string;\n /** URL to open when action is clicked */\n action?: string;\n /** Icon for the action button */\n icon?: string;\n /** If true, action is marked as destructive */\n destructive?: boolean;\n /** If true, requires authentication */\n authRequired?: boolean;\n}\n\n/**\n * Localized content for i18n support\n */\nexport interface LocalizedContent {\n /** Locale code (e.g., 'en', 'fr', 'de') */\n locale: string;\n /** Localized title */\n title: string;\n /** Localized body */\n body: string;\n}\n\n/**\n * Push notification message with rich features\n */\nexport interface RiviumPushMessage {\n /** Notification title */\n title: string;\n /** Notification body */\n body: string;\n /** Custom data payload */\n data?: Record<string, any>;\n /** If true, message is delivered silently */\n silent?: boolean;\n // Rich notification features\n /** Large image URL */\n imageUrl?: string;\n /** Icon/avatar URL */\n iconUrl?: string;\n /** Action buttons (max 2 in browsers) */\n actions?: NotificationAction[];\n /** Deep link URL */\n deepLink?: string;\n // Badge management\n /** Badge count */\n badge?: number;\n /** Badge action: set, increment, decrement, clear */\n badgeAction?: 'set' | 'increment' | 'decrement' | 'clear';\n // Sound and grouping\n /** Custom sound name */\n sound?: string;\n /** Thread ID for grouping */\n threadId?: string;\n /** Collapse key for replacing notifications */\n collapseKey?: string;\n /** Category for filtering */\n category?: string;\n // Priority and TTL\n /** Priority: default, high, low */\n priority?: 'default' | 'high' | 'low';\n /** Time to live in seconds */\n ttl?: number;\n // Localization\n /** Localized content variations */\n localizations?: LocalizedContent[];\n /** Target timezone */\n timezone?: string;\n // Tracking\n /** Unique message ID */\n messageId?: string;\n /** Campaign ID for analytics */\n campaignId?: string;\n\n // Legacy fields for backwards compatibility\n /** @deprecated Use iconUrl instead */\n icon?: string;\n /** @deprecated Use imageUrl instead */\n image?: string;\n /** Notification tag for grouping (legacy) */\n tag?: string;\n}\n\n/**\n * Device registration options\n */\nexport interface RegisterOptions {\n /** User identifier */\n userId?: string;\n /** Additional metadata */\n metadata?: Record<string, string>;\n}\n\n/**\n * Connection state\n */\nexport type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error';\n\n// ============================================================================\n// Callback Types\n// ============================================================================\n\nexport type OnMessageCallback = (message: RiviumPushMessage) => void;\nexport type OnConnectionStateCallback = (state: ConnectionState) => void;\nexport type OnRegisteredCallback = (deviceId: string) => void;\nexport type OnErrorCallback = (error: Error) => void;\nexport type OnDetailedErrorCallback = (error: RiviumPushError) => void;\nexport type OnNotificationClickCallback = (message: RiviumPushMessage, action?: string) => void;\nexport type OnActionClickedCallback = (actionId: string, message: RiviumPushMessage) => void;\nexport type OnReconnectingCallback = (state: ReconnectionState) => void;\nexport type OnNetworkStateCallback = (state: NetworkState) => void;\nexport type OnAppStateCallback = (state: AppState) => void;\nexport type RiviumPushAnalyticsCallback = (\n event: RiviumPushAnalyticsEvent,\n properties?: Record<string, any>\n) => void;\n\n// ============================================================================\n// Internal Constants\n// ============================================================================\n\n/** Internal server URL - not configurable by users */\nconst RIVIUM_PUSH_SERVER_URL = 'https://push-api.rivium.co';\n\n// ============================================================================\n// RiviumPush Web SDK Class\n// ============================================================================\n\n/**\n * RiviumPush Web SDK - Push notifications for browsers\n *\n * @example\n * ```typescript\n * import RiviumPush from '@rivium/push-web';\n *\n * // Initialize with API key\n * // MQTT config is auto-fetched from server\n * const riviumPush = new RiviumPush({\n * apiKey: 'rv_live_your_api_key', // Get from Rivium Console\n * });\n *\n * // Set up analytics tracking\n * riviumPush.setAnalyticsHandler((event, properties) => {\n * console.log('Analytics:', event, properties);\n * });\n *\n * // Set up error handling\n * riviumPush.onDetailedError((error) => {\n * console.error('Error:', error.code, error.message, error.details);\n * });\n *\n * // Set up message handling\n * riviumPush.onMessage((message) => {\n * console.log('Received:', message.title);\n * });\n *\n * // Register device\n * await riviumPush.register({ userId: 'user123' });\n * ```\n */\nclass RiviumPush {\n private config: Required<Pick<RiviumPushConfig, 'apiKey'>> & RiviumPushConfig;\n private deviceId: string | null = null;\n private subscriptionId: string | null = null;\n private userId: string | null = null;\n private pnSocket: PNSocket | null = null;\n private serviceWorkerRegistration: ServiceWorkerRegistration | null = null;\n private pushSubscription: PushSubscription | null = null;\n private connectionState: ConnectionState = 'disconnected';\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 10;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private subscribedTopics: Set<string> = new Set();\n private badgeCount = 0;\n private initialized = false;\n private initialMessage: RiviumPushMessage | null = null;\n\n // MQTT configuration fetched from server\n private mqttConfig: MqttConfigInternal | null = null;\n private mqttConfigFetched = false;\n private appId: string | null = null; // For MQTT topics and token refresh\n private appIdentifier: string | null = null; // For per-app message routing\n\n // VAPID public key fetched from server (for Web Push background notifications)\n private vapidPublicKey: string | null = null;\n\n // Log level\n private logLevel: RiviumPushLogLevel = RiviumPushLogLevel.DEBUG;\n\n // Analytics\n private analyticsCallback: RiviumPushAnalyticsCallback | null = null;\n private analyticsEnabled = false;\n\n // Callbacks\n private onMessageCallback: OnMessageCallback | null = null;\n private onConnectionStateCallback: OnConnectionStateCallback | null = null;\n private onRegisteredCallback: OnRegisteredCallback | null = null;\n private onErrorCallback: OnErrorCallback | null = null;\n private onDetailedErrorCallback: OnDetailedErrorCallback | null = null;\n private onNotificationClickCallback: OnNotificationClickCallback | null = null;\n private onActionClickedCallback: OnActionClickedCallback | null = null;\n private onReconnectingCallback: OnReconnectingCallback | null = null;\n private onNetworkStateCallback: OnNetworkStateCallback | null = null;\n private onAppStateCallback: OnAppStateCallback | null = null;\n\n constructor(config: RiviumPushConfig) {\n if (!config.apiKey) {\n throw new Error('RiviumPush: apiKey is required');\n }\n this.config = {\n serviceWorkerPath: '/rivium-push-sw.js',\n autoRegisterServiceWorker: true,\n mqttQos: 1,\n maxReconnectAttempts: 10,\n logLevel: RiviumPushLogLevel.ERROR,\n ...config,\n };\n\n this.maxReconnectAttempts = this.config.maxReconnectAttempts!;\n this.logLevel = this.config.logLevel!;\n\n if (typeof window === 'undefined') {\n // SSR environment (Next.js server-side) - skip browser-only initialization\n this.initialized = true;\n return;\n }\n\n this.deviceId = this.getOrCreateDeviceId();\n // Restore previously-issued subscriptionId so we can stream the new topic\n // immediately on page load — register() will refresh it.\n this.subscriptionId = localStorage.getItem('rivium_push_subscription_id') || null;\n // Restore the userId set in a previous session so we can re-register with\n // the right user identity automatically (matches OneSignal/Airship).\n this.userId = localStorage.getItem('rivium_push_user_id') || null;\n this.badgeCount = parseInt(localStorage.getItem('rivium_push_badge_count') || '0', 10);\n\n // Check for initial message (from notification click that opened the page)\n this.checkInitialMessage();\n\n // Set up event listeners\n // Listen for messages from service worker\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerMessage.bind(this));\n }\n\n // Listen for visibility changes (app state)\n document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));\n\n // Listen for network changes\n if ('connection' in navigator) {\n (navigator as any).connection?.addEventListener('change', this.handleNetworkChange.bind(this));\n }\n window.addEventListener('online', this.handleOnline.bind(this));\n window.addEventListener('offline', this.handleOffline.bind(this));\n\n this.initialized = true;\n this.trackEvent(RiviumPushAnalyticsEvent.SDK_INITIALIZED);\n\n this.log(RiviumPushLogLevel.INFO, 'RiviumPush SDK initialized');\n\n // Fetch MQTT config from server\n this.fetchMqttConfig();\n }\n\n /**\n * Fetch MQTT and VAPID configuration from server\n */\n private async fetchMqttConfig(): Promise<void> {\n try {\n this.log(RiviumPushLogLevel.DEBUG, 'Fetching config from server...');\n\n const response = await fetch(`${RIVIUM_PUSH_SERVER_URL}/devices/config`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n this.mqttConfig = data.mqtt;\n this.mqttConfigFetched = true;\n\n // Store VAPID public key for Web Push\n if (data.vapidPublicKey) {\n this.vapidPublicKey = data.vapidPublicKey;\n this.log(RiviumPushLogLevel.DEBUG, 'VAPID public key received from server');\n }\n\n this.log(RiviumPushLogLevel.INFO, `Config fetched successfully, vapid=${!!this.vapidPublicKey}`);\n } catch (error) {\n this.log(RiviumPushLogLevel.ERROR, 'Failed to fetch config:', error);\n this.emitError(RiviumPushErrorCode.INVALID_CONFIG, `Failed to fetch config: ${(error as Error).message}`);\n }\n }\n\n // ==========================================================================\n // Logging\n // ==========================================================================\n\n private log(level: RiviumPushLogLevel, message: string, ...args: any[]): void {\n if (level > this.logLevel) return;\n\n const prefix = '[RiviumPush]';\n switch (level) {\n case RiviumPushLogLevel.ERROR:\n console.error(prefix, message, ...args);\n break;\n case RiviumPushLogLevel.WARNING:\n console.warn(prefix, message, ...args);\n break;\n case RiviumPushLogLevel.INFO:\n console.info(prefix, message, ...args);\n break;\n case RiviumPushLogLevel.DEBUG:\n case RiviumPushLogLevel.VERBOSE:\n console.log(prefix, message, ...args);\n break;\n }\n }\n\n /**\n * Set the log level for SDK logging.\n *\n * @example\n * ```typescript\n * // In production, reduce logging\n * riviumPush.setLogLevel(RiviumPushLogLevel.ERROR);\n * ```\n */\n setLogLevel(level: RiviumPushLogLevel): void {\n this.logLevel = level;\n this.log(RiviumPushLogLevel.INFO, `Log level set to ${RiviumPushLogLevel[level]}`);\n }\n\n /**\n * Get current log level\n */\n getLogLevel(): RiviumPushLogLevel {\n return this.logLevel;\n }\n\n // ==========================================================================\n // Analytics\n // ==========================================================================\n\n private trackEvent(event: RiviumPushAnalyticsEvent, properties?: Record<string, any>): void {\n if (this.analyticsEnabled && this.analyticsCallback) {\n try {\n this.analyticsCallback(event, properties);\n } catch (e) {\n this.log(RiviumPushLogLevel.ERROR, 'Analytics callback error:', e);\n }\n }\n this.log(RiviumPushLogLevel.VERBOSE, `Analytics event: ${event}`, properties);\n }\n\n /**\n * Enable analytics tracking with a custom handler.\n *\n * @example\n * ```typescript\n * riviumPush.setAnalyticsHandler((event, properties) => {\n * // Send to your analytics service\n * analytics.track(`rivium_push_${event}`, properties);\n * });\n * ```\n */\n setAnalyticsHandler(callback: RiviumPushAnalyticsCallback): void {\n this.analyticsCallback = callback;\n this.analyticsEnabled = true;\n this.log(RiviumPushLogLevel.INFO, 'Analytics handler set');\n }\n\n /**\n * Disable analytics tracking\n */\n disableAnalytics(): void {\n this.analyticsCallback = null;\n this.analyticsEnabled = false;\n this.log(RiviumPushLogLevel.INFO, 'Analytics disabled');\n }\n\n /**\n * Check if analytics tracking is enabled\n */\n isAnalyticsEnabled(): boolean {\n return this.analyticsEnabled;\n }\n\n // ==========================================================================\n // Error Handling\n // ==========================================================================\n\n private emitError(code: RiviumPushErrorCode, details?: string): void {\n const error = new RiviumPushError(code, details);\n this.log(RiviumPushLogLevel.ERROR, `Error [${code}]: ${error.message}`, details);\n\n if (this.onDetailedErrorCallback) {\n this.onDetailedErrorCallback(error);\n }\n if (this.onErrorCallback) {\n this.onErrorCallback(error);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.CONNECTION_ERROR, {\n errorCode: code,\n errorMessage: error.message,\n details,\n });\n }\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n /**\n * Register device for push notifications\n */\n async register(options?: RegisterOptions): Promise<string> {\n try {\n // Wait for config to be fetched (includes VAPID key)\n if (!this.mqttConfigFetched) {\n this.log(RiviumPushLogLevel.DEBUG, 'Waiting for server config...');\n await this.waitForConfig();\n }\n\n // Register service worker if enabled\n if (this.config.autoRegisterServiceWorker) {\n await this.registerServiceWorker();\n }\n\n // Request notification permission\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_REQUESTED);\n const permission = await this.requestNotificationPermission();\n\n if (permission === 'denied') {\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_DENIED);\n this.emitError(RiviumPushErrorCode.PERMISSION_DENIED);\n throw new RiviumPushError(RiviumPushErrorCode.PERMISSION_DENIED);\n }\n\n if (permission === 'default') {\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_DENIED);\n this.emitError(RiviumPushErrorCode.PERMISSION_DISMISSED, 'User dismissed the permission prompt');\n throw new RiviumPushError(RiviumPushErrorCode.PERMISSION_DISMISSED);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.PERMISSION_GRANTED);\n\n // Get Web Push subscription for background notifications\n // Use VAPID key from server (preferred) or from config\n const vapidKey = this.vapidPublicKey || this.config.vapidPublicKey;\n if (vapidKey && this.serviceWorkerRegistration) {\n try {\n this.pushSubscription = await this.subscribeToPush(vapidKey);\n this.log(RiviumPushLogLevel.INFO, 'Web Push subscription created for background notifications');\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Web Push subscription failed (will use MQTT only):', e);\n }\n }\n\n // Register with backend.\n // Fall back to the persisted userId so callers can call register()\n // on every page load without forgetting the user identity (matches\n // OneSignal/Airship behaviour).\n const effectiveOptions: RegisterOptions = {\n ...options,\n userId: options?.userId ?? this.userId ?? undefined,\n };\n const response = await this.registerDevice(effectiveOptions);\n this.deviceId = response.deviceId;\n\n // Connect MQTT for real-time messages\n this.connectToGateway();\n\n if (this.onRegisteredCallback) {\n this.onRegisteredCallback(response.deviceId);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.DEVICE_REGISTERED, {\n deviceId: response.deviceId,\n userId: options?.userId,\n hasWebPush: !!this.pushSubscription,\n });\n\n this.log(RiviumPushLogLevel.INFO, 'Registered with device ID:', response.deviceId, 'Web Push:', !!this.pushSubscription);\n return response.deviceId;\n } catch (error) {\n if (error instanceof RiviumPushError) {\n throw error;\n }\n this.log(RiviumPushLogLevel.ERROR, 'Registration failed:', error);\n this.emitError(RiviumPushErrorCode.REGISTRATION_FAILED, (error as Error).message);\n throw error;\n }\n }\n\n /**\n * Wait for config to be fetched from server\n */\n private async waitForConfig(timeoutMs = 5000): Promise<void> {\n const startTime = Date.now();\n while (!this.mqttConfigFetched && Date.now() - startTime < timeoutMs) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n if (!this.mqttConfigFetched) {\n this.log(RiviumPushLogLevel.WARNING, 'Config fetch timed out, continuing without it');\n }\n }\n\n /**\n * Unregister device and disconnect\n */\n async unregister(): Promise<void> {\n this.disconnectFromGateway();\n\n if (this.pushSubscription) {\n await this.pushSubscription.unsubscribe();\n this.pushSubscription = null;\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.DEVICE_UNREGISTERED, {\n deviceId: this.deviceId,\n });\n\n this.log(RiviumPushLogLevel.INFO, 'Unregistered');\n }\n\n /**\n * Subscribe to a topic\n */\n async subscribeTopic(topic: string): Promise<void> {\n if (!topic || topic.trim() === '') {\n this.emitError(RiviumPushErrorCode.INVALID_TOPIC, 'Topic cannot be empty');\n return;\n }\n\n this.subscribedTopics.add(topic);\n\n // Register topic subscription on server (for Web Push delivery via sendToTopic)\n if (this.deviceId) {\n try {\n await fetch(`${RIVIUM_PUSH_SERVER_URL}/topics/subscribe`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n body: JSON.stringify({ deviceId: this.deviceId, topic }),\n });\n } catch (err) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to register topic on server:', err);\n }\n }\n\n // Also subscribe via MQTT for real-time foreground messages\n if (this.pnSocket && this.pnSocket.isConnected()) {\n const appId = this.config.apiKey.substring(0, 16);\n const channel = `rivium_push/${appId}/topic/${topic}`;\n this.pnSocket.stream(channel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.INFO, 'Subscribed to topic:', topic);\n this.trackEvent(RiviumPushAnalyticsEvent.TOPIC_SUBSCRIBED, { topic });\n }\n }\n\n /**\n * Unsubscribe from a topic\n */\n async unsubscribeTopic(topic: string): Promise<void> {\n this.subscribedTopics.delete(topic);\n\n // Unregister topic on server\n if (this.deviceId) {\n try {\n await fetch(`${RIVIUM_PUSH_SERVER_URL}/topics/unsubscribe`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n body: JSON.stringify({ deviceId: this.deviceId, topic }),\n });\n } catch (err) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to unregister topic on server:', err);\n }\n }\n\n if (this.pnSocket && this.pnSocket.isConnected()) {\n const appId = this.config.apiKey.substring(0, 16);\n const channel = `rivium_push/${appId}/topic/${topic}`;\n this.pnSocket.detach(channel);\n this.log(RiviumPushLogLevel.INFO, 'Unsubscribed from topic:', topic);\n this.trackEvent(RiviumPushAnalyticsEvent.TOPIC_UNSUBSCRIBED, { topic });\n }\n }\n\n /**\n * Check if connected to MQTT broker\n */\n isConnected(): boolean {\n return this.connectionState === 'connected';\n }\n\n /**\n * Get current device ID\n */\n getDeviceId(): string | null {\n return this.deviceId;\n }\n\n /**\n * Get the per-install subscription ID issued by the server during registration.\n * This is the canonical addressing key for inbox / A-B / in-app calls and the\n * new MQTT topic. Returns `null` until registration succeeds at least once.\n */\n getSubscriptionId(): string | null {\n return this.subscriptionId;\n }\n\n /**\n * Set user ID. Persisted in localStorage so subsequent page loads pick up\n * the same identity automatically (matches OneSignal/Airship behaviour).\n */\n async setUserId(userId: string): Promise<void> {\n this.userId = userId;\n localStorage.setItem('rivium_push_user_id', userId);\n\n // Re-register with new user ID\n await this.registerDevice({ userId });\n this.log(RiviumPushLogLevel.INFO, 'User ID set:', userId);\n }\n\n /**\n * Clear user ID. Call this on logout.\n */\n clearUserId(): void {\n this.userId = null;\n localStorage.removeItem('rivium_push_user_id');\n this.log(RiviumPushLogLevel.INFO, 'User ID cleared');\n }\n\n /**\n * Get the currently-stored userId, if any. Survives page reloads.\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Get the message that launched/opened the app (when user tapped a notification)\n * Returns null if the app was not opened from a notification tap\n */\n getInitialMessage(): RiviumPushMessage | null {\n return this.initialMessage;\n }\n\n /**\n * Get current badge count\n */\n getBadgeCount(): number {\n return this.badgeCount;\n }\n\n /**\n * Set badge count\n */\n setBadgeCount(count: number): void {\n this.badgeCount = Math.max(0, count);\n localStorage.setItem('rivium_push_badge_count', this.badgeCount.toString());\n\n // Update favicon badge\n this.updateFaviconBadge(this.badgeCount);\n\n // Use Badge API if available\n if ('setAppBadge' in navigator) {\n if (this.badgeCount > 0) {\n (navigator as any).setAppBadge(this.badgeCount);\n } else {\n (navigator as any).clearAppBadge();\n }\n }\n }\n\n /**\n * Clear badge\n */\n clearBadge(): void {\n this.setBadgeCount(0);\n }\n\n /**\n * Refresh MQTT JWT token (called automatically when token expires)\n * Can also be called manually if needed\n */\n async refreshMqttToken(): Promise<void> {\n if (!this.deviceId) {\n throw new RiviumPushError(RiviumPushErrorCode.NOT_INITIALIZED, 'Device not registered');\n }\n\n try {\n const response = await fetch(`${RIVIUM_PUSH_SERVER_URL}/devices/${this.deviceId}/mqtt-token/refresh`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new RiviumPushError(\n RiviumPushErrorCode.SERVER_ERROR,\n errorData.message || `HTTP ${response.status}`\n );\n }\n\n const data = await response.json();\n\n if (data.token && this.mqttConfig) {\n this.mqttConfig.token = data.token;\n this.log(RiviumPushLogLevel.INFO, 'MQTT token refreshed successfully');\n }\n } catch (error) {\n if (error instanceof RiviumPushError) throw error;\n throw new RiviumPushError(RiviumPushErrorCode.NETWORK_ERROR, (error as Error).message);\n }\n }\n\n /**\n * Get current network state\n */\n getNetworkState(): NetworkState {\n const connection = (navigator as any).connection;\n return {\n isAvailable: navigator.onLine,\n networkType: this.detectNetworkType(),\n effectiveType: connection?.effectiveType,\n downlink: connection?.downlink,\n rtt: connection?.rtt,\n };\n }\n\n /**\n * Get current app (visibility) state\n */\n getAppState(): AppState {\n return {\n isVisible: document.visibilityState === 'visible',\n visibilityState: document.visibilityState,\n };\n }\n\n /**\n * Check if notifications are supported\n */\n static isSupported(): boolean {\n return (\n typeof window !== 'undefined' &&\n 'Notification' in window &&\n 'serviceWorker' in navigator\n );\n }\n\n /**\n * Get current notification permission status\n */\n static getPermissionStatus(): NotificationPermission {\n if (typeof Notification === 'undefined') {\n return 'denied';\n }\n return Notification.permission;\n }\n\n // ==========================================================================\n // Event Listeners\n // ==========================================================================\n\n /**\n * Set callback for receiving messages\n */\n onMessage(callback: OnMessageCallback): () => void {\n this.onMessageCallback = callback;\n return () => {\n this.onMessageCallback = null;\n };\n }\n\n /**\n * Set callback for connection state changes\n */\n onConnectionState(callback: OnConnectionStateCallback): () => void {\n this.onConnectionStateCallback = callback;\n return () => {\n this.onConnectionStateCallback = null;\n };\n }\n\n /**\n * Set callback for registration success\n */\n onRegistered(callback: OnRegisteredCallback): () => void {\n this.onRegisteredCallback = callback;\n return () => {\n this.onRegisteredCallback = null;\n };\n }\n\n /**\n * Set callback for errors (simple)\n */\n onError(callback: OnErrorCallback): () => void {\n this.onErrorCallback = callback;\n return () => {\n this.onErrorCallback = null;\n };\n }\n\n /**\n * Set callback for detailed errors with error codes\n */\n onDetailedError(callback: OnDetailedErrorCallback): () => void {\n this.onDetailedErrorCallback = callback;\n return () => {\n this.onDetailedErrorCallback = null;\n };\n }\n\n /**\n * Set callback for notification clicks\n */\n onNotificationClick(callback: OnNotificationClickCallback): () => void {\n this.onNotificationClickCallback = callback;\n return () => {\n this.onNotificationClickCallback = null;\n };\n }\n\n /**\n * Set callback for action button clicks\n */\n onActionClicked(callback: OnActionClickedCallback): () => void {\n this.onActionClickedCallback = callback;\n return () => {\n this.onActionClickedCallback = null;\n };\n }\n\n /**\n * Set callback for reconnection state changes\n */\n onReconnecting(callback: OnReconnectingCallback): () => void {\n this.onReconnectingCallback = callback;\n return () => {\n this.onReconnectingCallback = null;\n };\n }\n\n /**\n * Set callback for network state changes\n */\n onNetworkState(callback: OnNetworkStateCallback): () => void {\n this.onNetworkStateCallback = callback;\n return () => {\n this.onNetworkStateCallback = null;\n };\n }\n\n /**\n * Set callback for app state changes (visibility)\n */\n onAppState(callback: OnAppStateCallback): () => void {\n this.onAppStateCallback = callback;\n return () => {\n this.onAppStateCallback = null;\n };\n }\n\n // ==========================================================================\n // Private Methods - Network & App State\n // ==========================================================================\n\n private detectNetworkType(): NetworkType {\n const connection = (navigator as any).connection;\n if (!connection) return NetworkType.UNKNOWN;\n\n const type = connection.type;\n switch (type) {\n case 'wifi':\n return NetworkType.WIFI;\n case 'cellular':\n return NetworkType.CELLULAR;\n case 'ethernet':\n return NetworkType.ETHERNET;\n case 'none':\n return NetworkType.NONE;\n default:\n return NetworkType.UNKNOWN;\n }\n }\n\n private handleNetworkChange(): void {\n const state = this.getNetworkState();\n this.log(RiviumPushLogLevel.DEBUG, 'Network state changed:', state);\n\n if (this.onNetworkStateCallback) {\n this.onNetworkStateCallback(state);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.NETWORK_STATE_CHANGED, {\n isAvailable: state.isAvailable,\n networkType: state.networkType,\n effectiveType: state.effectiveType,\n });\n }\n\n private handleOnline(): void {\n this.log(RiviumPushLogLevel.INFO, 'Network online');\n this.handleNetworkChange();\n\n // Reconnect if disconnected\n if (this.connectionState === 'disconnected' && this.deviceId) {\n this.log(RiviumPushLogLevel.INFO, 'Reconnecting after network restored');\n this.connectToGateway();\n }\n }\n\n private handleOffline(): void {\n this.log(RiviumPushLogLevel.INFO, 'Network offline');\n this.handleNetworkChange();\n }\n\n private handleVisibilityChange(): void {\n const state = this.getAppState();\n this.log(RiviumPushLogLevel.DEBUG, 'App state changed:', state);\n\n if (this.onAppStateCallback) {\n this.onAppStateCallback(state);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.APP_STATE_CHANGED, {\n isVisible: state.isVisible,\n visibilityState: state.visibilityState,\n });\n\n // Reconnect when becoming visible if disconnected\n if (state.isVisible && this.connectionState === 'disconnected' && this.deviceId) {\n this.log(RiviumPushLogLevel.INFO, 'Reconnecting after becoming visible');\n this.connectToGateway();\n }\n }\n\n // ==========================================================================\n // Private Methods - Initial Message\n // ==========================================================================\n\n private checkInitialMessage(): void {\n // Check URL parameters for notification data\n if (typeof window !== 'undefined') {\n const urlParams = new URLSearchParams(window.location.search);\n const notificationData = urlParams.get('rivium_push_notification');\n\n if (notificationData) {\n try {\n this.initialMessage = JSON.parse(decodeURIComponent(notificationData));\n this.log(RiviumPushLogLevel.INFO, 'Initial message found:', this.initialMessage);\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to parse initial message:', e);\n }\n }\n\n // Also check sessionStorage (set by service worker)\n const storedMessage = sessionStorage.getItem('rivium_push_initial_message');\n if (storedMessage && !this.initialMessage) {\n try {\n this.initialMessage = JSON.parse(storedMessage);\n sessionStorage.removeItem('rivium_push_initial_message');\n this.log(RiviumPushLogLevel.INFO, 'Initial message from session:', this.initialMessage);\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Failed to parse stored message:', e);\n }\n }\n }\n }\n\n // ==========================================================================\n // Private Methods - Service Worker & Push\n // ==========================================================================\n\n private async registerServiceWorker(): Promise<void> {\n if (!('serviceWorker' in navigator)) {\n throw new RiviumPushError(RiviumPushErrorCode.SERVICE_NOT_RUNNING, 'Service Workers not supported');\n }\n\n try {\n this.serviceWorkerRegistration = await navigator.serviceWorker.register(\n this.config.serviceWorkerPath!,\n { scope: '/' }\n );\n this.log(RiviumPushLogLevel.INFO, 'Service Worker registered');\n } catch (error) {\n this.log(RiviumPushLogLevel.ERROR, 'Service Worker registration failed:', error);\n throw new RiviumPushError(RiviumPushErrorCode.SERVICE_NOT_RUNNING, (error as Error).message);\n }\n }\n\n private async requestNotificationPermission(): Promise<NotificationPermission> {\n if (typeof Notification === 'undefined') {\n return 'denied';\n }\n\n if (Notification.permission === 'granted') {\n return 'granted';\n }\n\n return await Notification.requestPermission();\n }\n\n private async subscribeToPush(vapidPublicKey: string): Promise<PushSubscription> {\n if (!this.serviceWorkerRegistration) {\n throw new RiviumPushError(RiviumPushErrorCode.SERVICE_NOT_RUNNING, 'Service Worker not registered');\n }\n\n // Check if there's an existing subscription\n const existingSubscription = await this.serviceWorkerRegistration.pushManager.getSubscription();\n if (existingSubscription) {\n this.log(RiviumPushLogLevel.DEBUG, 'Using existing Push subscription');\n return existingSubscription;\n }\n\n const subscription = await this.serviceWorkerRegistration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: this.urlBase64ToUint8Array(vapidPublicKey) as BufferSource,\n });\n\n this.log(RiviumPushLogLevel.INFO, 'Push subscription created');\n return subscription;\n }\n\n private async registerDevice(options?: RegisterOptions): Promise<{ deviceId: string; subscriptionId?: string; mqtt?: { token?: string } }> {\n try {\n // Build request body (use window.location.origin as appIdentifier for per-app isolation)\n const requestBody: Record<string, any> = {\n deviceId: this.deviceId,\n platform: 'web',\n userId: options?.userId,\n appIdentifier: typeof window !== 'undefined' ? window.location.origin : undefined,\n metadata: {\n ...options?.metadata,\n userAgent: navigator.userAgent,\n language: navigator.language,\n url: window.location.origin,\n },\n };\n\n // Add Web Push subscription if available (for background notifications)\n if (this.pushSubscription) {\n const subscriptionJson = this.pushSubscription.toJSON();\n requestBody.webPushSubscription = {\n endpoint: subscriptionJson.endpoint,\n keys: {\n p256dh: subscriptionJson.keys?.p256dh || '',\n auth: subscriptionJson.keys?.auth || '',\n },\n };\n this.log(RiviumPushLogLevel.DEBUG, 'Sending Web Push subscription to server');\n }\n\n const response = await fetch(`${RIVIUM_PUSH_SERVER_URL}/devices/register`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new RiviumPushError(\n RiviumPushErrorCode.SERVER_ERROR,\n errorData.message || `HTTP ${response.status}`\n );\n }\n\n const data = await response.json();\n\n // Store appId for topic subscriptions\n if (data.appId) {\n this.appId = data.appId;\n }\n\n // Capture subscriptionId — the per-install UUID — and persist it.\n if (data.subscriptionId) {\n this.subscriptionId = data.subscriptionId;\n localStorage.setItem('rivium_push_subscription_id', data.subscriptionId);\n this.log(RiviumPushLogLevel.DEBUG, `Stored subscriptionId: ${data.subscriptionId}`);\n }\n\n // Store appIdentifier for per-app message routing\n if (data.appIdentifier) {\n this.appIdentifier = data.appIdentifier;\n }\n\n // Store connection token from registration response\n if (data.mqtt?.token && this.mqttConfig) {\n this.mqttConfig.token = data.mqtt.token;\n this.log(RiviumPushLogLevel.DEBUG, 'Connection token received from registration');\n }\n\n return data;\n } catch (error) {\n if (error instanceof RiviumPushError) throw error;\n throw new RiviumPushError(RiviumPushErrorCode.NETWORK_ERROR, (error as Error).message);\n }\n }\n\n // ==========================================================================\n // Private Methods - PN Protocol Connection\n // ==========================================================================\n\n private connectToGateway(): void {\n if (this.pnSocket) {\n this.pnSocket.close();\n }\n\n // Clear any pending reconnect timer\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n // Check if config is available\n if (!this.mqttConfig) {\n this.log(RiviumPushLogLevel.WARNING, 'Gateway config not available, retrying in 2s...');\n setTimeout(() => this.connectToGateway(), 2000);\n return;\n }\n\n // Check if we have JWT token for authentication\n if (!this.mqttConfig.token) {\n this.log(RiviumPushLogLevel.ERROR, 'Connection token not available. Device must be registered first.');\n this.emitError(RiviumPushErrorCode.AUTHENTICATION_FAILED, 'Connection token not available');\n return;\n }\n\n this.setConnectionState('connecting');\n\n // Use wsHost for WebSocket connections (via Cloudflare), fallback to host\n const gateway = this.mqttConfig.wsHost || this.mqttConfig.host;\n const port = this.mqttConfig.wsPort;\n\n // Determine if secure connection is needed\n const isSecurePage = typeof window !== 'undefined' && window.location.protocol === 'https:';\n const isSecurePort = port === 443;\n const secure = isSecurePage || isSecurePort;\n\n this.log(RiviumPushLogLevel.DEBUG, `Connecting to gateway (secure: ${secure})`);\n\n const clientId = `rivium_push_${this.appId}_${this.deviceId}`;\n\n // Build PNConfig using the protocol's builder\n const pnConfig = new PNConfigBuilder()\n .gateway(gateway)\n .port(port)\n .clientId(clientId)\n .auth(PNAuthFactory.token(this.mqttConfig.token))\n .secure(secure)\n .freshStart(false)\n .autoReconnect(false) // We handle reconnection ourselves\n .connectionTimeout(10)\n .build();\n\n this.pnSocket = new PNSocket(pnConfig);\n\n // Set up connection listener\n const connectionListener: PNConnectionListener = {\n onStateChanged: (state: PNState) => {\n this.log(RiviumPushLogLevel.DEBUG, `Connection state changed: ${state}`);\n },\n onConnected: () => {\n this.log(RiviumPushLogLevel.INFO, 'Connected to gateway');\n this.setConnectionState('connected');\n this.reconnectAttempts = 0;\n this.trackEvent(RiviumPushAnalyticsEvent.CONNECTED);\n\n const appId = this.config.apiKey.substring(0, 16);\n const appIdentifier = this.appIdentifier || (typeof window !== 'undefined' ? window.location.origin : '_default');\n\n // Per-install subscription channel — primary delivery channel for\n // every device-targeted message after the subscriptionId migration.\n if (this.subscriptionId) {\n const subscriptionChannel = `rivium_push/${appId}/sub/${this.subscriptionId}`;\n this.pnSocket!.stream(subscriptionChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.DEBUG, `Streaming from subscription channel ${subscriptionChannel}`);\n }\n\n // Stream broadcast channel\n const broadcastChannel = `rivium_push/${appId}/broadcast`;\n this.pnSocket!.stream(broadcastChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.DEBUG, 'Streaming from broadcast channel');\n\n // DEPRECATED: legacy device-scoped channel. The backend stopped\n // publishing here after the subscriptionId migration. Kept streamed\n // only to keep older test builds / out-of-tree backends working;\n // will be removed in a future SDK release.\n const deviceChannel = `rivium_push/${appId}/${this.deviceId}/${appIdentifier}`;\n this.pnSocket!.stream(deviceChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n this.log(RiviumPushLogLevel.DEBUG, 'Streaming from (deprecated) device channel');\n\n // Resubscribe to custom topics\n this.subscribedTopics.forEach((topic) => {\n const topicChannel = `rivium_push/${appId}/topic/${topic}`;\n this.pnSocket!.stream(topicChannel, (message: PNMessage) => {\n this.handlePNMessage(message);\n }, this.config.mqttQos as PNDeliveryMode);\n });\n },\n onDisconnected: (reason?: string) => {\n this.log(RiviumPushLogLevel.INFO, 'Disconnected from gateway', reason || '');\n this.setConnectionState('disconnected');\n this.trackEvent(RiviumPushAnalyticsEvent.DISCONNECTED);\n this.scheduleReconnect();\n },\n onReconnecting: (attempt: number, nextRetryMs: number) => {\n this.log(RiviumPushLogLevel.INFO, `Reconnecting attempt ${attempt} in ${nextRetryMs}ms`);\n },\n };\n\n this.pnSocket.addConnectionListener(connectionListener);\n\n // Set up error listener\n this.pnSocket.addErrorListener((error: PNProtocolError) => {\n this.log(RiviumPushLogLevel.ERROR, 'Gateway error:', error.message);\n this.setConnectionState('error');\n\n // Map PNProtocolError to RiviumPushErrorCode\n let errorCode = RiviumPushErrorCode.CONNECTION_FAILED;\n const errorMessage = error.message.toLowerCase();\n\n if (errorMessage.includes('timeout')) {\n errorCode = RiviumPushErrorCode.CONNECTION_TIMEOUT;\n } else if (errorMessage.includes('refused') || errorMessage.includes('not authorized')) {\n errorCode = RiviumPushErrorCode.CONNECTION_REFUSED;\n // Token might be expired - try to refresh it\n if (errorMessage.includes('not authorized')) {\n this.log(RiviumPushLogLevel.INFO, 'Token may be expired, attempting refresh...');\n this.refreshMqttToken().then(() => {\n this.log(RiviumPushLogLevel.INFO, 'Token refreshed, reconnecting...');\n this.connectToGateway();\n }).catch((refreshError) => {\n this.log(RiviumPushLogLevel.ERROR, 'Token refresh failed:', refreshError);\n this.emitError(RiviumPushErrorCode.AUTHENTICATION_FAILED, 'Token expired and refresh failed');\n });\n return;\n }\n } else if (errorMessage.includes('auth') || errorMessage.includes('credential')) {\n errorCode = RiviumPushErrorCode.AUTHENTICATION_FAILED;\n } else if (errorMessage.includes('ssl') || errorMessage.includes('tls')) {\n errorCode = RiviumPushErrorCode.SSL_ERROR;\n }\n\n this.emitError(errorCode, error.message);\n });\n\n // Open connection\n this.log(RiviumPushLogLevel.DEBUG, 'Opening connection to gateway...');\n this.pnSocket.open();\n }\n\n /**\n * Handle incoming PNMessage from the protocol layer\n */\n private handlePNMessage(message: PNMessage): void {\n try {\n const data = message.payloadAsJson();\n this.handleMqttMessage(message.channel, data);\n } catch (error) {\n this.log(RiviumPushLogLevel.ERROR, 'Message parse error:', error);\n this.emitError(RiviumPushErrorCode.MESSAGE_PARSE_ERROR, (error as Error).message);\n }\n }\n\n private disconnectFromGateway(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.pnSocket) {\n this.pnSocket.close();\n this.pnSocket = null;\n }\n\n this.setConnectionState('disconnected');\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log(RiviumPushLogLevel.WARNING, 'Max reconnect attempts reached');\n this.emitError(RiviumPushErrorCode.CONNECTION_FAILED, 'Max reconnect attempts reached');\n return;\n }\n\n // Check if we should reconnect (only if online and visible)\n if (!navigator.onLine) {\n this.log(RiviumPushLogLevel.DEBUG, 'Offline, skipping reconnect');\n return;\n }\n\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n this.reconnectAttempts++;\n\n this.log(RiviumPushLogLevel.INFO, `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);\n\n // Emit reconnecting state\n const reconnectionState: ReconnectionState = {\n retryAttempt: this.reconnectAttempts,\n nextRetryMs: delay,\n maxRetryAttempts: this.maxReconnectAttempts,\n };\n\n if (this.onReconnectingCallback) {\n this.onReconnectingCallback(reconnectionState);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.RETRY_STARTED, {\n retryAttempt: this.reconnectAttempts,\n nextRetryMs: delay,\n });\n\n this.reconnectTimer = setTimeout(() => {\n this.connectToGateway();\n }, delay);\n }\n\n private handleMqttMessage(topic: string, data: any): void {\n const message = this.normalizeMessage(data);\n\n this.log(RiviumPushLogLevel.DEBUG, 'Message received:', message.title);\n\n this.trackEvent(RiviumPushAnalyticsEvent.MESSAGE_RECEIVED, {\n messageId: message.messageId,\n title: message.title,\n silent: message.silent,\n hasImage: !!message.imageUrl,\n hasActions: !!message.actions?.length,\n });\n\n // Handle badge\n this.handleBadge(message);\n\n // Show notification if not silent and page is not visible\n if (!message.silent && document.visibilityState !== 'visible') {\n this.showRichNotification(message);\n this.trackEvent(RiviumPushAnalyticsEvent.MESSAGE_DISPLAYED, {\n messageId: message.messageId,\n title: message.title,\n });\n }\n\n if (this.onMessageCallback) {\n this.onMessageCallback(message);\n }\n }\n\n private normalizeMessage(data: any): RiviumPushMessage {\n // Get localized content\n const localizedTitle = this.getLocalizedContent(data, 'title');\n const localizedBody = this.getLocalizedContent(data, 'body');\n\n return {\n title: localizedTitle || data.title || '',\n body: localizedBody || data.body || '',\n data: data.data,\n silent: data.silent,\n // Rich features\n imageUrl: data.imageUrl || data.image,\n iconUrl: data.iconUrl || data.icon,\n actions: data.actions,\n deepLink: data.deepLink,\n // Badge\n badge: data.badge,\n badgeAction: data.badgeAction,\n // Sound and grouping\n sound: data.sound,\n threadId: data.threadId,\n collapseKey: data.collapseKey,\n category: data.category,\n // Priority\n priority: data.priority,\n ttl: data.ttl,\n // Localization\n localizations: data.localizations,\n timezone: data.timezone,\n // Tracking\n messageId: data.messageId,\n campaignId: data.campaignId,\n // Legacy fields\n icon: data.iconUrl || data.icon,\n image: data.imageUrl || data.image,\n tag: data.tag || data.collapseKey || data.threadId,\n };\n }\n\n private getLocalizedContent(data: any, field: 'title' | 'body'): string | null {\n if (!data.localizations || !Array.isArray(data.localizations)) {\n return null;\n }\n\n const deviceLocale = navigator.language.split('-')[0].toLowerCase();\n\n const localized = data.localizations.find((loc: LocalizedContent) =>\n loc.locale.toLowerCase().startsWith(deviceLocale)\n );\n\n return localized ? localized[field] : null;\n }\n\n private handleBadge(message: RiviumPushMessage): void {\n if (message.badge === undefined && !message.badgeAction) return;\n\n const action = message.badgeAction || 'set';\n let newBadge = message.badge || 0;\n\n switch (action) {\n case 'set':\n newBadge = message.badge || 0;\n break;\n case 'increment':\n newBadge = this.badgeCount + (message.badge || 1);\n break;\n case 'decrement':\n newBadge = Math.max(0, this.badgeCount - (message.badge || 1));\n break;\n case 'clear':\n newBadge = 0;\n break;\n }\n\n this.setBadgeCount(newBadge);\n }\n\n private handleServiceWorkerMessage(event: MessageEvent): void {\n const data = event.data;\n\n if (data.type === 'push-message') {\n // Message forwarded from service worker (when tab is visible)\n this.log(RiviumPushLogLevel.DEBUG, 'Push message received from SW:', data.message?.title);\n const message = this.normalizeMessage(data.message);\n\n this.trackEvent(RiviumPushAnalyticsEvent.MESSAGE_RECEIVED, {\n messageId: message.messageId,\n title: message.title,\n source: 'web-push',\n });\n\n // Handle badge\n this.handleBadge(message);\n\n // Notify callback\n if (this.onMessageCallback) {\n this.onMessageCallback(message);\n }\n } else if (data.type === 'notification-click') {\n this.log(RiviumPushLogLevel.INFO, 'Notification clicked from SW');\n const message = this.normalizeMessage(data.message || {});\n\n this.trackEvent(RiviumPushAnalyticsEvent.NOTIFICATION_CLICKED, {\n messageId: message.messageId,\n title: message.title,\n action: data.action,\n });\n\n if (this.onNotificationClickCallback) {\n this.onNotificationClickCallback(message, data.action);\n }\n } else if (data.type === 'action-clicked') {\n this.log(RiviumPushLogLevel.INFO, 'Action clicked from SW:', data.actionId);\n const message = this.normalizeMessage(data.message || {});\n\n this.trackEvent(RiviumPushAnalyticsEvent.ACTION_CLICKED, {\n actionId: data.actionId,\n messageId: message.messageId,\n title: message.title,\n });\n\n if (this.onActionClickedCallback) {\n this.onActionClickedCallback(data.actionId, message);\n }\n } else if (data.type === 'initial-message') {\n // Store initial message from service worker (when app opened via notification)\n this.log(RiviumPushLogLevel.INFO, 'Initial message received from SW');\n this.initialMessage = this.normalizeMessage(data.message || {});\n\n // Also trigger the notification click callback for initial messages\n if (this.onNotificationClickCallback && this.initialMessage) {\n this.onNotificationClickCallback(this.initialMessage, undefined);\n }\n } else if (data.type === 'navigate') {\n // Navigate to URL requested by service worker\n this.log(RiviumPushLogLevel.INFO, 'Navigating to:', data.url);\n if (data.url && typeof window !== 'undefined') {\n window.location.href = data.url;\n }\n }\n }\n\n private showRichNotification(message: RiviumPushMessage): void {\n if (Notification.permission !== 'granted') {\n return;\n }\n\n const options: NotificationOptions & { image?: string } = {\n body: message.body,\n icon: message.iconUrl || message.icon,\n badge: message.iconUrl || message.icon,\n image: message.imageUrl || message.image,\n tag: message.tag || message.collapseKey || message.threadId,\n data: {\n ...message.data,\n deepLink: message.deepLink,\n messageId: message.messageId,\n campaignId: message.campaignId,\n riviumPushMessage: message,\n },\n requireInteraction: message.priority === 'high',\n silent: message.sound === 'none',\n };\n\n // Add action buttons if supported\n if (message.actions && message.actions.length > 0) {\n const notificationActions = message.actions.slice(0, 2).map((action) => ({\n action: action.id,\n title: action.title,\n icon: action.icon,\n }));\n\n if (this.serviceWorkerRegistration) {\n (options as any).actions = notificationActions;\n }\n }\n\n if (this.serviceWorkerRegistration) {\n this.serviceWorkerRegistration.showNotification(message.title, options);\n } else {\n const notification = new Notification(message.title, options);\n\n notification.onclick = () => {\n window.focus();\n if (message.deepLink) {\n window.location.href = message.deepLink;\n }\n if (this.onNotificationClickCallback) {\n this.onNotificationClickCallback(message);\n }\n\n this.trackEvent(RiviumPushAnalyticsEvent.NOTIFICATION_CLICKED, {\n messageId: message.messageId,\n title: message.title,\n });\n\n notification.close();\n };\n }\n }\n\n private updateFaviconBadge(count: number): void {\n try {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const size = 32;\n canvas.width = size;\n canvas.height = size;\n\n const existingFavicon = document.querySelector('link[rel=\"icon\"]') as HTMLLinkElement;\n const faviconUrl = existingFavicon?.href || '/favicon.ico';\n\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n ctx.drawImage(img, 0, 0, size, size);\n\n if (count > 0) {\n const badgeSize = 14;\n const x = size - badgeSize / 2;\n const y = badgeSize / 2;\n\n ctx.beginPath();\n ctx.arc(x, y, badgeSize / 2 + 1, 0, 2 * Math.PI);\n ctx.fillStyle = '#ef4444';\n ctx.fill();\n\n ctx.fillStyle = '#ffffff';\n ctx.font = 'bold 10px sans-serif';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(count > 99 ? '99+' : count.toString(), x, y);\n }\n\n const newFavicon = document.createElement('link');\n newFavicon.rel = 'icon';\n newFavicon.href = canvas.toDataURL('image/png');\n\n if (existingFavicon) {\n existingFavicon.remove();\n }\n document.head.appendChild(newFavicon);\n };\n img.src = faviconUrl;\n } catch (e) {\n this.log(RiviumPushLogLevel.WARNING, 'Could not update favicon badge:', e);\n }\n }\n\n private setConnectionState(state: ConnectionState): void {\n this.connectionState = state;\n if (this.onConnectionStateCallback) {\n this.onConnectionStateCallback(state);\n }\n }\n\n private getOrCreateDeviceId(): string {\n const key = 'rivium_push_device_id';\n let deviceId = localStorage.getItem(key);\n\n if (!deviceId) {\n deviceId = 'web_' + this.generateUUID();\n localStorage.setItem(key, deviceId);\n }\n\n return deviceId;\n }\n\n private generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n const rawData = window.atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n\n return outputArray;\n }\n}\n\nexport default RiviumPush;\nexport { RiviumPush };\n"],"names":["RiviumPushErrorCode","ERROR_MESSAGES","CONNECTION_FAILED","CONNECTION_TIMEOUT","CONNECTION_LOST","CONNECTION_REFUSED","AUTHENTICATION_FAILED","SSL_ERROR","BROKER_UNAVAILABLE","SUBSCRIPTION_FAILED","UNSUBSCRIPTION_FAILED","INVALID_TOPIC","MESSAGE_DELIVERY_FAILED","MESSAGE_PARSE_ERROR","MESSAGE_TIMEOUT","INVALID_CONFIG","MISSING_API_KEY","MISSING_SERVER_URL","INVALID_CREDENTIALS","REGISTRATION_FAILED","DEVICE_ID_GENERATION_FAILED","SERVER_ERROR","NETWORK_ERROR","NOT_INITIALIZED","NOT_CONNECTED","ALREADY_CONNECTED","SERVICE_NOT_RUNNING","PERMISSION_DENIED","PERMISSION_DISMISSED","UNKNOWN_ERROR","RiviumPushError","Error","constructor","code","details","super","this","name","toJSON","message","RiviumPushAnalyticsEvent","RiviumPushLogLevel","NetworkType","RIVIUM_PUSH_SERVER_URL","RiviumPush","config","deviceId","subscriptionId","userId","pnSocket","serviceWorkerRegistration","pushSubscription","connectionState","reconnectAttempts","maxReconnectAttempts","reconnectTimer","subscribedTopics","Set","badgeCount","initialized","initialMessage","mqttConfig","mqttConfigFetched","appId","appIdentifier","vapidPublicKey","logLevel","DEBUG","analyticsCallback","analyticsEnabled","onMessageCallback","onConnectionStateCallback","onRegisteredCallback","onErrorCallback","onDetailedErrorCallback","onNotificationClickCallback","onActionClickedCallback","onReconnectingCallback","onNetworkStateCallback","onAppStateCallback","apiKey","serviceWorkerPath","autoRegisterServiceWorker","mqttQos","ERROR","window","getOrCreateDeviceId","localStorage","getItem","parseInt","checkInitialMessage","navigator","serviceWorker","addEventListener","handleServiceWorkerMessage","bind","document","handleVisibilityChange","_a","connection","handleNetworkChange","handleOnline","handleOffline","trackEvent","SDK_INITIALIZED","log","INFO","fetchMqttConfig","response","fetch","method","headers","ok","status","data","json","mqtt","error","emitError","level","args","prefix","console","WARNING","warn","info","VERBOSE","setLogLevel","getLogLevel","event","properties","e","setAnalyticsHandler","callback","disableAnalytics","isAnalyticsEnabled","CONNECTION_ERROR","errorCode","errorMessage","register","options","waitForConfig","registerServiceWorker","PERMISSION_REQUESTED","permission","requestNotificationPermission","PERMISSION_GRANTED","vapidKey","subscribeToPush","effectiveOptions","_b","undefined","registerDevice","connectToGateway","DEVICE_REGISTERED","hasWebPush","timeoutMs","startTime","Date","now","Promise","resolve","setTimeout","unregister","disconnectFromGateway","unsubscribe","DEVICE_UNREGISTERED","subscribeTopic","topic","trim","add","body","JSON","stringify","err","isConnected","channel","substring","stream","handlePNMessage","TOPIC_SUBSCRIBED","unsubscribeTopic","delete","detach","TOPIC_UNSUBSCRIBED","getDeviceId","getSubscriptionId","setUserId","setItem","clearUserId","removeItem","getUserId","getInitialMessage","getBadgeCount","setBadgeCount","count","Math","max","toString","updateFaviconBadge","setAppBadge","clearAppBadge","clearBadge","refreshMqttToken","errorData","catch","token","getNetworkState","isAvailable","onLine","networkType","detectNetworkType","effectiveType","downlink","rtt","getAppState","isVisible","visibilityState","isSupported","getPermissionStatus","Notification","onMessage","onConnectionState","onRegistered","onError","onDetailedError","onNotificationClick","onActionClicked","onReconnecting","onNetworkState","onAppState","UNKNOWN","type","WIFI","CELLULAR","ETHERNET","NONE","state","NETWORK_STATE_CHANGED","APP_STATE_CHANGED","notificationData","URLSearchParams","location","search","get","parse","decodeURIComponent","storedMessage","sessionStorage","scope","requestPermission","existingSubscription","pushManager","getSubscription","subscription","subscribe","userVisibleOnly","applicationServerKey","urlBase64ToUint8Array","requestBody","platform","origin","metadata","userAgent","language","url","subscriptionJson","webPushSubscription","endpoint","keys","p256dh","auth","_c","close","clearTimeout","setConnectionState","gateway","wsHost","host","port","wsPort","secure","protocol","clientId","pnConfig","PNConfigBuilder","PNAuthFactory","freshStart","autoReconnect","connectionTimeout","build","PNSocket","connectionListener","onStateChanged","onConnected","CONNECTED","subscriptionChannel","broadcastChannel","deviceChannel","forEach","topicChannel","onDisconnected","reason","DISCONNECTED","scheduleReconnect","attempt","nextRetryMs","addConnectionListener","addErrorListener","toLowerCase","includes","then","refreshError","open","payloadAsJson","handleMqttMessage","delay","min","pow","reconnectionState","retryAttempt","maxRetryAttempts","RETRY_STARTED","normalizeMessage","title","MESSAGE_RECEIVED","messageId","silent","hasImage","imageUrl","hasActions","actions","length","handleBadge","showRichNotification","MESSAGE_DISPLAYED","localizedTitle","getLocalizedContent","localizedBody","image","iconUrl","icon","deepLink","badge","badgeAction","sound","threadId","collapseKey","category","priority","ttl","localizations","timezone","campaignId","tag","field","Array","isArray","deviceLocale","split","localized","find","loc","locale","startsWith","action","newBadge","source","NOTIFICATION_CLICKED","actionId","ACTION_CLICKED","href","riviumPushMessage","requireInteraction","notificationActions","slice","map","id","showNotification","notification","onclick","focus","canvas","createElement","ctx","getContext","size","width","height","existingFavicon","querySelector","faviconUrl","img","Image","crossOrigin","onload","drawImage","badgeSize","x","y","beginPath","arc","PI","fillStyle","fill","font","textAlign","textBaseline","fillText","newFavicon","rel","toDataURL","remove","head","appendChild","src","key","generateUUID","crypto","randomUUID","replace","c","r","random","base64String","base64","repeat","rawData","atob","outputArray","Uint8Array","i","charCodeAt"],"mappings":"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"}
|