@dataxamas/tracker-js 2.7.0 → 2.8.0

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 2.8.0
2
+
3
+ - Session replay records at most 30 seconds per visit, counted from the visitor's first gesture instead of page
4
+ load. After the cap the visit is not recorded again; a new recording only starts after 10 minutes without
5
+ interaction.
6
+ - The 1-minute idle pause is gone: the 30-second cap already ends the recording first.
7
+
1
8
  ## 2.7.0
2
9
 
3
10
  - Web Vitals also collect FCP (First Contentful Paint) and TTFB (Time to First Byte), sent in the same batch as
package/dist/index.cjs CHANGED
@@ -315,11 +315,10 @@ var DataxamasLogs = class {
315
315
  };
316
316
 
317
317
  // ../tracker-shared/src/constants.ts
318
- var MAX_RECORDING_MS = 10 * 60 * 1e3;
318
+ var MAX_RECORDING_MS = 30 * 1e3;
319
319
  var MAX_EVENTS_PER_RECORDING = 15e3;
320
320
  var MAX_BYTES_PER_RECORDING = 5 * 1024 * 1024;
321
321
  var REPLAY_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
322
- var REPLAY_IDLE_SUSPEND_MS = 60 * 1e3;
323
322
 
324
323
  // src/tracking.ts
325
324
  var QUEUE_DELAY_MS = 4e3;
