@licenseseat/js 0.3.1 → 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"file":"LicenseSeat.d.ts","sourceRoot":"","sources":["../../src/LicenseSeat.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LicenseSeat.d.ts","sourceRoot":"","sources":["../../src/LicenseSeat.js"],"names":[],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  export { LicenseCache } from "./cache.js";
2
+ export { collectTelemetry } from "./telemetry.js";
2
3
  export default LicenseSeatSDK;
3
4
  import { LicenseSeatSDK } from "./LicenseSeat.js";
4
- export { LicenseSeatSDK, getSharedInstance, configure, resetSharedInstance } from "./LicenseSeat.js";
5
+ export { LicenseSeatSDK, SDK_VERSION, getSharedInstance, configure, resetSharedInstance } from "./LicenseSeat.js";
5
6
  export { APIError, ConfigurationError, LicenseError, CryptoError } from "./errors.js";
6
7
  export { parseActiveEntitlements, constantTimeEqual, canonicalJsonStringify, base64UrlDecode, generateDeviceId, getCsrfToken } from "./utils.js";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";;+BA6D+B,kBAAkB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";;;+BAiE+B,kBAAkB"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Collect telemetry data for the current environment.
3
+ * Returns a plain object with snake_case keys matching the server schema.
4
+ * Null/undefined values are filtered out.
5
+ *
6
+ * @param {string} sdkVersion - The SDK version string
7
+ * @param {Object} [options] - Additional options
8
+ * @param {string} [options.appVersion] - User-provided app version
9
+ * @param {string} [options.appBuild] - User-provided app build
10
+ * @returns {Object} Telemetry data object
11
+ */
12
+ export function collectTelemetry(sdkVersion: string, options?: {
13
+ appVersion?: string;
14
+ appBuild?: string;
15
+ }): any;
16
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/telemetry.js"],"names":[],"mappings":"AAwdA;;;;;;;;;;GAUG;AACH,6CANW,MAAM,YAEd;IAAyB,UAAU,GAA3B,MAAM;IACW,QAAQ,GAAzB,MAAM;CACd,OAoCF"}
@@ -58,6 +58,22 @@ export type LicenseSeatConfig = {
58
58
  * - Automatically initialize and validate cached license on construction
59
59
  */
60
60
  autoInitialize?: boolean;
61
+ /**
62
+ * - Enable telemetry collection on POST requests (set false for GDPR compliance)
63
+ */
64
+ telemetryEnabled?: boolean;
65
+ /**
66
+ * - Interval in ms between automatic heartbeats (default: 5 minutes, set 0 to disable)
67
+ */
68
+ heartbeatInterval?: number;
69
+ /**
70
+ * - User-provided app version string, sent as app_version in telemetry
71
+ */
72
+ appVersion?: string;
73
+ /**
74
+ * - User-provided app build identifier, sent as app_build in telemetry
75
+ */
76
+ appBuild?: string;
61
77
  };
62
78
  /**
63
79
  * License activation options
@@ -504,6 +520,23 @@ export type SigningKey = {
504
520
  */
505
521
  status: string;
506
522
  };
