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