@cloudsignal/pwa-sdk 1.2.1 → 1.2.3

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 CHANGED
@@ -5,6 +5,27 @@ 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
+ ## [1.2.3] - 2026-01-19
9
+
10
+ ### Added
11
+
12
+ - **Auto Installation Tracking** - Automatically registers PWA installations with the backend when detected
13
+ - `registerInstallation()` method for manual installation tracking without push subscription
14
+ - `isInstallationRegistered()` method to check if installation is already tracked
15
+ - `getInstallationId()` method to get stored installation registration ID
16
+ - `install:registered` event emitted when installation is registered with backend
17
+ - Installation tracking works with both HMAC (anonymous) and JWT (authenticated) modes
18
+
19
+ ### Changed
20
+
21
+ - `initialize()` now auto-calls `registerInstallation()` when PWA is detected as installed
22
+ - Prevents duplicate registrations using localStorage tracking
23
+
24
+ ### Backend Support
25
+
26
+ - `/api/v1/registration/install-only` endpoint now supports JWT authentication
27
+ - Installation registrations include `auth_method`, `auth_provider`, and `idp_user_id` for JWT mode
28
+
8
29
  ## [1.2.0] - 2025-01-17
9
30
 
10
31
  ### Added
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 CloudSignal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
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;
@@ -1586,6 +1595,72 @@ var PushNotificationManager = class {
1586
1595
  return null;
1587
1596
  }
1588
1597
  }
1598
+ /**
1599
+ * Register PWA installation without push subscription.
1600
+ * This tracks users who installed the PWA but haven't subscribed to notifications.
1601
+ * Called automatically when installation is detected.
1602
+ *
1603
+ * @returns Installation registration result or null if already registered/failed
1604
+ */
1605
+ async registerInstallation() {
1606
+ try {
1607
+ if (isInstallationRegistered(this.organizationId, this.serviceId)) {
1608
+ this.log("Installation already registered, skipping");
1609
+ return null;
1610
+ }
1611
+ const fingerprint = await generateBrowserFingerprint();
1612
+ const deviceInfo = this.deviceDetector.getDeviceInfo();
1613
+ const platformInfo = this.deviceDetector.getPlatformInfo();
1614
+ const installationData = {
1615
+ service_id: this.serviceId,
1616
+ browser_fingerprint: fingerprint || void 0,
1617
+ device_type: deviceInfo.deviceType,
1618
+ device_model: deviceInfo.deviceModel,
1619
+ browser_name: platformInfo.browser,
1620
+ browser_version: platformInfo.browserVersion,
1621
+ os_name: platformInfo.os,
1622
+ os_version: platformInfo.osVersion,
1623
+ user_agent: platformInfo.userAgent,
1624
+ display_mode: "standalone",
1625
+ is_installed: true,
1626
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1627
+ language: navigator.language || "en-US"
1628
+ };
1629
+ const url = `${this.serviceUrl}/api/v1/registration/install-only`;
1630
+ const response = await makeAuthenticatedRequestWithContext(
1631
+ this.authContext,
1632
+ "POST",
1633
+ url,
1634
+ installationData,
1635
+ this.onTokenExpired
1636
+ );
1637
+ if (!response.ok) {
1638
+ const errorText = await response.text();
1639
+ throw new Error(`Installation registration failed: ${response.status} - ${errorText}`);
1640
+ }
1641
+ const result = await response.json();
1642
+ markInstallationRegistered(this.organizationId, this.serviceId, result.registration_id);
1643
+ this.log(`Installation registered successfully: ${result.registration_id}`);
1644
+ return { registrationId: result.registration_id };
1645
+ } catch (error) {
1646
+ const err = error instanceof Error ? error : new Error(String(error));
1647
+ this.log(`Installation registration failed: ${err.message}`, "error");
1648
+ this.onError?.(err);
1649
+ return null;
1650
+ }
1651
+ }
1652
+ /**
1653
+ * Check if installation is already registered
1654
+ */
1655
+ isInstallationRegistered() {
1656
+ return isInstallationRegistered(this.organizationId, this.serviceId);
1657
+ }
1658
+ /**
1659
+ * Get stored installation registration ID
1660
+ */
1661
+ getInstallationId() {
1662
+ return getStorageItem(`install_id_${this.organizationId}_${this.serviceId}`);
1663
+ }
1589
1664
  /**
1590
1665
  * Request notification permission
1591
1666
  */
@@ -3117,7 +3192,7 @@ var IOSInstallBanner = class {
3117
3192
 
3118
3193
  // src/CloudSignalPWA.ts
3119
3194
  var DEFAULT_SERVICE_URL = "https://pwa.cloudsignal.app";
3120
- var SDK_VERSION = "1.2.0";
3195
+ var SDK_VERSION = "1.2.3";
3121
3196
  var CloudSignalPWA = class {
3122
3197
  constructor(config) {
3123
3198
  this.initialized = false;
@@ -3258,11 +3333,20 @@ var CloudSignalPWA = class {
3258
3333
  this.configureNotificationAnalytics(swReg);
3259
3334
  }
3260
3335
  if (this.iosInstallBanner && this.config.iosInstallBanner?.showOnFirstVisit !== false) {
3261
- const installState = this.installationManager.getState();
3262
- if (!installState.isInstalled) {
3336
+ const installState2 = this.installationManager.getState();
3337
+ if (!installState2.isInstalled) {
3263
3338
  this.iosInstallBanner.show();
3264
3339
  }
3265
3340
  }
3341
+ const installState = this.installationManager.getState();
3342
+ if (installState.isInstalled) {
3343
+ this.log("PWA installation detected, registering...");
3344
+ const installResult = await this.pushNotificationManager.registerInstallation();
3345
+ if (installResult) {
3346
+ this.log(`Installation registered: ${installResult.registrationId}`);
3347
+ this.emit("install:registered", { registrationId: installResult.registrationId });
3348
+ }
3349
+ }
3266
3350
  this.initialized = true;
3267
3351
  const result = {
3268
3352
  success: true,