@zaplier/sdk 1.4.2 → 1.6.1
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/dist/index.cjs +583 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +54 -4
- package/dist/index.esm.js +583 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/sdk.js +583 -12
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.min.js +1 -1
- package/dist/src/modules/auto-tracker.d.ts +101 -0
- package/dist/src/modules/auto-tracker.d.ts.map +1 -0
- package/dist/src/modules/global-interface.d.ts +14 -0
- package/dist/src/modules/global-interface.d.ts.map +1 -1
- package/dist/src/modules/session-replay.d.ts +40 -1
- package/dist/src/modules/session-replay.d.ts.map +1 -1
- package/dist/src/modules/visitor-persistence.d.ts +104 -0
- package/dist/src/modules/visitor-persistence.d.ts.map +1 -0
- package/dist/src/sdk.d.ts +24 -0
- package/dist/src/sdk.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -19016,14 +19016,20 @@ var n;
|
|
|
19016
19016
|
* Based on official rrweb example for maximum compatibility
|
|
19017
19017
|
*/
|
|
19018
19018
|
class SessionReplayEngine {
|
|
19019
|
-
constructor(sessionId, config = {}) {
|
|
19019
|
+
constructor(sessionId, visitorId, config = {}) {
|
|
19020
19020
|
this.events = [];
|
|
19021
19021
|
this.isActive = false;
|
|
19022
|
+
this.isPaused = false;
|
|
19023
|
+
this.lastActivityTime = Date.now();
|
|
19024
|
+
this.sessionStartTime = Date.now();
|
|
19022
19025
|
this.sessionId = sessionId;
|
|
19026
|
+
this.visitorId = visitorId;
|
|
19023
19027
|
this.config = {
|
|
19024
19028
|
enabled: true,
|
|
19025
19029
|
sampleRate: 1.0,
|
|
19026
19030
|
batchInterval: 10000, // 10 seconds like official example
|
|
19031
|
+
inactivityTimeout: 30000, // 30 seconds of inactivity
|
|
19032
|
+
pauseOnInactive: true, // Pause recording during inactivity
|
|
19027
19033
|
...config,
|
|
19028
19034
|
};
|
|
19029
19035
|
}
|
|
@@ -19042,13 +19048,18 @@ class SessionReplayEngine {
|
|
|
19042
19048
|
// Simple rrweb recording configuration like official example
|
|
19043
19049
|
this.rrwebStopRecord = record({
|
|
19044
19050
|
emit: (event) => {
|
|
19045
|
-
//
|
|
19046
|
-
this.
|
|
19051
|
+
// Update activity time on any event
|
|
19052
|
+
this.onActivity();
|
|
19053
|
+
// Only capture events if not paused
|
|
19054
|
+
if (!this.isPaused) {
|
|
19055
|
+
this.events.push(event);
|
|
19056
|
+
}
|
|
19047
19057
|
},
|
|
19048
19058
|
});
|
|
19049
19059
|
this.isActive = true;
|
|
19050
19060
|
this.startBatchTimer();
|
|
19051
|
-
|
|
19061
|
+
this.startInactivityTracking();
|
|
19062
|
+
console.log("[Zaplier] Session replay started - with inactivity detection");
|
|
19052
19063
|
return true;
|
|
19053
19064
|
}
|
|
19054
19065
|
catch (error) {
|
|
@@ -19072,6 +19083,10 @@ class SessionReplayEngine {
|
|
|
19072
19083
|
clearInterval(this.batchTimer);
|
|
19073
19084
|
this.batchTimer = undefined;
|
|
19074
19085
|
}
|
|
19086
|
+
if (this.inactivityTimer) {
|
|
19087
|
+
clearTimeout(this.inactivityTimer);
|
|
19088
|
+
this.inactivityTimer = undefined;
|
|
19089
|
+
}
|
|
19075
19090
|
// Send final batch
|
|
19076
19091
|
this.sendBatch();
|
|
19077
19092
|
}
|
|
@@ -19090,17 +19105,21 @@ class SessionReplayEngine {
|
|
|
19090
19105
|
if (this.events.length === 0) {
|
|
19091
19106
|
return;
|
|
19092
19107
|
}
|
|
19093
|
-
//
|
|
19108
|
+
// Enhanced payload structure with visitor linking
|
|
19094
19109
|
const payload = {
|
|
19095
19110
|
sessionId: this.sessionId,
|
|
19111
|
+
visitorId: this.visitorId,
|
|
19096
19112
|
events: [...this.events], // Copy events array
|
|
19097
19113
|
metadata: {
|
|
19098
19114
|
userAgent: navigator.userAgent,
|
|
19099
19115
|
timestamp: Date.now(),
|
|
19100
19116
|
startUrl: window.location.href,
|
|
19101
|
-
duration: Date.now() -
|
|
19117
|
+
duration: Date.now() - this.sessionStartTime,
|
|
19118
|
+
activeTime: this.getActiveTime(),
|
|
19102
19119
|
funnelSteps: [],
|
|
19103
19120
|
hasConversion: false,
|
|
19121
|
+
eventsCount: this.events.length,
|
|
19122
|
+
isPaused: this.isPaused,
|
|
19104
19123
|
},
|
|
19105
19124
|
};
|
|
19106
19125
|
// Reset events array like official example
|
|
@@ -19149,6 +19168,448 @@ class SessionReplayEngine {
|
|
|
19149
19168
|
isRecording() {
|
|
19150
19169
|
return this.isActive;
|
|
19151
19170
|
}
|
|
19171
|
+
/**
|
|
19172
|
+
* Check if recording is paused
|
|
19173
|
+
*/
|
|
19174
|
+
isPausedState() {
|
|
19175
|
+
return this.isPaused;
|
|
19176
|
+
}
|
|
19177
|
+
/**
|
|
19178
|
+
* Start inactivity tracking
|
|
19179
|
+
*/
|
|
19180
|
+
startInactivityTracking() {
|
|
19181
|
+
if (!this.config.pauseOnInactive)
|
|
19182
|
+
return;
|
|
19183
|
+
// Listen for user activity events
|
|
19184
|
+
const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];
|
|
19185
|
+
activityEvents.forEach(event => {
|
|
19186
|
+
document.addEventListener(event, this.onActivity.bind(this), true);
|
|
19187
|
+
});
|
|
19188
|
+
this.resetInactivityTimer();
|
|
19189
|
+
}
|
|
19190
|
+
/**
|
|
19191
|
+
* Handle activity event
|
|
19192
|
+
*/
|
|
19193
|
+
onActivity() {
|
|
19194
|
+
this.lastActivityTime = Date.now();
|
|
19195
|
+
// Resume if paused
|
|
19196
|
+
if (this.isPaused) {
|
|
19197
|
+
this.resume();
|
|
19198
|
+
}
|
|
19199
|
+
this.resetInactivityTimer();
|
|
19200
|
+
}
|
|
19201
|
+
/**
|
|
19202
|
+
* Reset inactivity timer
|
|
19203
|
+
*/
|
|
19204
|
+
resetInactivityTimer() {
|
|
19205
|
+
if (this.inactivityTimer) {
|
|
19206
|
+
clearTimeout(this.inactivityTimer);
|
|
19207
|
+
}
|
|
19208
|
+
this.inactivityTimer = window.setTimeout(() => {
|
|
19209
|
+
this.pauseForInactivity();
|
|
19210
|
+
}, this.config.inactivityTimeout);
|
|
19211
|
+
}
|
|
19212
|
+
/**
|
|
19213
|
+
* Pause recording due to inactivity
|
|
19214
|
+
*/
|
|
19215
|
+
pauseForInactivity() {
|
|
19216
|
+
if (this.isPaused)
|
|
19217
|
+
return;
|
|
19218
|
+
this.isPaused = true;
|
|
19219
|
+
console.log("[Zaplier] Session replay paused due to inactivity");
|
|
19220
|
+
}
|
|
19221
|
+
/**
|
|
19222
|
+
* Resume recording after activity
|
|
19223
|
+
*/
|
|
19224
|
+
resume() {
|
|
19225
|
+
if (!this.isPaused)
|
|
19226
|
+
return;
|
|
19227
|
+
this.isPaused = false;
|
|
19228
|
+
console.log("[Zaplier] Session replay resumed after activity");
|
|
19229
|
+
}
|
|
19230
|
+
/**
|
|
19231
|
+
* Get total active time (excluding paused periods)
|
|
19232
|
+
*/
|
|
19233
|
+
getActiveTime() {
|
|
19234
|
+
const totalTime = Date.now() - this.sessionStartTime;
|
|
19235
|
+
// For now, return total time. In production, track pause periods accurately
|
|
19236
|
+
return totalTime;
|
|
19237
|
+
}
|
|
19238
|
+
/**
|
|
19239
|
+
* Get visitor ID
|
|
19240
|
+
*/
|
|
19241
|
+
getVisitorId() {
|
|
19242
|
+
return this.visitorId;
|
|
19243
|
+
}
|
|
19244
|
+
}
|
|
19245
|
+
|
|
19246
|
+
/**
|
|
19247
|
+
* Auto Tracker - Sistema de tracking automático via data-* attributes
|
|
19248
|
+
*
|
|
19249
|
+
* Suporta:
|
|
19250
|
+
* - data-track-click="event-name"
|
|
19251
|
+
* - data-track-scroll="event-name"
|
|
19252
|
+
* - data-track-view="event-name"
|
|
19253
|
+
* - data-track-hover="event-name"
|
|
19254
|
+
* - data-track-form="event-name"
|
|
19255
|
+
*/
|
|
19256
|
+
class AutoTracker {
|
|
19257
|
+
constructor(sdkInstance, config = {}) {
|
|
19258
|
+
this.observedElements = new Set();
|
|
19259
|
+
/**
|
|
19260
|
+
* Event handlers (bound to this)
|
|
19261
|
+
*/
|
|
19262
|
+
this.handleClick = (event) => {
|
|
19263
|
+
const target = event.target;
|
|
19264
|
+
if (target.hasAttribute("data-track-click")) ;
|
|
19265
|
+
};
|
|
19266
|
+
this.handleScroll = () => {
|
|
19267
|
+
// Scroll is handled by element-specific listeners
|
|
19268
|
+
};
|
|
19269
|
+
this.handleHover = (event) => {
|
|
19270
|
+
// Hover is handled by element-specific listeners
|
|
19271
|
+
};
|
|
19272
|
+
this.handleForm = (event) => {
|
|
19273
|
+
// Form is handled by element-specific listeners
|
|
19274
|
+
};
|
|
19275
|
+
this.sdkInstance = sdkInstance;
|
|
19276
|
+
this.config = {
|
|
19277
|
+
enabled: true,
|
|
19278
|
+
trackClicks: true,
|
|
19279
|
+
trackScrolls: true,
|
|
19280
|
+
trackViews: true,
|
|
19281
|
+
trackHovers: true,
|
|
19282
|
+
trackForms: true,
|
|
19283
|
+
debug: false,
|
|
19284
|
+
...config,
|
|
19285
|
+
};
|
|
19286
|
+
}
|
|
19287
|
+
/**
|
|
19288
|
+
* Inicializar auto tracking
|
|
19289
|
+
*/
|
|
19290
|
+
start() {
|
|
19291
|
+
if (!this.config.enabled)
|
|
19292
|
+
return;
|
|
19293
|
+
if (this.config.debug) {
|
|
19294
|
+
console.log("[Zaplier AutoTracker] Iniciando auto tracking");
|
|
19295
|
+
}
|
|
19296
|
+
// Observer para novos elementos no DOM
|
|
19297
|
+
this.observeDOM();
|
|
19298
|
+
// Processar elementos já existentes
|
|
19299
|
+
this.processExistingElements();
|
|
19300
|
+
// Setup intersection observer para views
|
|
19301
|
+
if (this.config.trackViews) {
|
|
19302
|
+
this.setupViewTracking();
|
|
19303
|
+
}
|
|
19304
|
+
}
|
|
19305
|
+
/**
|
|
19306
|
+
* Parar auto tracking
|
|
19307
|
+
*/
|
|
19308
|
+
stop() {
|
|
19309
|
+
document.removeEventListener("click", this.handleClick);
|
|
19310
|
+
document.removeEventListener("scroll", this.handleScroll);
|
|
19311
|
+
document.removeEventListener("mouseover", this.handleHover);
|
|
19312
|
+
document.removeEventListener("submit", this.handleForm);
|
|
19313
|
+
if (this.intersectionObserver) {
|
|
19314
|
+
this.intersectionObserver.disconnect();
|
|
19315
|
+
}
|
|
19316
|
+
this.observedElements.clear();
|
|
19317
|
+
}
|
|
19318
|
+
/**
|
|
19319
|
+
* Observar mudanças no DOM para novos elementos
|
|
19320
|
+
*/
|
|
19321
|
+
observeDOM() {
|
|
19322
|
+
const observer = new MutationObserver((mutations) => {
|
|
19323
|
+
mutations.forEach((mutation) => {
|
|
19324
|
+
mutation.addedNodes.forEach((node) => {
|
|
19325
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
19326
|
+
this.processElement(node);
|
|
19327
|
+
}
|
|
19328
|
+
});
|
|
19329
|
+
});
|
|
19330
|
+
});
|
|
19331
|
+
observer.observe(document.body, {
|
|
19332
|
+
childList: true,
|
|
19333
|
+
subtree: true,
|
|
19334
|
+
});
|
|
19335
|
+
}
|
|
19336
|
+
/**
|
|
19337
|
+
* Processar elementos já existentes no DOM
|
|
19338
|
+
*/
|
|
19339
|
+
processExistingElements() {
|
|
19340
|
+
// Buscar todos os elementos com data-track-*
|
|
19341
|
+
const trackElements = document.querySelectorAll("[data-track-click], [data-track-scroll], [data-track-view], [data-track-hover], [data-track-form]");
|
|
19342
|
+
trackElements.forEach((element) => {
|
|
19343
|
+
this.processElement(element);
|
|
19344
|
+
});
|
|
19345
|
+
}
|
|
19346
|
+
/**
|
|
19347
|
+
* Processar um elemento específico
|
|
19348
|
+
*/
|
|
19349
|
+
processElement(element) {
|
|
19350
|
+
// Click tracking
|
|
19351
|
+
if (this.config.trackClicks && element.hasAttribute("data-track-click")) {
|
|
19352
|
+
this.setupClickTracking(element);
|
|
19353
|
+
}
|
|
19354
|
+
// Scroll tracking
|
|
19355
|
+
if (this.config.trackScrolls && element.hasAttribute("data-track-scroll")) {
|
|
19356
|
+
this.setupScrollTracking(element);
|
|
19357
|
+
}
|
|
19358
|
+
// View tracking
|
|
19359
|
+
if (this.config.trackViews && element.hasAttribute("data-track-view")) {
|
|
19360
|
+
this.setupElementViewTracking(element);
|
|
19361
|
+
}
|
|
19362
|
+
// Hover tracking
|
|
19363
|
+
if (this.config.trackHovers && element.hasAttribute("data-track-hover")) {
|
|
19364
|
+
this.setupHoverTracking(element);
|
|
19365
|
+
}
|
|
19366
|
+
// Form tracking
|
|
19367
|
+
if (this.config.trackForms && element.hasAttribute("data-track-form")) {
|
|
19368
|
+
this.setupFormTracking(element);
|
|
19369
|
+
}
|
|
19370
|
+
}
|
|
19371
|
+
/**
|
|
19372
|
+
* Setup click tracking
|
|
19373
|
+
*/
|
|
19374
|
+
setupClickTracking(element) {
|
|
19375
|
+
element.addEventListener("click", (event) => {
|
|
19376
|
+
const eventName = element.getAttribute("data-track-click");
|
|
19377
|
+
if (!eventName)
|
|
19378
|
+
return;
|
|
19379
|
+
const metadata = this.extractMetadata(element);
|
|
19380
|
+
this.trackEvent(eventName, {
|
|
19381
|
+
type: "click",
|
|
19382
|
+
element: element.tagName.toLowerCase(),
|
|
19383
|
+
...metadata,
|
|
19384
|
+
});
|
|
19385
|
+
if (this.config.debug) {
|
|
19386
|
+
console.log(`[AutoTracker] Click tracked: ${eventName}`, metadata);
|
|
19387
|
+
}
|
|
19388
|
+
});
|
|
19389
|
+
}
|
|
19390
|
+
/**
|
|
19391
|
+
* Setup scroll tracking
|
|
19392
|
+
*/
|
|
19393
|
+
setupScrollTracking(element) {
|
|
19394
|
+
let hasTriggered = false;
|
|
19395
|
+
const threshold = parseFloat(element.getAttribute("data-scroll-threshold") || "0.5");
|
|
19396
|
+
const handleScroll = () => {
|
|
19397
|
+
if (hasTriggered)
|
|
19398
|
+
return;
|
|
19399
|
+
const rect = element.getBoundingClientRect();
|
|
19400
|
+
const elementHeight = rect.height;
|
|
19401
|
+
const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
|
|
19402
|
+
const visibilityRatio = Math.max(0, visibleHeight) / elementHeight;
|
|
19403
|
+
if (visibilityRatio >= threshold) {
|
|
19404
|
+
hasTriggered = true;
|
|
19405
|
+
const eventName = element.getAttribute("data-track-scroll");
|
|
19406
|
+
if (!eventName)
|
|
19407
|
+
return;
|
|
19408
|
+
const metadata = this.extractMetadata(element);
|
|
19409
|
+
this.trackEvent(eventName, {
|
|
19410
|
+
type: "scroll",
|
|
19411
|
+
element: element.tagName.toLowerCase(),
|
|
19412
|
+
threshold,
|
|
19413
|
+
scrollDepth: window.scrollY,
|
|
19414
|
+
...metadata,
|
|
19415
|
+
});
|
|
19416
|
+
if (this.config.debug) {
|
|
19417
|
+
console.log(`[AutoTracker] Scroll tracked: ${eventName}`, metadata);
|
|
19418
|
+
}
|
|
19419
|
+
}
|
|
19420
|
+
};
|
|
19421
|
+
document.addEventListener("scroll", handleScroll, { passive: true });
|
|
19422
|
+
// Check immediately in case element is already in view
|
|
19423
|
+
setTimeout(handleScroll, 100);
|
|
19424
|
+
}
|
|
19425
|
+
/**
|
|
19426
|
+
* Setup view tracking usando Intersection Observer
|
|
19427
|
+
*/
|
|
19428
|
+
setupViewTracking() {
|
|
19429
|
+
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
19430
|
+
entries.forEach((entry) => {
|
|
19431
|
+
if (entry.isIntersecting) {
|
|
19432
|
+
const element = entry.target;
|
|
19433
|
+
const eventName = element.getAttribute("data-track-view");
|
|
19434
|
+
if (!eventName)
|
|
19435
|
+
return;
|
|
19436
|
+
const metadata = this.extractMetadata(element);
|
|
19437
|
+
this.trackEvent(eventName, {
|
|
19438
|
+
type: "view",
|
|
19439
|
+
element: element.tagName.toLowerCase(),
|
|
19440
|
+
intersectionRatio: entry.intersectionRatio,
|
|
19441
|
+
...metadata,
|
|
19442
|
+
});
|
|
19443
|
+
if (this.config.debug) {
|
|
19444
|
+
console.log(`[AutoTracker] View tracked: ${eventName}`, metadata);
|
|
19445
|
+
}
|
|
19446
|
+
// Remove from observation after first trigger
|
|
19447
|
+
this.intersectionObserver?.unobserve(element);
|
|
19448
|
+
}
|
|
19449
|
+
});
|
|
19450
|
+
}, {
|
|
19451
|
+
threshold: 0.5, // 50% visible
|
|
19452
|
+
rootMargin: "0px",
|
|
19453
|
+
});
|
|
19454
|
+
}
|
|
19455
|
+
/**
|
|
19456
|
+
* Setup view tracking para elemento específico
|
|
19457
|
+
*/
|
|
19458
|
+
setupElementViewTracking(element) {
|
|
19459
|
+
if (!this.intersectionObserver)
|
|
19460
|
+
return;
|
|
19461
|
+
if (!this.observedElements.has(element)) {
|
|
19462
|
+
this.intersectionObserver.observe(element);
|
|
19463
|
+
this.observedElements.add(element);
|
|
19464
|
+
}
|
|
19465
|
+
}
|
|
19466
|
+
/**
|
|
19467
|
+
* Setup hover tracking
|
|
19468
|
+
*/
|
|
19469
|
+
setupHoverTracking(element) {
|
|
19470
|
+
let hoverStartTime;
|
|
19471
|
+
const minHoverTime = parseInt(element.getAttribute("data-hover-time") || "1000");
|
|
19472
|
+
element.addEventListener("mouseenter", () => {
|
|
19473
|
+
hoverStartTime = Date.now();
|
|
19474
|
+
});
|
|
19475
|
+
element.addEventListener("mouseleave", () => {
|
|
19476
|
+
const hoverDuration = Date.now() - hoverStartTime;
|
|
19477
|
+
if (hoverDuration >= minHoverTime) {
|
|
19478
|
+
const eventName = element.getAttribute("data-track-hover");
|
|
19479
|
+
if (!eventName)
|
|
19480
|
+
return;
|
|
19481
|
+
const metadata = this.extractMetadata(element);
|
|
19482
|
+
this.trackEvent(eventName, {
|
|
19483
|
+
type: "hover",
|
|
19484
|
+
element: element.tagName.toLowerCase(),
|
|
19485
|
+
hoverDuration,
|
|
19486
|
+
minHoverTime,
|
|
19487
|
+
...metadata,
|
|
19488
|
+
});
|
|
19489
|
+
if (this.config.debug) {
|
|
19490
|
+
console.log(`[AutoTracker] Hover tracked: ${eventName}`, metadata);
|
|
19491
|
+
}
|
|
19492
|
+
}
|
|
19493
|
+
});
|
|
19494
|
+
}
|
|
19495
|
+
/**
|
|
19496
|
+
* Setup form tracking
|
|
19497
|
+
*/
|
|
19498
|
+
setupFormTracking(element) {
|
|
19499
|
+
if (element.tagName.toLowerCase() !== "form")
|
|
19500
|
+
return;
|
|
19501
|
+
element.addEventListener("submit", (event) => {
|
|
19502
|
+
const eventName = element.getAttribute("data-track-form");
|
|
19503
|
+
if (!eventName)
|
|
19504
|
+
return;
|
|
19505
|
+
const formData = new FormData(element);
|
|
19506
|
+
const metadata = this.extractMetadata(element);
|
|
19507
|
+
// Extract form fields (without sensitive data)
|
|
19508
|
+
const fields = {};
|
|
19509
|
+
for (const [key, value] of formData.entries()) {
|
|
19510
|
+
// Skip sensitive fields
|
|
19511
|
+
if (!this.isSensitiveField(key)) {
|
|
19512
|
+
fields[key] =
|
|
19513
|
+
typeof value === "string" ? value.substring(0, 100) : "file";
|
|
19514
|
+
}
|
|
19515
|
+
}
|
|
19516
|
+
this.trackEvent(eventName, {
|
|
19517
|
+
type: "form_submit",
|
|
19518
|
+
element: "form",
|
|
19519
|
+
fieldCount: Array.from(formData.entries()).length,
|
|
19520
|
+
fields,
|
|
19521
|
+
...metadata,
|
|
19522
|
+
});
|
|
19523
|
+
if (this.config.debug) {
|
|
19524
|
+
console.log(`[AutoTracker] Form submit tracked: ${eventName}`, metadata);
|
|
19525
|
+
}
|
|
19526
|
+
});
|
|
19527
|
+
}
|
|
19528
|
+
/**
|
|
19529
|
+
* Extrair metadata do elemento
|
|
19530
|
+
*/
|
|
19531
|
+
extractMetadata(element) {
|
|
19532
|
+
const metadata = {};
|
|
19533
|
+
// Extrair todos os data-meta-* attributes
|
|
19534
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
19535
|
+
if (attr.name.startsWith("data-meta-")) {
|
|
19536
|
+
const key = attr.name.replace("data-meta-", "");
|
|
19537
|
+
metadata[key] = attr.value;
|
|
19538
|
+
}
|
|
19539
|
+
});
|
|
19540
|
+
// Adicionar informações básicas
|
|
19541
|
+
if (element.id)
|
|
19542
|
+
metadata.elementId = element.id;
|
|
19543
|
+
if (element.className)
|
|
19544
|
+
metadata.elementClass = element.className;
|
|
19545
|
+
// Text content (limitado)
|
|
19546
|
+
const textContent = element.textContent?.trim();
|
|
19547
|
+
if (textContent && textContent.length > 0) {
|
|
19548
|
+
metadata.textContent = textContent.substring(0, 50);
|
|
19549
|
+
}
|
|
19550
|
+
// Position info
|
|
19551
|
+
const rect = element.getBoundingClientRect();
|
|
19552
|
+
metadata.elementPosition = {
|
|
19553
|
+
x: Math.round(rect.left),
|
|
19554
|
+
y: Math.round(rect.top),
|
|
19555
|
+
width: Math.round(rect.width),
|
|
19556
|
+
height: Math.round(rect.height),
|
|
19557
|
+
};
|
|
19558
|
+
return metadata;
|
|
19559
|
+
}
|
|
19560
|
+
/**
|
|
19561
|
+
* Verificar se um campo é sensível
|
|
19562
|
+
*/
|
|
19563
|
+
isSensitiveField(fieldName) {
|
|
19564
|
+
const sensitivePatterns = [
|
|
19565
|
+
/password/i,
|
|
19566
|
+
/pass/i,
|
|
19567
|
+
/pwd/i,
|
|
19568
|
+
/secret/i,
|
|
19569
|
+
/token/i,
|
|
19570
|
+
/api[_-]?key/i,
|
|
19571
|
+
/credit[_-]?card/i,
|
|
19572
|
+
/ssn/i,
|
|
19573
|
+
/social/i,
|
|
19574
|
+
/tax/i,
|
|
19575
|
+
];
|
|
19576
|
+
return sensitivePatterns.some((pattern) => pattern.test(fieldName));
|
|
19577
|
+
}
|
|
19578
|
+
/**
|
|
19579
|
+
* Enviar evento para o SDK
|
|
19580
|
+
*/
|
|
19581
|
+
trackEvent(eventName, metadata) {
|
|
19582
|
+
if (!this.sdkInstance)
|
|
19583
|
+
return;
|
|
19584
|
+
try {
|
|
19585
|
+
this.sdkInstance.trackCustomEvent(eventName, {
|
|
19586
|
+
autoTracked: true,
|
|
19587
|
+
timestamp: Date.now(),
|
|
19588
|
+
url: window.location.href,
|
|
19589
|
+
...metadata,
|
|
19590
|
+
});
|
|
19591
|
+
}
|
|
19592
|
+
catch (error) {
|
|
19593
|
+
if (this.config.debug) {
|
|
19594
|
+
console.error("[AutoTracker] Error sending event:", error);
|
|
19595
|
+
}
|
|
19596
|
+
}
|
|
19597
|
+
}
|
|
19598
|
+
/**
|
|
19599
|
+
* Configurar tracking
|
|
19600
|
+
*/
|
|
19601
|
+
configure(config) {
|
|
19602
|
+
this.config = { ...this.config, ...config };
|
|
19603
|
+
}
|
|
19604
|
+
/**
|
|
19605
|
+
* Obter estatísticas
|
|
19606
|
+
*/
|
|
19607
|
+
getStats() {
|
|
19608
|
+
return {
|
|
19609
|
+
observedElements: this.observedElements.size,
|
|
19610
|
+
config: this.config,
|
|
19611
|
+
};
|
|
19612
|
+
}
|
|
19152
19613
|
}
|
|
19153
19614
|
|
|
19154
19615
|
/**
|
|
@@ -19182,7 +19643,7 @@ const DEFAULT_CONFIG = {
|
|
|
19182
19643
|
*/
|
|
19183
19644
|
class ZaplierSDK {
|
|
19184
19645
|
constructor(userConfig) {
|
|
19185
|
-
this.version = "1.
|
|
19646
|
+
this.version = "1.6.0";
|
|
19186
19647
|
this.isInitialized = false;
|
|
19187
19648
|
this.eventQueue = [];
|
|
19188
19649
|
/**
|
|
@@ -19253,8 +19714,10 @@ class ZaplierSDK {
|
|
|
19253
19714
|
}
|
|
19254
19715
|
const sessionId = this.sessionId;
|
|
19255
19716
|
// When explicitly enabled, use 100% sample rate
|
|
19256
|
-
this.replayEngine = new SessionReplayEngine(sessionId, {
|
|
19717
|
+
this.replayEngine = new SessionReplayEngine(sessionId, this.backendVisitorId || 'unknown', {
|
|
19257
19718
|
sampleRate: 1.0, // Force 100% when explicitly enabled
|
|
19719
|
+
inactivityTimeout: 30000,
|
|
19720
|
+
pauseOnInactive: true,
|
|
19258
19721
|
});
|
|
19259
19722
|
// Connect to anti-adblock manager
|
|
19260
19723
|
if (this.antiAdblockManager) {
|
|
@@ -19279,8 +19742,10 @@ class ZaplierSDK {
|
|
|
19279
19742
|
this.sessionId = this.generateSessionId();
|
|
19280
19743
|
}
|
|
19281
19744
|
const sessionId = this.sessionId;
|
|
19282
|
-
this.replayEngine = new SessionReplayEngine(sessionId, {
|
|
19745
|
+
this.replayEngine = new SessionReplayEngine(sessionId, this.backendVisitorId || 'unknown', {
|
|
19283
19746
|
sampleRate: 1.0, // Force 100% when explicitly enabled
|
|
19747
|
+
inactivityTimeout: 30000,
|
|
19748
|
+
pauseOnInactive: true,
|
|
19284
19749
|
});
|
|
19285
19750
|
// Connect to anti-adblock manager
|
|
19286
19751
|
if (this.antiAdblockManager) {
|
|
@@ -19319,8 +19784,10 @@ class ZaplierSDK {
|
|
|
19319
19784
|
this.sessionId = this.generateSessionId();
|
|
19320
19785
|
}
|
|
19321
19786
|
const sessionId = this.sessionId;
|
|
19322
|
-
this.replayEngine = new SessionReplayEngine(sessionId, {
|
|
19787
|
+
this.replayEngine = new SessionReplayEngine(sessionId, this.backendVisitorId || 'unknown', {
|
|
19323
19788
|
sampleRate: 1.0, // Force recording when manually started
|
|
19789
|
+
inactivityTimeout: 30000,
|
|
19790
|
+
pauseOnInactive: true,
|
|
19324
19791
|
});
|
|
19325
19792
|
// Connect to anti-adblock manager
|
|
19326
19793
|
if (this.antiAdblockManager) {
|
|
@@ -19352,6 +19819,45 @@ class ZaplierSDK {
|
|
|
19352
19819
|
this.trackConversion("funnel_conversion", data.value, data.currency, data);
|
|
19353
19820
|
},
|
|
19354
19821
|
};
|
|
19822
|
+
/**
|
|
19823
|
+
* Auto Tracker API
|
|
19824
|
+
*/
|
|
19825
|
+
this.autoTrack = {
|
|
19826
|
+
enable: () => {
|
|
19827
|
+
if (!this.autoTracker) {
|
|
19828
|
+
this.initializeAutoTracker();
|
|
19829
|
+
}
|
|
19830
|
+
else {
|
|
19831
|
+
this.autoTracker.configure({ enabled: true });
|
|
19832
|
+
}
|
|
19833
|
+
if (this.config.debug) {
|
|
19834
|
+
console.log("[Zaplier] Auto tracking enabled");
|
|
19835
|
+
}
|
|
19836
|
+
},
|
|
19837
|
+
disable: () => {
|
|
19838
|
+
if (this.autoTracker) {
|
|
19839
|
+
this.autoTracker.stop();
|
|
19840
|
+
this.autoTracker = undefined;
|
|
19841
|
+
}
|
|
19842
|
+
if (this.config.debug) {
|
|
19843
|
+
console.log("[Zaplier] Auto tracking disabled");
|
|
19844
|
+
}
|
|
19845
|
+
},
|
|
19846
|
+
configure: (config) => {
|
|
19847
|
+
if (this.autoTracker) {
|
|
19848
|
+
this.autoTracker.configure(config);
|
|
19849
|
+
}
|
|
19850
|
+
if (this.config.debug) {
|
|
19851
|
+
console.log("[Zaplier] Auto tracking configured:", config);
|
|
19852
|
+
}
|
|
19853
|
+
},
|
|
19854
|
+
getStats: () => {
|
|
19855
|
+
return this.autoTracker ? this.autoTracker.getStats() : null;
|
|
19856
|
+
},
|
|
19857
|
+
isEnabled: () => {
|
|
19858
|
+
return !!this.autoTracker;
|
|
19859
|
+
},
|
|
19860
|
+
};
|
|
19355
19861
|
// Validate required config
|
|
19356
19862
|
if (!userConfig.token) {
|
|
19357
19863
|
throw new Error("Zaplier: token is required");
|
|
@@ -19393,6 +19899,8 @@ class ZaplierSDK {
|
|
|
19393
19899
|
this.initializeAntiAdblock();
|
|
19394
19900
|
// Then initialize tracking engines (they'll connect to anti-adblock)
|
|
19395
19901
|
this.initializeTrackingEngines();
|
|
19902
|
+
// Initialize auto tracker
|
|
19903
|
+
this.initializeAutoTracker();
|
|
19396
19904
|
// Process queued events
|
|
19397
19905
|
this.processEventQueue();
|
|
19398
19906
|
if (this.config.debug) {
|
|
@@ -19423,8 +19931,10 @@ class ZaplierSDK {
|
|
|
19423
19931
|
// When replay is explicitly enabled, use 100% sample rate to ensure recording
|
|
19424
19932
|
// The default replaySampling (0.1) is only for automatic/production sampling
|
|
19425
19933
|
const sampleRate = this.config.replay === true ? 1.0 : this.config.replaySampling;
|
|
19426
|
-
this.replayEngine = new SessionReplayEngine(this.sessionId, {
|
|
19934
|
+
this.replayEngine = new SessionReplayEngine(this.sessionId, this.backendVisitorId || 'unknown', {
|
|
19427
19935
|
sampleRate: sampleRate,
|
|
19936
|
+
inactivityTimeout: 30000,
|
|
19937
|
+
pauseOnInactive: true,
|
|
19428
19938
|
});
|
|
19429
19939
|
// Connect SDK instance for transport
|
|
19430
19940
|
this.replayEngine.setSDKInstance(this);
|
|
@@ -19459,6 +19969,29 @@ class ZaplierSDK {
|
|
|
19459
19969
|
console.error("[Zaplier] Failed to initialize tracking engines:", error);
|
|
19460
19970
|
}
|
|
19461
19971
|
}
|
|
19972
|
+
/**
|
|
19973
|
+
* Initialize Auto Tracker for data-* attributes
|
|
19974
|
+
*/
|
|
19975
|
+
initializeAutoTracker() {
|
|
19976
|
+
try {
|
|
19977
|
+
this.autoTracker = new AutoTracker(this, {
|
|
19978
|
+
enabled: true,
|
|
19979
|
+
trackClicks: true,
|
|
19980
|
+
trackScrolls: true,
|
|
19981
|
+
trackViews: true,
|
|
19982
|
+
trackHovers: true,
|
|
19983
|
+
trackForms: true,
|
|
19984
|
+
debug: this.config.debug,
|
|
19985
|
+
});
|
|
19986
|
+
this.autoTracker.start();
|
|
19987
|
+
if (this.config.debug) {
|
|
19988
|
+
console.log("[Zaplier] Auto Tracker initialized and started");
|
|
19989
|
+
}
|
|
19990
|
+
}
|
|
19991
|
+
catch (error) {
|
|
19992
|
+
console.error("[Zaplier] Failed to initialize Auto Tracker:", error);
|
|
19993
|
+
}
|
|
19994
|
+
}
|
|
19462
19995
|
/**
|
|
19463
19996
|
* Generate session ID
|
|
19464
19997
|
*/
|
|
@@ -20401,6 +20934,44 @@ const Zaplier = {
|
|
|
20401
20934
|
globalInstance.replay.markConversion(data);
|
|
20402
20935
|
}
|
|
20403
20936
|
},
|
|
20937
|
+
/**
|
|
20938
|
+
* Auto Tracker API
|
|
20939
|
+
*/
|
|
20940
|
+
autoTrack: {
|
|
20941
|
+
enable: () => {
|
|
20942
|
+
if (!globalInstance) {
|
|
20943
|
+
console.warn('[Zaplier] SDK not initialized. Call Zaplier.init() first');
|
|
20944
|
+
return;
|
|
20945
|
+
}
|
|
20946
|
+
globalInstance.autoTrack.enable();
|
|
20947
|
+
},
|
|
20948
|
+
disable: () => {
|
|
20949
|
+
if (!globalInstance) {
|
|
20950
|
+
console.warn('[Zaplier] SDK not initialized. Call Zaplier.init() first');
|
|
20951
|
+
return;
|
|
20952
|
+
}
|
|
20953
|
+
globalInstance.autoTrack.disable();
|
|
20954
|
+
},
|
|
20955
|
+
configure: (config) => {
|
|
20956
|
+
if (!globalInstance) {
|
|
20957
|
+
console.warn('[Zaplier] SDK not initialized. Call Zaplier.init() first');
|
|
20958
|
+
return;
|
|
20959
|
+
}
|
|
20960
|
+
globalInstance.autoTrack.configure(config);
|
|
20961
|
+
},
|
|
20962
|
+
getStats: () => {
|
|
20963
|
+
if (!globalInstance) {
|
|
20964
|
+
return null;
|
|
20965
|
+
}
|
|
20966
|
+
return globalInstance.autoTrack.getStats();
|
|
20967
|
+
},
|
|
20968
|
+
isEnabled: () => {
|
|
20969
|
+
if (!globalInstance) {
|
|
20970
|
+
return false;
|
|
20971
|
+
}
|
|
20972
|
+
return globalInstance.autoTrack.isEnabled();
|
|
20973
|
+
}
|
|
20974
|
+
},
|
|
20404
20975
|
/**
|
|
20405
20976
|
* Debug and utility functions
|
|
20406
20977
|
*/
|
|
@@ -20427,7 +20998,7 @@ const Zaplier = {
|
|
|
20427
20998
|
/**
|
|
20428
20999
|
* Version info
|
|
20429
21000
|
*/
|
|
20430
|
-
version: '
|
|
21001
|
+
version: '1.6.0'
|
|
20431
21002
|
};
|
|
20432
21003
|
/**
|
|
20433
21004
|
* Auto-initialization from script tag data attributes
|