@devskin/browser-sdk 1.0.32 → 1.0.34

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.
@@ -13585,11 +13585,12 @@ class Transport {
13585
13585
  // Send user identification immediately (don't queue)
13586
13586
  this.sendToBackend('/v1/analytics/identify', user);
13587
13587
  }
13588
- startSession(session) {
13589
- return __awaiter$1(this, void 0, void 0, function* () {
13588
+ startSession(session_1) {
13589
+ return __awaiter$1(this, arguments, void 0, function* (session, useBeacon = false) {
13590
13590
  // Send session start immediately to RUM endpoint
13591
13591
  // MUST await to ensure session is created before other requests
13592
- yield this.sendToBackend('/v1/rum/sessions', session);
13592
+ // Use beacon for page unload events (more reliable)
13593
+ yield this.sendToBackend('/v1/rum/sessions', session, useBeacon);
13593
13594
  });
13594
13595
  }
13595
13596
  sendError(error) {
@@ -13859,6 +13860,7 @@ class DevSkinSDK {
13859
13860
  this.anonymousId = null;
13860
13861
  this.sessionStartTime = 0;
13861
13862
  this.initialized = false;
13863
+ this.heartbeatInterval = null;
13862
13864
  // Collectors
13863
13865
  this.deviceCollector = null;
13864
13866
  this.locationCollector = null;
@@ -13959,6 +13961,8 @@ class DevSkinSDK {
13959
13961
  }
13960
13962
  // Track initial page view
13961
13963
  this.trackPageView();
13964
+ // Start heartbeat to update session duration every 30 seconds
13965
+ this.startHeartbeat();
13962
13966
  }).catch((err) => {
13963
13967
  console.error('[DevSkin] Failed to create session:', err);
13964
13968
  });
@@ -14178,42 +14182,68 @@ class DevSkinSDK {
14178
14182
  document.addEventListener('visibilitychange', () => {
14179
14183
  var _a, _b;
14180
14184
  if (document.hidden) {
14181
- // User switched tabs or minimized - flush immediately
14185
+ // User switched tabs or minimized - update duration and flush
14186
+ this.updateSessionDuration();
14182
14187
  (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush
14183
14188
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon
14184
14189
  }
14185
14190
  });
14186
14191
  // 2. pagehide - fires when page is being unloaded
14187
14192
  window.addEventListener('pagehide', (event) => {
14188
- var _a, _b, _c;
14193
+ var _a, _b;
14189
14194
  const isActualClose = !event.persisted;
14190
14195
  if (isActualClose) {
14191
14196
  // Tab is closing - end session
14192
14197
  this.track('page_unload');
14193
- if (this.sessionId && this.sessionStartTime) {
14194
- const endedAt = new Date();
14195
- const durationMs = Date.now() - this.sessionStartTime;
14196
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(Object.assign({ sessionId: this.sessionId, userId: this.userId || undefined, anonymousId: this.anonymousId, endedAt: endedAt.toISOString(), durationMs: durationMs, platform: 'web' }, this.getContextData()));
14197
- // Clear session storage since session is ending
14198
- sessionStorage.removeItem('devskin_session_id');
14199
- sessionStorage.removeItem('devskin_session_start');
14200
- }
14198
+ this.updateSessionDuration(true); // true = session ending
14199
+ // Clear session storage since session is ending
14200
+ sessionStorage.removeItem('devskin_session_id');
14201
+ sessionStorage.removeItem('devskin_session_start');
14201
14202
  }
14202
14203
  else {
14203
14204
  // Navigation - track page change
14204
14205
  this.track('page_navigation');
14206
+ this.updateSessionDuration();
14205
14207
  }
14206
14208
  // ALWAYS flush (whether navigation or close)
14207
- (_b = this.rrwebRecorder) === null || _b === void 0 ? void 0 : _b.stop(); // Stop recording and flush remaining events
14208
- (_c = this.transport) === null || _c === void 0 ? void 0 : _c.flush(true); // Use beacon for reliability
14209
+ (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush remaining events
14210
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
14209
14211
  });
14210
14212
  // 3. beforeunload - backup for older browsers
14211
14213
  window.addEventListener('beforeunload', () => {
14212
14214
  var _a, _b;
14215
+ this.updateSessionDuration(true);
14213
14216
  (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop();
14214
14217
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true);
14215
14218
  });
14216
14219
  }
14220
+ /**
14221
+ * Start heartbeat to update session duration periodically
14222
+ */
14223
+ startHeartbeat() {
14224
+ // Update duration every 30 seconds
14225
+ this.heartbeatInterval = setInterval(() => {
14226
+ this.updateSessionDuration();
14227
+ }, 30000); // 30 seconds
14228
+ }
14229
+ /**
14230
+ * Update session duration
14231
+ */
14232
+ updateSessionDuration(isEnding = false) {
14233
+ var _a, _b;
14234
+ if (!this.sessionId || !this.sessionStartTime)
14235
+ return;
14236
+ const durationMs = Date.now() - this.sessionStartTime;
14237
+ const payload = Object.assign({ sessionId: this.sessionId, userId: this.userId || undefined, anonymousId: this.anonymousId, durationMs: durationMs, platform: 'web' }, this.getContextData());
14238
+ if (isEnding) {
14239
+ payload.endedAt = new Date().toISOString();
14240
+ }
14241
+ // Use beacon if ending, otherwise regular request
14242
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(payload, isEnding);
14243
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
14244
+ console.log('[DevSkin] Session duration updated:', durationMs, 'ms', isEnding ? '(ending)' : '');
14245
+ }
14246
+ }
14217
14247
  }
14218
14248
  // Create singleton instance
14219
14249
  const DevSkin = new DevSkinSDK();