@@ -339,19 +338,19 @@ var DataxamasTracking = class {
339
338
  constructor(options) {
340
339
  /**
341
340
  * Primeiro gesto liga o gravador; os seguintes só o religam se ele tiver parado (aba que foi
342
- * para segundo plano, reload), no mesmo `recordingId`. Depois do teto ou de muito tempo parado,
343
- * o gesto abre uma gravação nova.
341
+ * para segundo plano, reload), no mesmo `recordingId`. Depois do teto a visita não grava mais;
342
+ * só um gesto depois de muito tempo parado abre uma gravação nova.
344
343
  */
345
344
  this.markReplayActivity = () => {
346
345
  if (this.replayHalted || this.cutoffPending) return;
347
346
  if (document.visibilityState === "hidden") return;
348
347
  const now = Date.now();
349
- if (this.replayCapped || now - this.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
348
+ if (now - this.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
350
349
  if (this.isRecording()) this.stopReplayRecorder(false);
351
350
  this.rotateRecording();
352
351
  }
353
352
  this.touchActivity(now);
354
- if (this.isRecording()) return;
353
+ if (this.replayCapped || this.isRecording()) return;
355
354
  this.startReplayRecorder();
356
355
  };
357
356
  if (!options.apiKey) {
@@ -634,14 +633,17 @@ var DataxamasTracking = class {
634
633
  this.lastActivityAt = Date.now();
635
634
  this.saveRecording();
636
635
  }
637
- /** Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa. */
636
+ /**
637
+ * Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa.
638
+ * Se ela já bateu o teto, segue sem gravar.
639
+ */
638
640
  resumeOrRotateRecording() {
639
641
  const stored = this.loadRecording();
640
- if (!stored || stored.capped || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
642
+ if (!stored || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
641
643
  this.rotateRecording();
642
644
  return;
643
645
  }
644
- this.replayCapped = false;
646
+ this.replayCapped = stored.capped;
645
647
  this.recordingId = stored.id;
646
648
  this.recordingStartedAt = stored.startedAt;
647
649
  this.recordingStartedAtMs = stored.startedAtMs;
@@ -727,8 +729,8 @@ var DataxamasTracking = class {
727
729
  }
728
730
  }
729
731
  /**
730
- * Bateu o teto: encerra a gravação e fica parado até a próxima interação, em vez de emendar um
731
- * clipe atrás do outro enquanto a página só anima sozinha.
732
+ * Bateu o teto: encerra a gravação e a visita não grava mais, em vez de emendar um clipe atrás do
733
+ * outro.
732
734
  */
733
735
  scheduleReplayCutoff() {
734
736
  if (this.cutoffPending) return;
@@ -751,11 +753,7 @@ var DataxamasTracking = class {
751
753
  }, REPLAY_FLUSH_INTERVAL_MS);
752
754
  setInterval(() => {
753
755
  if (!this.isRecording()) return;
754
- if (this.exceededReplayCaps()) {
755
- this.scheduleReplayCutoff();
756
- return;
757
- }
758
- if (Date.now() - this.lastActivityAt >= REPLAY_IDLE_SUSPEND_MS) this.stopReplayRecorder(false);
756
+ if (this.exceededReplayCaps()) this.scheduleReplayCutoff();
759
757
  }, CAP_CHECK_MS);
760
758
  window.addEventListener("pagehide", () => this.stopReplayRecorder(true));
761
759
  document.addEventListener("visibilitychange", () => {
@@ -777,9 +775,17 @@ var DataxamasTracking = class {
777
775
  }
778
776
  if (this.isRecording() || this.replayHalted) return;
779
777
  if (!this.recordingId) this.rotateRecording();
778
+ if (this.recordingSeq === 0) {
779
+ this.recordingStartedAt = (/* @__PURE__ */ new Date()).toISOString();
780
+ this.recordingStartedAtMs = Date.now();
781
+ }
780
782
  this.snapshotFlushed = false;
781
783
  this.stopRrweb = this.rrwebRecord({
782
784
  emit: (event) => {
785
+ if (this.exceededReplayCaps()) {
786
+ this.scheduleReplayCutoff();
787
+ return;
788
+ }
783
789
  this.replayBuffer.push(event);
784
790
  this.recordingEvents++;
785
791
  if (!this.snapshotFlushed && event.type === 2) {
@@ -787,10 +793,6 @@ var DataxamasTracking = class {
787
793
  this.flushReplay(false);
788
794
  return;
789
795
  }
790
- if (this.exceededReplayCaps()) {
791
- this.scheduleReplayCutoff();
792
- return;
793
- }
794
796
  if (this.replayBuffer.length >= REPLAY_FLUSH_SIZE) this.flushReplay(false);
795
797
  },
796
798
  maskAllInputs: true,
package/dist/index.d.cts CHANGED
@@ -158,7 +158,10 @@ declare class DataxamasTracking {
158
158
  private loadRecording;
159
159
  private saveRecording;
160
160
  private rotateRecording;
161
- /** Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa. */
161
+ /**
162
+ * Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa.
163
+ * Se ela já bateu o teto, segue sem gravar.
164
+ */
162
165
  private resumeOrRotateRecording;
163
166
  private touchActivity;
164
167
  private flushReplay;
@@ -170,14 +173,14 @@ declare class DataxamasTracking {
170
173
  private sendReplayBatch;
171
174
  private stopReplayRecorder;
172
175
  /**
173
- * Bateu o teto: encerra a gravação e fica parado até a próxima interação, em vez de emendar um
174
- * clipe atrás do outro enquanto a página só anima sozinha.
176
+ * Bateu o teto: encerra a gravação e a visita não grava mais, em vez de emendar um clipe atrás do
177
+ * outro.
175
178
  */
176
179
  private scheduleReplayCutoff;
177
180
  /**
178
181
  * Primeiro gesto liga o gravador; os seguintes só o religam se ele tiver parado (aba que foi
179
- * para segundo plano, reload), no mesmo `recordingId`. Depois do teto ou de muito tempo parado,
180
- * o gesto abre uma gravação nova.
182
+ * para segundo plano, reload), no mesmo `recordingId`. Depois do teto a visita não grava mais;
183
+ * só um gesto depois de muito tempo parado abre uma gravação nova.
181
184
  */
182
185
  private markReplayActivity;
183
186
  private setupReplayLifecycle;
package/dist/index.d.ts CHANGED
@@ -158,7 +158,10 @@ declare class DataxamasTracking {
158
158
  private loadRecording;
159
159
  private saveRecording;
160
160
  private rotateRecording;
161
- /** Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa. */
161
+ /**
162
+ * Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa.
163
+ * Se ela já bateu o teto, segue sem gravar.
164
+ */
162
165
  private resumeOrRotateRecording;
163
166
  private touchActivity;
164
167
  private flushReplay;
@@ -170,14 +173,14 @@ declare class DataxamasTracking {
170
173
  private sendReplayBatch;
171
174
  private stopReplayRecorder;
172
175
  /**
173
- * Bateu o teto: encerra a gravação e fica parado até a próxima interação, em vez de emendar um
174
- * clipe atrás do outro enquanto a página só anima sozinha.
176
+ * Bateu o teto: encerra a gravação e a visita não grava mais, em vez de emendar um clipe atrás do
177
+ * outro.
175
178
  */
176
179
  private scheduleReplayCutoff;
177
180
  /**
178
181
  * Primeiro gesto liga o gravador; os seguintes só o religam se ele tiver parado (aba que foi
179
- * para segundo plano, reload), no mesmo `recordingId`. Depois do teto ou de muito tempo parado,
180
- * o gesto abre uma gravação nova.
182
+ * para segundo plano, reload), no mesmo `recordingId`. Depois do teto a visita não grava mais;
183
+ * só um gesto depois de muito tempo parado abre uma gravação nova.
181
184
  */
182
185
  private markReplayActivity;
183
186
  private setupReplayLifecycle;
package/dist/index.js CHANGED
@@ -315,11 +315,10 @@ var DataxamasLogs = class {
315
315
  };
316
316
 
317
317
  // ../tracker-shared/src/constants.ts
318
- var MAX_RECORDING_MS = 10 * 60 * 1e3;
318
+ var MAX_RECORDING_MS = 30 * 1e3;
319
319
  var MAX_EVENTS_PER_RECORDING = 15e3;
320
320
  var MAX_BYTES_PER_RECORDING = 5 * 1024 * 1024;
321
321
  var REPLAY_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
322
- var REPLAY_IDLE_SUSPEND_MS = 60 * 1e3;
323
322
 
324
323
  // src/tracking.ts
325
324
  var QUEUE_DELAY_MS = 4e3;
@@ -339,19 +338,19 @@ var DataxamasTracking = class {
339
338
  constructor(options) {
340
339
  /**
341
340
  * Primeiro gesto liga o gravador; os seguintes só o religam se ele tiver parado (aba que foi
342
- * para segundo plano, reload), no mesmo `recordingId`. Depois do teto ou de muito tempo parado,
343
- * o gesto abre uma gravação nova.
341
+ * para segundo plano, reload), no mesmo `recordingId`. Depois do teto a visita não grava mais;
342
+ * só um gesto depois de muito tempo parado abre uma gravação nova.
344
343
  */
345
344
  this.markReplayActivity = () => {
346
345
  if (this.replayHalted || this.cutoffPending) return;
347
346
  if (document.visibilityState === "hidden") return;
348
347
  const now = Date.now();
349
- if (this.replayCapped || now - this.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
348
+ if (now - this.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
350
349
  if (this.isRecording()) this.stopReplayRecorder(false);
351
350
  this.rotateRecording();
352
351
  }
353
352
  this.touchActivity(now);
354
- if (this.isRecording()) return;
353
+ if (this.replayCapped || this.isRecording()) return;
355
354
  this.startReplayRecorder();
356
355
  };
357
356
  if (!options.apiKey) {
@@ -634,14 +633,17 @@ var DataxamasTracking = class {
634
633
  this.lastActivityAt = Date.now();
635
634
  this.saveRecording();
636
635
  }
637
- /** Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa. */
636
+ /**
637
+ * Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa.
638
+ * Se ela já bateu o teto, segue sem gravar.
639
+ */
638
640
  resumeOrRotateRecording() {
639
641
  const stored = this.loadRecording();
640
- if (!stored || stored.capped || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
642
+ if (!stored || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
641
643
  this.rotateRecording();
642
644
  return;
643
645
  }
644
- this.replayCapped = false;
646
+ this.replayCapped = stored.capped;
645
647
  this.recordingId = stored.id;
646
648
  this.recordingStartedAt = stored.startedAt;
647
649
  this.recordingStartedAtMs = stored.startedAtMs;
@@ -727,8 +729,8 @@ var DataxamasTracking = class {
727
729
  }
728
730
  }
729
731
  /**
730
- * Bateu o teto: encerra a gravação e fica parado até a próxima interação, em vez de emendar um
731
- * clipe atrás do outro enquanto a página só anima sozinha.
732
+ * Bateu o teto: encerra a gravação e a visita não grava mais, em vez de emendar um clipe atrás do
733
+ * outro.
732
734
  */
733
735
  scheduleReplayCutoff() {
734
736
  if (this.cutoffPending) return;
@@ -751,11 +753,7 @@ var DataxamasTracking = class {
751
753
  }, REPLAY_FLUSH_INTERVAL_MS);
752
754
  setInterval(() => {
753
755
  if (!this.isRecording()) return;
754
- if (this.exceededReplayCaps()) {
755
- this.scheduleReplayCutoff();
756
- return;
757
- }
758
- if (Date.now() - this.lastActivityAt >= REPLAY_IDLE_SUSPEND_MS) this.stopReplayRecorder(false);
756
+ if (this.exceededReplayCaps()) this.scheduleReplayCutoff();
759
757
  }, CAP_CHECK_MS);
760
758
  window.addEventListener("pagehide", () => this.stopReplayRecorder(true));
761
759
  document.addEventListener("visibilitychange", () => {
@@ -777,9 +775,17 @@ var DataxamasTracking = class {
777
775
  }
778
776
  if (this.isRecording() || this.replayHalted) return;
779
777
  if (!this.recordingId) this.rotateRecording();
778
+ if (this.recordingSeq === 0) {
779
+ this.recordingStartedAt = (/* @__PURE__ */ new Date()).toISOString();
780
+ this.recordingStartedAtMs = Date.now();
781
+ }
780
782
  this.snapshotFlushed = false;
781
783
  this.stopRrweb = this.rrwebRecord({
782
784
  emit: (event) => {
785
+ if (this.exceededReplayCaps()) {
786
+ this.scheduleReplayCutoff();
787
+ return;
788
+ }
783
789
  this.replayBuffer.push(event);
784
790
  this.recordingEvents++;
785
791
  if (!this.snapshotFlushed && event.type === 2) {
@@ -787,10 +793,6 @@ var DataxamasTracking = class {
787
793
  this.flushReplay(false);
788
794
  return;
789
795
  }
790
- if (this.exceededReplayCaps()) {
791
- this.scheduleReplayCutoff();
792
- return;
793
- }
794
796
  if (this.replayBuffer.length >= REPLAY_FLUSH_SIZE) this.flushReplay(false);
795
797
  },
796
798
  maskAllInputs: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataxamas/tracker-js",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "type": "module",
5
5
  "description": "Node.js SDK for Dataxamas Analytics API",
6
6
  "license": "MIT",