@dataxamas/tracker-js 2.3.0 → 2.5.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/README.md CHANGED
@@ -201,10 +201,35 @@ Begin collecting. Waits for `DOMContentLoaded` if the document is still loading,
201
201
  - **`CLICK`** — element `tagName`, `text`, `href`, `id`, `classes`, plus heat-map coordinates (viewport ratios, scroll offsets, layout fingerprint) when `heatMapEnabled`.
202
202
  - **`SCROLL`** — scroll-depth percentage (debounced).
203
203
 
204
- Events are queued and flushed to `POST /api/v1/events` with a 4s spacing. Every request sends `Authorization: Bearer <apiKey>`. The key is visible to anyone who opens the page, and it can also read events through `GET /api/v1/events` — treat it as public and rotate it from the dashboard if abused. A visitor session id is generated once and stored in `localStorage` under `@dataxamas/session`.
204
+ Events are queued and flushed to `POST /api/v1/events` with a 4s spacing. Every request sends `Authorization: Bearer <apiKey>`. The key is visible to anyone who opens the page, and it can also read events through `GET /api/v1/events` — treat it as public and rotate it from the dashboard if abused. Nothing is stored in the browser to identify the visitor: the server derives the session id from `sha256(daily salt + IP + user agent + websiteId)`, and the salt is replaced every day. A second key with a monthly salt links a visitor's visits to the same site within the month, for retention cohorts.
205
205
 
206
206
  If `replayEnabled` is on and the server config allows it, session replay starts: `rrweb` is lazy-imported and recordings are sent to `POST /api/v1/replay`.
207
207
 
208
+ #### `track(name, data?)`
209
+
210
+ Sends a custom event. No-op outside the browser.
211
+
212
+ ```ts
213
+ tracking.track("signup", { plan: "pro" });
214
+ ```
215
+
216
+ #### `identify(userId)`
217
+
218
+ Ties the next events to a user of your system, so retention recognizes the same person across months, browsers and devices. No-op outside the browser.
219
+
220
+ ```ts
221
+ // After sign-in, on every page load
222
+ tracking.identify(user.id);
223
+
224
+ // On sign-out
225
+ tracking.identify(null);
226
+ ```
227
+
228
+ - The id travels with each event, but the server stores only `sha256(websiteId + userId)` — never the raw value.
229
+ - It lives in memory only: call it again on every page load.
230
+ - Use an opaque, stable id — never an email or a name.
231
+ - Script tag: `window.dataxamas.track(name, data)` and `window.dataxamas.identify(userId)`.
232
+
208
233
  **Behavior notes**
209
234
 
210
235
  - Throws `"DataxamasTracking: This SDK can only be used in a browser environment."` if called on the server.
@@ -250,7 +275,7 @@ Exported for use in your own typings:
250
275
  ## Notes
251
276
 
252
277
  - **Default endpoint:** `https://dataxamas.com`. Override with `baseUrl`.
253
- - **Session storage key:** `localStorage["@dataxamas/session"]`.
278
+ - **Visitor identity:** computed server-side, no cookies or `localStorage` — a daily key for the visit, a monthly key (or the `identify()` hash) for retention. Only replay touches the browser, in `sessionStorage["@dataxamas/replay-recording"]`, and only for sampled visitors.
254
279
  - **Event flush spacing:** 4 seconds between queued analytics events.
255
280
  - **Replay masking classes:** `dx-mask` (text) and `dx-block` (element); all inputs masked by default.
256
281
  - **Bundle:** `rrweb` is bundled but split into its own async chunk, loaded only when replay starts — the server/import path never touches it.
