@cloudsignal/pwa-sdk 1.2.4 → 2.0.0

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/README.md CHANGED
@@ -12,12 +12,6 @@ Progressive Web App SDK for CloudSignal platform with push notifications, instal
12
12
  - **Service Worker** - Badge management, notification history, offline support
13
13
  - **TypeScript** - Full type definitions included
14
14
 
15
- ### v1.2.3 Features
16
- - **Auto Installation Tracking** - Automatically registers PWA installations with backend when detected
17
- - `registerInstallation()` method for manual installation tracking
18
- - `isInstallationRegistered()` / `getInstallationId()` helper methods
19
- - `install:registered` event for installation tracking
20
-
21
15
  ### v1.2.0 Features
22
16
  - **JWT Authentication** - User-linked registrations via JWT tokens (Supabase, Firebase, Auth0, Clerk)
23
17
  - **Dual Auth Mode** - HMAC for anonymous users, JWT for authenticated users
@@ -50,24 +44,6 @@ npm install @cloudsignal/pwa-sdk
50
44
  <script src="https://cdn.cloudsignal.io/cloudsignal-pwa.v1.0.0.js"></script>
51
45
  ```
52
46
 
53
- ## Prerequisites
54
-
55
- ### Environment Variables
56
-
57
- Set up your environment variables before using the SDK:
58
-
59
- ```bash
60
- # .env.local (Next.js) or .env
61
- NEXT_PUBLIC_CLOUDSIGNAL_ORG_ID=your-org-uuid
62
- NEXT_PUBLIC_CLOUDSIGNAL_SERVICE_ID=your-service-uuid
63
- NEXT_PUBLIC_CLOUDSIGNAL_ORG_SECRET=your-secret-key # For HMAC mode only
64
- ```
65
-
66
- **Where to get these values:**
67
- 1. **Organization ID** - From CloudSignal dashboard → Organization Settings
68
- 2. **Service ID** - From CloudSignal dashboard → PWA Services → Your Service
69
- 3. **Organization Secret** - From CloudSignal dashboard → API Keys (for HMAC mode)
70
-
71
47
  ## Quick Start
72
48
 
73
49
  ### Basic Usage
@@ -77,7 +53,7 @@ import { CloudSignalPWA } from '@cloudsignal/pwa-sdk'
77
53
 
78
54
  const pwa = new CloudSignalPWA({
79
55
  organizationId: 'your-org-uuid',
80
- organizationSecret: 'your-secret-key',
56
+ organizationPublishableKey: 'pk_your_publishable_key',
81
57
  serviceId: 'your-service-uuid',
82
58
  debug: true
83
59
  })
@@ -89,9 +65,8 @@ await pwa.initialize()
89
65
  const deviceInfo = pwa.getDeviceInfo()
90
66
  console.log('Device:', deviceInfo.deviceModel, deviceInfo.browser)
91
67
 
92
- // Check installation state - returns InstallationState object
93
- const installState = pwa.getInstallationState()
94
- if (installState.canBeInstalled && !installState.isInstalled) {
68
+ // Check installation state
69
+ if (pwa.canInstall()) {
95
70
  await pwa.showInstallPrompt()
96
71
  }
97
72
 
@@ -99,11 +74,6 @@ if (installState.canBeInstalled && !installState.isInstalled) {
99
74
  const registration = await pwa.registerForPush({
100
75
  userEmail: 'user@example.com'
101
76
  })
102
-
103
- // Access registration ID (note: registrationId, not id)
104
- if (registration) {
105
- console.log('Registered with ID:', registration.registrationId)
106
- }
107
77
  ```
108
78
 
109
79
  ### CDN Usage
