@cloudsignal/pwa-sdk 1.0.0 → 1.1.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.
@@ -8,14 +8,21 @@
8
8
  */
9
9
 
10
10
  // service-worker/service-worker.ts
11
- var CACHE_VERSION = "v1.0.0";
11
+ var CACHE_VERSION = "v1.1.0";
12
12
  var DB_NAME = "CloudSignalPWA";
13
- var DB_VERSION = 1;
13
+ var DB_VERSION = 2;
14
14
  var STORES = {
15
15
  BADGE: "badge",
16
16
  NOTIFICATIONS: "notifications",
17
17
  USER_PREFS: "userPreferences",
18
- SYNC_QUEUE: "syncQueue"
18
+ SYNC_QUEUE: "syncQueue",
19
+ ANALYTICS_QUEUE: "analyticsQueue"
20
+ };
21
+ var analyticsConfig = {
22
+ enabled: true,
23
+ endpoint: "",
24
+ organizationId: "",
25
+ organizationSecret: ""
19
26
  };
20
27
  var ServiceWorkerStorage = class {
21
28
  constructor() {
@@ -52,6 +59,14 @@
52
59
  });
53
60
  syncStore.createIndex("timestamp", "timestamp", { unique: false });
54
61
  }
62
+ if (!db.objectStoreNames.contains(STORES.ANALYTICS_QUEUE)) {
63
+ const analyticsStore = db.createObjectStore(STORES.ANALYTICS_QUEUE, {
64
+ keyPath: "id",
65
+ autoIncrement: true
66
+ });
67
+ analyticsStore.createIndex("timestamp", "timestamp", { unique: false });
68
+ analyticsStore.createIndex("eventType", "eventType", { unique: false });
69
+ }
55
70
  };
56
71
  });
57
72
  }
@@ -122,6 +137,49 @@
122
137
  }
123
138
  };
124
139
  }
140
+ // ============================================
141
+ // Analytics Queue Methods
142
+ // ============================================
143
+ async queueAnalyticsEvent(event) {
144
+ await this.ensureConnection();
145
+ const transaction = this.db.transaction([STORES.ANALYTICS_QUEUE], "readwrite");
146
+ const store = transaction.objectStore(STORES.ANALYTICS_QUEUE);
147
+ return this.promisifyRequest(store.add(event));
148
+ }
149
+ async getQueuedAnalyticsEvents(limit = 50) {
150
+ await this.ensureConnection();
151
+ const transaction = this.db.transaction([STORES.ANALYTICS_QUEUE], "readonly");
152
+ const store = transaction.objectStore(STORES.ANALYTICS_QUEUE);
153
+ const index = store.index("timestamp");
154
+ return new Promise((resolve, reject) => {
155
+ const events = [];
156
+ const request = index.openCursor();
157
+ request.onsuccess = (event) => {
158
+ const cursor = event.target.result;
159
+ if (cursor && events.length < limit) {
160
+ events.push(cursor.value);
161
+ cursor.continue();
162
+ } else {
163
+ resolve(events);
164
+ }
165
+ };
166
+ request.onerror = () => reject(request.error);
167
+ });
168
+ }
169
+ async removeAnalyticsEvents(ids) {
170
+ await this.ensureConnection();
171
+ const transaction = this.db.transaction([STORES.ANALYTICS_QUEUE], "readwrite");
172
+ const store = transaction.objectStore(STORES.ANALYTICS_QUEUE);
173
+ for (const id of ids) {
174
+ store.delete(id);
175
+ }
176
+ }
177
+ async getAnalyticsQueueCount() {
178
+ await this.ensureConnection();
179
+ const transaction = this.db.transaction([STORES.ANALYTICS_QUEUE], "readonly");
180
+ const store = transaction.objectStore(STORES.ANALYTICS_QUEUE);
181
+ return this.promisifyRequest(store.count());
182
+ }
125
183
  };
126
184
  var swStorage = new ServiceWorkerStorage();
127
185
  async function updateBadge(count) {
@@ -147,6 +205,57 @@
147
205
  await updateBadge(newCount);
148
206
  return newCount;
149
207
  }
