@zaplier/sdk 1.4.2 → 1.6.2
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 +876 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +56 -5
- package/dist/index.esm.js +876 -13
- package/dist/index.esm.js.map +1 -1
- package/dist/sdk.js +876 -13
- 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 +36 -0
- package/dist/src/modules/visitor-persistence.d.ts.map +1 -0
- package/dist/src/sdk.d.ts +26 -1
- package/dist/src/sdk.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/sdk.js
CHANGED
|
@@ -19018,14 +19018,20 @@
|
|
|
19018
19018
|
* Based on official rrweb example for maximum compatibility
|
|
19019
19019
|
*/
|
|
19020
19020
|
class SessionReplayEngine {
|
|
19021
|
-
constructor(sessionId, config = {}) {
|
|
19021
|
+
constructor(sessionId, visitorId, config = {}) {
|
|
19022
19022
|
this.events = [];
|
|
19023
19023
|
this.isActive = false;
|
|
19024
|
+
this.isPaused = false;
|
|
19025
|
+
this.lastActivityTime = Date.now();
|
|
19026
|
+
this.sessionStartTime = Date.now();
|
|
19024
19027
|
this.sessionId = sessionId;
|
|
19028
|
+
this.visitorId = visitorId;
|
|
19025
19029
|
this.config = {
|
|
19026
19030
|
enabled: true,
|
|
19027
19031
|
sampleRate: 1.0,
|
|
19028
19032
|
batchInterval: 10000, // 10 seconds like official example
|
|
19033
|
+
inactivityTimeout: 30000, // 30 seconds of inactivity
|
|
19034
|
+
pauseOnInactive: true, // Pause recording during inactivity
|
|
19029
19035
|
...config,
|
|
19030
19036
|
};
|
|
19031
19037
|
}
|
|
@@ -19044,13 +19050,18 @@
|
|
|
19044
19050
|
// Simple rrweb recording configuration like official example
|
|
19045
19051
|
this.rrwebStopRecord = record({
|
|
19046
19052
|
emit: (event) => {
|
|
19047
|
-
//
|
|
19048
|
-
this.
|
|
19053
|
+
// Update activity time on any event
|
|
19054
|
+
this.onActivity();
|
|
19055
|
+
// Only capture events if not paused
|
|
19056
|
+
if (!this.isPaused) {
|
|
19057
|
+
this.events.push(event);
|
|
19058
|
+
}
|
|
19049
19059
|
},
|
|
19050
19060
|
});
|
|
19051
19061
|
this.isActive = true;
|
|
19052
19062
|
this.startBatchTimer();
|
|
19053
|
-
|
|
19063
|
+
this.startInactivityTracking();
|
|
19064
|
+
console.log("[Zaplier] Session replay started - with inactivity detection");
|
|
19054
19065
|
return true;
|
|
19055
19066
|
}
|
|
19056
19067
|
catch (error) {
|
|
@@ -19074,6 +19085,10 @@
|
|
|
19074
19085
|
clearInterval(this.batchTimer);
|
|
19075
19086
|
this.batchTimer = undefined;
|
|
19076
19087
|
}
|
|
19088
|
+
if (this.inactivityTimer) {
|
|
19089
|
+
clearTimeout(this.inactivityTimer);
|
|
19090
|
+
this.inactivityTimer = undefined;
|
|
19091
|
+
}
|
|
19077
19092
|
// Send final batch
|
|
19078
19093
|
this.sendBatch();
|
|
19079
19094
|
}
|
|
@@ -19092,17 +19107,21 @@
|
|
|
19092
19107
|
if (this.events.length === 0) {
|
|
19093
19108
|
return;
|
|
19094
19109
|
}
|
|
19095
|
-
//
|
|
19110
|
+
// Enhanced payload structure with visitor linking
|
|
19096
19111
|
const payload = {
|
|
19097
19112
|
sessionId: this.sessionId,
|
|
19113
|
+
visitorId: this.visitorId,
|
|
19098
19114
|
events: [...this.events], // Copy events array
|
|
19099
19115
|
metadata: {
|
|
19100
19116
|
userAgent: navigator.userAgent,
|
|
19101
19117
|
timestamp: Date.now(),
|
|
19102
19118
|
startUrl: window.location.href,
|
|
19103
|
-
duration: Date.now() -
|
|
19119
|
+
duration: Date.now() - this.sessionStartTime,
|
|
19120
|
+
activeTime: this.getActiveTime(),
|
|
19104
19121
|
funnelSteps: [],
|
|
19105
19122
|
hasConversion: false,
|
|
19123
|
+
eventsCount: this.events.length,
|
|
19124
|
+
isPaused: this.isPaused,
|
|
19106
19125
|
},
|
|
19107
19126
|
};
|
|
19108
19127
|
// Reset events array like official example
|
|
@@ -19151,6 +19170,714 @@
|
|
|
19151
19170
|
isRecording() {
|
|
19152
19171
|
return this.isActive;
|
|
19153
19172
|
}
|
|
19173
|
+
/**
|
|
19174
|
+
* Check if recording is paused
|
|
19175
|
+
*/
|
|
19176
|
+
isPausedState() {
|
|
19177
|
+
return this.isPaused;
|
|
19178
|
+
}
|
|
19179
|
+
/**
|
|
19180
|
+
* Start inactivity tracking
|
|
19181
|
+
*/
|
|
19182
|
+
startInactivityTracking() {
|
|
19183
|
+
if (!this.config.pauseOnInactive)
|
|
19184
|
+
return;
|
|
19185
|
+
// Listen for user activity events
|
|
19186
|
+
const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];
|
|
19187
|
+
activityEvents.forEach(event => {
|
|
19188
|
+
document.addEventListener(event, this.onActivity.bind(this), true);
|
|
19189
|
+
});
|
|
19190
|
+
this.resetInactivityTimer();
|
|
19191
|
+
}
|
|
19192
|
+
/**
|
|
19193
|
+
* Handle activity event
|
|
19194
|
+
*/
|
|
19195
|
+
onActivity() {
|
|
19196
|
+
this.lastActivityTime = Date.now();
|
|
19197
|
+
// Resume if paused
|
|
19198
|
+
if (this.isPaused) {
|
|
19199
|
+
this.resume();
|
|
19200
|
+
}
|
|
19201
|
+
this.resetInactivityTimer();
|
|
19202
|
+
}
|
|
19203
|
+
/**
|
|
19204
|
+
* Reset inactivity timer
|
|
19205
|
+
*/
|
|
19206
|
+
resetInactivityTimer() {
|
|
19207
|
+
if (this.inactivityTimer) {
|
|
19208
|
+
clearTimeout(this.inactivityTimer);
|
|
19209
|
+
}
|
|
19210
|
+
this.inactivityTimer = window.setTimeout(() => {
|
|
19211
|
+
this.pauseForInactivity();
|
|
19212
|
+
}, this.config.inactivityTimeout);
|
|
19213
|
+
}
|
|
19214
|
+
/**
|
|
19215
|
+
* Pause recording due to inactivity
|
|
19216
|
+
*/
|
|
19217
|
+
pauseForInactivity() {
|
|
19218
|
+
if (this.isPaused)
|
|
19219
|
+
return;
|
|
19220
|
+
this.isPaused = true;
|
|
19221
|
+
console.log("[Zaplier] Session replay paused due to inactivity");
|
|
19222
|
+
}
|
|
19223
|
+
/**
|
|
19224
|
+
* Resume recording after activity
|
|
19225
|
+
*/
|
|
19226
|
+
resume() {
|
|
19227
|
+
if (!this.isPaused)
|
|
19228
|
+
return;
|
|
19229
|
+
this.isPaused = false;
|
|
19230
|
+
console.log("[Zaplier] Session replay resumed after activity");
|
|
19231
|
+
}
|
|
19232
|
+
/**
|
|
19233
|
+
* Get total active time (excluding paused periods)
|
|
19234
|
+
*/
|
|
19235
|
+
getActiveTime() {
|
|
19236
|
+
const totalTime = Date.now() - this.sessionStartTime;
|
|
19237
|
+
// For now, return total time. In production, track pause periods accurately
|
|
19238
|
+
return totalTime;
|
|
19239
|
+
}
|
|
19240
|
+
/**
|
|
19241
|
+
* Get visitor ID
|
|
19242
|
+
*/
|
|
19243
|
+
getVisitorId() {
|
|
19244
|
+
return this.visitorId;
|
|
19245
|
+
}
|
|
19246
|
+
}
|
|
19247
|
+
|
|
19248
|
+
/**
|
|
19249
|
+
* Auto Tracker - Sistema de tracking automático via data-* attributes
|
|
19250
|
+
*
|
|
19251
|
+
* Suporta:
|
|
19252
|
+
* - data-track-click="event-name"
|
|
19253
|
+
* - data-track-scroll="event-name"
|
|
19254
|
+
* - data-track-view="event-name"
|
|
19255
|
+
* - data-track-hover="event-name"
|
|
19256
|
+
* - data-track-form="event-name"
|
|
19257
|
+
*/
|
|
19258
|
+
class AutoTracker {
|
|
19259
|
+
constructor(sdkInstance, config = {}) {
|
|
19260
|
+
this.observedElements = new Set();
|
|
19261
|
+
/**
|
|
19262
|
+
* Event handlers (bound to this)
|
|
19263
|
+
*/
|
|
19264
|
+
this.handleClick = (event) => {
|
|
19265
|
+
const target = event.target;
|
|
19266
|
+
if (target.hasAttribute("data-track-click")) ;
|
|
19267
|
+
};
|
|
19268
|
+
this.handleScroll = () => {
|
|
19269
|
+
// Scroll is handled by element-specific listeners
|
|
19270
|
+
};
|
|
19271
|
+
this.handleHover = (event) => {
|
|
19272
|
+
// Hover is handled by element-specific listeners
|
|
19273
|
+
};
|
|
19274
|
+
this.handleForm = (event) => {
|
|
19275
|
+
// Form is handled by element-specific listeners
|
|
19276
|
+
};
|
|
19277
|
+
this.sdkInstance = sdkInstance;
|
|
19278
|
+
this.config = {
|
|
19279
|
+
enabled: true,
|
|
19280
|
+
trackClicks: true,
|
|
19281
|
+
trackScrolls: true,
|
|
19282
|
+
trackViews: true,
|
|
19283
|
+
trackHovers: true,
|
|
19284
|
+
trackForms: true,
|
|
19285
|
+
debug: false,
|
|
19286
|
+
...config,
|
|
19287
|
+
};
|
|
19288
|
+
}
|
|
19289
|
+
/**
|
|
19290
|
+
* Inicializar auto tracking
|
|
19291
|
+
*/
|
|
19292
|
+
start() {
|
|
19293
|
+
if (!this.config.enabled)
|
|
19294
|
+
return;
|
|
19295
|
+
if (this.config.debug) {
|
|
19296
|
+
console.log("[Zaplier AutoTracker] Iniciando auto tracking");
|
|
19297
|
+
}
|
|
19298
|
+
// Observer para novos elementos no DOM
|
|
19299
|
+
this.observeDOM();
|
|
19300
|
+
// Processar elementos já existentes
|
|
19301
|
+
this.processExistingElements();
|
|
19302
|
+
// Setup intersection observer para views
|
|
19303
|
+
if (this.config.trackViews) {
|
|
19304
|
+
this.setupViewTracking();
|
|
19305
|
+
}
|
|
19306
|
+
}
|
|
19307
|
+
/**
|
|
19308
|
+
* Parar auto tracking
|
|
19309
|
+
*/
|
|
19310
|
+
stop() {
|
|
19311
|
+
document.removeEventListener("click", this.handleClick);
|
|
19312
|
+
document.removeEventListener("scroll", this.handleScroll);
|
|
19313
|
+
document.removeEventListener("mouseover", this.handleHover);
|
|
19314
|
+
document.removeEventListener("submit", this.handleForm);
|
|
19315
|
+
if (this.intersectionObserver) {
|
|
19316
|
+
this.intersectionObserver.disconnect();
|
|
19317
|
+
}
|
|
19318
|
+
this.observedElements.clear();
|
|
19319
|
+
}
|
|
19320
|
+
/**
|
|
19321
|
+
* Observar mudanças no DOM para novos elementos
|
|
19322
|
+
*/
|
|
19323
|
+
observeDOM() {
|
|
19324
|
+
const observer = new MutationObserver((mutations) => {
|
|
19325
|
+
mutations.forEach((mutation) => {
|
|
19326
|
+
mutation.addedNodes.forEach((node) => {
|
|
19327
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
19328
|
+
this.processElement(node);
|
|
19329
|
+
}
|
|
19330
|
+
});
|
|
19331
|
+
});
|
|
19332
|
+
});
|
|
19333
|
+
observer.observe(document.body, {
|
|
19334
|
+
childList: true,
|
|
19335
|
+
subtree: true,
|
|
19336
|
+
});
|
|
19337
|
+
}
|
|
19338
|
+
/**
|
|
19339
|
+
* Processar elementos já existentes no DOM
|
|
19340
|
+
*/
|
|
19341
|
+
processExistingElements() {
|
|
19342
|
+
// Buscar todos os elementos com data-track-*
|
|
19343
|
+
const trackElements = document.querySelectorAll("[data-track-click], [data-track-scroll], [data-track-view], [data-track-hover], [data-track-form]");
|
|
19344
|
+
trackElements.forEach((element) => {
|
|
19345
|
+
this.processElement(element);
|
|
19346
|
+
});
|
|
19347
|
+
}
|
|
19348
|
+
/**
|
|
19349
|
+
* Processar um elemento específico
|
|
19350
|
+
*/
|
|
19351
|
+
processElement(element) {
|
|
19352
|
+
// Click tracking
|
|
19353
|
+
if (this.config.trackClicks && element.hasAttribute("data-track-click")) {
|
|
19354
|
+
this.setupClickTracking(element);
|
|
19355
|
+
}
|
|
19356
|
+
// Scroll tracking
|
|
19357
|
+
if (this.config.trackScrolls && element.hasAttribute("data-track-scroll")) {
|
|
19358
|
+
this.setupScrollTracking(element);
|
|
19359
|
+
}
|
|
19360
|
+
// View tracking
|
|
19361
|
+
if (this.config.trackViews && element.hasAttribute("data-track-view")) {
|
|
19362
|
+
this.setupElementViewTracking(element);
|
|
19363
|
+
}
|
|
19364
|
+
// Hover tracking
|
|
19365
|
+
if (this.config.trackHovers && element.hasAttribute("data-track-hover")) {
|
|
19366
|
+
this.setupHoverTracking(element);
|
|
19367
|
+
}
|
|
19368
|
+
// Form tracking
|
|
19369
|
+
if (this.config.trackForms && element.hasAttribute("data-track-form")) {
|
|
19370
|
+
this.setupFormTracking(element);
|
|
19371
|
+
}
|
|
19372
|
+
}
|
|
19373
|
+
/**
|
|
19374
|
+
* Setup click tracking
|
|
19375
|
+
*/
|
|
19376
|
+
setupClickTracking(element) {
|
|
19377
|
+
element.addEventListener("click", (event) => {
|
|
19378
|
+
const eventName = element.getAttribute("data-track-click");
|
|
19379
|
+
if (!eventName)
|
|
19380
|
+
return;
|
|
19381
|
+
const metadata = this.extractMetadata(element);
|
|
19382
|
+
this.trackEvent(eventName, {
|
|
19383
|
+
type: "click",
|
|
19384
|
+
element: element.tagName.toLowerCase(),
|
|
19385
|
+
...metadata,
|
|
19386
|
+
});
|
|
19387
|
+
if (this.config.debug) {
|
|
19388
|
+
console.log(`[AutoTracker] Click tracked: ${eventName}`, metadata);
|
|
19389
|
+
}
|
|
19390
|
+
});
|
|
19391
|
+
}
|
|
19392
|
+
/**
|
|
19393
|
+
* Setup scroll tracking
|
|
19394
|
+
*/
|
|
19395
|
+
setupScrollTracking(element) {
|
|
19396
|
+
let hasTriggered = false;
|
|
19397
|
+
const threshold = parseFloat(element.getAttribute("data-scroll-threshold") || "0.5");
|
|
19398
|
+
const handleScroll = () => {
|
|
19399
|
+
if (hasTriggered)
|
|
19400
|
+
return;
|
|
19401
|
+
const rect = element.getBoundingClientRect();
|
|
19402
|
+
const elementHeight = rect.height;
|
|
19403
|
+
const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
|
|
19404
|
+
const visibilityRatio = Math.max(0, visibleHeight) / elementHeight;
|
|
19405
|
+
if (visibilityRatio >= threshold) {
|
|
19406
|
+
hasTriggered = true;
|
|
19407
|
+
const eventName = element.getAttribute("data-track-scroll");
|
|
19408
|
+
if (!eventName)
|
|
19409
|
+
return;
|
|
19410
|
+
const metadata = this.extractMetadata(element);
|
|
19411
|
+
this.trackEvent(eventName, {
|
|
19412
|
+
type: "scroll",
|
|
19413
|
+
element: element.tagName.toLowerCase(),
|
|
19414
|
+
threshold,
|
|
19415
|
+
scrollDepth: window.scrollY,
|
|
19416
|
+
...metadata,
|
|
19417
|
+
});
|
|
19418
|
+
if (this.config.debug) {
|
|
19419
|
+
console.log(`[AutoTracker] Scroll tracked: ${eventName}`, metadata);
|
|
19420
|
+
}
|
|
19421
|
+
}
|
|
19422
|
+
};
|
|
19423
|
+
document.addEventListener("scroll", handleScroll, { passive: true });
|
|
19424
|
+
// Check immediately in case element is already in view
|
|
19425
|
+
setTimeout(handleScroll, 100);
|
|
19426
|
+
}
|
|
19427
|
+
/**
|
|
19428
|
+
* Setup view tracking usando Intersection Observer
|
|
19429
|
+
*/
|
|
19430
|
+
setupViewTracking() {
|
|
19431
|
+
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
19432
|
+
entries.forEach((entry) => {
|
|
19433
|
+
if (entry.isIntersecting) {
|
|
19434
|
+
const element = entry.target;
|
|
19435
|
+
const eventName = element.getAttribute("data-track-view");
|
|
19436
|
+
if (!eventName)
|
|
19437
|
+
return;
|
|
19438
|
+
const metadata = this.extractMetadata(element);
|
|
19439
|
+
this.trackEvent(eventName, {
|
|
19440
|
+
type: "view",
|
|
19441
|
+
element: element.tagName.toLowerCase(),
|
|
19442
|
+
intersectionRatio: entry.intersectionRatio,
|
|
19443
|
+
...metadata,
|
|
19444
|
+
});
|
|
19445
|
+
if (this.config.debug) {
|
|
19446
|
+
console.log(`[AutoTracker] View tracked: ${eventName}`, metadata);
|
|
19447
|
+
}
|
|
19448
|
+
// Remove from observation after first trigger
|
|
19449
|
+
this.intersectionObserver?.unobserve(element);
|
|
19450
|
+
}
|
|
19451
|
+
});
|
|
19452
|
+
}, {
|
|
19453
|
+
threshold: 0.5, // 50% visible
|
|
19454
|
+
rootMargin: "0px",
|
|
19455
|
+
});
|
|
19456
|
+
}
|
|
19457
|
+
/**
|
|
19458
|
+
* Setup view tracking para elemento específico
|
|
19459
|
+
*/
|
|
19460
|
+
setupElementViewTracking(element) {
|
|
19461
|
+
if (!this.intersectionObserver)
|
|
19462
|
+
return;
|
|
19463
|
+
if (!this.observedElements.has(element)) {
|
|
19464
|
+
this.intersectionObserver.observe(element);
|
|
19465
|
+
this.observedElements.add(element);
|
|
19466
|
+
}
|
|
19467
|
+
}
|
|
19468
|
+
/**
|
|
19469
|
+
* Setup hover tracking
|
|
19470
|
+
*/
|
|
19471
|
+
setupHoverTracking(element) {
|
|
19472
|
+
let hoverStartTime;
|
|
19473
|
+
const minHoverTime = parseInt(element.getAttribute("data-hover-time") || "1000");
|
|
19474
|
+
element.addEventListener("mouseenter", () => {
|
|
19475
|
+
hoverStartTime = Date.now();
|
|
19476
|
+
});
|
|
19477
|
+
element.addEventListener("mouseleave", () => {
|
|
19478
|
+
const hoverDuration = Date.now() - hoverStartTime;
|
|
19479
|
+
if (hoverDuration >= minHoverTime) {
|
|
19480
|
+
const eventName = element.getAttribute("data-track-hover");
|
|
19481
|
+
if (!eventName)
|
|
19482
|
+
return;
|
|
19483
|
+
const metadata = this.extractMetadata(element);
|
|
19484
|
+
this.trackEvent(eventName, {
|
|
19485
|
+
type: "hover",
|
|
19486
|
+
element: element.tagName.toLowerCase(),
|
|
19487
|
+
hoverDuration,
|
|
19488
|
+
minHoverTime,
|
|
19489
|
+
...metadata,
|
|
19490
|
+
});
|
|
19491
|
+
if (this.config.debug) {
|
|
19492
|
+
console.log(`[AutoTracker] Hover tracked: ${eventName}`, metadata);
|
|
19493
|
+
}
|
|
19494
|
+
}
|
|
19495
|
+
});
|
|
19496
|
+
}
|
|
19497
|
+
/**
|
|
19498
|
+
* Setup form tracking
|
|
19499
|
+
*/
|
|
19500
|
+
setupFormTracking(element) {
|
|
19501
|
+
if (element.tagName.toLowerCase() !== "form")
|
|
19502
|
+
return;
|
|
19503
|
+
element.addEventListener("submit", (event) => {
|
|
19504
|
+
const eventName = element.getAttribute("data-track-form");
|
|
19505
|
+
if (!eventName)
|
|
19506
|
+
return;
|
|
19507
|
+
const formData = new FormData(element);
|
|
19508
|
+
const metadata = this.extractMetadata(element);
|
|
19509
|
+
// Extract form fields (without sensitive data)
|
|
19510
|
+
const fields = {};
|
|
19511
|
+
for (const [key, value] of formData.entries()) {
|
|
19512
|
+
// Skip sensitive fields
|
|
19513
|
+
if (!this.isSensitiveField(key)) {
|
|
19514
|
+
fields[key] =
|
|
19515
|
+
typeof value === "string" ? value.substring(0, 100) : "file";
|
|
19516
|
+
}
|
|
19517
|
+
}
|
|
19518
|
+
this.trackEvent(eventName, {
|
|
19519
|
+
type: "form_submit",
|
|
19520
|
+
element: "form",
|
|
19521
|
+
fieldCount: Array.from(formData.entries()).length,
|
|
19522
|
+
fields,
|
|
19523
|
+
...metadata,
|
|
19524
|
+
});
|
|
19525
|
+
if (this.config.debug) {
|
|
19526
|
+
console.log(`[AutoTracker] Form submit tracked: ${eventName}`, metadata);
|
|
19527
|
+
}
|
|
19528
|
+
});
|
|
19529
|
+
}
|
|
19530
|
+
/**
|
|
19531
|
+
* Extrair metadata do elemento
|
|
19532
|
+
*/
|
|
19533
|
+
extractMetadata(element) {
|
|
19534
|
+
const metadata = {};
|
|
19535
|
+
// Extrair todos os data-meta-* attributes
|
|
19536
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
19537
|
+
if (attr.name.startsWith("data-meta-")) {
|
|
19538
|
+
const key = attr.name.replace("data-meta-", "");
|
|
19539
|
+
metadata[key] = attr.value;
|
|
19540
|
+
}
|
|
19541
|
+
});
|
|
19542
|
+
// Adicionar informações básicas
|
|
19543
|
+
if (element.id)
|
|
19544
|
+
metadata.elementId = element.id;
|
|
19545
|
+
if (element.className)
|
|
19546
|
+
metadata.elementClass = element.className;
|
|
19547
|
+
// Text content (limitado)
|
|
19548
|
+
const textContent = element.textContent?.trim();
|
|
19549
|
+
if (textContent && textContent.length > 0) {
|
|
19550
|
+
metadata.textContent = textContent.substring(0, 50);
|
|
19551
|
+
}
|
|
19552
|
+
// Position info
|
|
19553
|
+
const rect = element.getBoundingClientRect();
|
|
19554
|
+
metadata.elementPosition = {
|
|
19555
|
+
x: Math.round(rect.left),
|
|
19556
|
+
y: Math.round(rect.top),
|
|
19557
|
+
width: Math.round(rect.width),
|
|
19558
|
+
height: Math.round(rect.height),
|
|
19559
|
+
};
|
|
19560
|
+
return metadata;
|
|
19561
|
+
}
|
|
19562
|
+
/**
|
|
19563
|
+
* Verificar se um campo é sensível
|
|
19564
|
+
*/
|
|
19565
|
+
isSensitiveField(fieldName) {
|
|
19566
|
+
const sensitivePatterns = [
|
|
19567
|
+
/password/i,
|
|
19568
|
+
/pass/i,
|
|
19569
|
+
/pwd/i,
|
|
19570
|
+
/secret/i,
|
|
19571
|
+
/token/i,
|
|
19572
|
+
/api[_-]?key/i,
|
|
19573
|
+
/credit[_-]?card/i,
|
|
19574
|
+
/ssn/i,
|
|
19575
|
+
/social/i,
|
|
19576
|
+
/tax/i,
|
|
19577
|
+
];
|
|
19578
|
+
return sensitivePatterns.some((pattern) => pattern.test(fieldName));
|
|
19579
|
+
}
|
|
19580
|
+
/**
|
|
19581
|
+
* Enviar evento para o SDK
|
|
19582
|
+
*/
|
|
19583
|
+
trackEvent(eventName, metadata) {
|
|
19584
|
+
if (!this.sdkInstance)
|
|
19585
|
+
return;
|
|
19586
|
+
try {
|
|
19587
|
+
this.sdkInstance.trackCustomEvent(eventName, {
|
|
19588
|
+
autoTracked: true,
|
|
19589
|
+
timestamp: Date.now(),
|
|
19590
|
+
url: window.location.href,
|
|
19591
|
+
...metadata,
|
|
19592
|
+
});
|
|
19593
|
+
}
|
|
19594
|
+
catch (error) {
|
|
19595
|
+
if (this.config.debug) {
|
|
19596
|
+
console.error("[AutoTracker] Error sending event:", error);
|
|
19597
|
+
}
|
|
19598
|
+
}
|
|
19599
|
+
}
|
|
19600
|
+
/**
|
|
19601
|
+
* Configurar tracking
|
|
19602
|
+
*/
|
|
19603
|
+
configure(config) {
|
|
19604
|
+
this.config = { ...this.config, ...config };
|
|
19605
|
+
}
|
|
19606
|
+
/**
|
|
19607
|
+
* Obter estatísticas
|
|
19608
|
+
*/
|
|
19609
|
+
getStats() {
|
|
19610
|
+
return {
|
|
19611
|
+
observedElements: this.observedElements.size,
|
|
19612
|
+
config: this.config,
|
|
19613
|
+
};
|
|
19614
|
+
}
|
|
19615
|
+
}
|
|
19616
|
+
|
|
19617
|
+
/**
|
|
19618
|
+
* Visitor Persistence Manager for SDK
|
|
19619
|
+
* Implements client-side visitor identification with localStorage camouflage
|
|
19620
|
+
*/
|
|
19621
|
+
// Camuflaged storage keys to avoid detection/blocking
|
|
19622
|
+
const STORAGE_KEYS = {
|
|
19623
|
+
session: "__zp_s", // Session identifier
|
|
19624
|
+
visitor: "__zp_v", // Visitor identifier (camuflado)
|
|
19625
|
+
device: "__zp_d", // Device fingerprint cache
|
|
19626
|
+
prefs: "__zp_p", // User preferences (decoy storage)
|
|
19627
|
+
analytics: "__zp_a", // Analytics preferences (additional decoy)
|
|
19628
|
+
};
|
|
19629
|
+
/**
|
|
19630
|
+
* Multi-layer persistence manager with fallbacks
|
|
19631
|
+
*/
|
|
19632
|
+
class PersistenceManager {
|
|
19633
|
+
// 1. LocalStorage (primary) - Most persistent
|
|
19634
|
+
static setLocal(key, value) {
|
|
19635
|
+
try {
|
|
19636
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
19637
|
+
localStorage.setItem(key, value);
|
|
19638
|
+
return true;
|
|
19639
|
+
}
|
|
19640
|
+
}
|
|
19641
|
+
catch (e) {
|
|
19642
|
+
// Storage disabled/private mode
|
|
19643
|
+
}
|
|
19644
|
+
return false;
|
|
19645
|
+
}
|
|
19646
|
+
static getLocal(key) {
|
|
19647
|
+
try {
|
|
19648
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
19649
|
+
return localStorage.getItem(key);
|
|
19650
|
+
}
|
|
19651
|
+
}
|
|
19652
|
+
catch (e) {
|
|
19653
|
+
// Storage disabled/private mode
|
|
19654
|
+
}
|
|
19655
|
+
return null;
|
|
19656
|
+
}
|
|
19657
|
+
// 2. SessionStorage (secondary) - Session-only
|
|
19658
|
+
static setSession(key, value) {
|
|
19659
|
+
try {
|
|
19660
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
19661
|
+
sessionStorage.setItem(key, value);
|
|
19662
|
+
return true;
|
|
19663
|
+
}
|
|
19664
|
+
}
|
|
19665
|
+
catch (e) {
|
|
19666
|
+
// Storage disabled/private mode
|
|
19667
|
+
}
|
|
19668
|
+
return false;
|
|
19669
|
+
}
|
|
19670
|
+
static getSession(key) {
|
|
19671
|
+
try {
|
|
19672
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
19673
|
+
return sessionStorage.getItem(key);
|
|
19674
|
+
}
|
|
19675
|
+
}
|
|
19676
|
+
catch (e) {
|
|
19677
|
+
// Storage disabled/private mode
|
|
19678
|
+
}
|
|
19679
|
+
return null;
|
|
19680
|
+
}
|
|
19681
|
+
// 3. Cookie (tertiary) - Cross-session with expiration
|
|
19682
|
+
static setCookie(key, value, days = 365) {
|
|
19683
|
+
try {
|
|
19684
|
+
if (typeof document !== "undefined") {
|
|
19685
|
+
const expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();
|
|
19686
|
+
document.cookie = `${key}=${value}; expires=${expires}; path=/; SameSite=Lax`;
|
|
19687
|
+
return true;
|
|
19688
|
+
}
|
|
19689
|
+
}
|
|
19690
|
+
catch (e) {
|
|
19691
|
+
// Cookies disabled
|
|
19692
|
+
}
|
|
19693
|
+
return false;
|
|
19694
|
+
}
|
|
19695
|
+
static getCookie(key) {
|
|
19696
|
+
try {
|
|
19697
|
+
if (typeof document !== "undefined") {
|
|
19698
|
+
const name = key + "=";
|
|
19699
|
+
const decodedCookie = decodeURIComponent(document.cookie);
|
|
19700
|
+
const ca = decodedCookie.split(";");
|
|
19701
|
+
for (let i = 0; i < ca.length; i++) {
|
|
19702
|
+
let c = ca[i];
|
|
19703
|
+
if (c) {
|
|
19704
|
+
while (c.charAt(0) === " ") {
|
|
19705
|
+
c = c.substring(1);
|
|
19706
|
+
}
|
|
19707
|
+
if (c.indexOf(name) === 0) {
|
|
19708
|
+
return c.substring(name.length, c.length);
|
|
19709
|
+
}
|
|
19710
|
+
}
|
|
19711
|
+
}
|
|
19712
|
+
}
|
|
19713
|
+
}
|
|
19714
|
+
catch (e) {
|
|
19715
|
+
// Cookies disabled
|
|
19716
|
+
}
|
|
19717
|
+
return null;
|
|
19718
|
+
}
|
|
19719
|
+
// 4. Memory (fallback) - Current session only
|
|
19720
|
+
static setMemory(key, value) {
|
|
19721
|
+
this.memoryStore.set(key, value);
|
|
19722
|
+
return true;
|
|
19723
|
+
}
|
|
19724
|
+
static getMemory(key) {
|
|
19725
|
+
return this.memoryStore.get(key) || null;
|
|
19726
|
+
}
|
|
19727
|
+
// Multi-layer get with fallbacks
|
|
19728
|
+
static get(key) {
|
|
19729
|
+
// Try localStorage first (most persistent)
|
|
19730
|
+
let value = this.getLocal(key);
|
|
19731
|
+
if (value)
|
|
19732
|
+
return { value, method: "localStorage" };
|
|
19733
|
+
// Try sessionStorage (session-only)
|
|
19734
|
+
value = this.getSession(key);
|
|
19735
|
+
if (value)
|
|
19736
|
+
return { value, method: "sessionStorage" };
|
|
19737
|
+
// Try cookies (cross-session)
|
|
19738
|
+
value = this.getCookie(key);
|
|
19739
|
+
if (value)
|
|
19740
|
+
return { value, method: "cookie" };
|
|
19741
|
+
// Try memory (current session)
|
|
19742
|
+
value = this.getMemory(key);
|
|
19743
|
+
if (value)
|
|
19744
|
+
return { value, method: "memory" };
|
|
19745
|
+
return { value: null, method: "none" };
|
|
19746
|
+
}
|
|
19747
|
+
// Multi-layer set with fallbacks
|
|
19748
|
+
static set(key, value) {
|
|
19749
|
+
// Try localStorage first (most persistent)
|
|
19750
|
+
if (this.setLocal(key, value)) {
|
|
19751
|
+
return { success: true, method: "localStorage" };
|
|
19752
|
+
}
|
|
19753
|
+
// Try sessionStorage (session-only)
|
|
19754
|
+
if (this.setSession(key, value)) {
|
|
19755
|
+
return { success: true, method: "sessionStorage" };
|
|
19756
|
+
}
|
|
19757
|
+
// Try cookies (cross-session)
|
|
19758
|
+
if (this.setCookie(key, value)) {
|
|
19759
|
+
return { success: true, method: "cookie" };
|
|
19760
|
+
}
|
|
19761
|
+
// Fallback to memory (current session)
|
|
19762
|
+
this.setMemory(key, value);
|
|
19763
|
+
return { success: true, method: "memory" };
|
|
19764
|
+
}
|
|
19765
|
+
}
|
|
19766
|
+
PersistenceManager.memoryStore = new Map();
|
|
19767
|
+
/**
|
|
19768
|
+
* Advanced Visitor Identity Manager with Camouflaged Storage
|
|
19769
|
+
*/
|
|
19770
|
+
class VisitorIdentityManager {
|
|
19771
|
+
generateVisitorId() {
|
|
19772
|
+
return "vis_" + Array.from(crypto.getRandomValues(new Uint8Array(16)))
|
|
19773
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
19774
|
+
.join('');
|
|
19775
|
+
}
|
|
19776
|
+
generateSessionId() {
|
|
19777
|
+
return "ses_" + Array.from(crypto.getRandomValues(new Uint8Array(8)))
|
|
19778
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
19779
|
+
.join('') + "_" + Date.now().toString(36);
|
|
19780
|
+
}
|
|
19781
|
+
createCamouflageData(visitorId, sessionId, stableCoreHash) {
|
|
19782
|
+
return {
|
|
19783
|
+
theme: "auto",
|
|
19784
|
+
lang: (navigator.language || "en-US").substring(0, 2),
|
|
19785
|
+
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
19786
|
+
analytics: true,
|
|
19787
|
+
cookies: true,
|
|
19788
|
+
_v: visitorId, // Hidden visitor ID
|
|
19789
|
+
_s: sessionId, // Hidden session ID
|
|
19790
|
+
_sc: stableCoreHash, // Hidden stable core
|
|
19791
|
+
ts: Date.now(),
|
|
19792
|
+
};
|
|
19793
|
+
}
|
|
19794
|
+
extractFromCamouflage(data) {
|
|
19795
|
+
try {
|
|
19796
|
+
const parsed = JSON.parse(data);
|
|
19797
|
+
if (parsed._v && parsed._s && parsed._sc) {
|
|
19798
|
+
return {
|
|
19799
|
+
visitorId: parsed._v,
|
|
19800
|
+
sessionId: parsed._s,
|
|
19801
|
+
stableCoreHash: parsed._sc,
|
|
19802
|
+
persistenceMethod: "localStorage",
|
|
19803
|
+
confidence: 0.95,
|
|
19804
|
+
createdAt: parsed.ts || Date.now(),
|
|
19805
|
+
lastSeen: Date.now(),
|
|
19806
|
+
reused: true,
|
|
19807
|
+
};
|
|
19808
|
+
}
|
|
19809
|
+
}
|
|
19810
|
+
catch (e) {
|
|
19811
|
+
// Invalid data
|
|
19812
|
+
}
|
|
19813
|
+
return null;
|
|
19814
|
+
}
|
|
19815
|
+
async getOrCreateVisitorIdentity(params) {
|
|
19816
|
+
const { stableCoreHash } = params;
|
|
19817
|
+
// Try to recover existing identity from camouflaged storage
|
|
19818
|
+
const { value: storedData, method } = PersistenceManager.get(STORAGE_KEYS.prefs);
|
|
19819
|
+
if (storedData) {
|
|
19820
|
+
const existingIdentity = this.extractFromCamouflage(storedData);
|
|
19821
|
+
if (existingIdentity && existingIdentity.stableCoreHash === stableCoreHash) {
|
|
19822
|
+
// Update last seen time
|
|
19823
|
+
const updatedCamouflage = this.createCamouflageData(existingIdentity.visitorId, existingIdentity.sessionId, stableCoreHash);
|
|
19824
|
+
PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(updatedCamouflage));
|
|
19825
|
+
return {
|
|
19826
|
+
...existingIdentity,
|
|
19827
|
+
persistenceMethod: method,
|
|
19828
|
+
lastSeen: Date.now(),
|
|
19829
|
+
reused: true,
|
|
19830
|
+
};
|
|
19831
|
+
}
|
|
19832
|
+
}
|
|
19833
|
+
// Create new visitor identity
|
|
19834
|
+
const newVisitorId = this.generateVisitorId();
|
|
19835
|
+
const newSessionId = params.sessionId || this.generateSessionId();
|
|
19836
|
+
const newIdentity = {
|
|
19837
|
+
visitorId: newVisitorId,
|
|
19838
|
+
sessionId: newSessionId,
|
|
19839
|
+
stableCoreHash,
|
|
19840
|
+
deviceFingerprint: params.deviceFingerprint,
|
|
19841
|
+
persistenceMethod: "localStorage",
|
|
19842
|
+
confidence: 0.90,
|
|
19843
|
+
createdAt: Date.now(),
|
|
19844
|
+
lastSeen: Date.now(),
|
|
19845
|
+
reused: false,
|
|
19846
|
+
};
|
|
19847
|
+
// Store in camouflaged format
|
|
19848
|
+
const camouflageData = this.createCamouflageData(newVisitorId, newSessionId, stableCoreHash);
|
|
19849
|
+
const { method: storageMethod } = PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(camouflageData));
|
|
19850
|
+
newIdentity.persistenceMethod = storageMethod;
|
|
19851
|
+
// Also store device fingerprint cache for faster lookups
|
|
19852
|
+
PersistenceManager.set(STORAGE_KEYS.device, params.deviceFingerprint);
|
|
19853
|
+
return newIdentity;
|
|
19854
|
+
}
|
|
19855
|
+
// Get current visitor ID without creating new one
|
|
19856
|
+
getCurrentVisitorId() {
|
|
19857
|
+
const { value: storedData } = PersistenceManager.get(STORAGE_KEYS.prefs);
|
|
19858
|
+
if (storedData) {
|
|
19859
|
+
const identity = this.extractFromCamouflage(storedData);
|
|
19860
|
+
return identity?.visitorId || null;
|
|
19861
|
+
}
|
|
19862
|
+
return null;
|
|
19863
|
+
}
|
|
19864
|
+
// Clear all stored identity data
|
|
19865
|
+
clearIdentity() {
|
|
19866
|
+
// Remove from all storage layers
|
|
19867
|
+
try {
|
|
19868
|
+
PersistenceManager.setLocal(STORAGE_KEYS.prefs, "");
|
|
19869
|
+
PersistenceManager.setLocal(STORAGE_KEYS.device, "");
|
|
19870
|
+
PersistenceManager.setSession(STORAGE_KEYS.prefs, "");
|
|
19871
|
+
PersistenceManager.setSession(STORAGE_KEYS.device, "");
|
|
19872
|
+
PersistenceManager.setCookie(STORAGE_KEYS.prefs, "", -1); // Expire immediately
|
|
19873
|
+
PersistenceManager.setCookie(STORAGE_KEYS.device, "", -1);
|
|
19874
|
+
PersistenceManager.setMemory(STORAGE_KEYS.prefs, "");
|
|
19875
|
+
PersistenceManager.setMemory(STORAGE_KEYS.device, "");
|
|
19876
|
+
}
|
|
19877
|
+
catch (e) {
|
|
19878
|
+
// Silent fail
|
|
19879
|
+
}
|
|
19880
|
+
}
|
|
19154
19881
|
}
|
|
19155
19882
|
|
|
19156
19883
|
/**
|
|
@@ -19184,7 +19911,7 @@
|
|
|
19184
19911
|
*/
|
|
19185
19912
|
class ZaplierSDK {
|
|
19186
19913
|
constructor(userConfig) {
|
|
19187
|
-
this.version = "1.
|
|
19914
|
+
this.version = "1.6.0";
|
|
19188
19915
|
this.isInitialized = false;
|
|
19189
19916
|
this.eventQueue = [];
|
|
19190
19917
|
/**
|
|
@@ -19255,8 +19982,10 @@
|
|
|
19255
19982
|
}
|
|
19256
19983
|
const sessionId = this.sessionId;
|
|
19257
19984
|
// When explicitly enabled, use 100% sample rate
|
|
19258
|
-
this.replayEngine = new SessionReplayEngine(sessionId, {
|
|
19985
|
+
this.replayEngine = new SessionReplayEngine(sessionId, this.backendVisitorId || 'unknown', {
|
|
19259
19986
|
sampleRate: 1.0, // Force 100% when explicitly enabled
|
|
19987
|
+
inactivityTimeout: 30000,
|
|
19988
|
+
pauseOnInactive: true,
|
|
19260
19989
|
});
|
|
19261
19990
|
// Connect to anti-adblock manager
|
|
19262
19991
|
if (this.antiAdblockManager) {
|
|
@@ -19281,8 +20010,10 @@
|
|
|
19281
20010
|
this.sessionId = this.generateSessionId();
|
|
19282
20011
|
}
|
|
19283
20012
|
const sessionId = this.sessionId;
|
|
19284
|
-
this.replayEngine = new SessionReplayEngine(sessionId, {
|
|
20013
|
+
this.replayEngine = new SessionReplayEngine(sessionId, this.backendVisitorId || 'unknown', {
|
|
19285
20014
|
sampleRate: 1.0, // Force 100% when explicitly enabled
|
|
20015
|
+
inactivityTimeout: 30000,
|
|
20016
|
+
pauseOnInactive: true,
|
|
19286
20017
|
});
|
|
19287
20018
|
// Connect to anti-adblock manager
|
|
19288
20019
|
if (this.antiAdblockManager) {
|
|
@@ -19321,8 +20052,10 @@
|
|
|
19321
20052
|
this.sessionId = this.generateSessionId();
|
|
19322
20053
|
}
|
|
19323
20054
|
const sessionId = this.sessionId;
|
|
19324
|
-
this.replayEngine = new SessionReplayEngine(sessionId, {
|
|
20055
|
+
this.replayEngine = new SessionReplayEngine(sessionId, this.backendVisitorId || 'unknown', {
|
|
19325
20056
|
sampleRate: 1.0, // Force recording when manually started
|
|
20057
|
+
inactivityTimeout: 30000,
|
|
20058
|
+
pauseOnInactive: true,
|
|
19326
20059
|
});
|
|
19327
20060
|
// Connect to anti-adblock manager
|
|
19328
20061
|
if (this.antiAdblockManager) {
|
|
@@ -19354,6 +20087,45 @@
|
|
|
19354
20087
|
this.trackConversion("funnel_conversion", data.value, data.currency, data);
|
|
19355
20088
|
},
|
|
19356
20089
|
};
|
|
20090
|
+
/**
|
|
20091
|
+
* Auto Tracker API
|
|
20092
|
+
*/
|
|
20093
|
+
this.autoTrack = {
|
|
20094
|
+
enable: () => {
|
|
20095
|
+
if (!this.autoTracker) {
|
|
20096
|
+
this.initializeAutoTracker();
|
|
20097
|
+
}
|
|
20098
|
+
else {
|
|
20099
|
+
this.autoTracker.configure({ enabled: true });
|
|
20100
|
+
}
|
|
20101
|
+
if (this.config.debug) {
|
|
20102
|
+
console.log("[Zaplier] Auto tracking enabled");
|
|
20103
|
+
}
|
|
20104
|
+
},
|
|
20105
|
+
disable: () => {
|
|
20106
|
+
if (this.autoTracker) {
|
|
20107
|
+
this.autoTracker.stop();
|
|
20108
|
+
this.autoTracker = undefined;
|
|
20109
|
+
}
|
|
20110
|
+
if (this.config.debug) {
|
|
20111
|
+
console.log("[Zaplier] Auto tracking disabled");
|
|
20112
|
+
}
|
|
20113
|
+
},
|
|
20114
|
+
configure: (config) => {
|
|
20115
|
+
if (this.autoTracker) {
|
|
20116
|
+
this.autoTracker.configure(config);
|
|
20117
|
+
}
|
|
20118
|
+
if (this.config.debug) {
|
|
20119
|
+
console.log("[Zaplier] Auto tracking configured:", config);
|
|
20120
|
+
}
|
|
20121
|
+
},
|
|
20122
|
+
getStats: () => {
|
|
20123
|
+
return this.autoTracker ? this.autoTracker.getStats() : null;
|
|
20124
|
+
},
|
|
20125
|
+
isEnabled: () => {
|
|
20126
|
+
return !!this.autoTracker;
|
|
20127
|
+
},
|
|
20128
|
+
};
|
|
19357
20129
|
// Validate required config
|
|
19358
20130
|
if (!userConfig.token) {
|
|
19359
20131
|
throw new Error("Zaplier: token is required");
|
|
@@ -19395,6 +20167,8 @@
|
|
|
19395
20167
|
this.initializeAntiAdblock();
|
|
19396
20168
|
// Then initialize tracking engines (they'll connect to anti-adblock)
|
|
19397
20169
|
this.initializeTrackingEngines();
|
|
20170
|
+
// Initialize auto tracker
|
|
20171
|
+
this.initializeAutoTracker();
|
|
19398
20172
|
// Process queued events
|
|
19399
20173
|
this.processEventQueue();
|
|
19400
20174
|
if (this.config.debug) {
|
|
@@ -19425,8 +20199,10 @@
|
|
|
19425
20199
|
// When replay is explicitly enabled, use 100% sample rate to ensure recording
|
|
19426
20200
|
// The default replaySampling (0.1) is only for automatic/production sampling
|
|
19427
20201
|
const sampleRate = this.config.replay === true ? 1.0 : this.config.replaySampling;
|
|
19428
|
-
this.replayEngine = new SessionReplayEngine(this.sessionId, {
|
|
20202
|
+
this.replayEngine = new SessionReplayEngine(this.sessionId, this.backendVisitorId || 'unknown', {
|
|
19429
20203
|
sampleRate: sampleRate,
|
|
20204
|
+
inactivityTimeout: 30000,
|
|
20205
|
+
pauseOnInactive: true,
|
|
19430
20206
|
});
|
|
19431
20207
|
// Connect SDK instance for transport
|
|
19432
20208
|
this.replayEngine.setSDKInstance(this);
|
|
@@ -19461,6 +20237,29 @@
|
|
|
19461
20237
|
console.error("[Zaplier] Failed to initialize tracking engines:", error);
|
|
19462
20238
|
}
|
|
19463
20239
|
}
|
|
20240
|
+
/**
|
|
20241
|
+
* Initialize Auto Tracker for data-* attributes
|
|
20242
|
+
*/
|
|
20243
|
+
initializeAutoTracker() {
|
|
20244
|
+
try {
|
|
20245
|
+
this.autoTracker = new AutoTracker(this, {
|
|
20246
|
+
enabled: true,
|
|
20247
|
+
trackClicks: true,
|
|
20248
|
+
trackScrolls: true,
|
|
20249
|
+
trackViews: true,
|
|
20250
|
+
trackHovers: true,
|
|
20251
|
+
trackForms: true,
|
|
20252
|
+
debug: this.config.debug,
|
|
20253
|
+
});
|
|
20254
|
+
this.autoTracker.start();
|
|
20255
|
+
if (this.config.debug) {
|
|
20256
|
+
console.log("[Zaplier] Auto Tracker initialized and started");
|
|
20257
|
+
}
|
|
20258
|
+
}
|
|
20259
|
+
catch (error) {
|
|
20260
|
+
console.error("[Zaplier] Failed to initialize Auto Tracker:", error);
|
|
20261
|
+
}
|
|
20262
|
+
}
|
|
19464
20263
|
/**
|
|
19465
20264
|
* Generate session ID
|
|
19466
20265
|
*/
|
|
@@ -19516,10 +20315,12 @@
|
|
|
19516
20315
|
}
|
|
19517
20316
|
/**
|
|
19518
20317
|
* Collect fingerprint and generate visitor ID (IMPROVED - 2024)
|
|
19519
|
-
* Enhanced with better incognito detection
|
|
20318
|
+
* Enhanced with better incognito detection and localStorage persistence
|
|
19520
20319
|
*/
|
|
19521
20320
|
async collectFingerprint() {
|
|
19522
20321
|
try {
|
|
20322
|
+
// Initialize visitor identity manager
|
|
20323
|
+
this.visitorIdentityManager = new VisitorIdentityManager();
|
|
19523
20324
|
// Use appropriate fingerprinting based on GDPR mode
|
|
19524
20325
|
const result = this.config.gdprMode
|
|
19525
20326
|
? await getLightweightFingerprint()
|
|
@@ -19537,6 +20338,26 @@
|
|
|
19537
20338
|
sdkVersion: this.version,
|
|
19538
20339
|
};
|
|
19539
20340
|
}
|
|
20341
|
+
// Generate or retrieve visitor identity using persistent storage
|
|
20342
|
+
const visitorIdentity = await this.visitorIdentityManager.getOrCreateVisitorIdentity({
|
|
20343
|
+
fingerprintHash: result.data.hash,
|
|
20344
|
+
stableCoreHash: result.data.stableCoreHash,
|
|
20345
|
+
deviceFingerprint: result.data.hash,
|
|
20346
|
+
sessionId: this.sessionId,
|
|
20347
|
+
userAgent: navigator.userAgent,
|
|
20348
|
+
ipAddress: undefined, // Will be determined server-side
|
|
20349
|
+
});
|
|
20350
|
+
// Set the persistent visitor ID
|
|
20351
|
+
this.visitorId = visitorIdentity.visitorId;
|
|
20352
|
+
if (this.config.debug) {
|
|
20353
|
+
console.log("[Zaplier] Visitor identity resolved:", {
|
|
20354
|
+
visitorId: this.visitorId,
|
|
20355
|
+
sessionId: this.sessionId,
|
|
20356
|
+
persistenceMethod: visitorIdentity.persistenceMethod,
|
|
20357
|
+
confidence: visitorIdentity.confidence,
|
|
20358
|
+
isNewVisitor: !visitorIdentity.reused,
|
|
20359
|
+
});
|
|
20360
|
+
}
|
|
19540
20361
|
if (this.config.debug) {
|
|
19541
20362
|
console.log("[Zaplier] Fingerprint collected:", {
|
|
19542
20363
|
components: result.collectedComponents,
|
|
@@ -19643,6 +20464,10 @@
|
|
|
19643
20464
|
try {
|
|
19644
20465
|
const payload = {
|
|
19645
20466
|
// workspaceId moved to query parameter
|
|
20467
|
+
// Visitor ID (persistido via localStorage camuflado)
|
|
20468
|
+
visitorId: this.visitorId,
|
|
20469
|
+
// Session ID (gerado por sessão)
|
|
20470
|
+
sessionId: this.sessionId,
|
|
19646
20471
|
fingerprintHash: this.fingerprint?.hash,
|
|
19647
20472
|
stableCoreHash: this.fingerprint?.stableCoreHash,
|
|
19648
20473
|
stableCoreVector: this.fingerprint?.stableCoreVector,
|
|
@@ -20403,6 +21228,44 @@
|
|
|
20403
21228
|
globalInstance.replay.markConversion(data);
|
|
20404
21229
|
}
|
|
20405
21230
|
},
|
|
21231
|
+
/**
|
|
21232
|
+
* Auto Tracker API
|
|
21233
|
+
*/
|
|
21234
|
+
autoTrack: {
|
|
21235
|
+
enable: () => {
|
|
21236
|
+
if (!globalInstance) {
|
|
21237
|
+
console.warn('[Zaplier] SDK not initialized. Call Zaplier.init() first');
|
|
21238
|
+
return;
|
|
21239
|
+
}
|
|
21240
|
+
globalInstance.autoTrack.enable();
|
|
21241
|
+
},
|
|
21242
|
+
disable: () => {
|
|
21243
|
+
if (!globalInstance) {
|
|
21244
|
+
console.warn('[Zaplier] SDK not initialized. Call Zaplier.init() first');
|
|
21245
|
+
return;
|
|
21246
|
+
}
|
|
21247
|
+
globalInstance.autoTrack.disable();
|
|
21248
|
+
},
|
|
21249
|
+
configure: (config) => {
|
|
21250
|
+
if (!globalInstance) {
|
|
21251
|
+
console.warn('[Zaplier] SDK not initialized. Call Zaplier.init() first');
|
|
21252
|
+
return;
|
|
21253
|
+
}
|
|
21254
|
+
globalInstance.autoTrack.configure(config);
|
|
21255
|
+
},
|
|
21256
|
+
getStats: () => {
|
|
21257
|
+
if (!globalInstance) {
|
|
21258
|
+
return null;
|
|
21259
|
+
}
|
|
21260
|
+
return globalInstance.autoTrack.getStats();
|
|
21261
|
+
},
|
|
21262
|
+
isEnabled: () => {
|
|
21263
|
+
if (!globalInstance) {
|
|
21264
|
+
return false;
|
|
21265
|
+
}
|
|
21266
|
+
return globalInstance.autoTrack.isEnabled();
|
|
21267
|
+
}
|
|
21268
|
+
},
|
|
20406
21269
|
/**
|
|
20407
21270
|
* Debug and utility functions
|
|
20408
21271
|
*/
|
|
@@ -20429,7 +21292,7 @@
|
|
|
20429
21292
|
/**
|
|
20430
21293
|
* Version info
|
|
20431
21294
|
*/
|
|
20432
|
-
version: '
|
|
21295
|
+
version: '1.6.0'
|
|
20433
21296
|
};
|
|
20434
21297
|
/**
|
|
20435
21298
|
* Auto-initialization from script tag data attributes
|