@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 +156 -1
- package/dist/index.js +462 -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 +128 -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.1";
|
|
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,42 @@ 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
|
+
this.heartbeatTimer = setInterval(() => {
|
|
936
|
+
this.heartbeat()
|
|
937
|
+
.then(() => this.emit("heartbeat:cycle", { nextRunAt: new Date(Date.now() + interval) }))
|
|
938
|
+
.catch((err) => this.log("Heartbeat timer failed:", err));
|
|
939
|
+
}, interval);
|
|
940
|
+
|
|
941
|
+
this.log("Heartbeat timer started (interval:", interval, "ms)");
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Stop the separate heartbeat timer
|
|
946
|
+
* @returns {void}
|
|
947
|
+
* @private
|
|
948
|
+
*/
|
|
949
|
+
stopHeartbeat() {
|
|
950
|
+
if (this.heartbeatTimer) {
|
|
951
|
+
clearInterval(this.heartbeatTimer);
|
|
952
|
+
this.heartbeatTimer = null;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
854
956
|
/**
|
|
855
957
|
* Start connectivity polling (when offline)
|
|
856
958
|
* @returns {void}
|
|
@@ -862,10 +964,12 @@ export class LicenseSeatSDK {
|
|
|
862
964
|
const healthCheck = async () => {
|
|
863
965
|
try {
|
|
864
966
|
// New v1 API: GET /health
|
|
865
|
-
await fetch(`${this.config.apiBaseUrl}/health`, {
|
|
967
|
+
const res = await fetch(`${this.config.apiBaseUrl}/health`, {
|
|
866
968
|
method: "GET",
|
|
867
969
|
credentials: "omit",
|
|
868
970
|
});
|
|
971
|
+
// Consume the response body to release the connection
|
|
972
|
+
await res.text().catch(() => {});
|
|
869
973
|
|
|
870
974
|
if (!this.online) {
|
|
871
975
|
this.online = true;
|
|
@@ -904,10 +1008,10 @@ export class LicenseSeatSDK {
|
|
|
904
1008
|
// ============================================================
|
|
905
1009
|
|
|
906
1010
|
/**
|
|
907
|
-
*
|
|
908
|
-
*
|
|
1011
|
+
* Download and cache the offline token and its corresponding public signing key.
|
|
1012
|
+
* Emits `offlineToken:ready` on success. Safe to call multiple times — concurrent
|
|
1013
|
+
* calls are deduplicated automatically.
|
|
909
1014
|
* @returns {Promise<void>}
|
|
910
|
-
* @private
|
|
911
1015
|
*/
|
|
912
1016
|
async syncOfflineAssets() {
|
|
913
1017
|
// Prevent concurrent syncs
|
|
@@ -965,9 +1069,10 @@ export class LicenseSeatSDK {
|
|
|
965
1069
|
}
|
|
966
1070
|
|
|
967
1071
|
/**
|
|
968
|
-
* Verify cached offline token
|
|
1072
|
+
* Verify the cached offline token and return a validation result.
|
|
1073
|
+
* Use this to validate the license when the device is offline.
|
|
1074
|
+
* The offline token must have been previously downloaded via {@link syncOfflineAssets}.
|
|
969
1075
|
* @returns {Promise<import('./types.js').ValidationResult>}
|
|
970
|
-
* @private
|
|
971
1076
|
*/
|
|
972
1077
|
async verifyCachedOffline() {
|
|
973
1078
|
const signed = this.cache.getOfflineToken();
|
|
@@ -1127,12 +1232,22 @@ export class LicenseSeatSDK {
|
|
|
1127
1232
|
);
|
|
1128
1233
|
}
|
|
1129
1234
|
|
|
1235
|
+
// Inject telemetry into POST request bodies
|
|
1236
|
+
const method = options.method || "GET";
|
|
1237
|
+
let body = options.body;
|
|
1238
|
+
if (method === "POST" && body && this.config.telemetryEnabled !== false) {
|
|
1239
|
+
body = { ...body, telemetry: collectTelemetry(SDK_VERSION, {
|
|
1240
|
+
appVersion: this.config.appVersion,
|
|
1241
|
+
appBuild: this.config.appBuild,
|
|
1242
|
+
}) };
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1130
1245
|
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
1131
1246
|
try {
|
|
1132
1247
|
const response = await fetch(url, {
|
|
1133
|
-
method:
|
|
1248
|
+
method: method,
|
|
1134
1249
|
headers: headers,
|
|
1135
|
-
body:
|
|
1250
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
1136
1251
|
credentials: "omit",
|
|
1137
1252
|
});
|
|
1138
1253
|
|
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;
|