@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.
@@ -4983,6 +4983,7 @@ class RRWebRecorder {
4983
4983
  this.events = [];
4984
4984
  this.onEventsReady = null;
4985
4985
  this.flushInterval = null;
4986
+ this.hasFullSnapshot = false;
4986
4987
  this.sessionId = sessionId;
4987
4988
  this.config = config;
4988
4989
  this.onEventsReady = onEventsReady;
@@ -5000,9 +5001,15 @@ class RRWebRecorder {
5000
5001
  console.log('[RRWeb] Starting session recording:', this.sessionId);
5001
5002
  this.stopFn = record({
5002
5003
  emit: (event) => {
5004
+ // Check if this is a FullSnapshot (type 2)
5005
+ if (event.type === 2) {
5006
+ this.hasFullSnapshot = true;
5007
+ console.log('[RRWeb] FullSnapshot captured! Recording is ready.');
5008
+ }
5003
5009
  this.events.push(event);
5004
- // Flush events periodically to avoid memory buildup
5005
- if (this.events.length >= 50) {
5010
+ // Only flush if we have the FullSnapshot
5011
+ // This ensures the first batch always contains the full DOM
5012
+ if (this.hasFullSnapshot && this.events.length >= 50) {
5006
5013
  this.flush();
5007
5014
  }
5008
5015
  },
@@ -5042,8 +5049,9 @@ class RRWebRecorder {
5042
5049
  recordCrossOriginIframes: false, // Security: don't record cross-origin iframes
5043
5050
  });
5044
5051
  // Set up periodic flush (every 10 seconds)
5052
+ // Only flush if we have FullSnapshot to ensure first batch is complete
5045
5053
  this.flushInterval = window.setInterval(() => {
5046
- if (this.events.length > 0) {
5054
+ if (this.hasFullSnapshot && this.events.length > 0) {
5047
5055
  this.flush();
5048
5056
  }
5049
5057
  }, 10000);
@@ -5063,8 +5071,11 @@ class RRWebRecorder {
5063
5071
  clearInterval(this.flushInterval);
5064
5072
  this.flushInterval = null;
5065
5073
  }
5066
- // Flush remaining events
5074
+ // Flush remaining events (even without FullSnapshot, to not lose data)
5067
5075
  if (this.events.length > 0) {
5076
+ if (!this.hasFullSnapshot) {
5077
+ console.warn('[RRWeb] Flushing events without FullSnapshot - recording may not replay correctly');
5078
+ }
5068
5079
  this.flush();
5069
5080
  }
5070
5081
  }
@@ -5129,6 +5140,13 @@ class Transport {
5129
5140
  this.enqueue('performance', metric);
5130
5141
  }
5131
5142
  sendRecordingEvents(sessionId, events) {
5143
+ // DEBUG: Log event types being sent
5144
+ const eventTypes = events.reduce((acc, e) => {
5145
+ acc[e.type] = (acc[e.type] || 0) + 1;
5146
+ return acc;
5147
+ }, {});
5148
+ console.log(`[DevSkin SDK] Sending ${events.length} recording events:`, eventTypes);
5149
+ console.log(`[DevSkin SDK] First 3 events:`, events.slice(0, 3).map(e => ({ type: e.type, timestamp: e.timestamp })));
5132
5150
  // Recording events can be large, send immediately
5133
5151
  this.sendToBackend('/v1/rum/recordings', {
5134
5152
  session_id: sessionId,
@@ -5258,6 +5276,7 @@ class DevSkinSDK {
5258
5276
  this.sessionId = null;
5259
5277
  this.userId = null;
5260
5278
  this.anonymousId = null;
5279
+ this.sessionStartTime = 0;
5261
5280
  this.initialized = false;
5262
5281
  // Collectors
5263
5282
  this.deviceCollector = null;
@@ -5287,12 +5306,12 @@ class DevSkinSDK {
5287
5306
  this.transport = new Transport(this.config);
5288
5307
  // Generate anonymous ID if not exists
5289
5308
  this.anonymousId = this.getOrCreateAnonymousId();
5290
- // Start session
5291
- this.startSession();
5292
- // Initialize collectors
5309
+ // Initialize collectors BEFORE starting session (so getContextData works)
5293
5310
  this.deviceCollector = new DeviceCollector(this.config);
5294
5311
  this.locationCollector = new LocationCollector(this.config);
5295
5312
  this.browserCollector = new BrowserCollector(this.config);
5313
+ // Start session (will now include device/browser/location data)
5314
+ this.startSession();
5296
5315
  if (this.config.captureWebVitals) {
5297
5316
  this.performanceCollector = new PerformanceCollector(this.config, this.transport);
5298
5317
  this.performanceCollector.start();
@@ -5452,6 +5471,7 @@ class DevSkinSDK {
5452
5471
  startSession() {
5453
5472
  var _a;
5454
5473
  this.sessionId = this.generateId();
5474
+ this.sessionStartTime = Date.now();
5455
5475
  const sessionData = Object.assign({ session_id: this.sessionId, user_id: this.userId || undefined, anonymous_id: this.anonymousId, started_at: new Date().toISOString() }, this.getContextData());
5456
5476
  (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession(sessionData);
5457
5477
  }
@@ -5491,10 +5511,20 @@ class DevSkinSDK {
5491
5511
  }
5492
5512
  setupUnloadTracking() {
5493
5513
  window.addEventListener('beforeunload', () => {
5494
- var _a;
5514
+ var _a, _b;
5495
5515
  this.track('page_unload');
5516
+ // Send session end update with duration
5517
+ if (this.sessionId && this.sessionStartTime) {
5518
+ const endedAt = new Date();
5519
+ const durationMs = Date.now() - this.sessionStartTime;
5520
+ (_a = this.transport) === null || _a === void 0 ? void 0 : _a.startSession({
5521
+ session_id: this.sessionId,
5522
+ ended_at: endedAt.toISOString(),
5523
+ duration_ms: durationMs,
5524
+ });
5525
+ }
5496
5526
  // Send any pending data
5497
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.flush();
5527
+ (_b = this.transport) === null || _b === void 0 ? void 0 : _b.flush(true); // Use beacon for reliability
5498
5528
  });
5499
5529
  }
5500
5530
  }