@devskin/browser-sdk 1.0.34 → 1.0.36

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.
@@ -13425,15 +13425,20 @@
13425
13425
  record.mirror = mirror;
13426
13426
 
13427
13427
  class RRWebRecorder {
13428
- constructor(sessionId, config, onEventsReady) {
13428
+ constructor(sessionId, config, onEventsReady, sessionStartTime = 0) {
13429
13429
  this.stopFn = null;
13430
13430
  this.events = [];
13431
13431
  this.onEventsReady = null;
13432
13432
  this.flushInterval = null;
13433
13433
  this.hasFullSnapshot = false;
13434
+ this.sessionStartTime = 0; // Session start time (for relative timestamps)
13435
+ this.recordingStartTime = 0; // When this recorder instance started
13434
13436
  this.sessionId = sessionId;
13435
13437
  this.config = config;
13436
13438
  this.onEventsReady = onEventsReady;
13439
+ this.sessionStartTime = sessionStartTime || Date.now();
13440
+ this.recordingStartTime = Date.now();
13441
+ console.log(`[RRWeb] Recording initialized - session started at ${this.sessionStartTime}, recording started at ${this.recordingStartTime}`);
13437
13442
  }
13438
13443
  start() {
13439
13444
  if (!this.config.enabled) {
@@ -13447,6 +13452,13 @@
13447
13452
  try {
13448
13453
  this.stopFn = record({
13449
13454
  emit: (event) => {
13455
+ // Convert absolute timestamps to relative to session start
13456
+ // This ensures continuity across page navigations within same session
13457
+ const originalTimestamp = event.timestamp;
13458
+ event.timestamp = event.timestamp - this.sessionStartTime;
13459
+ if (this.config.enabled && event.type === 2) {
13460
+ console.log(`[RRWeb] FullSnapshot - original: ${originalTimestamp}, relative: ${event.timestamp}, session start: ${this.sessionStartTime}`);
13461
+ }
13450
13462
  this.events.push(event);
13451
13463
  // Check if this is a FullSnapshot (type 2)
13452
13464
  if (event.type === 2) {
@@ -13932,6 +13944,7 @@
13932
13944
  // Initialize session recording with rrweb
13933
13945
  if ((_a = this.config.sessionRecording) === null || _a === void 0 ? void 0 : _a.enabled) {
13934
13946
  // Use RRWebRecorder for complete DOM recording
13947
+ // Pass sessionStartTime to ensure timestamp continuity across page navigations
13935
13948
  this.rrwebRecorder = new RRWebRecorder(this.sessionId, {
13936
13949
  enabled: true,
13937
13950
  sampleRate: this.config.sessionRecording.sampling || 0.5,
@@ -13954,7 +13967,8 @@
13954
13967
  var _a;
13955
13968
  // Send rrweb events to backend
13956
13969
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.sendRecordingEvents(this.sessionId, events);
13957
- });
13970
+ }, this.sessionStartTime // Pass session start time for timestamp continuity
13971
+ );
13958
13972
  // Start recording immediately (session already created)
13959
13973
  this.rrwebRecorder.start();
13960
13974
  if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.debug) {
@@ -14180,6 +14194,7 @@
14180
14194
  }
14181
14195
  setupUnloadTracking() {
14182
14196
  // CRITICAL: Flush data BEFORE page unloads to avoid losing final events
14197
+ // IMPORTANT: NEVER clear sessionStorage - it expires naturally when tab closes
14183
14198
  // 1. visibilitychange - fires when tab is hidden (most reliable)
14184
14199
  document.addEventListener('visibilitychange', () => {
14185
14200
  var _a, _b;
@@ -14191,30 +14206,23 @@
14191
14206
  }
14192
14207
  });
14193
14208
  // 2. pagehide - fires when page is being unloaded
14194
- window.addEventListener('pagehide', (event) => {
14209
+ window.addEventListener('pagehide', () => {
14195
14210
  var _a, _b;
14196
- const isActualClose = !event.persisted;
14197
- if (isActualClose) {
14198
- // Tab is closing - end session
14199
- this.track('page_unload');
14200
- this.updateSessionDuration(true); // true = session ending
14201
- // Clear session storage since session is ending
14202
- sessionStorage.removeItem('devskin_session_id');
14203
- sessionStorage.removeItem('devskin_session_start');
14204
- }
14205
- else {
14206
- // Navigation - track page change
14207
- this.track('page_navigation');
14208
- this.updateSessionDuration();
14209
- }
14210
- // ALWAYS flush (whether navigation or close)
14211
+ // Track navigation (we can't distinguish between page navigation and tab close reliably)
14212
+ this.track('page_navigation');
14213
+ // Update duration but DON'T mark as ending (let heartbeat timeout handle session expiry)
14214
+ this.updateSessionDuration(false);
14215
+ // NEVER clear sessionStorage - it persists across navigations in same tab
14216
+ // and expires automatically when tab actually closes
14217
+ // Flush data before page unloads
14211
14218
  (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop(); // Stop recording and flush remaining events
14212
14219
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
14213
14220
  });
14214
14221
  // 3. beforeunload - backup for older browsers
14215
14222
  window.addEventListener('beforeunload', () => {
14216
14223
  var _a, _b;
14217
- this.updateSessionDuration(true);
14224
+ // Update duration but DON'T mark as ending
14225
+ this.updateSessionDuration(false);
14218
14226
  (_a = this.rrwebRecorder) === null || _a === void 0 ? void 0 : _a.stop();
14219
14227
  (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true);
14220
14228
  });