@cloudsignal/pwa-sdk 2.0.0 → 2.1.2
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/CHANGELOG.md +13 -0
- package/dist/{chunk-IQHSODT4.js → chunk-NSF32IGO.js} +0 -2
- package/dist/hmac-YQQ5XQWC.js +1 -0
- package/dist/index.cjs +91 -4
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.global.js +4 -5
- package/dist/index.js +94 -7
- package/dist/service-worker.js +0 -2
- package/package.json +2 -2
- package/dist/chunk-IQHSODT4.js.map +0 -1
- package/dist/hmac-WITZIX2O.js +0 -3
- package/dist/hmac-WITZIX2O.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.global.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/service-worker.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.1.0] - 2026-04-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Auto-register PWA installations on `initialize()`** — when the SDK detects it is running in standalone / installed mode, it automatically POSTs to `/api/v1/registration/install-only` so the device shows up in the Clients tab without requiring the user to enable push first. A subsequent `registerForPush()` call upgrades the same row server-side (matched by browser fingerprint).
|
|
13
|
+
- `registerInstallation()` public method on `CloudSignalPWA` for manual install tracking.
|
|
14
|
+
- `install:registered` event fires after the backend acknowledges the install registration. Payload: `{ registrationId }`.
|
|
15
|
+
- Installation registrations are guarded by a localStorage flag (`isInstallationRegistered` / `markInstallationRegistered`) so repeat `initialize()` calls within the same storage session are no-ops.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Restores the auto-install-registration feature originally shipped in 1.2.3 that was inadvertently removed in commit `42935abf`.
|
|
20
|
+
|
|
8
21
|
## [2.0.0] - 2026-04-23
|
|
9
22
|
|
|
10
23
|
### Changed (BREAKING)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generateAuthHeaders, generateHMACSignature, isValidUUID, makeAuthenticatedRequest } from './chunk-NSF32IGO.js';
|
package/dist/index.cjs
CHANGED
|
@@ -1141,6 +1141,15 @@ function removeRegistrationId(organizationId, serviceId) {
|
|
|
1141
1141
|
const key = `registration_${organizationId}_${serviceId}`;
|
|
1142
1142
|
return removeStorageItem(key);
|
|
1143
1143
|
}
|
|
1144
|
+
function isInstallationRegistered(organizationId, serviceId) {
|
|
1145
|
+
const key = `install_registered_${organizationId}_${serviceId}`;
|
|
1146
|
+
return getStorageItem(key) === true;
|
|
1147
|
+
}
|
|
1148
|
+
function markInstallationRegistered(organizationId, serviceId, registrationId) {
|
|
1149
|
+
const key = `install_registered_${organizationId}_${serviceId}`;
|
|
1150
|
+
setStorageItem(`install_id_${organizationId}_${serviceId}`, registrationId);
|
|
1151
|
+
return setStorageItem(key, true);
|
|
1152
|
+
}
|
|
1144
1153
|
var IndexedDBStorage = class {
|
|
1145
1154
|
constructor(dbName = "CloudSignalPWA", dbVersion = 1) {
|
|
1146
1155
|
this.db = null;
|
|
@@ -1451,6 +1460,13 @@ var PushNotificationManager = class {
|
|
|
1451
1460
|
this.onTokenExpired
|
|
1452
1461
|
);
|
|
1453
1462
|
if (!response.ok) {
|
|
1463
|
+
if (response.status === 422) {
|
|
1464
|
+
const body = await response.clone().json().catch(() => null);
|
|
1465
|
+
if (body?.detail?.code === "user_identity_required") {
|
|
1466
|
+
this.log("Skipping push registration: service requires user identity");
|
|
1467
|
+
return null;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1454
1470
|
const errorText = await response.text();
|
|
1455
1471
|
throw new Error(`Registration failed: ${response.status} - ${errorText}`);
|
|
1456
1472
|
}
|
|
@@ -1473,6 +1489,71 @@ var PushNotificationManager = class {
|
|
|
1473
1489
|
return null;
|
|
1474
1490
|
}
|
|
1475
1491
|
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Register a PWA installation without a push subscription.
|
|
1494
|
+
*
|
|
1495
|
+
* Tracks users who installed the PWA (standalone display mode) but
|
|
1496
|
+
* haven't granted notification permission yet. A subsequent
|
|
1497
|
+
* ``registerForPush()`` call upgrades the same row to a full push
|
|
1498
|
+
* registration (matched server-side by browser fingerprint).
|
|
1499
|
+
*
|
|
1500
|
+
* Guarded by a localStorage flag so repeat ``initialize()`` calls in
|
|
1501
|
+
* the same storage session are no-ops.
|
|
1502
|
+
*/
|
|
1503
|
+
async registerInstallation() {
|
|
1504
|
+
try {
|
|
1505
|
+
if (isInstallationRegistered(this.organizationId, this.serviceId)) {
|
|
1506
|
+
this.log("Installation already registered, skipping");
|
|
1507
|
+
return null;
|
|
1508
|
+
}
|
|
1509
|
+
const fingerprint = await generateBrowserFingerprint();
|
|
1510
|
+
const deviceInfo = this.deviceDetector.getDeviceInfo();
|
|
1511
|
+
const platformInfo = this.deviceDetector.getPlatformInfo();
|
|
1512
|
+
const installationData = {
|
|
1513
|
+
service_id: this.serviceId,
|
|
1514
|
+
browser_fingerprint: fingerprint || void 0,
|
|
1515
|
+
device_type: deviceInfo.deviceType,
|
|
1516
|
+
device_model: deviceInfo.deviceModel,
|
|
1517
|
+
browser_name: platformInfo.browser,
|
|
1518
|
+
browser_version: platformInfo.browserVersion,
|
|
1519
|
+
os_name: platformInfo.os,
|
|
1520
|
+
os_version: platformInfo.osVersion,
|
|
1521
|
+
user_agent: platformInfo.userAgent,
|
|
1522
|
+
display_mode: "standalone",
|
|
1523
|
+
is_installed: true,
|
|
1524
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
1525
|
+
language: navigator.language || "en-US"
|
|
1526
|
+
};
|
|
1527
|
+
const url = `${this.serviceUrl}/api/v1/registration/install-only`;
|
|
1528
|
+
const response = await makeAuthenticatedRequestWithContext(
|
|
1529
|
+
this.authContext,
|
|
1530
|
+
"POST",
|
|
1531
|
+
url,
|
|
1532
|
+
installationData,
|
|
1533
|
+
this.onTokenExpired
|
|
1534
|
+
);
|
|
1535
|
+
if (!response.ok) {
|
|
1536
|
+
if (response.status === 422) {
|
|
1537
|
+
const body = await response.clone().json().catch(() => null);
|
|
1538
|
+
if (body?.detail?.code === "user_identity_required") {
|
|
1539
|
+
this.log("Skipping install-only registration: service requires user identity");
|
|
1540
|
+
return null;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
const errorText = await response.text();
|
|
1544
|
+
throw new Error(`Installation registration failed: ${response.status} - ${errorText}`);
|
|
1545
|
+
}
|
|
1546
|
+
const result = await response.json();
|
|
1547
|
+
markInstallationRegistered(this.organizationId, this.serviceId, result.registration_id);
|
|
1548
|
+
this.log(`Installation registered: ${result.registration_id}`);
|
|
1549
|
+
return { registrationId: result.registration_id };
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1552
|
+
this.log(`Installation registration failed: ${err.message}`, "error");
|
|
1553
|
+
this.onError?.(err);
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1476
1557
|
/**
|
|
1477
1558
|
* Unregister from push notifications
|
|
1478
1559
|
*/
|
|
@@ -3258,11 +3339,19 @@ var CloudSignalPWA = class {
|
|
|
3258
3339
|
this.configureNotificationAnalytics(swReg);
|
|
3259
3340
|
}
|
|
3260
3341
|
if (this.iosInstallBanner && this.config.iosInstallBanner?.showOnFirstVisit !== false) {
|
|
3261
|
-
const
|
|
3262
|
-
if (!
|
|
3342
|
+
const installState2 = this.installationManager.getState();
|
|
3343
|
+
if (!installState2.isInstalled) {
|
|
3263
3344
|
this.iosInstallBanner.show();
|
|
3264
3345
|
}
|
|
3265
3346
|
}
|
|
3347
|
+
const installState = this.installationManager.getState();
|
|
3348
|
+
if (installState.isInstalled) {
|
|
3349
|
+
this.log("PWA installation detected, registering\u2026");
|
|
3350
|
+
const installResult = await this.pushNotificationManager.registerInstallation();
|
|
3351
|
+
if (installResult) {
|
|
3352
|
+
this.emit("install:registered", { registrationId: installResult.registrationId });
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3266
3355
|
this.initialized = true;
|
|
3267
3356
|
const result = {
|
|
3268
3357
|
success: true,
|
|
@@ -4171,5 +4260,3 @@ exports.removeStorageItem = removeStorageItem;
|
|
|
4171
4260
|
exports.setRegistrationId = setRegistrationId;
|
|
4172
4261
|
exports.setStorageItem = setStorageItem;
|
|
4173
4262
|
exports.updateAuthToken = updateAuthToken;
|
|
4174
|
-
//# sourceMappingURL=index.cjs.map
|
|
4175
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -521,7 +521,7 @@ interface NotificationAction {
|
|
|
521
521
|
/**
|
|
522
522
|
* Event types emitted by the SDK
|
|
523
523
|
*/
|
|
524
|
-
type PWAEvent = 'install:available' | 'install:accepted' | 'install:dismissed' | 'install:completed' | 'install:error' | 'push:registered' | 'push:unregistered' | 'push:updated' | 'push:error' | 'push:received' | 'push:clicked' | 'permission:granted' | 'permission:denied' | 'permission:prompt' | 'sw:registered' | 'sw:updated' | 'sw:error' | 'sw:activated' | 'config:loaded' | 'config:error' | 'heartbeat:started' | 'heartbeat:stopped' | 'heartbeat:sent' | 'heartbeat:error' | 'heartbeat:intervalChanged' | 'heartbeat:pausedForBattery' | 'heartbeat:resumedFromBattery' | 'network:online' | 'network:offline' | 'state:changed' | 'wakeLock:acquired' | 'wakeLock:released' | 'wakeLock:error' | 'offlineQueue:queued' | 'offlineQueue:processed' | 'offlineQueue:flushed' | 'iosBanner:shown' | 'iosBanner:dismissed' | 'iosBanner:installClicked' | 'auth:tokenUpdated';
|
|
524
|
+
type PWAEvent = 'install:available' | 'install:accepted' | 'install:dismissed' | 'install:completed' | 'install:registered' | 'install:error' | 'push:registered' | 'push:unregistered' | 'push:updated' | 'push:error' | 'push:received' | 'push:clicked' | 'permission:granted' | 'permission:denied' | 'permission:prompt' | 'sw:registered' | 'sw:updated' | 'sw:error' | 'sw:activated' | 'config:loaded' | 'config:error' | 'heartbeat:started' | 'heartbeat:stopped' | 'heartbeat:sent' | 'heartbeat:error' | 'heartbeat:intervalChanged' | 'heartbeat:pausedForBattery' | 'heartbeat:resumedFromBattery' | 'network:online' | 'network:offline' | 'state:changed' | 'wakeLock:acquired' | 'wakeLock:released' | 'wakeLock:error' | 'offlineQueue:queued' | 'offlineQueue:processed' | 'offlineQueue:flushed' | 'iosBanner:shown' | 'iosBanner:dismissed' | 'iosBanner:installClicked' | 'auth:tokenUpdated';
|
|
525
525
|
/**
|
|
526
526
|
* Event handler function
|
|
527
527
|
*/
|
|
@@ -1364,6 +1364,20 @@ declare class PushNotificationManager {
|
|
|
1364
1364
|
* Register for push notifications
|
|
1365
1365
|
*/
|
|
1366
1366
|
register(options?: RegisterOptions): Promise<Registration | null>;
|
|
1367
|
+
/**
|
|
1368
|
+
* Register a PWA installation without a push subscription.
|
|
1369
|
+
*
|
|
1370
|
+
* Tracks users who installed the PWA (standalone display mode) but
|
|
1371
|
+
* haven't granted notification permission yet. A subsequent
|
|
1372
|
+
* ``registerForPush()`` call upgrades the same row to a full push
|
|
1373
|
+
* registration (matched server-side by browser fingerprint).
|
|
1374
|
+
*
|
|
1375
|
+
* Guarded by a localStorage flag so repeat ``initialize()`` calls in
|
|
1376
|
+
* the same storage session are no-ops.
|
|
1377
|
+
*/
|
|
1378
|
+
registerInstallation(): Promise<{
|
|
1379
|
+
registrationId: string;
|
|
1380
|
+
} | null>;
|
|
1367
1381
|
/**
|
|
1368
1382
|
* Unregister from push notifications
|
|
1369
1383
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -521,7 +521,7 @@ interface NotificationAction {
|
|
|
521
521
|
/**
|
|
522
522
|
* Event types emitted by the SDK
|
|
523
523
|
*/
|
|
524
|
-
type PWAEvent = 'install:available' | 'install:accepted' | 'install:dismissed' | 'install:completed' | 'install:error' | 'push:registered' | 'push:unregistered' | 'push:updated' | 'push:error' | 'push:received' | 'push:clicked' | 'permission:granted' | 'permission:denied' | 'permission:prompt' | 'sw:registered' | 'sw:updated' | 'sw:error' | 'sw:activated' | 'config:loaded' | 'config:error' | 'heartbeat:started' | 'heartbeat:stopped' | 'heartbeat:sent' | 'heartbeat:error' | 'heartbeat:intervalChanged' | 'heartbeat:pausedForBattery' | 'heartbeat:resumedFromBattery' | 'network:online' | 'network:offline' | 'state:changed' | 'wakeLock:acquired' | 'wakeLock:released' | 'wakeLock:error' | 'offlineQueue:queued' | 'offlineQueue:processed' | 'offlineQueue:flushed' | 'iosBanner:shown' | 'iosBanner:dismissed' | 'iosBanner:installClicked' | 'auth:tokenUpdated';
|
|
524
|
+
type PWAEvent = 'install:available' | 'install:accepted' | 'install:dismissed' | 'install:completed' | 'install:registered' | 'install:error' | 'push:registered' | 'push:unregistered' | 'push:updated' | 'push:error' | 'push:received' | 'push:clicked' | 'permission:granted' | 'permission:denied' | 'permission:prompt' | 'sw:registered' | 'sw:updated' | 'sw:error' | 'sw:activated' | 'config:loaded' | 'config:error' | 'heartbeat:started' | 'heartbeat:stopped' | 'heartbeat:sent' | 'heartbeat:error' | 'heartbeat:intervalChanged' | 'heartbeat:pausedForBattery' | 'heartbeat:resumedFromBattery' | 'network:online' | 'network:offline' | 'state:changed' | 'wakeLock:acquired' | 'wakeLock:released' | 'wakeLock:error' | 'offlineQueue:queued' | 'offlineQueue:processed' | 'offlineQueue:flushed' | 'iosBanner:shown' | 'iosBanner:dismissed' | 'iosBanner:installClicked' | 'auth:tokenUpdated';
|
|
525
525
|
/**
|
|
526
526
|
* Event handler function
|
|
527
527
|
*/
|
|
@@ -1364,6 +1364,20 @@ declare class PushNotificationManager {
|
|
|
1364
1364
|
* Register for push notifications
|
|
1365
1365
|
*/
|
|
1366
1366
|
register(options?: RegisterOptions): Promise<Registration | null>;
|
|
1367
|
+
/**
|
|
1368
|
+
* Register a PWA installation without a push subscription.
|
|
1369
|
+
*
|
|
1370
|
+
* Tracks users who installed the PWA (standalone display mode) but
|
|
1371
|
+
* haven't granted notification permission yet. A subsequent
|
|
1372
|
+
* ``registerForPush()`` call upgrades the same row to a full push
|
|
1373
|
+
* registration (matched server-side by browser fingerprint).
|
|
1374
|
+
*
|
|
1375
|
+
* Guarded by a localStorage flag so repeat ``initialize()`` calls in
|
|
1376
|
+
* the same storage session are no-ops.
|
|
1377
|
+
*/
|
|
1378
|
+
registerInstallation(): Promise<{
|
|
1379
|
+
registrationId: string;
|
|
1380
|
+
} | null>;
|
|
1367
1381
|
/**
|
|
1368
1382
|
* Unregister from push notifications
|
|
1369
1383
|
*/
|