@glasstrace/sdk 0.12.3 → 0.12.4

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/dist/index.js CHANGED
@@ -259,6 +259,7 @@ function loadFsSyncOrNull() {
259
259
  var currentConfig = null;
260
260
  var configCacheChecked = false;
261
261
  var rateLimitBackoff = false;
262
+ var lastInitSucceeded = false;
262
263
  function loadCachedConfig(projectRoot) {
263
264
  const modules = loadFsSyncOrNull();
264
265
  if (!modules) return null;
@@ -428,6 +429,7 @@ async function writeClaimedKey(newApiKey, projectRoot) {
428
429
  }
429
430
  }
430
431
  async function performInit(config, anonKey, sdkVersion, healthReport) {
432
+ lastInitSucceeded = false;
431
433
  if (rateLimitBackoff) {
432
434
  rateLimitBackoff = false;
433
435
  return null;
@@ -456,6 +458,7 @@ async function performInit(config, anonKey, sdkVersion, healthReport) {
456
458
  if (healthReport) {
457
459
  acknowledgeHealthReport(healthReport);
458
460
  }
461
+ lastInitSucceeded = true;
459
462
  await saveCachedConfig(result);
460
463
  if (result.claimResult) {
461
464
  try {
@@ -502,7 +505,6 @@ async function performInit(config, anonKey, sdkVersion, healthReport) {
502
505
  return null;
503
506
  }
504
507
  } catch (err) {
505
- recordInitFailure();
506
508
  console.warn(
507
509
  `[glasstrace] Unexpected init error: ${err instanceof Error ? err.message : String(err)}`
508
510
  );
@@ -532,6 +534,16 @@ function getClaimResult() {
532
534
  function _setCurrentConfig(config) {
533
535
  currentConfig = config;
534
536
  }
537
+ function consumeRateLimitFlag() {
538
+ if (rateLimitBackoff) {
539
+ rateLimitBackoff = false;
540
+ return true;
541
+ }
542
+ return false;
543
+ }
544
+ function didLastInitSucceed() {
545
+ return lastInitSucceeded;
546
+ }
535
547
 
536
548
  // src/span-processor.ts
537
549
  var GlasstraceSpanProcessor = class {
@@ -3619,6 +3631,109 @@ async function configureOtel(config, sessionManager) {
3619
3631
  registerShutdownHooks(provider);
3620
3632
  }
3621
3633
 
3634
+ // src/heartbeat.ts
3635
+ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
3636
+ var BACKOFF_BASE_MS = HEARTBEAT_INTERVAL_MS;
3637
+ var BACKOFF_MAX_MS = 30 * 60 * 1e3;
3638
+ var BACKOFF_JITTER = 0.2;
3639
+ var heartbeatTimer = null;
3640
+ var heartbeatGeneration = 0;
3641
+ var backoffAttempts = 0;
3642
+ var backoffUntil = 0;
3643
+ var tickInProgress = false;
3644
+ var _shutdownHandler2 = null;
3645
+ function startHeartbeat(config, anonKey, sdkVersion, generation, onClaimTransition) {
3646
+ if (heartbeatTimer !== null) return;
3647
+ heartbeatGeneration = generation;
3648
+ heartbeatTimer = setInterval(() => {
3649
+ void heartbeatTick(config, anonKey, sdkVersion, generation, onClaimTransition);
3650
+ }, HEARTBEAT_INTERVAL_MS);
3651
+ heartbeatTimer.unref();
3652
+ registerShutdownHandlers(config, anonKey, sdkVersion);
3653
+ if (config.verbose) {
3654
+ sdkLog("info", "[glasstrace] Heartbeat started (5-minute interval).");
3655
+ }
3656
+ }
3657
+ function stopHeartbeat() {
3658
+ if (heartbeatTimer !== null) {
3659
+ clearInterval(heartbeatTimer);
3660
+ heartbeatTimer = null;
3661
+ }
3662
+ removeShutdownHandlers();
3663
+ }
3664
+ async function heartbeatTick(config, anonKey, sdkVersion, generation, onClaimTransition) {
3665
+ if (tickInProgress) return;
3666
+ tickInProgress = true;
3667
+ try {
3668
+ if (generation !== heartbeatGeneration) {
3669
+ stopHeartbeat();
3670
+ return;
3671
+ }
3672
+ if (Date.now() < backoffUntil) {
3673
+ if (config.verbose) {
3674
+ sdkLog("info", "[glasstrace] Heartbeat skipped (rate-limit backoff).");
3675
+ }
3676
+ return;
3677
+ }
3678
+ const healthReport = collectHealthReport(sdkVersion);
3679
+ const initResult = await performInit(config, anonKey, sdkVersion, healthReport);
3680
+ if (generation !== heartbeatGeneration) return;
3681
+ if (initResult === null && consumeRateLimitFlag()) {
3682
+ backoffAttempts++;
3683
+ const delay = Math.min(
3684
+ BACKOFF_BASE_MS * Math.pow(2, backoffAttempts - 1),
3685
+ BACKOFF_MAX_MS
3686
+ );
3687
+ const jitter = delay * BACKOFF_JITTER * (Math.random() * 2 - 1);
3688
+ backoffUntil = Date.now() + delay + jitter;
3689
+ if (config.verbose) {
3690
+ sdkLog("info", `[glasstrace] Heartbeat backing off for ${Math.round((delay + jitter) / 1e3)}s.`);
3691
+ }
3692
+ } else {
3693
+ backoffAttempts = 0;
3694
+ backoffUntil = 0;
3695
+ }
3696
+ if (initResult?.claimResult) {
3697
+ onClaimTransition(initResult.claimResult.newApiKey);
3698
+ }
3699
+ if (config.verbose) {
3700
+ sdkLog("info", "[glasstrace] Heartbeat completed.");
3701
+ }
3702
+ } finally {
3703
+ tickInProgress = false;
3704
+ }
3705
+ }
3706
+ function registerShutdownHandlers(config, anonKey, sdkVersion) {
3707
+ if (typeof process === "undefined" || typeof process.once !== "function") {
3708
+ return;
3709
+ }
3710
+ let shutdownFired = false;
3711
+ const handler = (signal) => {
3712
+ if (shutdownFired) return;
3713
+ shutdownFired = true;
3714
+ if (heartbeatTimer !== null) {
3715
+ clearInterval(heartbeatTimer);
3716
+ heartbeatTimer = null;
3717
+ }
3718
+ const healthReport = collectHealthReport(sdkVersion);
3719
+ void performInit(config, anonKey, sdkVersion, healthReport).catch(() => {
3720
+ }).finally(() => {
3721
+ removeShutdownHandlers();
3722
+ process.kill(process.pid, signal);
3723
+ });
3724
+ };
3725
+ _shutdownHandler2 = handler;
3726
+ process.once("SIGTERM", _shutdownHandler2);
3727
+ process.once("SIGINT", _shutdownHandler2);
3728
+ }
3729
+ function removeShutdownHandlers() {
3730
+ if (_shutdownHandler2 && typeof process !== "undefined") {
3731
+ process.removeListener("SIGTERM", _shutdownHandler2);
3732
+ process.removeListener("SIGINT", _shutdownHandler2);
3733
+ _shutdownHandler2 = null;
3734
+ }
3735
+ }
3736
+
3622
3737
  // src/register.ts
3623
3738
  var consoleCaptureInstalled = false;
3624
3739
  var discoveryHandler = null;
@@ -3772,14 +3887,20 @@ async function backgroundInit(config, anonKeyForInit, generation) {
3772
3887
  if (config.verbose) {
3773
3888
  console.info("[glasstrace] Background init firing.");
3774
3889
  }
3775
- const healthReport = collectHealthReport("0.12.3");
3776
- const initResult = await performInit(config, anonKeyForInit, "0.12.3", healthReport);
3890
+ const healthReport = collectHealthReport("0.12.4");
3891
+ const initResult = await performInit(config, anonKeyForInit, "0.12.4", healthReport);
3777
3892
  if (generation !== registrationGeneration) return;
3778
3893
  if (initResult?.claimResult) {
3779
3894
  setResolvedApiKey(initResult.claimResult.newApiKey);
3780
3895
  notifyApiKeyResolved();
3781
3896
  }
3782
3897
  maybeInstallConsoleCapture();
3898
+ if (didLastInitSucceed()) {
3899
+ startHeartbeat(config, anonKeyForInit, "0.12.4", generation, (newApiKey) => {
3900
+ setResolvedApiKey(newApiKey);
3901
+ notifyApiKeyResolved();
3902
+ });
3903
+ }
3783
3904
  }
3784
3905
  function getDiscoveryHandler() {
3785
3906
  return discoveryHandler;