@devskin/browser-sdk 1.0.3 → 1.0.5

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.
@@ -4987,6 +4987,7 @@ class RRWebRecorder {
4987
4987
  this.events = [];
4988
4988
  this.onEventsReady = null;
4989
4989
  this.flushInterval = null;
4990
+ this.hasFullSnapshot = false;
4990
4991
  this.sessionId = sessionId;
4991
4992
  this.config = config;
4992
4993
  this.onEventsReady = onEventsReady;
@@ -5004,9 +5005,15 @@ class RRWebRecorder {
5004
5005
  console.log('[RRWeb] Starting session recording:', this.sessionId);
5005
5006
  this.stopFn = record({
5006
5007
  emit: (event) => {
5008
+ // Check if this is a FullSnapshot (type 2)
5009
+ if (event.type === 2) {
5010
+ this.hasFullSnapshot = true;
5011
+ console.log('[RRWeb] FullSnapshot captured! Recording is ready.');
5012
+ }
5007
5013
  this.events.push(event);
5008
- // Flush events periodically to avoid memory buildup
5009
- if (this.events.length >= 50) {
5014
+ // Only flush if we have the FullSnapshot
5015
+ // This ensures the first batch always contains the full DOM
5016
+ if (this.hasFullSnapshot && this.events.length >= 50) {
5010
5017
  this.flush();
5011
5018
  }
5012
5019
  },
@@ -5046,8 +5053,9 @@ class RRWebRecorder {
5046
5053
  recordCrossOriginIframes: false, // Security: don't record cross-origin iframes
5047
5054
  });
5048
5055
  // Set up periodic flush (every 10 seconds)
5056
+ // Only flush if we have FullSnapshot to ensure first batch is complete
5049
5057
  this.flushInterval = window.setInterval(() => {
5050
- if (this.events.length > 0) {
5058
+ if (this.hasFullSnapshot && this.events.length > 0) {
5051
5059
  this.flush();
5052
5060
  }
5053
5061
  }, 10000);
@@ -5067,8 +5075,11 @@ class RRWebRecorder {
5067
5075
  clearInterval(this.flushInterval);
5068
5076
  this.flushInterval = null;
5069
5077
  }
5070
- // Flush remaining events
5078
+ // Flush remaining events (even without FullSnapshot, to not lose data)
5071
5079
  if (this.events.length > 0) {
5080
+ if (!this.hasFullSnapshot) {
5081
+ console.warn('[RRWeb] Flushing events without FullSnapshot - recording may not replay correctly');
5082
+ }
5072
5083
  this.flush();
5073
5084
  }
5074
5085
  }
@@ -5133,6 +5144,13 @@ class Transport {
5133
5144
  this.enqueue('performance', metric);
5134
5145
  }
5135
5146
  sendRecordingEvents(sessionId, events) {
5147
+ // DEBUG: Log event types being sent
5148
+ const eventTypes = events.reduce((acc, e) => {
5149
+ acc[e.type] = (acc[e.type] || 0) + 1;
5150
+ return acc;
5151
+ }, {});
5152
+ console.log(`[DevSkin SDK] Sending ${events.length} recording events:`, eventTypes);
5153
+ console.log(`[DevSkin SDK] First 3 events:`, events.slice(0, 3).map(e => ({ type: e.type, timestamp: e.timestamp })));
5136
5154
  // Recording events can be large, send immediately
5137
5155
  this.sendToBackend('/v1/rum/recordings', {
5138
5156
  session_id: sessionId,
@@ -5262,6 +5280,7 @@ class DevSkinSDK {
5262
5280
  this.sessionId = null;
5263
5281
  this.userId = null;
5264
5282
  this.anonymousId = null;
5283
+ this.sessionStartTime = 0;
5265
5284
  this.initialized = false;
5266
5285
  // Collectors
5267
5286
  this.deviceCollector = null;
@@ -5291,12 +5310,12 @@ class DevSkinSDK {
5291
5310
  this.transport = new Transport(this.config);
5292
5311
  // Generate anonymous ID if not exists
5293
5312
  this.anonymousId = this.getOrCreateAnonymousId();
5294
- // Start session
5295
- this.startSession();
5296
- // Initialize collectors
5313
+ // Initialize collectors BEFORE starting session (so getContextData works)
5297
5314
  this.deviceCollector = new DeviceCollector(this.config);
5298
5315
  this.locationCollector = new LocationCollector(this.config);
5299
5316
  this.browserCollector = new BrowserCollector(this.config);
5317
+ // Start session (will now include device/browser/location data)
5318
+ this.startSession();
5300
5319
  if (this.config.captureWebVitals) {
5301
5320
  this.performanceCollector = new PerformanceCollector(this.config, this.transport);
5302
5321
  this.performanceCollector.start();
@@ -5456,6 +5475,7 @@ class DevSkinSDK {
5456
5475
  startSession() {
5457
5476
  var _a;
5458
5477
  this.sessionId = this.generateId();
5478
+ this.sessionStartTime = Date.now();
5459
5479
  const sessionData = Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, started_at: new Date().toISOString() }, this.getContextData());
5460
5480
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(sessionData);
5461
5481
  }
@@ -5495,10 +5515,20 @@ class DevSkinSDK {
5495
5515
  }
5496
5516
  setupUnloadTracking() {
5497
5517
  window.addEventListener('beforeunload', () => {
5498
- var _a;
5518
+ var _a, _b;
5499
5519
  this.track('page_unload');
5520
+ // Send session end update with duration
5521
+ if (this.sessionId && this.sessionStartTime) {
5522
+ const endedAt = new Date();
5523
+ const durationMs = Date.now() - this.sessionStartTime;
5524
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession({
5525
+ session_id: this.sessionId,
5526
+ ended_at: endedAt.toISOString(),
5527
+ duration_ms: durationMs,
5528
+ });
5529
+ }
5500
5530
  // Send any pending data
5501
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.flush();
5531
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
5502
5532
  });
5503
5533
  }
5504
5534
  }