@licenseseat/js 0.3.1 → 0.4.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 CHANGED
@@ -13,6 +13,8 @@ The official JavaScript/TypeScript SDK for [LicenseSeat](https://licenseseat.com
13
13
  - **License activation & deactivation** – Activate licenses with automatic device fingerprinting
14
14
  - **Online & offline validation** – Validate licenses with optional offline fallback
15
15
  - **Entitlement checking** – Check feature access with `hasEntitlement()` and `checkEntitlement()`
16
+ - **Heartbeat** – Automatic periodic heartbeats to report device activity
17
+ - **Telemetry** – Auto-collected device and environment data sent with each API request
16
18
  - **Local caching** – Secure localStorage-based caching with clock tamper detection
17
19
  - **Auto-retry with exponential backoff** – Resilient network handling
18
20
  - **Event-driven architecture** – Subscribe to SDK lifecycle events
@@ -138,6 +140,14 @@ const sdk = new LicenseSeat({
138
140
  autoValidateInterval: 3600000, // 1 hour (in ms)
139
141
  autoInitialize: true, // Auto-validate cached license on init
140
142
 
143
+ // Heartbeat
144
+ heartbeatInterval: 300000, // 5 minutes (in ms), 0 to disable
145
+
146
+ // Telemetry
147
+ telemetryEnabled: true, // Set false to disable (e.g. GDPR)
148
+ appVersion: '1.2.0', // Your app version (sent in telemetry)
149
+ appBuild: '42', // Your app build number (sent in telemetry)
150
+
141
151
  // Offline Support
142
152
  offlineFallbackEnabled: false, // Enable offline validation fallback
143
153
  maxOfflineDays: 0, // Max days offline (0 = disabled)
@@ -164,6 +174,10 @@ const sdk = new LicenseSeat({
164
174
  | `storagePrefix` | `string` | `'licenseseat_'` | Prefix for localStorage keys |
165
175
  | `autoValidateInterval` | `number` | `3600000` | Auto-validation interval in ms (1 hour) |
166
176
  | `autoInitialize` | `boolean` | `true` | Auto-initialize and validate cached license |
177
+ | `heartbeatInterval` | `number` | `300000` | Heartbeat interval in ms (5 minutes). Set `0` to disable |
178
+ | `telemetryEnabled` | `boolean` | `true` | Enable telemetry collection. Set `false` for GDPR compliance |
179
+ | `appVersion` | `string` | `null` | Your app version string (sent as `app_version` in telemetry) |
180
+ | `appBuild` | `string` | `null` | Your app build identifier (sent as `app_build` in telemetry) |
167
181
  | `offlineFallbackEnabled` | `boolean` | `false` | Enable offline validation on network errors |
168
182
  | `maxOfflineDays` | `number` | `0` | Maximum days license works offline (0 = disabled) |
169
183
  | `maxRetries` | `number` | `3` | Max retry attempts for failed API calls |
@@ -325,6 +339,23 @@ try {
325
339
 
326
340
  > **Note:** This method tests API connectivity, not API key validity. A successful response means the API is reachable. Authentication errors will surface when calling protected endpoints like `activate()` or `validateLicense()`.
327
341
 
342
+ #### `sdk.heartbeat()`
343
+
344
+ Send a heartbeat to report that the current device is still active. Heartbeats are sent automatically at the configured `heartbeatInterval`, but you can also send one manually.
345
+
346
+ ```javascript
347
+ try {
348
+ const result = await sdk.heartbeat();
349
+ console.log('Heartbeat received at:', result.received_at);
350
+ } catch (error) {
351
+ console.error('Heartbeat failed:', error);
352
+ }
353
+ ```
354
+
355
+ Returns `undefined` if no active license is cached. When auto-heartbeat is enabled (the default), the SDK sends heartbeats every 5 minutes while a license is active. Auto-heartbeat starts automatically after `activate()` or when the SDK initializes with a cached license.
356
+
357
+ To disable auto-heartbeat, set `heartbeatInterval: 0` in the configuration.
358
+
328
359
  #### `sdk.reset()`
329
360
 
330
361
  Clear all cached data and reset SDK state.
@@ -403,6 +434,9 @@ sdk.off('activation:success', handler);
403
434
  | **Auto-Validation** | | |
404
435
  | `autovalidation:cycle` | Auto-validation scheduled | `{ nextRunAt: Date }` |
405
436
  | `autovalidation:stopped` | Auto-validation stopped | – |
437
+ | **Heartbeat** | | |
438
+ | `heartbeat:success` | Heartbeat acknowledged by server | `HeartbeatResponse` |
439
+ | `heartbeat:cycle` | Auto-heartbeat tick completed | `{ nextRunAt: Date }` |
406
440
  | **Network** | | |
407
441
  | `network:online` | Network connectivity restored | – |
408
442
  | `network:offline` | Network connectivity lost | `{ error }` |
@@ -568,6 +602,126 @@ console.log('Signature valid:', isValid);
568
602
 
569
603
  ---
570
604
 
605
+ ## Telemetry
606
+
607
+ The SDK automatically collects non-PII (non-personally-identifiable) device and environment data and includes it with every POST request sent to the LicenseSeat API. This data helps you understand what platforms and environments your customers use.
608
+
609
+ Telemetry is **enabled by default** and can be disabled at any time.
610
+
611
+ ### Collected Fields
612
+
613
+ | Field | Type | Example | Description |
614
+ | -------------------- | -------- | ---------------------- | ------------------------------------------------ |
615
+ | `sdk_name` | `string` | `"js"` | Always `"js"` for this SDK |
616
+ | `sdk_version` | `string` | `"0.4.0"` | SDK version |
617
+ | `os_name` | `string` | `"macOS"` | Operating system name |
618
+ | `os_version` | `string` | `"14.2.1"` | Operating system version |
619
+ | `platform` | `string` | `"browser"` | Runtime platform (`browser`, `node`, `electron`, `react-native`, `deno`, `bun`) |
620
+ | `device_model` | `string` | `null` | Device model (Chromium userAgentData only) |
621
+ | `device_type` | `string` | `"desktop"` | Device type (`desktop`, `phone`, `tablet`, `server`) |
622
+ | `locale` | `string` | `"en-US"` | Full locale string |
623
+ | `timezone` | `string` | `"America/New_York"` | IANA timezone |
624
+ | `language` | `string` | `"en"` | 2-letter language code |
625
+ | `architecture` | `string` | `"arm64"` | CPU architecture |
626
+ | `cpu_cores` | `number` | `10` | Number of logical CPU cores |
627
+ | `memory_gb` | `number` | `16` | Approximate RAM in GB (Chrome/Node.js only) |
628
+ | `screen_resolution` | `string` | `"1920x1080"` | Screen resolution |
629
+ | `display_scale` | `number` | `2` | Device pixel ratio |
630
+ | `browser_name` | `string` | `"Chrome"` | Browser name (browser environments only) |
631
+ | `browser_version` | `string` | `"123.0"` | Browser version (browser environments only) |
632
+ | `runtime_version` | `string` | `"20.11.0"` | Runtime version (Node.js, Deno, Bun, Electron) |
633
+ | `app_version` | `string` | `"1.2.0"` | Your app version (from `appVersion` config) |
634
+ | `app_build` | `string` | `"42"` | Your app build (from `appBuild` config) |
635
+
636
+ Fields that cannot be detected in the current environment are omitted (not sent as `null`).
637
+
638
+ ### Providing App Version
639
+
640
+ Pass your own app version and build number via configuration so they appear in telemetry:
641
+
642
+ ```javascript
643
+ const sdk = new LicenseSeat({
644
+ apiKey: 'your-key',
645
+ productSlug: 'your-product',
646
+ appVersion: '1.2.0',
647
+ appBuild: '42'
648
+ });
649
+ ```
650
+
651
+ ### Disabling Telemetry
652
+
653
+ To disable telemetry collection entirely (for example, to comply with GDPR or other privacy regulations):
654
+
655
+ ```javascript
656
+ const sdk = new LicenseSeat({
657
+ apiKey: 'your-key',
658
+ productSlug: 'your-product',
659
+ telemetryEnabled: false
660
+ });
661
+ ```
662
+
663
+ When telemetry is disabled, no device or environment data is attached to API requests.
664
+
665
+ ### Privacy
666
+
667
+ Telemetry collects only non-personally-identifiable information. No IP addresses, user names, email addresses, or other PII are collected by the SDK. The data is used solely to help you understand the platforms and environments where your software is used.
668
+
669
+ Telemetry is opt-out: set `telemetryEnabled: false` to disable it completely.
670
+
671
+ ---
672
+
673
+ ## Heartbeat
674
+
675
+ The SDK sends periodic heartbeat signals to let the server know a device is still actively using the license. This enables usage analytics and helps detect inactive seats.
676
+
677
+ ### How It Works
678
+
679
+ - After a license is activated (or when the SDK initializes with a cached license), a heartbeat timer starts automatically.
680
+ - The default interval is **5 minutes** (`300000` ms), configurable via `heartbeatInterval`.
681
+ - Each heartbeat sends the current `device_id` to the server.
682
+ - Heartbeats also run alongside auto-validation cycles.
683
+
684
+ ### Manual Heartbeat
685
+
686
+ You can send a heartbeat at any time:
687
+
688
+ ```javascript
689
+ await sdk.heartbeat();
690
+ ```
691
+
692
+ ### Configuring the Interval
693
+
694
+ ```javascript
695
+ const sdk = new LicenseSeat({
696
+ apiKey: 'your-key',
697
+ productSlug: 'your-product',
698
+ heartbeatInterval: 600000 // 10 minutes
699
+ });
700
+ ```
701
+
702
+ Set `heartbeatInterval: 0` to disable auto-heartbeat entirely. You can still call `sdk.heartbeat()` manually.
703
+
704
+ ### Heartbeat Events
705
+
706
+ | Event | Description | Data |
707
+ | -------------------- | ---------------------------------- | ----------------------- |
708
+ | `heartbeat:success` | Heartbeat acknowledged by server | `HeartbeatResponse` |
709
+ | `heartbeat:cycle` | Auto-heartbeat tick completed | `{ nextRunAt: Date }` |
710
+
711
+ ```javascript
712
+ sdk.on('heartbeat:success', (data) => {
713
+ console.log('Heartbeat received at:', data.received_at);
714
+ });
715
+ ```
716
+
717
+ ### Heartbeat Lifecycle
718
+
719
+ - **Starts** automatically after `sdk.activate()` succeeds, or on SDK init if a cached license exists.
720
+ - **Stops** automatically when `sdk.deactivate()`, `sdk.reset()`, or `sdk.destroy()` is called.
721
+ - Heartbeat failures are logged (in debug mode) but do not throw or interrupt the SDK.
722
+
723
+ ---
724
+
571
725
  ## Error Handling
572
726
 
573
727
  The SDK exports custom error classes for precise error handling:
@@ -833,6 +987,7 @@ licenseseat-js/
833
987
  ├── src/
834
988
  │ ├── index.js # Entry point, exports
835
989
  │ ├── LicenseSeat.js # Main SDK class
990
+ │ ├── telemetry.js # Telemetry collection (device/environment data)
836
991
  │ ├── cache.js # LicenseCache (localStorage)
837
992
  │ ├── errors.js # Error classes
838
993
  │ ├── types.js # JSDoc type definitions
@@ -936,7 +1091,7 @@ Once published to npm, the package is automatically available on CDNs:
936
1091
  **Version pinning** (recommended for production):
937
1092
  ```html
938
1093
  <script type="module">
939
- import LicenseSeat from 'https://esm.sh/@licenseseat/js@0.3.0';
1094
+ import LicenseSeat from 'https://esm.sh/@licenseseat/js@0.4.0';
940
1095
  </script>
941
1096
  ```
942
1097