208
+ async function trackNotificationEvent(notificationId, eventType, metadata) {
209
+ if (!analyticsConfig.enabled || !notificationId) {
210
+ return;
211
+ }
212
+ const event = {
213
+ notificationId,
214
+ eventType,
215
+ timestamp: Date.now(),
216
+ metadata
217
+ };
218
+ await swStorage.queueAnalyticsEvent(event);
219
+ console.log(`[CloudSignal SW] Analytics: ${eventType} event queued for ${notificationId}`);
220
+ if (navigator.onLine && analyticsConfig.endpoint) {
221
+ await flushAnalyticsQueue();
222
+ }
223
+ }
224
+ async function flushAnalyticsQueue() {
225
+ if (!analyticsConfig.endpoint || !analyticsConfig.organizationId) {
226
+ return;
227
+ }
228
+ const events = await swStorage.getQueuedAnalyticsEvents(50);
229
+ if (events.length === 0) {
230
+ return;
231
+ }
232
+ try {
233
+ const response = await fetch(analyticsConfig.endpoint, {
234
+ method: "POST",
235
+ headers: {
236
+ "Content-Type": "application/json",
237
+ "X-Organization-ID": analyticsConfig.organizationId
238
+ },
239
+ body: JSON.stringify({
240
+ events: events.map((e) => ({
241
+ notification_id: e.notificationId,
242
+ event_type: e.eventType,
243
+ timestamp: e.timestamp,
244
+ metadata: e.metadata
245
+ }))
246
+ })
247
+ });
248
+ if (response.ok) {
249
+ const ids = events.map((e) => e.id).filter(Boolean);
250
+ await swStorage.removeAnalyticsEvents(ids);
251
+ console.log(`[CloudSignal SW] Analytics: Flushed ${events.length} events`);
252
+ } else {
253
+ console.log(`[CloudSignal SW] Analytics: Failed to flush, will retry (${response.status})`);
254
+ }
255
+ } catch (error) {
256
+ console.log("[CloudSignal SW] Analytics: Network error, events remain queued");
257
+ }
258
+ }
150
259
  self.addEventListener("install", (event) => {
151
260
  console.log(`[CloudSignal SW] Installing service worker ${CACHE_VERSION}`);
152
261
  self.skipWaiting();
@@ -176,6 +285,7 @@
176
285
  body: event.data.text()
177
286
  };
178
287
  }
288
+ const notificationId = data.notification_id || data.notificationId || data.tag || `notif-${Date.now()}`;
179
289
  const title = data.title || "CloudSignal";
180
290
  const options = {
181
291
  body: data.body || "",
@@ -184,7 +294,11 @@
184
294
  image: data.image,
185
295
  tag: data.tag || "cloudsignal-notification",
186
296
  requireInteraction: data.requireInteraction || false,
187
- data: data.data || {},
297
+ data: {
298
+ ...data.data || {},
299
+ notificationId
300
+ // Store ID in notification data for click/close tracking
301
+ },
188
302
  actions: data.actions || [],
189
303
  vibrate: data.vibrate,
190
304
  renotify: data.renotify || false,
@@ -203,7 +317,13 @@
203
317
  body: options.body,
204
318
  icon: options.icon,
205
319
  tag: options.tag,
206
- data: options.data
320
+ data: options.data,
321
+ notificationId
322
+ });
323
+ await trackNotificationEvent(notificationId, "displayed", {
324
+ title,
325
+ hasImage: !!data.image,
326
+ hasActions: (data.actions?.length || 0) > 0
207
327
  });
208
328
  })()
209
329
  );
@@ -213,8 +333,17 @@
213
333
  const notification = event.notification;
214
334
  const action = event.action;
215
335
  const data = notification.data || {};
336
+ const notificationId = data.notificationId;
216
337
  notification.close();
217
338
  event.waitUntil(decrementBadge());
339
+ if (notificationId) {
340
+ event.waitUntil(
341
+ trackNotificationEvent(notificationId, "clicked", {
342
+ action: action || "body",
343
+ hasUrl: !!data.url
344
+ })
345
+ );
346
+ }
218
347
  if (action) {
219
348
  console.log(`[CloudSignal SW] Action clicked: ${action}`);
220
349
  }
@@ -234,10 +363,20 @@
234
363
  });
235
364
  self.addEventListener("notificationclose", (event) => {
236
365
  console.log("[CloudSignal SW] Notification closed without click");
366
+ const notification = event.notification;
367
+ const data = notification.data || {};
368
+ const notificationId = data.notificationId;
369
+ if (notificationId) {
370
+ event.waitUntil(
371
+ trackNotificationEvent(notificationId, "dismissed", {
372
+ timeSinceDisplay: data.timestamp ? Date.now() - data.timestamp : void 0
373
+ })
374
+ );
375
+ }
237
376
  });
238
377
  self.addEventListener("message", (event) => {
239
378
  console.log("[CloudSignal SW] Message received:", event.data);
240
- const { type, count } = event.data;
379
+ const { type, count, config } = event.data;
241
380
  switch (type) {
242
381
  case "SKIP_WAITING":
243
382
  self.skipWaiting();
@@ -264,6 +403,29 @@
264
403
  })
265
404
  );
266
405
  break;
