@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.
- package/README.md +156 -1
- package/dist/index.bundled.js +2775 -0
- package/dist/index.js +467 -13
- package/dist/types/LicenseSeat.d.ts +40 -7
- package/dist/types/LicenseSeat.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +16 -0
- package/dist/types/telemetry.d.ts.map +1 -0
- package/dist/types/types.d.ts +33 -0
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/LicenseSeat.js +136 -13
- package/src/global.d.ts +23 -0
- package/src/index.js +6 -2
- package/src/telemetry.js +518 -0
- package/src/types.js +12 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LicenseSeat.d.ts","sourceRoot":"","sources":["../../src/LicenseSeat.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LicenseSeat.d.ts","sourceRoot":"","sources":["../../src/LicenseSeat.js"],"names":[],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -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;;;;;;;;;
|
|
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
package/src/LicenseSeat.js
CHANGED
|
@@ -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)
|
|
826
|
-
|
|
827
|
-
|
|
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
|
-
*
|
|
908
|
-
*
|
|
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:
|
|
1256
|
+
method: method,
|
|
1134
1257
|
headers: headers,
|
|
1135
|
-
body:
|
|
1258
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
1136
1259
|
credentials: "omit",
|
|
1137
1260
|
});
|
|
1138
1261
|
|
package/src/global.d.ts
ADDED
|
@@ -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.
|
|
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;
|