@cloudsignal/pwa-sdk 1.0.0 → 1.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/README.md +128 -3
- package/dist/index.cjs +2010 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +855 -3
- package/dist/index.d.ts +855 -3
- package/dist/index.global.js +140 -3
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +2004 -83
- package/dist/index.js.map +1 -1
- package/dist/service-worker.js +171 -6
- package/dist/service-worker.js.map +1 -1
- package/package.json +10 -3
package/dist/service-worker.js
CHANGED
|
@@ -8,14 +8,21 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
// service-worker/service-worker.ts
|
|
11
|
-
var CACHE_VERSION = "v1.
|
|
11
|
+
var CACHE_VERSION = "v1.1.0";
|
|
12
12
|
var DB_NAME = "CloudSignalPWA";
|
|
13
|
-
var DB_VERSION =
|
|
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:
|
|
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.
|
|
4
|
-
"description": "CloudSignal PWA SDK - Progressive Web App features with push notifications, installation management, and
|
|
3
|
+
"version": "1.1.1",
|
|
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",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"clean": "rm -rf dist",
|
|
34
34
|
"prepublishOnly": "npm run clean && npm run build",
|
|
35
35
|
"test": "vitest run",
|
|
36
|
-
"test:watch": "vitest"
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"test:coverage": "vitest run --coverage"
|
|
37
38
|
},
|
|
38
39
|
"keywords": [
|
|
39
40
|
"pwa",
|
|
@@ -44,6 +45,10 @@
|
|
|
44
45
|
"web-push",
|
|
45
46
|
"install-prompt",
|
|
46
47
|
"offline",
|
|
48
|
+
"offline-first",
|
|
49
|
+
"wake-lock",
|
|
50
|
+
"notification-analytics",
|
|
51
|
+
"ios-pwa",
|
|
47
52
|
"typescript"
|
|
48
53
|
],
|
|
49
54
|
"author": "CloudSignal",
|
|
@@ -65,6 +70,8 @@
|
|
|
65
70
|
},
|
|
66
71
|
"devDependencies": {
|
|
67
72
|
"@types/node": "^20.0.0",
|
|
73
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
74
|
+
"jsdom": "^24.0.0",
|
|
68
75
|
"tsup": "^8.0.0",
|
|
69
76
|
"typescript": "^5.0.0",
|
|
70
77
|
"vitest": "^1.0.0"
|