523
+ /**
524
+ * Heartbeat response from the API
525
+ */
526
+ export type HeartbeatResponse = {
527
+ /**
528
+ * - Object type ("heartbeat")
529
+ */
530
+ object: string;
531
+ /**
532
+ * - ISO8601 timestamp of when the heartbeat was received
533
+ */
534
+ received_at: string;
535
+ /**
536
+ * - The license object
537
+ */
538
+ license: LicenseObject;
539
+ };
507
540
  /**
508
541
  * Health check response
509
542
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.js"],"names":[],"mappings":";;;;;;;iBASc,MAAM;;;;kBACN,MAAM;;;;aACN,MAAM;;;;oBACN,MAAM;;;;2BACN,MAAM;;;;6BACN,MAAM;;;;iBACN,MAAM;;;;iBACN,MAAM;;;;YACN,OAAO;;;;oCACP,MAAM;;;;6BACN,OAAO;;;;qBACP,MAAM;;;;qBACN,MAAM;;;;qBACN,OAAO;;;;;;;;;eAMP,MAAM;;;;iBACN,MAAM;;;;;;;;;;;;;eAON,MAAM;;;;;;;;;YAMN,MAAM;;;;QACN,MAAM;;;;eACN,MAAM;;;;kBACN,MAAM;;;;iBACN,MAAM;;;;kBACN,MAAM;;;;qBACN,MAAM,GAAC,IAAI;;;;iBACX,MAAM;;;;;;;;aAEN,aAAa;;;;;;;;;YAMb,MAAM;;;;mBACN,MAAM;;;;oBACN,MAAM;;;;;;;;;SAMN,MAAM;;;;YACN,MAAM;;;;gBACN,MAAM;;;;iBACN,MAAM,GAAC,IAAI;;;;UACX,MAAM;;;;cACN,MAAM;;;;iBACN,MAAM;;;;kBACN,MAAM;;;;yBACN,WAAW,EAAE;;;;;;;;aAEb,WAAW;;;;;;;;;UAMX,MAAM;;;;UACN,MAAM;;;;;;;;;iBAMN,MAAM;;;;eACN,MAAM;;;;iBACN,kBAAkB;;;;kBAClB,MAAM;;;;oBACN,MAAM;;;;iBACN,gBAAgB;;;;;;;;;SAMhB,MAAM;;;;gBACN,MAAM,GAAC,IAAI;;;;cACX,MAAO,IAAI;;;;;;;;;WAMX,OAAO;;;;cACP,OAAO;;;;WACP,MAAM;;;;cACN,MAAM;;;;eACN,iBAAiB,EAAE;;;;cACnB,aAAa;;;;iBACb,kBAAkB;;;;0BAClB,WAAW,EAAE;;;;iBACb,OAAO;;;;;;;;;UAMP,MAAM;;;;aACN,MAAM;;;;;;;;;YAMN,OAAO;;;;aACP,MAAM;;;;iBACN,MAAM;;;;kBACN,WAAW;;;;;;;;;YAMX,MAAM;;;;cACN,MAAM;;;;cACN,MAAM;;;;aACN,MAAM;;;;mBACN,MAAM;;;;qBACN,MAAM;;;;mBACN,WAAW,EAAE;;;;;;;;;YAMb,MAAM;;;;WACN,mBAAmB;;;;eACnB,qBAAqB;;;;eACrB,MAAM;;;;;;;;;oBAMN,MAAM;;;;iBACN,MAAM;;;;kBACN,MAAM;;;;cACN,MAAM;;;;UACN,MAAM;;;;iBACN,MAAM,GAAC,IAAI;;;;gBACX,MAAM,GAAC,IAAI;;;;SACX,MAAM;;;;SACN,MAAM;;;;SACN,MAAM;;;;yBACN,MAAM,GAAC,IAAI;;;;SACX,MAAM;;;;kBACN,kBAAkB,EAAE;;;;;;;;;;;;;SAOpB,MAAM;;;;iBACN,MAAM,GAAC,IAAI;;;;;;;;;eAMX,MAAM;;;;YACN,MAAM;;;;WACN,MAAM;;;;;;;;;YAMN,MAAM;;;;YACN,MAAM;;;;eACN,MAAM;;;;gBACN,MAAM;;;;iBACN,MAAM;;;;YACN,MAAM;;;;;;;;;YAMN,MAAM;;;;YACN,MAAM;;;;iBACN,MAAM;;;;eACN,MAAM;;;;;mCAMT,GAAC,KACC,IAAI;;;;qCAMJ,IAAI;;;;;;;;YAMH,cAAc;;;;WACd,MAAM;;;;cACN,MAAM;;;;;;;;;UAMN,MAAM;;;;aACN,MAAM"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.js"],"names":[],"mappings":";;;;;;;iBASc,MAAM;;;;kBACN,MAAM;;;;aACN,MAAM;;;;oBACN,MAAM;;;;2BACN,MAAM;;;;6BACN,MAAM;;;;iBACN,MAAM;;;;iBACN,MAAM;;;;YACN,OAAO;;;;oCACP,MAAM;;;;6BACN,OAAO;;;;qBACP,MAAM;;;;qBACN,MAAM;;;;qBACN,OAAO;;;;uBACP,OAAO;;;;wBACP,MAAM;;;;iBACN,MAAM;;;;eACN,MAAM;;;;;;;;;eAMN,MAAM;;;;iBACN,MAAM;;;;;;;;;;;;;eAON,MAAM;;;;;;;;;YAMN,MAAM;;;;QACN,MAAM;;;;eACN,MAAM;;;;kBACN,MAAM;;;;iBACN,MAAM;;;;kBACN,MAAM;;;;qBACN,MAAM,GAAC,IAAI;;;;iBACX,MAAM;;;;;;;;aAEN,aAAa;;;;;;;;;YAMb,MAAM;;;;mBACN,MAAM;;;;oBACN,MAAM;;;;;;;;;SAMN,MAAM;;;;YACN,MAAM;;;;gBACN,MAAM;;;;iBACN,MAAM,GAAC,IAAI;;;;UACX,MAAM;;;;cACN,MAAM;;;;iBACN,MAAM;;;;kBACN,MAAM;;;;yBACN,WAAW,EAAE;;;;;;;;aAEb,WAAW;;;;;;;;;UAMX,MAAM;;;;UACN,MAAM;;;;;;;;;iBAMN,MAAM;;;;eACN,MAAM;;;;iBACN,kBAAkB;;;;kBAClB,MAAM;;;;oBACN,MAAM;;;;iBACN,gBAAgB;;;;;;;;;SAMhB,MAAM;;;;gBACN,MAAM,GAAC,IAAI;;;;cACX,MAAO,IAAI;;;;;;;;;WAMX,OAAO;;;;cACP,OAAO;;;;WACP,MAAM;;;;cACN,MAAM;;;;eACN,iBAAiB,EAAE;;;;cACnB,aAAa;;;;iBACb,kBAAkB;;;;0BAClB,WAAW,EAAE;;;;iBACb,OAAO;;;;;;;;;UAMP,MAAM;;;;aACN,MAAM;;;;;;;;;YAMN,OAAO;;;;aACP,MAAM;;;;iBACN,MAAM;;;;kBACN,WAAW;;;;;;;;;YAMX,MAAM;;;;cACN,MAAM;;;;cACN,MAAM;;;;aACN,MAAM;;;;mBACN,MAAM;;;;qBACN,MAAM;;;;mBACN,WAAW,EAAE;;;;;;;;;YAMb,MAAM;;;;WACN,mBAAmB;;;;eACnB,qBAAqB;;;;eACrB,MAAM;;;;;;;;;oBAMN,MAAM;;;;iBACN,MAAM;;;;kBACN,MAAM;;;;cACN,MAAM;;;;UACN,MAAM;;;;iBACN,MAAM,GAAC,IAAI;;;;gBACX,MAAM,GAAC,IAAI;;;;SACX,MAAM;;;;SACN,MAAM;;;;SACN,MAAM;;;;yBACN,MAAM,GAAC,IAAI;;;;SACX,MAAM;;;;kBACN,kBAAkB,EAAE;;;;;;;;;;;;;SAOpB,MAAM;;;;iBACN,MAAM,GAAC,IAAI;;;;;;;;;eAMX,MAAM;;;;YACN,MAAM;;;;WACN,MAAM;;;;;;;;;YAMN,MAAM;;;;YACN,MAAM;;;;eACN,MAAM;;;;gBACN,MAAM;;;;iBACN,MAAM;;;;YACN,MAAM;;;;;;;;;YAMN,MAAM;;;;iBACN,MAAM;;;;aACN,aAAa;;;;;;;;;YAMb,MAAM;;;;YACN,MAAM;;;;iBACN,MAAM;;;;eACN,MAAM;;;;;mCAMT,GAAC,KACC,IAAI;;;;qCAMJ,IAAI;;;;;;;;YAMH,cAAc;;;;WACd,MAAM;;;;cACN,MAAM;;;;;;;;;UAMN,MAAM;;;;aACN,MAAM"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@licenseseat/js",
3
- "version": "0.3.1",
3
+ "version": "0.4.2",
4
4
  "description": "Official JavaScript SDK for LicenseSeat – simple, secure software licensing.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -31,6 +31,13 @@ import {
31
31
  sleep,
32
32
  getCsrfToken,
33
33
  } from "./utils.js";
34
+ import { collectTelemetry } from "./telemetry.js";
35
+
36
+ /**
37
+ * SDK version constant
38
+ * @type {string}
39
+ */
40
+ export const SDK_VERSION = "0.4.2";
34
41
 
