@licenseseat/js 0.3.0 → 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 +312 -9
- package/dist/index.global.js +2339 -0
- package/dist/index.js +479 -23
- package/dist/types/LicenseSeat.d.ts +51 -13
- 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 +147 -25
- package/src/global.d.ts +23 -0
- package/src/index.js +7 -2
- package/src/telemetry.js +518 -0
- package/src/types.js +12 -0
package/dist/index.js
CHANGED
|
@@ -300,12 +300,12 @@ function generateDeviceId() {
|
|
|
300
300
|
return `node-${hashCode(os + "|" + arch)}`;
|
|
301
301
|
}
|
|
302
302
|
const nav = window.navigator;
|
|
303
|
-
const
|
|
303
|
+
const screen2 = window.screen;
|
|
304
304
|
const data = [
|
|
305
305
|
nav.userAgent,
|
|
306
306
|
nav.language,
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
screen2.colorDepth,
|
|
308
|
+
screen2.width + "x" + screen2.height,
|
|
309
309
|
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
310
310
|
nav.hardwareConcurrency,
|
|
311
311
|
getCanvasFingerprint()
|
|
@@ -320,7 +320,366 @@ function getCsrfToken() {
|
|
|
320
320
|
return token ? token.content : "";
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
// src/telemetry.js
|
|
324
|
+
function detectOSName() {
|
|
325
|
+
if (typeof process !== "undefined" && process.platform) {
|
|
326
|
+
const map = {
|
|
327
|
+
darwin: "macOS",
|
|
328
|
+
win32: "Windows",
|
|
329
|
+
linux: "Linux",
|
|
330
|
+
freebsd: "FreeBSD",
|
|
331
|
+
sunos: "SunOS"
|
|
332
|
+
};
|
|
333
|
+
return map[process.platform] || process.platform;
|
|
334
|
+
}
|
|
335
|
+
if (typeof navigator !== "undefined") {
|
|
336
|
+
if (navigator.userAgentData && navigator.userAgentData.platform) {
|
|
337
|
+
return navigator.userAgentData.platform;
|
|
338
|
+
}
|
|
339
|
+
const ua = navigator.userAgent || "";
|
|
340
|
+
if (/Android/i.test(ua))
|
|
341
|
+
return "Android";
|
|
342
|
+
if (/iPhone|iPad|iPod/i.test(ua))
|
|
343
|
+
return "iOS";
|
|
344
|
+
if (/Mac/i.test(ua))
|
|
345
|
+
return "macOS";
|
|
346
|
+
if (/Win/i.test(ua))
|
|
347
|
+
return "Windows";
|
|
348
|
+
if (/Linux/i.test(ua))
|
|
349
|
+
return "Linux";
|
|
350
|
+
}
|
|
351
|
+
return "Unknown";
|
|
352
|
+
}
|
|
353
|
+
function detectOSVersion() {
|
|
354
|
+
if (typeof process !== "undefined" && process.version) {
|
|
355
|
+
try {
|
|
356
|
+
const os = await_free_os_release();
|
|
357
|
+
if (os)
|
|
358
|
+
return os;
|
|
359
|
+
} catch (_) {
|
|
360
|
+
}
|
|
361
|
+
return process.version;
|
|
362
|
+
}
|
|
363
|
+
if (typeof navigator !== "undefined") {
|
|
364
|
+
const ua = navigator.userAgent || "";
|
|
365
|
+
const macMatch = ua.match(/Mac OS X\s+([\d._]+)/);
|
|
366
|
+
if (macMatch)
|
|
367
|
+
return macMatch[1].replace(/_/g, ".");
|
|
368
|
+
const winMatch = ua.match(/Windows NT\s+([\d.]+)/);
|
|
369
|
+
if (winMatch)
|
|
370
|
+
return winMatch[1];
|
|
371
|
+
const androidMatch = ua.match(/Android\s+([\d.]+)/);
|
|
372
|
+
if (androidMatch)
|
|
373
|
+
return androidMatch[1];
|
|
374
|
+
const iosMatch = ua.match(/OS\s+([\d._]+)/);
|
|
375
|
+
if (iosMatch)
|
|
376
|
+
return iosMatch[1].replace(/_/g, ".");
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
function await_free_os_release() {
|
|
381
|
+
try {
|
|
382
|
+
const os = new Function("try { return require('os') } catch(e) { return null }")();
|
|
383
|
+
if (os && os.release)
|
|
384
|
+
return os.release();
|
|
385
|
+
} catch (_) {
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
function dynamicRequire(moduleName) {
|
|
390
|
+
try {
|
|
391
|
+
return new Function("m", "try { return require(m) } catch(e) { return null }")(moduleName);
|
|
392
|
+
} catch (_) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function detectPlatform() {
|
|
397
|
+
if (typeof process !== "undefined") {
|
|
398
|
+
if (process.versions && process.versions.electron)
|
|
399
|
+
return "electron";
|
|
400
|
+
if (process.versions && process.versions.bun)
|
|
401
|
+
return "bun";
|
|
402
|
+
if (process.versions && process.versions.node)
|
|
403
|
+
return "node";
|
|
404
|
+
}
|
|
405
|
+
if (typeof Deno !== "undefined")
|
|
406
|
+
return "deno";
|
|
407
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative")
|
|
408
|
+
return "react-native";
|
|
409
|
+
if (typeof window !== "undefined")
|
|
410
|
+
return "browser";
|
|
411
|
+
return "unknown";
|
|
412
|
+
}
|
|
413
|
+
function detectDeviceModel() {
|
|
414
|
+
try {
|
|
415
|
+
if (typeof navigator !== "undefined" && navigator.userAgentData) {
|
|
416
|
+
return navigator.userAgentData.model || null;
|
|
417
|
+
}
|
|
418
|
+
} catch (_) {
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
function detectLocale() {
|
|
423
|
+
if (typeof navigator !== "undefined" && navigator.language) {
|
|
424
|
+
return navigator.language;
|
|
425
|
+
}
|
|
426
|
+
if (typeof Intl !== "undefined") {
|
|
427
|
+
try {
|
|
428
|
+
return Intl.DateTimeFormat().resolvedOptions().locale || null;
|
|
429
|
+
} catch (_) {
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (typeof process !== "undefined" && process.env) {
|
|
433
|
+
return process.env.LANG || process.env.LC_ALL || null;
|
|
434
|
+
}
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
function detectTimezone() {
|
|
438
|
+
if (typeof Intl !== "undefined") {
|
|
439
|
+
try {
|
|
440
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || null;
|
|
441
|
+
} catch (_) {
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
function detectDeviceType() {
|
|
447
|
+
try {
|
|
448
|
+
const platform = detectPlatform();
|
|
449
|
+
if (platform === "node" || platform === "bun" || platform === "deno")
|
|
450
|
+
return "server";
|
|
451
|
+
if (platform === "electron")
|
|
452
|
+
return "desktop";
|
|
453
|
+
if (platform === "react-native") {
|
|
454
|
+
if (typeof screen !== "undefined" && screen.width) {
|
|
455
|
+
return screen.width < 768 ? "phone" : "tablet";
|
|
456
|
+
}
|
|
457
|
+
return "phone";
|
|
458
|
+
}
|
|
459
|
+
if (typeof navigator !== "undefined") {
|
|
460
|
+
if (navigator.userAgentData && typeof navigator.userAgentData.mobile === "boolean") {
|
|
461
|
+
if (navigator.userAgentData.mobile) {
|
|
462
|
+
if (typeof screen !== "undefined" && screen.width >= 768)
|
|
463
|
+
return "tablet";
|
|
464
|
+
return "phone";
|
|
465
|
+
}
|
|
466
|
+
return "desktop";
|
|
467
|
+
}
|
|
468
|
+
if (navigator.maxTouchPoints > 0) {
|
|
469
|
+
if (typeof screen !== "undefined" && screen.width >= 768)
|
|
470
|
+
return "tablet";
|
|
471
|
+
return "phone";
|
|
472
|
+
}
|
|
473
|
+
return "desktop";
|
|
474
|
+
}
|
|
475
|
+
} catch (_) {
|
|
476
|
+
}
|
|
477
|
+
return "unknown";
|
|
478
|
+
}
|
|
479
|
+
function detectArchitecture() {
|
|
480
|
+
try {
|
|
481
|
+
if (typeof process !== "undefined" && process.arch) {
|
|
482
|
+
const map = { ia32: "x86", x64: "x64", arm: "arm", arm64: "arm64" };
|
|
483
|
+
return map[process.arch] || process.arch;
|
|
484
|
+
}
|
|
485
|
+
if (typeof navigator !== "undefined" && navigator.userAgentData) {
|
|
486
|
+
if (navigator.userAgentData.architecture) {
|
|
487
|
+
return navigator.userAgentData.architecture;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
} catch (_) {
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
function detectCpuCores() {
|
|
495
|
+
try {
|
|
496
|
+
if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) {
|
|
497
|
+
return navigator.hardwareConcurrency;
|
|
498
|
+
}
|
|
499
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
500
|
+
const os = dynamicRequire("os");
|
|
501
|
+
if (os && os.cpus) {
|
|
502
|
+
const cpus = os.cpus();
|
|
503
|
+
if (cpus && cpus.length)
|
|
504
|
+
return cpus.length;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
} catch (_) {
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
function detectMemoryGb() {
|
|
512
|
+
try {
|
|
513
|
+
if (typeof navigator !== "undefined" && navigator.deviceMemory) {
|
|
514
|
+
return navigator.deviceMemory;
|
|
515
|
+
}
|
|
516
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
517
|
+
const os = dynamicRequire("os");
|
|
518
|
+
if (os && os.totalmem) {
|
|
519
|
+
return Math.round(os.totalmem() / (1024 * 1024 * 1024));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
} catch (_) {
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
function detectLanguage() {
|
|
527
|
+
try {
|
|
528
|
+
const locale = detectLocale();
|
|
529
|
+
if (locale) {
|
|
530
|
+
const lang = locale.split(/[-_]/)[0];
|
|
531
|
+
if (lang && lang.length >= 2)
|
|
532
|
+
return lang.toLowerCase();
|
|
533
|
+
}
|
|
534
|
+
} catch (_) {
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
function detectScreenResolution() {
|
|
539
|
+
try {
|
|
540
|
+
if (typeof screen !== "undefined" && screen.width && screen.height) {
|
|
541
|
+
return `${screen.width}x${screen.height}`;
|
|
542
|
+
}
|
|
543
|
+
} catch (_) {
|
|
544
|
+
}
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
function detectDisplayScale() {
|
|
548
|
+
try {
|
|
549
|
+
if (typeof window !== "undefined" && window.devicePixelRatio) {
|
|
550
|
+
return window.devicePixelRatio;
|
|
551
|
+
}
|
|
552
|
+
} catch (_) {
|
|
553
|
+
}
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
function detectBrowserName() {
|
|
557
|
+
try {
|
|
558
|
+
if (typeof navigator === "undefined")
|
|
559
|
+
return null;
|
|
560
|
+
if (navigator.userAgentData && navigator.userAgentData.brands) {
|
|
561
|
+
const brands = navigator.userAgentData.brands;
|
|
562
|
+
for (const b of brands) {
|
|
563
|
+
const name = b.brand || "";
|
|
564
|
+
if (/^(Google Chrome|Microsoft Edge|Opera|Brave|Vivaldi|Samsung Internet)$/i.test(name)) {
|
|
565
|
+
return name;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
for (const b of brands) {
|
|
569
|
+
if ((b.brand || "").toLowerCase() === "chromium")
|
|
570
|
+
return "Chrome";
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const ua = navigator.userAgent || "";
|
|
574
|
+
if (/Edg\//i.test(ua))
|
|
575
|
+
return "Edge";
|
|
576
|
+
if (/OPR\//i.test(ua) || /Opera/i.test(ua))
|
|
577
|
+
return "Opera";
|
|
578
|
+
if (/Brave/i.test(ua))
|
|
579
|
+
return "Brave";
|
|
580
|
+
if (/Vivaldi/i.test(ua))
|
|
581
|
+
return "Vivaldi";
|
|
582
|
+
if (/Firefox/i.test(ua))
|
|
583
|
+
return "Firefox";
|
|
584
|
+
if (/SamsungBrowser/i.test(ua))
|
|
585
|
+
return "Samsung Internet";
|
|
586
|
+
if (/CriOS/i.test(ua))
|
|
587
|
+
return "Chrome";
|
|
588
|
+
if (/Chrome/i.test(ua))
|
|
589
|
+
return "Chrome";
|
|
590
|
+
if (/Safari/i.test(ua))
|
|
591
|
+
return "Safari";
|
|
592
|
+
} catch (_) {
|
|
593
|
+
}
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
function detectBrowserVersion() {
|
|
597
|
+
try {
|
|
598
|
+
if (typeof navigator === "undefined")
|
|
599
|
+
return null;
|
|
600
|
+
if (navigator.userAgentData && navigator.userAgentData.brands) {
|
|
601
|
+
const brands = navigator.userAgentData.brands;
|
|
602
|
+
for (const b of brands) {
|
|
603
|
+
const name = b.brand || "";
|
|
604
|
+
if (/^(Google Chrome|Microsoft Edge|Opera|Brave|Vivaldi|Samsung Internet)$/i.test(name)) {
|
|
605
|
+
return b.version || null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
for (const b of brands) {
|
|
609
|
+
if ((b.brand || "").toLowerCase() === "chromium")
|
|
610
|
+
return b.version || null;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
const ua = navigator.userAgent || "";
|
|
614
|
+
const patterns = [
|
|
615
|
+
/Edg\/([\d.]+)/,
|
|
616
|
+
/OPR\/([\d.]+)/,
|
|
617
|
+
/Firefox\/([\d.]+)/,
|
|
618
|
+
/SamsungBrowser\/([\d.]+)/,
|
|
619
|
+
/CriOS\/([\d.]+)/,
|
|
620
|
+
/Chrome\/([\d.]+)/,
|
|
621
|
+
/Version\/([\d.]+).*Safari/
|
|
622
|
+
];
|
|
623
|
+
for (const re of patterns) {
|
|
624
|
+
const m = ua.match(re);
|
|
625
|
+
if (m)
|
|
626
|
+
return m[1];
|
|
627
|
+
}
|
|
628
|
+
} catch (_) {
|
|
629
|
+
}
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
function detectRuntimeVersion() {
|
|
633
|
+
try {
|
|
634
|
+
if (typeof process !== "undefined" && process.versions) {
|
|
635
|
+
if (process.versions.bun)
|
|
636
|
+
return process.versions.bun;
|
|
637
|
+
if (process.versions.electron)
|
|
638
|
+
return process.versions.electron;
|
|
639
|
+
if (process.versions.node)
|
|
640
|
+
return process.versions.node;
|
|
641
|
+
}
|
|
642
|
+
if (typeof Deno !== "undefined" && Deno.version)
|
|
643
|
+
return Deno.version.deno;
|
|
644
|
+
} catch (_) {
|
|
645
|
+
}
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
function collectTelemetry(sdkVersion, options) {
|
|
649
|
+
const locale = detectLocale();
|
|
650
|
+
const raw = {
|
|
651
|
+
sdk_version: sdkVersion,
|
|
652
|
+
sdk_name: "js",
|
|
653
|
+
os_name: detectOSName(),
|
|
654
|
+
os_version: detectOSVersion(),
|
|
655
|
+
platform: detectPlatform(),
|
|
656
|
+
device_model: detectDeviceModel(),
|
|
657
|
+
device_type: detectDeviceType(),
|
|
658
|
+
locale,
|
|
659
|
+
timezone: detectTimezone(),
|
|
660
|
+
language: detectLanguage(),
|
|
661
|
+
architecture: detectArchitecture(),
|
|
662
|
+
cpu_cores: detectCpuCores(),
|
|
663
|
+
memory_gb: detectMemoryGb(),
|
|
664
|
+
screen_resolution: detectScreenResolution(),
|
|
665
|
+
display_scale: detectDisplayScale(),
|
|
666
|
+
browser_name: detectBrowserName(),
|
|
667
|
+
browser_version: detectBrowserVersion(),
|
|
668
|
+
runtime_version: detectRuntimeVersion(),
|
|
669
|
+
app_version: options && options.appVersion || null,
|
|
670
|
+
app_build: options && options.appBuild || null
|
|
671
|
+
};
|
|
672
|
+
const result = {};
|
|
673
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
674
|
+
if (value != null) {
|
|
675
|
+
result[key] = value;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return result;
|
|
679
|
+
}
|
|
680
|
+
|
|
323
681
|
// src/LicenseSeat.js
|
|
682
|
+
var SDK_VERSION = "0.4.1";
|
|
324
683
|
var DEFAULT_CONFIG = {
|
|
325
684
|
apiBaseUrl: "https://licenseseat.com/api/v1",
|
|
326
685
|
productSlug: null,
|
|
@@ -328,6 +687,8 @@ var DEFAULT_CONFIG = {
|
|
|
328
687
|
storagePrefix: "licenseseat_",
|
|
329
688
|
autoValidateInterval: 36e5,
|
|
330
689
|
// 1 hour
|
|
690
|
+
heartbeatInterval: 3e5,
|
|
691
|
+
// 5 minutes
|
|
331
692
|
networkRecheckInterval: 3e4,
|
|
332
693
|
// 30 seconds
|
|
333
694
|
maxRetries: 3,
|
|
@@ -342,7 +703,13 @@ var DEFAULT_CONFIG = {
|
|
|
342
703
|
// 0 = disabled
|
|
343
704
|
maxClockSkewMs: 5 * 60 * 1e3,
|
|
344
705
|
// 5 minutes
|
|
345
|
-
autoInitialize: true
|
|
706
|
+
autoInitialize: true,
|
|
707
|
+
telemetryEnabled: true,
|
|
708
|
+
// Set false to disable telemetry (e.g. for GDPR compliance)
|
|
709
|
+
appVersion: null,
|
|
710
|
+
// User-provided app version, sent as app_version in telemetry
|
|
711
|
+
appBuild: null
|
|
712
|
+
// User-provided app build, sent as app_build in telemetry
|
|
346
713
|
};
|
|
347
714
|
var LicenseSeatSDK = class {
|
|
348
715
|
/**
|
|
@@ -356,6 +723,7 @@ var LicenseSeatSDK = class {
|
|
|
356
723
|
};
|
|
357
724
|
this.eventListeners = {};
|
|
358
725
|
this.validationTimer = null;
|
|
726
|
+
this.heartbeatTimer = null;
|
|
359
727
|
this.cache = new LicenseCache(this.config.storagePrefix);
|
|
360
728
|
this.online = true;
|
|
361
729
|
this.currentAutoLicenseKey = null;
|
|
@@ -402,6 +770,7 @@ var LicenseSeatSDK = class {
|
|
|
402
770
|
}
|
|
403
771
|
if (this.config.apiKey) {
|
|
404
772
|
this.startAutoValidation(cachedLicense.license_key);
|
|
773
|
+
this.startHeartbeat();
|
|
405
774
|
this.validateLicense(cachedLicense.license_key).catch((err) => {
|
|
406
775
|
this.log("Background validation failed:", err);
|
|
407
776
|
if (err instanceof APIError && (err.status === 401 || err.status === 501)) {
|
|
@@ -457,6 +826,7 @@ var LicenseSeatSDK = class {
|
|
|
457
826
|
this.cache.setLicense(licenseData);
|
|
458
827
|
this.cache.updateValidation({ valid: true, optimistic: true });
|
|
459
828
|
this.startAutoValidation(licenseKey);
|
|
829
|
+
this.startHeartbeat();
|
|
460
830
|
this.syncOfflineAssets();
|
|
461
831
|
this.scheduleOfflineRefresh();
|
|
462
832
|
this.emit("activation:success", licenseData);
|
|
@@ -495,6 +865,7 @@ var LicenseSeatSDK = class {
|
|
|
495
865
|
this.cache.clearLicense();
|
|
496
866
|
this.cache.clearOfflineToken();
|
|
497
867
|
this.stopAutoValidation();
|
|
868
|
+
this.stopHeartbeat();
|
|
498
869
|
this.emit("deactivation:success", response);
|
|
499
870
|
return response;
|
|
500
871
|
} catch (error) {
|
|
@@ -656,7 +1027,7 @@ var LicenseSeatSDK = class {
|
|
|
656
1027
|
if (options.ttlDays) {
|
|
657
1028
|
body.ttl_days = options.ttlDays;
|
|
658
1029
|
}
|
|
659
|
-
const path = `/products/${this.config.productSlug}/licenses/${encodeURIComponent(license.license_key)}/
|
|
1030
|
+
const path = `/products/${this.config.productSlug}/licenses/${encodeURIComponent(license.license_key)}/offline_token`;
|
|
660
1031
|
const response = await this.apiCall(path, {
|
|
661
1032
|
method: "POST",
|
|
662
1033
|
body: Object.keys(body).length > 0 ? body : void 0
|
|
@@ -690,7 +1061,7 @@ var LicenseSeatSDK = class {
|
|
|
690
1061
|
}
|
|
691
1062
|
try {
|
|
692
1063
|
this.log(`Fetching signing key for kid: ${keyId}`);
|
|
693
|
-
const response = await this.apiCall(`/
|
|
1064
|
+
const response = await this.apiCall(`/signing_keys/${encodeURIComponent(keyId)}`, {
|
|
694
1065
|
method: "GET"
|
|
695
1066
|
});
|
|
696
1067
|
if (response && response.public_key) {
|
|
@@ -796,11 +1167,12 @@ var LicenseSeatSDK = class {
|
|
|
796
1167
|
};
|
|
797
1168
|
}
|
|
798
1169
|
/**
|
|
799
|
-
* Test
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
* @
|
|
803
|
-
* @throws {
|
|
1170
|
+
* Test API connectivity
|
|
1171
|
+
* Makes a request to the health endpoint to verify connectivity.
|
|
1172
|
+
* Note: To fully verify API key validity, attempt an actual operation like activate() or validateLicense().
|
|
1173
|
+
* @returns {Promise<{authenticated: boolean, healthy: boolean, api_version: string}>}
|
|
1174
|
+
* @throws {ConfigurationError} If API key is not configured
|
|
1175
|
+
* @throws {APIError} If the health check fails
|
|
804
1176
|
*/
|
|
805
1177
|
async testAuth() {
|
|
806
1178
|
if (!this.config.apiKey) {
|
|
@@ -810,20 +1182,55 @@ var LicenseSeatSDK = class {
|
|
|
810
1182
|
}
|
|
811
1183
|
try {
|
|
812
1184
|
this.emit("auth_test:start");
|
|
813
|
-
const response = await this.apiCall("/
|
|
814
|
-
|
|
815
|
-
|
|
1185
|
+
const response = await this.apiCall("/health", { method: "GET" });
|
|
1186
|
+
const result = {
|
|
1187
|
+
authenticated: true,
|
|
1188
|
+
// API key was included in request
|
|
1189
|
+
healthy: response.status === "healthy",
|
|
1190
|
+
api_version: response.api_version
|
|
1191
|
+
};
|
|
1192
|
+
this.emit("auth_test:success", result);
|
|
1193
|
+
return result;
|
|
816
1194
|
} catch (error) {
|
|
817
1195
|
this.emit("auth_test:error", { error });
|
|
818
1196
|
throw error;
|
|
819
1197
|
}
|
|
820
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Send a heartbeat for the current license.
|
|
1201
|
+
* Heartbeats let the server know the device is still active.
|
|
1202
|
+
* @returns {Promise<Object|undefined>} Heartbeat response, or undefined if no active license
|
|
1203
|
+
* @throws {ConfigurationError} When productSlug is not configured
|
|
1204
|
+
* @throws {APIError} When the API request fails
|
|
1205
|
+
*/
|
|
1206
|
+
async heartbeat() {
|
|
1207
|
+
if (!this.config.productSlug) {
|
|
1208
|
+
throw new ConfigurationError("productSlug is required for heartbeat");
|
|
1209
|
+
}
|
|
1210
|
+
const cached = this.cache.getLicense();
|
|
1211
|
+
if (!cached) {
|
|
1212
|
+
this.log("No active license for heartbeat");
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
const body = { device_id: cached.device_id };
|
|
1216
|
+
const response = await this.apiCall(
|
|
1217
|
+
`/products/${this.config.productSlug}/licenses/${encodeURIComponent(cached.license_key)}/heartbeat`,
|
|
1218
|
+
{
|
|
1219
|
+
method: "POST",
|
|
1220
|
+
body
|
|
1221
|
+
}
|
|
1222
|
+
);
|
|
1223
|
+
this.emit("heartbeat:success", response);
|
|
1224
|
+
this.log("Heartbeat sent successfully");
|
|
1225
|
+
return response;
|
|
1226
|
+
}
|
|
821
1227
|
/**
|
|
822
1228
|
* Clear all data and reset SDK state
|
|
823
1229
|
* @returns {void}
|
|
824
1230
|
*/
|
|
825
1231
|
reset() {
|
|
826
1232
|
this.stopAutoValidation();
|
|
1233
|
+
this.stopHeartbeat();
|
|
827
1234
|
this.stopConnectivityPolling();
|
|
828
1235
|
if (this.offlineRefreshTimer) {
|
|
829
1236
|
clearInterval(this.offlineRefreshTimer);
|
|
@@ -843,6 +1250,7 @@ var LicenseSeatSDK = class {
|
|
|
843
1250
|
destroy() {
|
|
844
1251
|
this.destroyed = true;
|
|
845
1252
|
this.stopAutoValidation();
|
|
1253
|
+
this.stopHeartbeat();
|
|
846
1254
|
this.stopConnectivityPolling();
|
|
847
1255
|
if (this.offlineRefreshTimer) {
|
|
848
1256
|
clearInterval(this.offlineRefreshTimer);
|
|
@@ -915,8 +1323,14 @@ var LicenseSeatSDK = class {
|
|
|
915
1323
|
this.stopAutoValidation();
|
|
916
1324
|
this.currentAutoLicenseKey = licenseKey;
|
|
917
1325
|
const validationInterval = this.config.autoValidateInterval;
|
|
1326
|
+
if (!validationInterval || validationInterval <= 0) {
|
|
1327
|
+
this.log("Auto-validation disabled (interval:", validationInterval, ")");
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
918
1330
|
const performAndReschedule = () => {
|
|
919
|
-
this.validateLicense(licenseKey).
|
|
1331
|
+
this.validateLicense(licenseKey).then(() => {
|
|
1332
|
+
this.heartbeat().catch((err) => this.log("Heartbeat failed:", err));
|
|
1333
|
+
}).catch((err) => {
|
|
920
1334
|
this.log("Auto-validation failed:", err);
|
|
921
1335
|
this.emit("validation:auto-failed", { licenseKey, error: err });
|
|
922
1336
|
});
|
|
@@ -941,6 +1355,35 @@ var LicenseSeatSDK = class {
|
|
|
941
1355
|
this.emit("autovalidation:stopped");
|
|
942
1356
|
}
|
|
943
1357
|
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Start separate heartbeat timer
|
|
1360
|
+
* Sends periodic heartbeats between auto-validation cycles.
|
|
1361
|
+
* @returns {void}
|
|
1362
|
+
* @private
|
|
1363
|
+
*/
|
|
1364
|
+
startHeartbeat() {
|
|
1365
|
+
this.stopHeartbeat();
|
|
1366
|
+
const interval = this.config.heartbeatInterval;
|
|
1367
|
+
if (!interval || interval <= 0) {
|
|
1368
|
+
this.log("Heartbeat timer disabled (interval:", interval, ")");
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
this.heartbeatTimer = setInterval(() => {
|
|
1372
|
+
this.heartbeat().then(() => this.emit("heartbeat:cycle", { nextRunAt: new Date(Date.now() + interval) })).catch((err) => this.log("Heartbeat timer failed:", err));
|
|
1373
|
+
}, interval);
|
|
1374
|
+
this.log("Heartbeat timer started (interval:", interval, "ms)");
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Stop the separate heartbeat timer
|
|
1378
|
+
* @returns {void}
|
|
1379
|
+
* @private
|
|
1380
|
+
*/
|
|
1381
|
+
stopHeartbeat() {
|
|
1382
|
+
if (this.heartbeatTimer) {
|
|
1383
|
+
clearInterval(this.heartbeatTimer);
|
|
1384
|
+
this.heartbeatTimer = null;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
944
1387
|
/**
|
|
945
1388
|
* Start connectivity polling (when offline)
|
|
946
1389
|
* @returns {void}
|
|
@@ -951,10 +1394,12 @@ var LicenseSeatSDK = class {
|
|
|
951
1394
|
return;
|
|
952
1395
|
const healthCheck = async () => {
|
|
953
1396
|
try {
|
|
954
|
-
await fetch(`${this.config.apiBaseUrl}/health`, {
|
|
1397
|
+
const res = await fetch(`${this.config.apiBaseUrl}/health`, {
|
|
955
1398
|
method: "GET",
|
|
956
1399
|
credentials: "omit"
|
|
957
1400
|
});
|
|
1401
|
+
await res.text().catch(() => {
|
|
1402
|
+
});
|
|
958
1403
|
if (!this.online) {
|
|
959
1404
|
this.online = true;
|
|
960
1405
|
this.emit("network:online");
|
|
@@ -987,10 +1432,10 @@ var LicenseSeatSDK = class {
|
|
|
987
1432
|
// Offline License Management
|
|
988
1433
|
// ============================================================
|
|
989
1434
|
/**
|
|
990
|
-
*
|
|
991
|
-
*
|
|
1435
|
+
* Download and cache the offline token and its corresponding public signing key.
|
|
1436
|
+
* Emits `offlineToken:ready` on success. Safe to call multiple times — concurrent
|
|
1437
|
+
* calls are deduplicated automatically.
|
|
992
1438
|
* @returns {Promise<void>}
|
|
993
|
-
* @private
|
|
994
1439
|
*/
|
|
995
1440
|
async syncOfflineAssets() {
|
|
996
1441
|
if (this.syncingOfflineAssets || this.destroyed) {
|
|
@@ -1041,9 +1486,10 @@ var LicenseSeatSDK = class {
|
|
|
1041
1486
|
);
|
|
1042
1487
|
}
|
|
1043
1488
|
/**
|
|
1044
|
-
* Verify cached offline token
|
|
1489
|
+
* Verify the cached offline token and return a validation result.
|
|
1490
|
+
* Use this to validate the license when the device is offline.
|
|
1491
|
+
* The offline token must have been previously downloaded via {@link syncOfflineAssets}.
|
|
1045
1492
|
* @returns {Promise<import('./types.js').ValidationResult>}
|
|
1046
|
-
* @private
|
|
1047
1493
|
*/
|
|
1048
1494
|
async verifyCachedOffline() {
|
|
1049
1495
|
const signed = this.cache.getOfflineToken();
|
|
@@ -1176,12 +1622,20 @@ var LicenseSeatSDK = class {
|
|
|
1176
1622
|
"[Warning] No API key configured for LicenseSeat SDK. Authenticated endpoints will fail."
|
|
1177
1623
|
);
|
|
1178
1624
|
}
|
|
1625
|
+
const method = options.method || "GET";
|
|
1626
|
+
let body = options.body;
|
|
1627
|
+
if (method === "POST" && body && this.config.telemetryEnabled !== false) {
|
|
1628
|
+
body = { ...body, telemetry: collectTelemetry(SDK_VERSION, {
|
|
1629
|
+
appVersion: this.config.appVersion,
|
|
1630
|
+
appBuild: this.config.appBuild
|
|
1631
|
+
}) };
|
|
1632
|
+
}
|
|
1179
1633
|
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
1180
1634
|
try {
|
|
1181
1635
|
const response = await fetch(url, {
|
|
1182
|
-
method
|
|
1636
|
+
method,
|
|
1183
1637
|
headers,
|
|
1184
|
-
body:
|
|
1638
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
1185
1639
|
credentials: "omit"
|
|
1186
1640
|
});
|
|
1187
1641
|
const data = await response.json();
|
|
@@ -1305,8 +1759,10 @@ export {
|
|
|
1305
1759
|
LicenseCache,
|
|
1306
1760
|
LicenseError,
|
|
1307
1761
|
LicenseSeatSDK,
|
|
1762
|
+
SDK_VERSION,
|
|
1308
1763
|
base64UrlDecode,
|
|
1309
1764
|
canonicalJsonStringify,
|
|
1765
|
+
collectTelemetry,
|
|
1310
1766
|
configure,
|
|
1311
1767
|
constantTimeEqual,
|
|
1312
1768
|
src_default as default,
|