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