35
42
  /**
36
43
  * Default configuration values
@@ -41,6 +48,7 @@ const DEFAULT_CONFIG = {
41
48
  productSlug: null, // Required: Product slug for API calls (e.g., "my-app")
42
49
  storagePrefix: "licenseseat_",
43
50
  autoValidateInterval: 3600000, // 1 hour
51
+ heartbeatInterval: 300000, // 5 minutes
44
52
  networkRecheckInterval: 30000, // 30 seconds
45
53
  maxRetries: 3,
46
54
  retryDelay: 1000,
@@ -51,6 +59,9 @@ const DEFAULT_CONFIG = {
51
59
  maxOfflineDays: 0, // 0 = disabled
52
60
  maxClockSkewMs: 5 * 60 * 1000, // 5 minutes
53
61
  autoInitialize: true,
62
+ telemetryEnabled: true, // Set false to disable telemetry (e.g. for GDPR compliance)
63
+ appVersion: null, // User-provided app version, sent as app_version in telemetry
64
+ appBuild: null, // User-provided app build, sent as app_build in telemetry
54
65
  };
55
66
 
56
67
  /**
@@ -104,6 +115,13 @@ export class LicenseSeatSDK {
104
115
  */
105
116
  this.validationTimer = null;
106
117
 
118
+ /**
119
+ * Heartbeat timer ID (separate from auto-validation)
120
+ * @type {ReturnType<typeof setInterval>|null}
121
+ * @private
122
+ */
123
+ this.heartbeatTimer = null;
124
+
107
125
  /**
108
126
  * License cache manager
109
127
  * @type {LicenseCache}
@@ -205,9 +223,10 @@ export class LicenseSeatSDK {
205
223
  .catch(() => {});
206
224
  }
207
225
 
208
- // Start auto-validation if API key is configured
226
+ // Start auto-validation and heartbeat if API key is configured
209
227
  if (this.config.apiKey) {
210
228
  this.startAutoValidation(cachedLicense.license_key);
229
+ this.startHeartbeat();
211
230
 
212
231
  // Validate in background
213
232
  this.validateLicense(cachedLicense.license_key).catch((err) => {
@@ -278,6 +297,7 @@ export class LicenseSeatSDK {
278
297
  this.cache.setLicense(licenseData);
279
298
  this.cache.updateValidation({ valid: true, optimistic: true });
280
299
  this.startAutoValidation(licenseKey);
300
+ this.startHeartbeat();
281
301
  this.syncOfflineAssets();
282
302
  this.scheduleOfflineRefresh();
283
303
 
@@ -323,6 +343,7 @@ export class LicenseSeatSDK {
323
343
  this.cache.clearLicense();
324
344
  this.cache.clearOfflineToken();
325
345
  this.stopAutoValidation();
346
+ this.stopHeartbeat();
326
347
 
327
348
  this.emit("deactivation:success", response);
328
349
  return response;
@@ -715,12 +736,46 @@ export class LicenseSeatSDK {
715
736
  }
716
737
  }
717
738
 
739
+ /**
740
+ * Send a heartbeat for the current license.
741
+ * Heartbeats let the server know the device is still active.
742
+ * @returns {Promise<Object|undefined>} Heartbeat response, or undefined if no active license
743
+ * @throws {ConfigurationError} When productSlug is not configured
744
+ * @throws {APIError} When the API request fails
745
+ */
746
+ async heartbeat() {
747
+ if (!this.config.productSlug) {
748
+ throw new ConfigurationError("productSlug is required for heartbeat");
749
+ }
750
+
751
+ const cached = this.cache.getLicense();
752
+ if (!cached) {
753
+ this.log("No active license for heartbeat");
754
+ return;
755
+ }
756
+
757
+ const body = { device_id: cached.device_id };
758
+
759
+ const response = await this.apiCall(
760
+ `/products/${this.config.productSlug}/licenses/${encodeURIComponent(cached.license_key)}/heartbeat`,
761
+ {
762
+ method: "POST",
763
+ body: body,
764
+ }
765
+ );
766
+
767
+ this.emit("heartbeat:success", response);
768
+ this.log("Heartbeat sent successfully");
769
+ return response;
770
+ }
771
+
718
772
  /**
719
773
  * Clear all data and reset SDK state
720
774
  * @returns {void}
721
775
  */