package/dist/index.cjs CHANGED
@@ -328,7 +328,7 @@ var REPLAY_RECORDING_KEY = `${STORAGE_PREFIX}/replay-recording`;
328
328
  function isStoredRecording(value) {
329
329
  if (typeof value !== "object" || value === null) return false;
330
330
  const stored = value;
331
- return typeof stored.id === "string" && typeof stored.sessionId === "string" && typeof stored.startedAt === "string" && typeof stored.startedAtMs === "number" && typeof stored.seq === "number" && typeof stored.events === "number" && typeof stored.lastActivityAt === "number" && typeof stored.capped === "boolean";
331
+ return typeof stored.id === "string" && typeof stored.startedAt === "string" && typeof stored.startedAtMs === "number" && typeof stored.seq === "number" && typeof stored.events === "number" && typeof stored.lastActivityAt === "number" && typeof stored.capped === "boolean";
332
332
  }
333
333
  var KEEPALIVE_LIMIT_BYTES = 60 * 1024;
334
334
  var DataxamasTracking = class {
@@ -356,14 +356,15 @@ var DataxamasTracking = class {
356
356
  this.apiKey = options.apiKey;
357
357
  this.baseUrl = _nullishCoalesce(options.baseUrl, () => ( BASE_URL));
358
358
  this.allowLocalhost = _nullishCoalesce(options.allowLocalhost, () => ( false));
359
- this.sessionId = null;
359
+ this.started = false;
360
360
  this.currentPage = isBrowser() ? window.location.pathname : "";
361
+ this.userId = null;
361
362
  this.heatMapEnabled = _nullishCoalesce(options.heatMapEnabled, () => ( true));
362
363
  this.replayEnabled = _nullishCoalesce(options.replayEnabled, () => ( true));
363
364
  this.webVitalsEnabled = _nullishCoalesce(options.webVitalsEnabled, () => ( true));
364
365
  this.pendingVitals = /* @__PURE__ */ new Map();
365
366
  this.vitalsPath = "";
366
- this.replaySampleRate = 1;
367
+ this.replaySampled = false;
367
368
  this.eventQueue = [];
368
369
  this.isProcessing = false;
369
370
  this.rrwebRecord = null;
@@ -386,21 +387,6 @@ var DataxamasTracking = class {
386
387
  isLocalhost() {
387
388
  return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
388
389
  }
389
- getSessionId() {
390
- try {
391
- return window.localStorage.getItem(`${STORAGE_PREFIX}/session`);
392
- } catch (_) {
393
- return null;
394
- }
395
- }
396
- createSessionId() {
397
- const id = crypto.randomUUID();
398
- try {
399
- window.localStorage.setItem(`${STORAGE_PREFIX}/session`, id);
400
- } catch (_) {
401
- }
402
- return id;
403
- }
404
390
  async processQueue() {
405
391
  if (this.isProcessing || this.eventQueue.length === 0) return;
406
392
  this.isProcessing = true;
@@ -436,14 +422,14 @@ var DataxamasTracking = class {
436
422
  path = this.currentPage,
437
423
  data = {}
438
424
  }) {
439
- if (!this.sessionId) return;
425
+ if (!this.started) return;
440
426
  this.eventQueue.push({
441
427
  name,
442
428
  path,
443
429
  referrer: document.referrer,
444
430
  query: name === "PAGE_VIEW" ? this.utmQuery() : void 0,
445
- sessionId: this.sessionId,
446
- data
431
+ data,
432
+ userId: _nullishCoalesce(this.userId, () => ( void 0))
447
433
  });
448
434
  await this.processQueue();
449
435
  }
@@ -468,9 +454,7 @@ var DataxamasTracking = class {
468
454
  const cfg = await res.json();
469
455
  if (typeof cfg.heatMapEnabled === "boolean") this.heatMapEnabled = cfg.heatMapEnabled;
470
456
  if (typeof cfg.replayEnabled === "boolean") this.replayEnabled = cfg.replayEnabled;
471
- if (typeof cfg.replaySampleRate === "number" && cfg.replaySampleRate >= 0 && cfg.replaySampleRate <= 1) {
472
- this.replaySampleRate = cfg.replaySampleRate;
473
- }
457
+ if (typeof cfg.replaySampled === "boolean") this.replaySampled = cfg.replaySampled;
474
458
  } catch (_) {
475
459
  }
476
460
  }
@@ -551,14 +535,13 @@ var DataxamasTracking = class {
551
535
  });
552
536
  }