@@ -113,7 +83,7 @@ if (registration) {
113
83
  <script>
114
84
  const pwa = new CloudSignalPWA.CloudSignalPWA({
115
85
  organizationId: 'your-org-uuid',
116
- organizationSecret: 'your-secret-key',
86
+ organizationPublishableKey: 'pk_your_publishable_key',
117
87
  serviceId: 'your-service-uuid'
118
88
  })
119
89
 
@@ -154,7 +124,7 @@ const registration = await pwa.registerForPush()
154
124
  // Start with HMAC (anonymous)
155
125
  const pwa = new CloudSignalPWA({
156
126
  organizationId: 'your-org-uuid',
157
- organizationSecret: 'your-secret-key',
127
+ organizationPublishableKey: 'pk_your_publishable_key',
158
128
  serviceId: 'your-service-uuid'
159
129
  })
160
130
 
@@ -180,7 +150,7 @@ new CloudSignalPWA(config: PWAConfig)
180
150
  | Option | Type | Required | Description |
181
151
  |--------|------|----------|-------------|
182
152
  | `organizationId` | string | Yes | CloudSignal organization UUID |
183
- | `organizationSecret` | string | No* | Organization secret key (HMAC mode) |
153
+ | `organizationPublishableKey` | string | No* | Organization publishable key `pk_*` (HMAC mode) |
184
154
  | `userToken` | string | No* | JWT token from identity provider |
185
155
  | `onTokenExpired` | function | No | Callback to refresh JWT on 401 |
186
156
  | `serviceId` | string | Yes | PWA service UUID |
@@ -189,7 +159,7 @@ new CloudSignalPWA(config: PWAConfig)
189
159
  | `serviceWorker` | object | No | Service worker config |
190
160
  | `heartbeat` | object | No | Heartbeat config |
191
161
 
192
- *Either `organizationSecret` or `userToken` must be provided
162
+ *Either `organizationPublishableKey` or `userToken` must be provided
193
163
 
194
164
  ### Initialization
195
165
 
@@ -213,28 +183,11 @@ pwa.canInstall(): boolean
213
183
  // Check if PWA is already installed
214
184
  pwa.isInstalled(): boolean
215
185
 
216
- // Get installation state (full details)
186
+ // Get installation state
217
187
  pwa.getInstallationState(): InstallationState
218
- // Returns: {
219
- // isInstalled: boolean,
220
- // canBeInstalled: boolean,
221
- // needsManualInstall: boolean,
222
- // showManualInstructions: boolean,
223
- // installSteps: string[],
224
- // displayMode: 'browser' | 'standalone' | 'minimal-ui' | 'fullscreen'
225
- // }
226
188
 
227
189
  // Get install steps for current platform
228
190
  pwa.getInstallSteps(): string[]
229
-
230
- // Register installation with backend (v1.2.3)
231
- await pwa.registerInstallation(): Promise<{ registrationId: string } | null>
232
-
233
- // Check if installation is registered (v1.2.3)
234
- pwa.isInstallationRegistered(): boolean
235
-
236
- // Get installation registration ID (v1.2.3)
237
- pwa.getInstallationId(): string | null
238
191
  ```
239
192
 
240
193
  ### Push Notifications
@@ -320,27 +273,13 @@ pwa.getWakeLockState(): WakeLockState
320
273
 
321
274
  ```typescript
322
275
  // Queue a request for later (when offline)
323
- // Signature: queueRequest(url, method, options)
324
- await pwa.queueOfflineRequest(
325
- url: string,
326
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
327
- options?: {
328
- headers?: Record<string, string>,
329
- body?: string,
330
- requestType?: 'registration' | 'heartbeat' | 'analytics' | 'preferences' | 'unregister' | 'custom',
331
- priority?: number,
332
- maxRetries?: number,
333
- metadata?: Record<string, any>
334
- }
335
- ): Promise<number | null> // Returns queue ID or null
276
+ await pwa.queueOfflineRequest(url, method, body?, headers?): Promise<string | null>
336
277
 
337
- // Process queued requests - returns array of results
338
- await pwa.processOfflineQueue(): Promise<QueueProcessResult[]>
339
- // QueueProcessResult: { id: number, success: boolean, statusCode?: number, error?: string, shouldRetry: boolean }
278
+ // Process queued requests
279
+ await pwa.processOfflineQueue(): Promise<QueueProcessResult>
340
280
 
341
281
  // Get queue statistics
342
282
  await pwa.getOfflineQueueStats(): Promise<OfflineQueueStats>
343
- // OfflineQueueStats: { totalQueued: number, byType: Record<string, number>, oldestRequest?: number, newestRequest?: number }
344
283
 
345
284
  // Clear all queued requests
346
285
  await pwa.clearOfflineQueue(): Promise<void>
@@ -384,75 +323,40 @@ pwa.off(event: PWAEvent, handler: (data) => void): void
384
323
 
385
324
  **Available Events:**
386
325
 
387
- **Installation Events:**
388
- - `install:available` - Install prompt is available. Handler: `(data: { platforms: string[] }) => void`
389
- - `install:accepted` - User accepted install. Handler: `(data: { outcome: 'accepted', platform?: string }) => void`
390
- - `install:dismissed` - User dismissed install. Handler: `(data: { outcome: 'dismissed' }) => void`
391
- - `install:completed` - PWA was installed
392
- - `install:registered` - PWA installation registered with backend (v1.2.3). Handler: `(data: { registrationId: string }) => void`
393
- - `install:error` - Installation error occurred
394
-
395
- **Push Notification Events:**
396
- - `push:registered` - Push registration successful. Handler: `(data: { registrationId: string, endpoint: string }) => void`
397
- - `push:unregistered` - Push unregistration successful
398
- - `push:updated` - Push registration updated
399
- - `push:error` - Push operation failed. Handler: `(data: { error: Error }) => void`
400
- - `push:received` - Push notification received. Handler: `(data: { payload: NotificationPayload, timestamp: number }) => void`
401
- - `push:clicked` - Notification clicked. Handler: `(data: { action?: string, data?: any, url?: string }) => void`
402
-
403
- **Permission Events:**
404
- - `permission:granted` - Notification permission granted
405
- - `permission:denied` - Notification permission denied
406
- - `permission:prompt` - Permission prompt shown
407
-
408
- **Service Worker Events:**
409
- - `sw:registered` - Service worker registered
410
- - `sw:updated` - Service worker updated
411
- - `sw:error` - Service worker error
412
- - `sw:activated` - Service worker activated
413
-
414
- **Config Events:**
415
- - `config:loaded` - Service config downloaded. Handler: `(data: { config: PWAServiceConfig }) => void`
416
- - `config:error` - Config download failed
417
-
418
- **Heartbeat Events:**
419
- - `heartbeat:started` - Heartbeat started
420
- - `heartbeat:stopped` - Heartbeat stopped
421
- - `heartbeat:sent` - Heartbeat sent successfully
422
- - `heartbeat:error` - Heartbeat failed
423
- - `heartbeat:intervalChanged` - Heartbeat interval adjusted (v1.1.0)
424
- - `heartbeat:pausedForBattery` - Heartbeat paused due to low battery (v1.1.0)
425
- - `heartbeat:resumedFromBattery` - Heartbeat resumed (v1.1.0)
426
-
427
- **Network Events:**
428
- - `network:online` - Network came online
429
- - `network:offline` - Network went offline
430
-
431
- **Wake Lock Events (v1.1.0):**
432
- - `wakeLock:acquired` - Screen wake lock acquired
433
- - `wakeLock:released` - Screen wake lock released
434
- - `wakeLock:error` - Wake lock operation failed
435
-
436
- **Offline Queue Events (v1.1.0):**
437
- - `offlineQueue:queued` - Request added to offline queue
438
- - `offlineQueue:processed` - Queued request processed
439
- - `offlineQueue:flushed` - All queued requests processed
440
-
441
- **iOS Banner Events (v1.1.0):**
442
- - `iosBanner:shown` - iOS install banner shown
443
- - `iosBanner:dismissed` - iOS install banner dismissed
444
- - `iosBanner:installClicked` - User clicked install on iOS banner
445
-
446
- **Authentication Events (v1.2.0):**
447
- - `auth:tokenUpdated` - JWT token updated/upgraded
448
-
449
- **State Events:**
450
- - `state:changed` - SDK state changed
326
+ | Event | Description |
327
+ |-------|-------------|
328
+ | `install:available` | Install prompt is available |
329
+ | `install:accepted` | User accepted install |
330
+ | `install:dismissed` | User dismissed install |
331
+ | `install:completed` | PWA was installed |
332
+ | `push:registered` | Push registration successful |
333
+ | `push:unregistered` | Push unregistration successful |
334
+ | `push:error` | Push operation failed |
335
+ | `permission:denied` | Notification permission denied |
336
+ | `config:loaded` | Service config downloaded |
337
+ | `config:error` | Config download failed |
338
+ | `heartbeat:sent` | Heartbeat sent successfully |
339
+ | `heartbeat:error` | Heartbeat failed |
340
+ | `heartbeat:intervalChanged` | Heartbeat interval adjusted (v1.1.0) |
341
+ | `heartbeat:pausedForBattery` | Heartbeat paused due to low battery (v1.1.0) |
342
+ | `heartbeat:resumedFromBattery` | Heartbeat resumed (v1.1.0) |
343
+ | `wakeLock:acquired` | Screen wake lock acquired (v1.1.0) |
344
+ | `wakeLock:released` | Screen wake lock released (v1.1.0) |
345
+ | `wakeLock:error` | Wake lock operation failed (v1.1.0) |
346
+ | `offlineQueue:queued` | Request added to offline queue (v1.1.0) |
347
+ | `offlineQueue:processed` | Queued request processed (v1.1.0) |
348
+ | `offlineQueue:flushed` | All queued requests processed (v1.1.0) |
349
+ | `iosBanner:shown` | iOS install banner shown (v1.1.0) |
350
+ | `iosBanner:dismissed` | iOS install banner dismissed (v1.1.0) |
351
+ | `iosBanner:installClicked` | User clicked install on iOS banner (v1.1.0) |
352
+ | `auth:tokenUpdated` | JWT token updated/upgraded (v1.2.0) |
353
+ | `network:online` | Network came online |
354
+ | `network:offline` | Network went offline |
355
+ | `sw:registered` | Service worker registered |
356
+ | `sw:updated` | Service worker updated |
451
357
 
452
358
  ## Service Worker Setup
453
359
 
454
- **IMPORTANT:** The service worker MUST be placed at your app's root (e.g., `/service-worker.js` or `/public/service-worker.js`) to ensure correct scope. Service workers can only control pages within their scope.
455
-
456
360
  Copy the service worker to your app's root directory:
457
361
 
458
362
  ```bash
@@ -466,43 +370,6 @@ Or download from CDN:
466
370
  curl -o public/service-worker.js https://cdn.cloudsignal.io/service-worker.v1.0.0.js
467
371
  ```
468
372
 
469
- ### Custom Service Worker Path
470
-
471
- If you need a different path or filename:
472
-
473
- ```javascript
474
- const pwa = new CloudSignalPWA({
475
- // ...other config
476
- serviceWorker: {
477
- path: '/sw.js', // Custom path
478
- scope: '/', // Must match or be parent of your app routes
479
- autoRegister: true,
480
- updateBehavior: 'auto' // 'prompt' | 'auto' | 'manual'
481
- }
482
- })
483
- ```
484
-
485
- ### Workbox/Serwist Compatibility
486
-
487
- **IMPORTANT:** CloudSignal's service worker is **not compatible** with Workbox, Serwist, or other PWA service worker libraries. You must use one or the other.
488
-
489
- **Why?** CloudSignal's service worker handles:
490
- - Push notification reception and display
491
- - Dynamic manifest downloading and caching
492
- - Notification click routing
493
- - Badge management
494
- - Offline notification history
495
-
496
- These features require full control of the service worker lifecycle.
497
-
498
- **If you're currently using Workbox/Serwist:**
499
- 1. Remove the existing service worker library (`@serwist/next`, `next-pwa`, `workbox-webpack-plugin`, etc.)
500
- 2. Use CloudSignal's service worker instead
501
- 3. If you need precaching, consider using the browser's native Cache API in your application code
502
-
503
- **If you need features from Workbox (like precaching):**
504
- Open an issue on GitHub - we may add these capabilities to the CloudSignal service worker in future versions.
505
-
506
373
  ## PWA Manifest
507
374
 
508
375
  Create a `manifest.json` in your app's root:
@@ -570,197 +437,7 @@ info.isOnline // true/false
570
437
  info.connectionType // '4g', '3g', '2g', 'unknown'
571
438
  ```
572
439
 
573
- ## Framework Integration Examples
574
-
575
- ### Next.js (App Router)
576
-
577
- **IMPORTANT:** The SDK uses browser APIs and must be loaded client-side only.
578
-
579
- ```tsx
580
- // components/CloudSignalProvider.tsx
581
- 'use client'
582
-
583
- import { useEffect, useState, createContext, useContext, ReactNode } from 'react'
584
- import type { CloudSignalPWA as CloudSignalPWAType } from '@cloudsignal/pwa-sdk'
585
-
586
- type PWAContextType = {
587
- pwa: CloudSignalPWAType | null
588
- isInitialized: boolean
589
- canInstall: boolean
590
- isRegistered: boolean
591
- }
592
-
593
- const PWAContext = createContext<PWAContextType>({
594
- pwa: null,
595
- isInitialized: false,
596
- canInstall: false,
597
- isRegistered: false
598
- })
599
-
600
- export function CloudSignalProvider({ children }: { children: ReactNode }) {
601
- const [pwa, setPwa] = useState<CloudSignalPWAType | null>(null)
602
- const [isInitialized, setIsInitialized] = useState(false)
603
- const [canInstall, setCanInstall] = useState(false)
604
- const [isRegistered, setIsRegistered] = useState(false)
605
-
606
- useEffect(() => {
607
- // Dynamic import - required for Next.js App Router
608
- const initPWA = async () => {
609
- try {
610
- const { CloudSignalPWA } = await import('@cloudsignal/pwa-sdk')
611
-
612
- const instance = new CloudSignalPWA({
613
- organizationId: process.env.NEXT_PUBLIC_CLOUDSIGNAL_ORG_ID!,
614
- organizationSecret: process.env.NEXT_PUBLIC_CLOUDSIGNAL_ORG_SECRET!,
615
- serviceId: process.env.NEXT_PUBLIC_CLOUDSIGNAL_SERVICE_ID!,
616
- debug: process.env.NODE_ENV === 'development'
617
- })
618
-
619
- await instance.initialize()
620
-
621
- setPwa(instance)
622
- setIsInitialized(true)
623
- setCanInstall(instance.canInstall())
624
- setIsRegistered(instance.isRegistered())
625
-
626
- // Set up event listeners
627
- instance.on('install:available', () => setCanInstall(true))
628
- instance.on('install:completed', () => setCanInstall(false))
629
- instance.on('push:registered', () => setIsRegistered(true))
630
- instance.on('push:unregistered', () => setIsRegistered(false))
631
- } catch (error) {
632
- console.error('Failed to initialize CloudSignal PWA:', error)
633
- }
634
- }
635
-
636
- initPWA()
637
- }, [])
638
-
639
- return (
640
- <PWAContext.Provider value={{ pwa, isInitialized, canInstall, isRegistered }}>
641
- {children}
642
- </PWAContext.Provider>
643
- )
644
- }
645
-
646
- export const usePWA = () => useContext(PWAContext)
647
- ```
648
-
649
- ```tsx
650
- // app/layout.tsx
651
- import { CloudSignalProvider } from '@/components/CloudSignalProvider'
652
-
653
- export default function RootLayout({ children }: { children: React.ReactNode }) {
654
- return (
655
- <html>
656
- <body>
657
- <CloudSignalProvider>
658
- {children}
659
- </CloudSignalProvider>
660
- </body>
661
- </html>
662
- )
663
- }
664
- ```
665
-
666
- ```tsx
667
- // components/InstallButton.tsx
668
- 'use client'
669
-
670
- import { usePWA } from './CloudSignalProvider'
671
-
672
- export function InstallButton() {
673
- const { pwa, canInstall } = usePWA()
674
-
675
- if (!canInstall) return null
676
-
677
- const handleInstall = async () => {
678
- const result = await pwa?.showInstallPrompt()
679
- if (result?.accepted) {
680
- console.log('App installed!')
681
- }
682
- }
683
-
684
- return <button onClick={handleInstall}>Install App</button>
685
- }
686
- ```
687
-
688
- ### Next.js with Supabase Auth
689
-
690
- ```tsx
691
- // components/CloudSignalSupabaseProvider.tsx
692
- 'use client'
693
-
694
- import { useEffect, useState, createContext, useContext, ReactNode } from 'react'
695
- import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
696
- import type { CloudSignalPWA as CloudSignalPWAType } from '@cloudsignal/pwa-sdk'
697
-
698
- const supabase = createClientComponentClient()
699
-
700
- type PWAContextType = {
701
- pwa: CloudSignalPWAType | null
702
- isInitialized: boolean
703
- }
704
-
705
- const PWAContext = createContext<PWAContextType>({ pwa: null, isInitialized: false })
706
-
707
- export function CloudSignalSupabaseProvider({ children }: { children: ReactNode }) {
708
- const [pwa, setPwa] = useState<CloudSignalPWAType | null>(null)
709
- const [isInitialized, setIsInitialized] = useState(false)
710
-
711
- useEffect(() => {
712
- let instance: CloudSignalPWAType | null = null
713
-
714
- const initPWA = async () => {
715
- const { CloudSignalPWA } = await import('@cloudsignal/pwa-sdk')
716
- const { data: { session } } = await supabase.auth.getSession()
717
-
718
- instance = new CloudSignalPWA({
719
- organizationId: process.env.NEXT_PUBLIC_CLOUDSIGNAL_ORG_ID!,
720
- serviceId: process.env.NEXT_PUBLIC_CLOUDSIGNAL_SERVICE_ID!,
721
- // Use JWT if user is logged in, fall back to HMAC
722
- userToken: session?.access_token,
723
- organizationSecret: !session ? process.env.NEXT_PUBLIC_CLOUDSIGNAL_ORG_SECRET : undefined,
724
- onTokenExpired: async () => {
725
- const { data } = await supabase.auth.refreshSession()
726
- return data.session?.access_token || ''
727
- }
728
- })
729
-
730
- await instance.initialize()
731
- setPwa(instance)
732
- setIsInitialized(true)
733
- }
734
-
735
- initPWA()
736
-
737
- // Listen for auth changes to update token
738
- const { data: { subscription } } = supabase.auth.onAuthStateChange(
739
- async (event, session) => {
740
- if (instance && session?.access_token) {
741
- instance.setUserToken(session.access_token)
742
- // Re-register to link to user
743
- await instance.registerForPush()
744
- }
745
- }
746
- )
747
-
748
- return () => {
749
- subscription.unsubscribe()
750
- }
751
- }, [])
752
-
753
- return (
754
- <PWAContext.Provider value={{ pwa, isInitialized }}>
755
- {children}
756
- </PWAContext.Provider>
757
- )
758
- }
759
-
760
- export const usePWA = () => useContext(PWAContext)
761
- ```
762
-
763
- ### React (Create React App / Vite)
440
+ ## Example: React Integration
764
441
 
765
442
  ```jsx
766
443
  import { useEffect, useState } from 'react'
@@ -768,7 +445,7 @@ import { CloudSignalPWA } from '@cloudsignal/pwa-sdk'
768
445
 
769
446
  const pwa = new CloudSignalPWA({
770
447
  organizationId: process.env.REACT_APP_ORG_ID,
771
- organizationSecret: process.env.REACT_APP_ORG_SECRET,
448
+ organizationPublishableKey: process.env.REACT_APP_CLOUDSIGNAL_PUBLISHABLE_KEY,
772
449
  serviceId: process.env.REACT_APP_SERVICE_ID
773
450
  })
774
451
 
@@ -857,7 +534,7 @@ import {
857
534
  ```javascript
858
535
  const pwa = new CloudSignalPWA({
859
536
  organizationId: 'your-org-uuid',
860
- organizationSecret: 'your-secret-key',
537
+ organizationPublishableKey: 'pk_your_publishable_key',
861
538
  serviceId: 'your-service-uuid',
862
539
 
863
540
  // Wake Lock (prevents screen sleep)
@@ -892,147 +569,6 @@ const pwa = new CloudSignalPWA({
892
569
  })
893
570
  ```
894
571
 
895
- ## Troubleshooting
896
-
897
- ### Service Worker Issues
898
-
899
- **Problem:** Service worker not registering
900
- ```
901
- DOMException: Failed to register a ServiceWorker
902
- ```
903
-
904
- **Solutions:**
905
- 1. Ensure service worker is at root path (`/service-worker.js`)
906
- 2. Check HTTPS is enabled (required except on localhost)
907
- 3. Verify service worker path in config matches actual file location
908
- 4. Check browser console for detailed error
909
-
910
- **Problem:** Push notifications not received after service worker update
911
-
912
- **Solution:** The SDK handles this automatically with `updateBehavior: 'auto'`. If using manual mode, call `pwa.getServiceWorkerManager().update()` after deploy.
913
-
914
- ### Permission Issues
915
-
916
- **Problem:** Permission prompt never appears
917
-
918
- **Causes:**
919
- 1. User previously denied permission (check `pwa.getDeviceInfo().notificationPermission`)
920
- 2. Site not served over HTTPS
921
- 3. Browser settings blocking notifications globally
922
-
923
- **Solution for denied permission:**
924
- ```javascript
925
- const deviceInfo = pwa.getDeviceInfo()
926
- if (deviceInfo.notificationPermission === 'denied') {
927
- // Guide user to browser settings
928
- alert('Please enable notifications in your browser settings')
929
- }
930
- ```
931
-
932
- ### iOS-Specific Issues
933
-
934
- **Problem:** Install prompt not showing on iOS Safari
935
-
936
- **Explanation:** iOS Safari doesn't support the Web App Install Banner API. Use the iOS Install Banner feature:
937
- ```javascript
938
- const pwa = new CloudSignalPWA({
939
- // ...config
940
- iosInstallBanner: {
941
- enabled: true,
942
- appName: 'My App',
943
- showOnFirstVisit: true
944
- }
945
- })
946
- ```
947
-
948
- **Problem:** Push notifications not working on iOS
949
-
950
- **Requirements:**
951
- - iOS 16.4+
952
- - PWA must be installed to home screen
953
- - User must grant permission after installation
954
-
955
- ### Token/Auth Issues
956
-
957
- **Problem:** 401 errors after token expires
958
-
959
- **Solution:** Implement `onTokenExpired` callback:
960
- ```javascript
961
- const pwa = new CloudSignalPWA({
962
- // ...config
963
- onTokenExpired: async () => {
964
- // Your token refresh logic
965
- const newToken = await refreshMyToken()
966
- return newToken
967
- }
968
- })
969
- ```
970
-
971
- **Problem:** Registration not linked to user after login
972
-
973
- **Solution:** Call `setUserToken()` followed by `registerForPush()`:
974
- ```javascript
975
- pwa.setUserToken(newJWT)
976
- await pwa.registerForPush() // Re-registers with user identity
977
- ```
978
-
979
- ### Debug Mode
980
-
981
- Enable debug logging to troubleshoot issues:
982
- ```javascript
983
- const pwa = new CloudSignalPWA({
984
- // ...config
985
- debug: true // Logs all SDK operations to console
986
- })
987
- ```
988
-
989
- ## Production Checklist
990
-
991
- Before deploying to production, verify:
992
-
993
- - [ ] **HTTPS enabled** - Required for service workers and push notifications
994
- - [ ] **Service worker at root** - `/service-worker.js` or `/public/service-worker.js`
995
- - [ ] **Manifest.json configured** - Icons, name, start_url, display mode
996
- - [ ] **Environment variables set** - Organization ID, Service ID, Secret/JWT config
997
- - [ ] **VAPID keys configured** - In CloudSignal dashboard
998
- - [ ] **Test on target devices** - iOS Safari, Chrome, Firefox, Edge
999
- - [ ] **Test install flow** - Both automatic prompt and iOS manual instructions
1000
- - [ ] **Test push notifications** - Send test notification from dashboard
1001
- - [ ] **Test offline behavior** - Verify offline queue and reconnection
1002
- - [ ] **Token refresh tested** - If using JWT, verify refresh flow works
1003
- - [ ] **Error handling** - Graceful degradation when features unavailable
1004
-
1005
- ## Migration from Other Services
1006
-
1007
- ### From Firebase Cloud Messaging (FCM)
1008
-
1009
- CloudSignal uses Web Push directly (not FCM). Key differences:
1010
- - No Firebase SDK dependency
1011
- - VAPID keys instead of FCM server key
1012
- - Direct Web Push Protocol
1013
-
1014
- ```javascript
1015
- // Before (FCM)
1016
- import { getMessaging, getToken } from 'firebase/messaging'
1017
- const token = await getToken(messaging, { vapidKey: '...' })
1018
-
1019
- // After (CloudSignal)
1020
- import { CloudSignalPWA } from '@cloudsignal/pwa-sdk'
1021
- const registration = await pwa.registerForPush()
1022
- ```
1023
-
1024
- ### From OneSignal
1025
-
1026
- Remove OneSignal SDK and replace with CloudSignal:
1027
- ```javascript
1028
- // Before
1029
- OneSignal.push(['init', { appId: '...' }])
1030
-
1031
- // After
1032
- const pwa = new CloudSignalPWA({ organizationId: '...', serviceId: '...' })
1033
- await pwa.initialize()
1034
- ```
1035
-
1036
572
  ## License
1037
573
 
1038
574
  MIT License - Copyright (c) 2024-2025 CloudSignal