722
776
  reset() {
723
777
  this.stopAutoValidation();
778
+ this.stopHeartbeat();
724
779
  this.stopConnectivityPolling();
725
780
  if (this.offlineRefreshTimer) {
726
781
  clearInterval(this.offlineRefreshTimer);
@@ -741,6 +796,7 @@ export class LicenseSeatSDK {
741
796
  destroy() {
742
797
  this.destroyed = true;
743
798
  this.stopAutoValidation();
799
+ this.stopHeartbeat();
744
800
  this.stopConnectivityPolling();
745
801
  if (this.offlineRefreshTimer) {
746
802
  clearInterval(this.offlineRefreshTimer);
@@ -821,11 +877,21 @@ export class LicenseSeatSDK {
821
877
  this.currentAutoLicenseKey = licenseKey;
822
878
  const validationInterval = this.config.autoValidateInterval;
823
879
 
880
+ // Don't start auto-validation if interval is 0 or negative
881
+ if (!validationInterval || validationInterval <= 0) {
882
+ this.log("Auto-validation disabled (interval:", validationInterval, ")");
883
+ return;
884
+ }
885
+
824
886
  const performAndReschedule = () => {
825
- this.validateLicense(licenseKey).catch((err) => {
826
- this.log("Auto-validation failed:", err);
827
- this.emit("validation:auto-failed", { licenseKey, error: err });
828
- });
887
+ this.validateLicense(licenseKey)
888
+ .then(() => {
889
+ this.heartbeat().catch((err) => this.log("Heartbeat failed:", err));
890
+ })
891
+ .catch((err) => {
892
+ this.log("Auto-validation failed:", err);
893
+ this.emit("validation:auto-failed", { licenseKey, error: err });
894
+ });
829
895
  this.emit("autovalidation:cycle", {
830
896
  nextRunAt: new Date(Date.now() + validationInterval),
831
897
  });
@@ -851,6 +917,50 @@ export class LicenseSeatSDK {
851
917
  }
852
918
  }
853
919
 
920
+ /**
921
+ * Start separate heartbeat timer
922
+ * Sends periodic heartbeats between auto-validation cycles.
923
+ * @returns {void}
924
+ * @private
925
+ */
926
+ startHeartbeat() {
927
+ this.stopHeartbeat();
928
+
929
+ const interval = this.config.heartbeatInterval;
930
+ if (!interval || interval <= 0) {
931
+ this.log("Heartbeat timer disabled (interval:", interval, ")");
932
+ return;
933
+ }
934
+
935
+ if (!this.config.productSlug) {
936
+ this.log("Heartbeat timer disabled (productSlug not configured)");
937
+ return;
938
+ }
939
+
940
+ this.heartbeatTimer = setInterval(() => {
941
+ this.heartbeat()
942
+ .then(() => this.emit("heartbeat:cycle", { nextRunAt: new Date(Date.now() + interval) }))
943
+ .catch((err) => this.log("Heartbeat timer failed:", err));
944
+ }, interval);
945
+
946
+ // Emit initial cycle event immediately (like auto-validation does)
947
+ this.emit("heartbeat:cycle", { nextRunAt: new Date(Date.now() + interval) });
948
+
949
+ this.log("Heartbeat timer started (interval:", interval, "ms)");
950
+ }
951
+
952
+ /**
953
+ * Stop the separate heartbeat timer
954
+ * @returns {void}
955
+ * @private
956
+ */
957
+ stopHeartbeat() {
958
+ if (this.heartbeatTimer) {
959
+ clearInterval(this.heartbeatTimer);
960
+ this.heartbeatTimer = null;
961
+ }
962
+ }
963
+
854
964
  /**
855
965
  * Start connectivity polling (when offline)
856
966
  * @returns {void}
@@ -862,10 +972,12 @@ export class LicenseSeatSDK {
862
972
  const healthCheck = async () => {
863
973
  try {
864
974
  // New v1 API: GET /health
865
- await fetch(`${this.config.apiBaseUrl}/health`, {
975
+ const res = await fetch(`${this.config.apiBaseUrl}/health`, {
866
976
  method: "GET",
867
977
  credentials: "omit",
868
978
  });
979
+ // Consume the response body to release the connection
980
+ await res.text().catch(() => {});
869
981
 
870
982
  if (!this.online) {
871
983
  this.online = true;
@@ -904,10 +1016,10 @@ export class LicenseSeatSDK {
904
1016
  // ============================================================
905
1017
 
906
1018
  /**
907
- * Fetch and cache offline token and signing key
908
- * Uses a lock to prevent concurrent calls from causing race conditions
1019
+ * Download and cache the offline token and its corresponding public signing key.
1020
+ * Emits `offlineToken:ready` on success. Safe to call multiple times concurrent
1021
+ * calls are deduplicated automatically.
909
1022
  * @returns {Promise<void>}
910
- * @private
911
1023
  */
912
1024
  async syncOfflineAssets() {
913
1025
  // Prevent concurrent syncs
@@ -965,9 +1077,10 @@ export class LicenseSeatSDK {
965
1077
  }
966
1078
 
967
1079
  /**
968
- * Verify cached offline token
1080
+ * Verify the cached offline token and return a validation result.
1081
+ * Use this to validate the license when the device is offline.
1082
+ * The offline token must have been previously downloaded via {@link syncOfflineAssets}.
969
1083
  * @returns {Promise<import('./types.js').ValidationResult>}
970
- * @private
971
1084
  */
972
1085
  async verifyCachedOffline() {
973
1086
  const signed = this.cache.getOfflineToken();
@@ -1127,12 +1240,22 @@ export class LicenseSeatSDK {
1127
1240
  );
1128
1241
  }
1129
1242
 
1243
+ // Inject telemetry into POST request bodies
1244
+ const method = options.method || "GET";
1245
+ let body = options.body;
1246
+ if (method === "POST" && body && this.config.telemetryEnabled !== false) {
1247
+ body = { ...body, telemetry: collectTelemetry(SDK_VERSION, {
1248
+ appVersion: this.config.appVersion,
1249
+ appBuild: this.config.appBuild,
1250
+ }) };
1251
+ }
1252
+
1130
1253
  for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
1131
1254
  try {
1132
1255
  const response = await fetch(url, {
1133
- method: options.method || "GET",
1256
+ method: method,
1134
1257
  headers: headers,
1135
- body: options.body ? JSON.stringify(options.body) : undefined,
1258
+ body: body ? JSON.stringify(body) : undefined,
1136
1259
  credentials: "omit",
1137
1260
  });
1138
1261
 
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Type augmentations for non-standard browser APIs used by the telemetry module.
3
+ * These APIs exist in Chromium-based browsers but are not part of the standard
4
+ * TypeScript DOM lib.
5
+ */
6
+
7
+ interface NavigatorUABrandVersion {
8
+ brand: string;
9
+ version: string;
10
+ }
11
+
12
+ interface NavigatorUAData {
13
+ brands: NavigatorUABrandVersion[];
14
+ mobile: boolean;
15
+ platform: string;
16
+ architecture?: string;
17
+ model?: string;
18
+ }
19
+
20
+ interface Navigator {
21
+ userAgentData?: NavigatorUAData;
22
+ deviceMemory?: number;
23
+ }
package/src/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * for apps, games, and plugins.
6
6
  *
7
7
  * @module @licenseseat/js
8
- * @version 0.3.0
8
+ * @version 0.4.1
9
9
  *
10
10
  * @example
11
11
  * ```js
@@ -31,9 +31,10 @@
31
31
  * ```
32
32
  */
33
33
 
34
- // Re-export the main SDK class
34
+ // Re-export the main SDK class and version
35
35
  export {
36
36
  LicenseSeatSDK,
37
+ SDK_VERSION,
37
38
  getSharedInstance,
38
39
  configure,
39
40
  resetSharedInstance,
@@ -58,6 +59,9 @@ export {
58
59
  getCsrfToken,
59
60
  } from "./utils.js";
60
61
 
62
+ // Re-export telemetry collection (for advanced use cases)
63
+ export { collectTelemetry } from "./telemetry.js";
64
+
61
65
  // Default export - the main SDK class
62
66
  import { LicenseSeatSDK } from "./LicenseSeat.js";
63
67
  export default LicenseSeatSDK;