553
537
  flushVitals() {
554
- if (!this.sessionId || this.pendingVitals.size === 0) return;
538
+ if (this.pendingVitals.size === 0) return;
555
539
  const metrics = Array.from(this.pendingVitals.values());
556
540
  this.pendingVitals.clear();
557
541
  window.fetch(`${this.baseUrl}/api/v1/vitals`, {
558
542
  method: "POST",
559
543
  headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
560
544
  body: JSON.stringify({
561
- sessionId: this.sessionId,
562
545
  path: this.vitalsPath,
563
546
  viewportWidth: window.innerWidth,
564
547
  metrics
@@ -602,27 +585,6 @@ var DataxamasTracking = class {
602
585
  const layoutFingerprint = `${this.currentPage}:${Math.round(pageWidth / 100)}x${Math.round(pageHeight / 100)}`;
603
586
  return { viewportWidth, viewportHeight, pageWidth, pageHeight, layoutFingerprint };
604
587
  }
605
- /**
606
- * Sorteio por visitante, não por gravação: quem entrou no sorteio tem a sessão inteira, em vez
607
- * de um replay picotado que não dá para assistir.
608
- */
609
- isReplaySampled() {
610
- const rate = this.replaySampleRate;
611
- if (rate >= 1) return true;
612
- if (rate <= 0) return false;
613
- const key = `${STORAGE_PREFIX}/replay-sample`;
614
- try {
615
- const [storedRate, storedDecision] = (_nullishCoalesce(window.localStorage.getItem(key), () => ( ""))).split(":");
616
- if (Number(storedRate) === rate && (storedDecision === "1" || storedDecision === "0")) {
617
- return storedDecision === "1";
618
- }
619
- const sampled = Math.random() < rate;
620
- window.localStorage.setItem(key, `${rate}:${sampled ? "1" : "0"}`);
621
- return sampled;
622
- } catch (_) {
623
- return Math.random() < rate;
624
- }
625
- }
626
588
  isRecording() {
627
589
  return Boolean(this.stopRrweb);
628
590
  }
@@ -638,10 +600,9 @@ var DataxamasTracking = class {
638
600
  }
639
601
  }
640
602
  saveRecording() {
641
- if (!this.sessionId || !this.recordingId || !this.recordingStartedAt) return;
603
+ if (!this.recordingId || !this.recordingStartedAt) return;
642
604
  const stored = {
643
605
  id: this.recordingId,
644
- sessionId: this.sessionId,
645
606
  startedAt: this.recordingStartedAt,
646
607
  startedAtMs: this.recordingStartedAtMs,
647
608
  seq: this.recordingSeq,
@@ -667,7 +628,7 @@ var DataxamasTracking = class {
667
628
  /** Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa. */
668
629
  resumeOrRotateRecording() {
669
630
  const stored = this.loadRecording();
670
- if (!stored || stored.capped || stored.sessionId !== this.sessionId || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
631
+ if (!stored || stored.capped || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
671
632
  this.rotateRecording();
672
633
  return;
673
634
  }
@@ -690,7 +651,6 @@ var DataxamasTracking = class {
690
651
  const events = this.replayBuffer;
691
652
  this.replayBuffer = [];
692
653
  const payload = JSON.stringify({
693
- sessionId: this.sessionId,
694
654
  recordingId: this.recordingId,
695
655
  path: this.currentPage,
696
656
  startedAt: this.recordingStartedAt,
@@ -839,15 +799,15 @@ var DataxamasTracking = class {
839
799
  });
840
800
  }
841
801
  startTracking() {
842
- this.sessionId = _nullishCoalesce(this.getSessionId(), () => ( this.createSessionId()));
843
- this.resumeOrRotateRecording();
802
+ this.started = true;
844
803
  this.sendEvent({ name: "PAGE_VIEW" });
845
804
  this.trackPageChanges();
846
805
  this.trackClicks();
847
806
  this.trackScroll();
848
807
  this.trackWebVitals();
849
808
  this.fetchWebsiteConfig().then(() => {
850
- if (!this.replayEnabled || !this.isReplaySampled()) return;
809
+ if (!this.replayEnabled || !this.replaySampled) return;
810
+ this.resumeOrRotateRecording();
851
811
  this.setupReplayLifecycle();
852
812
  });
853
813
  }
@@ -856,6 +816,14 @@ var DataxamasTracking = class {
856
816
  if (!isBrowser()) return;
857
817
  void this.sendEvent({ name, data });
858
818
  }
819
+ /**
820
+ * Liga os próximos eventos ao usuário logado no site (o id do seu sistema), para a retenção reconhecer quem volta
821
+ * em outro navegador ou depois da virada do mês. O servidor guarda só um hash. `null` desfaz, no logout.
822
+ */
823
+ identify(userId) {
824
+ if (!isBrowser()) return;
825
+ this.userId = _optionalChain([userId, 'optionalAccess', _29 => _29.trim, 'call', _30 => _30()]) || null;
826
+ }
859
827
  start() {
860
828
  if (!isBrowser()) {
861
829
  throw new Error("DataxamasTracking: This SDK can only be used in a browser environment.");
package/dist/index.d.cts CHANGED
@@ -109,14 +109,15 @@ declare class DataxamasTracking {
109
109
  private apiKey;
110
110
  private baseUrl;
111
111
  private allowLocalhost;
112
- private sessionId;
112
+ private started;
113
113
  private currentPage;
114
+ private userId;
114
115
  private heatMapEnabled;
115
116
  private replayEnabled;
116
117
  private webVitalsEnabled;
117
118
  private pendingVitals;
118
119
  private vitalsPath;
119
- private replaySampleRate;
120
+ private replaySampled;
120
121
  private eventQueue;
121
122
  private isProcessing;
122
123
  private rrwebRecord;
@@ -137,8 +138,6 @@ declare class DataxamasTracking {
137
138
  private lastActivitySavedAt;
138
139
  constructor(options: DataxamasTrackingOptions);
139
140
  private isLocalhost;
140
- private getSessionId;
141
- private createSessionId;
142
141
  private processQueue;
143
142
  private utmQuery;
144
143
  private sendEvent;
@@ -153,11 +152,6 @@ declare class DataxamasTracking {
153
152
  private flushVitals;
154
153
  private trackWebVitals;
155
154
  private measureReplay;
156
- /**
157
- * Sorteio por visitante, não por gravação: quem entrou no sorteio tem a sessão inteira, em vez
158
- * de um replay picotado que não dá para assistir.
159
- */
160
- private isReplaySampled;
161
155
  private isRecording;
162
156
  private exceededReplayCaps;
163
157
  private loadRecording;
@@ -190,6 +184,11 @@ declare class DataxamasTracking {
190
184
  private startTracking;
191
185
  /** Evento customizado (ex.: `signup_done`), usado em metas e funis. Antes do `start()` não envia nada. */
192
186
  track(name: string, data?: Record<string, unknown>): void;
187
+ /**
188
+ * Liga os próximos eventos ao usuário logado no site (o id do seu sistema), para a retenção reconhecer quem volta
189
+ * em outro navegador ou depois da virada do mês. O servidor guarda só um hash. `null` desfaz, no logout.
190
+ */
191
+ identify(userId: string | null): void;
193
192
  start(): void;
194
193
  }
195
194
 
package/dist/index.d.ts CHANGED
@@ -109,14 +109,15 @@ declare class DataxamasTracking {
109
109
  private apiKey;
110
110
  private baseUrl;
111
111
  private allowLocalhost;
112
- private sessionId;
112
+ private started;
113
113
  private currentPage;
114
+ private userId;
114
115
  private heatMapEnabled;
115
116
  private replayEnabled;
116
117
  private webVitalsEnabled;
117
118
  private pendingVitals;
118
119
  private vitalsPath;
119
- private replaySampleRate;
120
+ private replaySampled;
120
121
  private eventQueue;
121
122
  private isProcessing;
122
123
  private rrwebRecord;
@@ -137,8 +138,6 @@ declare class DataxamasTracking {
137
138
  private lastActivitySavedAt;
138
139
  constructor(options: DataxamasTrackingOptions);
139
140
  private isLocalhost;
140
- private getSessionId;
141
- private createSessionId;
142
141
  private processQueue;
143
142
  private utmQuery;
144
143
  private sendEvent;
@@ -153,11 +152,6 @@ declare class DataxamasTracking {
153
152
  private flushVitals;
154
153
  private trackWebVitals;
155
154
  private measureReplay;
156
- /**
157
- * Sorteio por visitante, não por gravação: quem entrou no sorteio tem a sessão inteira, em vez
158
- * de um replay picotado que não dá para assistir.
159
- */
160
- private isReplaySampled;
161
155
  private isRecording;
162
156
  private exceededReplayCaps;
163
157
  private loadRecording;
@@ -190,6 +184,11 @@ declare class DataxamasTracking {
190
184
  private startTracking;
191
185
  /** Evento customizado (ex.: `signup_done`), usado em metas e funis. Antes do `start()` não envia nada. */
192
186
  track(name: string, data?: Record<string, unknown>): void;
187
+ /**
188
+ * Liga os próximos eventos ao usuário logado no site (o id do seu sistema), para a retenção reconhecer quem volta
189
+ * em outro navegador ou depois da virada do mês. O servidor guarda só um hash. `null` desfaz, no logout.
190
+ */
191
+ identify(userId: string | null): void;
193
192
  start(): void;
194
193
  }
195
194
 
package/dist/index.js CHANGED
@@ -328,7 +328,7 @@ var REPLAY_RECORDING_KEY = `${STORAGE_PREFIX}/replay-recording`;
328
328
  function isStoredRecording(value) {
329
329
  if (typeof value !== "object" || value === null) return false;
330
330
  const stored = value;
331
- return typeof stored.id === "string" && typeof stored.sessionId === "string" && typeof stored.startedAt === "string" && typeof stored.startedAtMs === "number" && typeof stored.seq === "number" && typeof stored.events === "number" && typeof stored.lastActivityAt === "number" && typeof stored.capped === "boolean";
331
+ return typeof stored.id === "string" && typeof stored.startedAt === "string" && typeof stored.startedAtMs === "number" && typeof stored.seq === "number" && typeof stored.events === "number" && typeof stored.lastActivityAt === "number" && typeof stored.capped === "boolean";
332
332
  }
333
333
  var KEEPALIVE_LIMIT_BYTES = 60 * 1024;
334
334
  var DataxamasTracking = class {
@@ -356,14 +356,15 @@ var DataxamasTracking = class {
356
356
  this.apiKey = options.apiKey;
357
357
  this.baseUrl = options.baseUrl ?? BASE_URL;
358
358
  this.allowLocalhost = options.allowLocalhost ?? false;
359
- this.sessionId = null;
359
+ this.started = false;
360
360
  this.currentPage = isBrowser() ? window.location.pathname : "";
361
+ this.userId = null;
361
362
  this.heatMapEnabled = options.heatMapEnabled ?? true;
362
363
  this.replayEnabled = options.replayEnabled ?? true;
363
364
  this.webVitalsEnabled = options.webVitalsEnabled ?? true;
364
365
  this.pendingVitals = /* @__PURE__ */ new Map();
365
366
  this.vitalsPath = "";
366
- this.replaySampleRate = 1;
367
+ this.replaySampled = false;
367
368
  this.eventQueue = [];
368
369
  this.isProcessing = false;
369
370
  this.rrwebRecord = null;
@@ -386,21 +387,6 @@ var DataxamasTracking = class {
386
387
  isLocalhost() {
387
388
  return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
388
389
  }
389
- getSessionId() {
390
- try {
391
- return window.localStorage.getItem(`${STORAGE_PREFIX}/session`);
392
- } catch (_) {
393
- return null;
394
- }
395
- }
396
- createSessionId() {
397
- const id = crypto.randomUUID();
398
- try {
399
- window.localStorage.setItem(`${STORAGE_PREFIX}/session`, id);
400
- } catch (_) {
401
- }
402
- return id;
403
- }
404
390
  async processQueue() {
405
391
  if (this.isProcessing || this.eventQueue.length === 0) return;
406
392
  this.isProcessing = true;
@@ -436,14 +422,14 @@ var DataxamasTracking = class {
436
422
  path = this.currentPage,
437
423
  data = {}
438
424
  }) {
439
- if (!this.sessionId) return;
425
+ if (!this.started) return;
440
426
  this.eventQueue.push({
441
427
  name,
442
428
  path,
443
429
  referrer: document.referrer,
444
430
  query: name === "PAGE_VIEW" ? this.utmQuery() : void 0,
445
- sessionId: this.sessionId,
446
- data
431
+ data,
432
+ userId: this.userId ?? void 0
447
433
  });
448
434
  await this.processQueue();
449
435
  }
@@ -468,9 +454,7 @@ var DataxamasTracking = class {
468
454
  const cfg = await res.json();
469
455
  if (typeof cfg.heatMapEnabled === "boolean") this.heatMapEnabled = cfg.heatMapEnabled;
470
456
  if (typeof cfg.replayEnabled === "boolean") this.replayEnabled = cfg.replayEnabled;
471
- if (typeof cfg.replaySampleRate === "number" && cfg.replaySampleRate >= 0 && cfg.replaySampleRate <= 1) {
472
- this.replaySampleRate = cfg.replaySampleRate;
473
- }
457
+ if (typeof cfg.replaySampled === "boolean") this.replaySampled = cfg.replaySampled;
474
458
  } catch (_) {
475
459
  }
476
460
  }
@@ -551,14 +535,13 @@ var DataxamasTracking = class {
551
535
  });
552
536
  }
553
537
  flushVitals() {
554
- if (!this.sessionId || this.pendingVitals.size === 0) return;
538
+ if (this.pendingVitals.size === 0) return;
555
539
  const metrics = Array.from(this.pendingVitals.values());
556
540
  this.pendingVitals.clear();
557
541
  window.fetch(`${this.baseUrl}/api/v1/vitals`, {
558
542
  method: "POST",
559
543
  headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
560
544
  body: JSON.stringify({
561
- sessionId: this.sessionId,
562
545
  path: this.vitalsPath,
563
546
  viewportWidth: window.innerWidth,
564
547
  metrics
@@ -602,27 +585,6 @@ var DataxamasTracking = class {
602
585
  const layoutFingerprint = `${this.currentPage}:${Math.round(pageWidth / 100)}x${Math.round(pageHeight / 100)}`;
603
586
  return { viewportWidth, viewportHeight, pageWidth, pageHeight, layoutFingerprint };
604
587
  }
605
- /**
606
- * Sorteio por visitante, não por gravação: quem entrou no sorteio tem a sessão inteira, em vez
607
- * de um replay picotado que não dá para assistir.
608
- */
609
- isReplaySampled() {
610
- const rate = this.replaySampleRate;
611
- if (rate >= 1) return true;
612
- if (rate <= 0) return false;
613
- const key = `${STORAGE_PREFIX}/replay-sample`;
614
- try {
615
- const [storedRate, storedDecision] = (window.localStorage.getItem(key) ?? "").split(":");
616
- if (Number(storedRate) === rate && (storedDecision === "1" || storedDecision === "0")) {
617
- return storedDecision === "1";
618
- }
619
- const sampled = Math.random() < rate;
620
- window.localStorage.setItem(key, `${rate}:${sampled ? "1" : "0"}`);
621
- return sampled;
622
- } catch (_) {
623
- return Math.random() < rate;
624
- }
625
- }
626
588
  isRecording() {
627
589
  return Boolean(this.stopRrweb);
628
590
  }
@@ -638,10 +600,9 @@ var DataxamasTracking = class {
638
600
  }
639
601
  }
640
602
  saveRecording() {
641
- if (!this.sessionId || !this.recordingId || !this.recordingStartedAt) return;
603
+ if (!this.recordingId || !this.recordingStartedAt) return;
642
604
  const stored = {
643
605
  id: this.recordingId,
644
- sessionId: this.sessionId,
645
606
  startedAt: this.recordingStartedAt,
646
607
  startedAtMs: this.recordingStartedAtMs,
647
608
  seq: this.recordingSeq,
@@ -667,7 +628,7 @@ var DataxamasTracking = class {
667
628
  /** Reload ou navegação completa na mesma aba continua a gravação, se a visita ainda estiver ativa. */
668
629
  resumeOrRotateRecording() {
669
630
  const stored = this.loadRecording();
670
- if (!stored || stored.capped || stored.sessionId !== this.sessionId || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
631
+ if (!stored || stored.capped || Date.now() - stored.lastActivityAt >= REPLAY_IDLE_TIMEOUT_MS) {
671
632
  this.rotateRecording();
672
633
  return;
673
634
  }
@@ -690,7 +651,6 @@ var DataxamasTracking = class {
690
651
  const events = this.replayBuffer;
691
652
  this.replayBuffer = [];
692
653
  const payload = JSON.stringify({
693
- sessionId: this.sessionId,
694
654
  recordingId: this.recordingId,
695
655
  path: this.currentPage,
696
656
  startedAt: this.recordingStartedAt,
@@ -839,15 +799,15 @@ var DataxamasTracking = class {
839
799
  });
840
800
  }
841
801
  startTracking() {
842
- this.sessionId = this.getSessionId() ?? this.createSessionId();
843
- this.resumeOrRotateRecording();
802
+ this.started = true;
844
803
  this.sendEvent({ name: "PAGE_VIEW" });
845
804
  this.trackPageChanges();
846
805
  this.trackClicks();
847
806
  this.trackScroll();
848
807
  this.trackWebVitals();
849
808
  this.fetchWebsiteConfig().then(() => {
850
- if (!this.replayEnabled || !this.isReplaySampled()) return;
809
+ if (!this.replayEnabled || !this.replaySampled) return;
810
+ this.resumeOrRotateRecording();
851
811
  this.setupReplayLifecycle();
852
812
  });
853
813
  }
@@ -856,6 +816,14 @@ var DataxamasTracking = class {
856
816
  if (!isBrowser()) return;
857
817
  void this.sendEvent({ name, data });
858
818
  }
819
+ /**
820
+ * Liga os próximos eventos ao usuário logado no site (o id do seu sistema), para a retenção reconhecer quem volta
821
+ * em outro navegador ou depois da virada do mês. O servidor guarda só um hash. `null` desfaz, no logout.
822
+ */
823
+ identify(userId) {
824
+ if (!isBrowser()) return;
825
+ this.userId = userId?.trim() || null;
826
+ }
859
827
  start() {
860
828
  if (!isBrowser()) {
861
829
  throw new Error("DataxamasTracking: This SDK can only be used in a browser environment.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataxamas/tracker-js",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "description": "Node.js SDK for Dataxamas Analytics API",
6
6
  "license": "MIT",