406
+ // Analytics configuration
407
+ case "CONFIGURE_ANALYTICS":
408
+ if (config) {
409
+ analyticsConfig = {
410
+ ...analyticsConfig,
411
+ ...config
412
+ };
413
+ console.log("[CloudSignal SW] Analytics configured:", {
414
+ enabled: analyticsConfig.enabled,
415
+ endpoint: analyticsConfig.endpoint ? "set" : "not set"
416
+ });
417
+ }
418
+ break;
419
+ case "FLUSH_ANALYTICS":
420
+ event.waitUntil(flushAnalyticsQueue());
421
+ break;
422
+ case "GET_ANALYTICS_QUEUE_COUNT":
423
+ event.waitUntil(
424
+ swStorage.getAnalyticsQueueCount().then((queueCount) => {
425
+ event.ports?.[0]?.postMessage({ count: queueCount });
426
+ })
427
+ );
428
+ break;
267
429
  }
268
430
  });
269
431
  self.addEventListener("sync", (event) => {
@@ -274,6 +436,9 @@
274
436
  Promise.resolve()
275
437
  );
276
438
  }
439
+ if (event.tag === "cloudsignal-analytics-sync") {
440
+ event.waitUntil(flushAnalyticsQueue());
441
+ }
277
442
  });
278
443
  self.addEventListener("periodicsync", (event) => {
279
444
  console.log("[CloudSignal SW] Periodic sync event:", event.tag);
@@ -1 +1 @@
1
- {"version":3,"sources":["../service-worker/service-worker.ts"],"names":[],"mappings":"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","file":"service-worker.js","sourcesContent":["/**\n * CloudSignal PWA Service Worker\n * Handles push notifications, badge management, and offline functionality\n */\n\n/// <reference lib=\"webworker\" />\n\ndeclare const self: ServiceWorkerGlobalScope\n\nconst CACHE_VERSION = 'v1.0.0'\nconst DB_NAME = 'CloudSignalPWA'\nconst DB_VERSION = 1\n\n// IndexedDB Store names\nconst STORES = {\n BADGE: 'badge',\n NOTIFICATIONS: 'notifications',\n USER_PREFS: 'userPreferences',\n SYNC_QUEUE: 'syncQueue',\n}\n\n// ============================================\n// IndexedDB Storage\n// ============================================\n\nclass ServiceWorkerStorage {\n private db: IDBDatabase | null = null\n\n async init(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION)\n\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n this.db = request.result\n resolve(this.db)\n }\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result\n\n if (!db.objectStoreNames.contains(STORES.BADGE)) {\n db.createObjectStore(STORES.BADGE)\n }\n\n if (!db.objectStoreNames.contains(STORES.NOTIFICATIONS)) {\n const notificationStore = db.createObjectStore(STORES.NOTIFICATIONS, {\n keyPath: 'id',\n autoIncrement: true,\n })\n notificationStore.createIndex('timestamp', 'timestamp', { unique: false })\n notificationStore.createIndex('read', 'read', { unique: false })\n }\n\n if (!db.objectStoreNames.contains(STORES.USER_PREFS)) {\n db.createObjectStore(STORES.USER_PREFS)\n }\n\n if (!db.objectStoreNames.contains(STORES.SYNC_QUEUE)) {\n const syncStore = db.createObjectStore(STORES.SYNC_QUEUE, {\n keyPath: 'id',\n autoIncrement: true,\n })\n syncStore.createIndex('timestamp', 'timestamp', { unique: false })\n }\n }\n })\n }\n\n private async ensureConnection(): Promise<void> {\n if (!this.db) {\n await this.init()\n }\n }\n\n private promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n }\n\n async getBadgeCount(): Promise<number> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.BADGE], 'readonly')\n const store = transaction.objectStore(STORES.BADGE)\n const count = await this.promisifyRequest(store.get('count'))\n return count || 0\n }\n\n async setBadgeCount(count: number): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.BADGE], 'readwrite')\n const store = transaction.objectStore(STORES.BADGE)\n await this.promisifyRequest(store.put(count, 'count'))\n }\n\n async incrementBadgeCount(increment: number = 1): Promise<number> {\n const currentCount = await this.getBadgeCount()\n const newCount = Math.max(0, currentCount + increment)\n await this.setBadgeCount(newCount)\n return newCount\n }\n\n async saveNotification(notification: Record<string, any>): Promise<number> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.NOTIFICATIONS], 'readwrite')\n const store = transaction.objectStore(STORES.NOTIFICATIONS)\n\n const notificationData = {\n ...notification,\n timestamp: Date.now(),\n read: false,\n }\n\n return this.promisifyRequest(store.add(notificationData))\n }\n\n async markNotificationAsRead(notificationId: number): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.NOTIFICATIONS], 'readwrite')\n const store = transaction.objectStore(STORES.NOTIFICATIONS)\n\n const notification = await this.promisifyRequest(store.get(notificationId))\n if (notification) {\n notification.read = true\n await this.promisifyRequest(store.put(notification))\n }\n }\n\n async cleanOldNotifications(daysToKeep: number = 30): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.NOTIFICATIONS], 'readwrite')\n const store = transaction.objectStore(STORES.NOTIFICATIONS)\n const index = store.index('timestamp')\n\n const cutoffTime = Date.now() - daysToKeep * 24 * 60 * 60 * 1000\n const range = IDBKeyRange.upperBound(cutoffTime)\n\n const cursor = index.openCursor(range)\n cursor.onsuccess = (event) => {\n const result = (event.target as IDBRequest).result\n if (result) {\n store.delete(result.primaryKey)\n result.continue()\n }\n }\n }\n}\n\nconst swStorage = new ServiceWorkerStorage()\n\n// ============================================\n// Badge Management\n// ============================================\n\nasync function updateBadge(count: number): Promise<void> {\n try {\n if (count <= 0) {\n await (self.navigator as any).clearAppBadge?.()\n await swStorage.setBadgeCount(0)\n } else {\n await (self.navigator as any).setAppBadge?.(count)\n await swStorage.setBadgeCount(count)\n }\n } catch (error) {\n console.log('Badge API not supported or failed:', error)\n }\n}\n\nasync function incrementBadge(): Promise<number> {\n const newCount = await swStorage.incrementBadgeCount(1)\n await updateBadge(newCount)\n return newCount\n}\n\nasync function decrementBadge(): Promise<number> {\n const newCount = await swStorage.incrementBadgeCount(-1)\n await updateBadge(newCount)\n return newCount\n}\n\n// ============================================\n// Service Worker Lifecycle Events\n// ============================================\n\nself.addEventListener('install', (event) => {\n console.log(`[CloudSignal SW] Installing service worker ${CACHE_VERSION}`)\n // Skip waiting to activate immediately\n self.skipWaiting()\n})\n\nself.addEventListener('activate', (event) => {\n console.log(`[CloudSignal SW] Activating service worker ${CACHE_VERSION}`)\n event.waitUntil(\n // Claim all clients immediately\n self.clients.claim().then(() => {\n // Clean old notifications\n return swStorage.cleanOldNotifications(30)\n })\n )\n})\n\n// ============================================\n// Push Notification Events\n// ============================================\n\nself.addEventListener('push', (event) => {\n console.log('[CloudSignal SW] Push event received')\n\n if (!event.data) {\n console.log('[CloudSignal SW] Push event has no data')\n return\n }\n\n let data: any\n\n try {\n data = event.data.json()\n } catch (e) {\n console.log('[CloudSignal SW] Failed to parse push data as JSON')\n data = {\n title: 'New Notification',\n body: event.data.text(),\n }\n }\n\n const title = data.title || 'CloudSignal'\n const options: NotificationOptions = {\n body: data.body || '',\n icon: data.icon || '/icon-192x192.png',\n badge: data.badge || '/badge-72x72.png',\n image: data.image,\n tag: data.tag || 'cloudsignal-notification',\n requireInteraction: data.requireInteraction || false,\n data: data.data || {},\n actions: data.actions || [],\n vibrate: data.vibrate,\n renotify: data.renotify || false,\n silent: data.silent || false,\n timestamp: data.timestamp || Date.now(),\n }\n\n // Handle notification URL in data\n if (data.url) {\n options.data = { ...options.data, url: data.url }\n }\n\n event.waitUntil(\n (async () => {\n // Show notification\n await self.registration.showNotification(title, options)\n\n // Increment badge count\n await incrementBadge()\n\n // Save notification to history\n await swStorage.saveNotification({\n title,\n body: options.body,\n icon: options.icon,\n tag: options.tag,\n data: options.data,\n })\n })()\n )\n})\n\n// ============================================\n// Notification Click Events\n// ============================================\n\nself.addEventListener('notificationclick', (event) => {\n console.log('[CloudSignal SW] Notification click event')\n\n const notification = event.notification\n const action = event.action\n const data = notification.data || {}\n\n // Close the notification\n notification.close()\n\n // Decrement badge count\n event.waitUntil(decrementBadge())\n\n // Handle action buttons\n if (action) {\n console.log(`[CloudSignal SW] Action clicked: ${action}`)\n // Custom action handling can be added here\n }\n\n // Determine URL to open\n const urlToOpen = data.url || '/'\n\n event.waitUntil(\n self.clients\n .matchAll({ type: 'window', includeUncontrolled: true })\n .then((windowClients) => {\n // Check if there's already a window/tab open\n for (const client of windowClients) {\n if (client.url === urlToOpen && 'focus' in client) {\n return client.focus()\n }\n }\n\n // If no existing window, open a new one\n if (self.clients.openWindow) {\n return self.clients.openWindow(urlToOpen)\n }\n })\n )\n})\n\n// ============================================\n// Notification Close Events\n// ============================================\n\nself.addEventListener('notificationclose', (event) => {\n console.log('[CloudSignal SW] Notification closed without click')\n // Optionally track notification dismissals\n})\n\n// ============================================\n// Message Events (from main thread)\n// ============================================\n\nself.addEventListener('message', (event) => {\n console.log('[CloudSignal SW] Message received:', event.data)\n\n const { type, count } = event.data\n\n switch (type) {\n case 'SKIP_WAITING':\n self.skipWaiting()\n break\n\n case 'CLEAR_BADGE':\n event.waitUntil(updateBadge(0))\n break\n\n case 'SET_BADGE':\n if (typeof count === 'number') {\n event.waitUntil(updateBadge(count))\n }\n break\n\n case 'GET_BADGE_COUNT':\n event.waitUntil(\n swStorage.getBadgeCount().then((badgeCount) => {\n event.ports?.[0]?.postMessage({ count: badgeCount })\n })\n )\n break\n\n case 'SYNC_BADGE':\n // Sync badge count with actual notifications\n event.waitUntil(\n self.registration.getNotifications().then(async (notifications) => {\n await updateBadge(notifications.length)\n })\n )\n break\n }\n})\n\n// ============================================\n// Sync Events (Background Sync)\n// ============================================\n\nself.addEventListener('sync', (event) => {\n console.log('[CloudSignal SW] Sync event:', event.tag)\n\n if (event.tag === 'cloudsignal-sync') {\n // Handle background sync\n event.waitUntil(\n // Add sync logic here\n Promise.resolve()\n )\n }\n})\n\n// ============================================\n// Periodic Sync Events\n// ============================================\n\nself.addEventListener('periodicsync', (event: any) => {\n console.log('[CloudSignal SW] Periodic sync event:', event.tag)\n\n if (event.tag === 'cloudsignal-periodic') {\n event.waitUntil(\n // Add periodic sync logic here\n swStorage.cleanOldNotifications(30)\n )\n }\n})\n\nconsole.log(`[CloudSignal SW] Service worker loaded ${CACHE_VERSION}`)\n"]}
1
+ {"version":3,"sources":["../service-worker/service-worker.ts"],"names":[],"mappings":"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","file":"service-worker.js","sourcesContent":["/**\n * CloudSignal PWA Service Worker\n * Handles push notifications, badge management, offline functionality,\n * and notification analytics tracking\n */\n\n/// <reference lib=\"webworker\" />\n\ndeclare const self: ServiceWorkerGlobalScope\n\nconst CACHE_VERSION = 'v1.1.0'\nconst DB_NAME = 'CloudSignalPWA'\nconst DB_VERSION = 2\n\n// IndexedDB Store names\nconst STORES = {\n BADGE: 'badge',\n NOTIFICATIONS: 'notifications',\n USER_PREFS: 'userPreferences',\n SYNC_QUEUE: 'syncQueue',\n ANALYTICS_QUEUE: 'analyticsQueue',\n}\n\n// Analytics event types\ntype NotificationEventType = 'displayed' | 'clicked' | 'dismissed'\n\ninterface NotificationAnalyticsEvent {\n id?: number\n notificationId: string\n eventType: NotificationEventType\n timestamp: number\n metadata?: Record<string, any>\n}\n\n// Configuration (can be updated via postMessage)\nlet analyticsConfig = {\n enabled: true,\n endpoint: '',\n organizationId: '',\n organizationSecret: '',\n}\n\n// ============================================\n// IndexedDB Storage\n// ============================================\n\nclass ServiceWorkerStorage {\n private db: IDBDatabase | null = null\n\n async init(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION)\n\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n this.db = request.result\n resolve(this.db)\n }\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result\n\n if (!db.objectStoreNames.contains(STORES.BADGE)) {\n db.createObjectStore(STORES.BADGE)\n }\n\n if (!db.objectStoreNames.contains(STORES.NOTIFICATIONS)) {\n const notificationStore = db.createObjectStore(STORES.NOTIFICATIONS, {\n keyPath: 'id',\n autoIncrement: true,\n })\n notificationStore.createIndex('timestamp', 'timestamp', { unique: false })\n notificationStore.createIndex('read', 'read', { unique: false })\n }\n\n if (!db.objectStoreNames.contains(STORES.USER_PREFS)) {\n db.createObjectStore(STORES.USER_PREFS)\n }\n\n if (!db.objectStoreNames.contains(STORES.SYNC_QUEUE)) {\n const syncStore = db.createObjectStore(STORES.SYNC_QUEUE, {\n keyPath: 'id',\n autoIncrement: true,\n })\n syncStore.createIndex('timestamp', 'timestamp', { unique: false })\n }\n\n // Analytics queue for notification events (v1.1.0)\n if (!db.objectStoreNames.contains(STORES.ANALYTICS_QUEUE)) {\n const analyticsStore = db.createObjectStore(STORES.ANALYTICS_QUEUE, {\n keyPath: 'id',\n autoIncrement: true,\n })\n analyticsStore.createIndex('timestamp', 'timestamp', { unique: false })\n analyticsStore.createIndex('eventType', 'eventType', { unique: false })\n }\n }\n })\n }\n\n private async ensureConnection(): Promise<void> {\n if (!this.db) {\n await this.init()\n }\n }\n\n private promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n }\n\n async getBadgeCount(): Promise<number> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.BADGE], 'readonly')\n const store = transaction.objectStore(STORES.BADGE)\n const count = await this.promisifyRequest(store.get('count'))\n return count || 0\n }\n\n async setBadgeCount(count: number): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.BADGE], 'readwrite')\n const store = transaction.objectStore(STORES.BADGE)\n await this.promisifyRequest(store.put(count, 'count'))\n }\n\n async incrementBadgeCount(increment: number = 1): Promise<number> {\n const currentCount = await this.getBadgeCount()\n const newCount = Math.max(0, currentCount + increment)\n await this.setBadgeCount(newCount)\n return newCount\n }\n\n async saveNotification(notification: Record<string, any>): Promise<number> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.NOTIFICATIONS], 'readwrite')\n const store = transaction.objectStore(STORES.NOTIFICATIONS)\n\n const notificationData = {\n ...notification,\n timestamp: Date.now(),\n read: false,\n }\n\n return this.promisifyRequest(store.add(notificationData))\n }\n\n async markNotificationAsRead(notificationId: number): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.NOTIFICATIONS], 'readwrite')\n const store = transaction.objectStore(STORES.NOTIFICATIONS)\n\n const notification = await this.promisifyRequest(store.get(notificationId))\n if (notification) {\n notification.read = true\n await this.promisifyRequest(store.put(notification))\n }\n }\n\n async cleanOldNotifications(daysToKeep: number = 30): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.NOTIFICATIONS], 'readwrite')\n const store = transaction.objectStore(STORES.NOTIFICATIONS)\n const index = store.index('timestamp')\n\n const cutoffTime = Date.now() - daysToKeep * 24 * 60 * 60 * 1000\n const range = IDBKeyRange.upperBound(cutoffTime)\n\n const cursor = index.openCursor(range)\n cursor.onsuccess = (event) => {\n const result = (event.target as IDBRequest).result\n if (result) {\n store.delete(result.primaryKey)\n result.continue()\n }\n }\n }\n\n // ============================================\n // Analytics Queue Methods\n // ============================================\n\n async queueAnalyticsEvent(event: Omit<NotificationAnalyticsEvent, 'id'>): Promise<number> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.ANALYTICS_QUEUE], 'readwrite')\n const store = transaction.objectStore(STORES.ANALYTICS_QUEUE)\n return this.promisifyRequest(store.add(event))\n }\n\n async getQueuedAnalyticsEvents(limit: number = 50): Promise<NotificationAnalyticsEvent[]> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.ANALYTICS_QUEUE], 'readonly')\n const store = transaction.objectStore(STORES.ANALYTICS_QUEUE)\n const index = store.index('timestamp')\n\n return new Promise((resolve, reject) => {\n const events: NotificationAnalyticsEvent[] = []\n const request = index.openCursor()\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result\n if (cursor && events.length < limit) {\n events.push(cursor.value)\n cursor.continue()\n } else {\n resolve(events)\n }\n }\n\n request.onerror = () => reject(request.error)\n })\n }\n\n async removeAnalyticsEvents(ids: number[]): Promise<void> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.ANALYTICS_QUEUE], 'readwrite')\n const store = transaction.objectStore(STORES.ANALYTICS_QUEUE)\n\n for (const id of ids) {\n store.delete(id)\n }\n }\n\n async getAnalyticsQueueCount(): Promise<number> {\n await this.ensureConnection()\n const transaction = this.db!.transaction([STORES.ANALYTICS_QUEUE], 'readonly')\n const store = transaction.objectStore(STORES.ANALYTICS_QUEUE)\n return this.promisifyRequest(store.count())\n }\n}\n\nconst swStorage = new ServiceWorkerStorage()\n\n// ============================================\n// Badge Management\n// ============================================\n\nasync function updateBadge(count: number): Promise<void> {\n try {\n if (count <= 0) {\n await (self.navigator as any).clearAppBadge?.()\n await swStorage.setBadgeCount(0)\n } else {\n await (self.navigator as any).setAppBadge?.(count)\n await swStorage.setBadgeCount(count)\n }\n } catch (error) {\n console.log('Badge API not supported or failed:', error)\n }\n}\n\nasync function incrementBadge(): Promise<number> {\n const newCount = await swStorage.incrementBadgeCount(1)\n await updateBadge(newCount)\n return newCount\n}\n\nasync function decrementBadge(): Promise<number> {\n const newCount = await swStorage.incrementBadgeCount(-1)\n await updateBadge(newCount)\n return newCount\n}\n\n// ============================================\n// Notification Analytics\n// ============================================\n\n/**\n * Track a notification analytics event\n */\nasync function trackNotificationEvent(\n notificationId: string,\n eventType: NotificationEventType,\n metadata?: Record<string, any>\n): Promise<void> {\n if (!analyticsConfig.enabled || !notificationId) {\n return\n }\n\n const event: Omit<NotificationAnalyticsEvent, 'id'> = {\n notificationId,\n eventType,\n timestamp: Date.now(),\n metadata,\n }\n\n // Queue the event\n await swStorage.queueAnalyticsEvent(event)\n console.log(`[CloudSignal SW] Analytics: ${eventType} event queued for ${notificationId}`)\n\n // Attempt to send immediately if online\n if (navigator.onLine && analyticsConfig.endpoint) {\n await flushAnalyticsQueue()\n }\n}\n\n/**\n * Flush queued analytics events to the backend\n */\nasync function flushAnalyticsQueue(): Promise<void> {\n if (!analyticsConfig.endpoint || !analyticsConfig.organizationId) {\n return\n }\n\n const events = await swStorage.getQueuedAnalyticsEvents(50)\n if (events.length === 0) {\n return\n }\n\n try {\n const response = await fetch(analyticsConfig.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Organization-ID': analyticsConfig.organizationId,\n },\n body: JSON.stringify({\n events: events.map((e) => ({\n notification_id: e.notificationId,\n event_type: e.eventType,\n timestamp: e.timestamp,\n metadata: e.metadata,\n })),\n }),\n })\n\n if (response.ok) {\n // Remove successfully sent events\n const ids = events.map((e) => e.id!).filter(Boolean)\n await swStorage.removeAnalyticsEvents(ids)\n console.log(`[CloudSignal SW] Analytics: Flushed ${events.length} events`)\n } else {\n console.log(`[CloudSignal SW] Analytics: Failed to flush, will retry (${response.status})`)\n }\n } catch (error) {\n console.log('[CloudSignal SW] Analytics: Network error, events remain queued')\n }\n}\n\n// ============================================\n// Service Worker Lifecycle Events\n// ============================================\n\nself.addEventListener('install', (event) => {\n console.log(`[CloudSignal SW] Installing service worker ${CACHE_VERSION}`)\n // Skip waiting to activate immediately\n self.skipWaiting()\n})\n\nself.addEventListener('activate', (event) => {\n console.log(`[CloudSignal SW] Activating service worker ${CACHE_VERSION}`)\n event.waitUntil(\n // Claim all clients immediately\n self.clients.claim().then(() => {\n // Clean old notifications\n return swStorage.cleanOldNotifications(30)\n })\n )\n})\n\n// ============================================\n// Push Notification Events\n// ============================================\n\nself.addEventListener('push', (event) => {\n console.log('[CloudSignal SW] Push event received')\n\n if (!event.data) {\n console.log('[CloudSignal SW] Push event has no data')\n return\n }\n\n let data: any\n\n try {\n data = event.data.json()\n } catch (e) {\n console.log('[CloudSignal SW] Failed to parse push data as JSON')\n data = {\n title: 'New Notification',\n body: event.data.text(),\n }\n }\n\n // Extract notification ID for analytics tracking\n const notificationId = data.notification_id || data.notificationId || data.tag || `notif-${Date.now()}`\n\n const title = data.title || 'CloudSignal'\n const options: NotificationOptions = {\n body: data.body || '',\n icon: data.icon || '/icon-192x192.png',\n badge: data.badge || '/badge-72x72.png',\n image: data.image,\n tag: data.tag || 'cloudsignal-notification',\n requireInteraction: data.requireInteraction || false,\n data: {\n ...(data.data || {}),\n notificationId, // Store ID in notification data for click/close tracking\n },\n actions: data.actions || [],\n vibrate: data.vibrate,\n renotify: data.renotify || false,\n silent: data.silent || false,\n timestamp: data.timestamp || Date.now(),\n }\n\n // Handle notification URL in data\n if (data.url) {\n options.data = { ...options.data, url: data.url }\n }\n\n event.waitUntil(\n (async () => {\n // Show notification\n await self.registration.showNotification(title, options)\n\n // Increment badge count\n await incrementBadge()\n\n // Save notification to history\n await swStorage.saveNotification({\n title,\n body: options.body,\n icon: options.icon,\n tag: options.tag,\n data: options.data,\n notificationId,\n })\n\n // Track 'displayed' analytics event\n await trackNotificationEvent(notificationId, 'displayed', {\n title,\n hasImage: !!data.image,\n hasActions: (data.actions?.length || 0) > 0,\n })\n })()\n )\n})\n\n// ============================================\n// Notification Click Events\n// ============================================\n\nself.addEventListener('notificationclick', (event) => {\n console.log('[CloudSignal SW] Notification click event')\n\n const notification = event.notification\n const action = event.action\n const data = notification.data || {}\n const notificationId = data.notificationId\n\n // Close the notification\n notification.close()\n\n // Decrement badge count\n event.waitUntil(decrementBadge())\n\n // Track 'clicked' analytics event\n if (notificationId) {\n event.waitUntil(\n trackNotificationEvent(notificationId, 'clicked', {\n action: action || 'body',\n hasUrl: !!data.url,\n })\n )\n }\n\n // Handle action buttons\n if (action) {\n console.log(`[CloudSignal SW] Action clicked: ${action}`)\n // Custom action handling can be added here\n }\n\n // Determine URL to open\n const urlToOpen = data.url || '/'\n\n event.waitUntil(\n self.clients\n .matchAll({ type: 'window', includeUncontrolled: true })\n .then((windowClients) => {\n // Check if there's already a window/tab open\n for (const client of windowClients) {\n if (client.url === urlToOpen && 'focus' in client) {\n return client.focus()\n }\n }\n\n // If no existing window, open a new one\n if (self.clients.openWindow) {\n return self.clients.openWindow(urlToOpen)\n }\n })\n )\n})\n\n// ============================================\n// Notification Close Events\n// ============================================\n\nself.addEventListener('notificationclose', (event) => {\n console.log('[CloudSignal SW] Notification closed without click')\n\n const notification = event.notification\n const data = notification.data || {}\n const notificationId = data.notificationId\n\n // Track 'dismissed' analytics event\n if (notificationId) {\n event.waitUntil(\n trackNotificationEvent(notificationId, 'dismissed', {\n timeSinceDisplay: data.timestamp ? Date.now() - data.timestamp : undefined,\n })\n )\n }\n})\n\n// ============================================\n// Message Events (from main thread)\n// ============================================\n\nself.addEventListener('message', (event) => {\n console.log('[CloudSignal SW] Message received:', event.data)\n\n const { type, count, config } = event.data\n\n switch (type) {\n case 'SKIP_WAITING':\n self.skipWaiting()\n break\n\n case 'CLEAR_BADGE':\n event.waitUntil(updateBadge(0))\n break\n\n case 'SET_BADGE':\n if (typeof count === 'number') {\n event.waitUntil(updateBadge(count))\n }\n break\n\n case 'GET_BADGE_COUNT':\n event.waitUntil(\n swStorage.getBadgeCount().then((badgeCount) => {\n event.ports?.[0]?.postMessage({ count: badgeCount })\n })\n )\n break\n\n case 'SYNC_BADGE':\n // Sync badge count with actual notifications\n event.waitUntil(\n self.registration.getNotifications().then(async (notifications) => {\n await updateBadge(notifications.length)\n })\n )\n break\n\n // Analytics configuration\n case 'CONFIGURE_ANALYTICS':\n if (config) {\n analyticsConfig = {\n ...analyticsConfig,\n ...config,\n }\n console.log('[CloudSignal SW] Analytics configured:', {\n enabled: analyticsConfig.enabled,\n endpoint: analyticsConfig.endpoint ? 'set' : 'not set',\n })\n }\n break\n\n case 'FLUSH_ANALYTICS':\n event.waitUntil(flushAnalyticsQueue())\n break\n\n case 'GET_ANALYTICS_QUEUE_COUNT':\n event.waitUntil(\n swStorage.getAnalyticsQueueCount().then((queueCount) => {\n event.ports?.[0]?.postMessage({ count: queueCount })\n })\n )\n break\n }\n})\n\n// ============================================\n// Sync Events (Background Sync)\n// ============================================\n\nself.addEventListener('sync', (event) => {\n console.log('[CloudSignal SW] Sync event:', event.tag)\n\n if (event.tag === 'cloudsignal-sync') {\n // Handle background sync\n event.waitUntil(\n // Add sync logic here\n Promise.resolve()\n )\n }\n\n // Flush analytics when back online\n if (event.tag === 'cloudsignal-analytics-sync') {\n event.waitUntil(flushAnalyticsQueue())\n }\n})\n\n// ============================================\n// Periodic Sync Events\n// ============================================\n\nself.addEventListener('periodicsync', (event: any) => {\n console.log('[CloudSignal SW] Periodic sync event:', event.tag)\n\n if (event.tag === 'cloudsignal-periodic') {\n event.waitUntil(\n // Add periodic sync logic here\n swStorage.cleanOldNotifications(30)\n )\n }\n})\n\nconsole.log(`[CloudSignal SW] Service worker loaded ${CACHE_VERSION}`)\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudsignal/pwa-sdk",
3
- "version": "1.0.0",
4
- "description": "CloudSignal PWA SDK - Progressive Web App features with push notifications, installation management, and device tracking",
3
+ "version": "1.1.0",
4
+ "description": "CloudSignal PWA SDK - Progressive Web App features with push notifications, installation management, device tracking, offline queue, wake lock, and notification analytics",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
7
7
  "browser": "dist/index.global.js",
@@ -44,6 +44,10 @@
44
44
  "web-push",
45
45
  "install-prompt",
46
46
  "offline",
47
+ "offline-first",
48
+ "wake-lock",
49
+ "notification-analytics",
50
+ "ios-pwa",
47
51
  "typescript"
48
52
  ],
49
53
  "author": "CloudSignal",