@agroideas/event-tracker-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +308 -0
- package/dist/index.d.mts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.global.js +722 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +749 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +727 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
destroy: () => destroy,
|
|
25
|
+
flush: () => flush,
|
|
26
|
+
identify: () => identify,
|
|
27
|
+
init: () => init,
|
|
28
|
+
tracker: () => tracker
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/core/EventBuffer.ts
|
|
33
|
+
var BATCH_INTERVAL = 1e4;
|
|
34
|
+
var MAX_BUFFER_SIZE = 15;
|
|
35
|
+
var EventBuffer = class {
|
|
36
|
+
constructor(config) {
|
|
37
|
+
this._flushTimer = null;
|
|
38
|
+
this._buffer = [];
|
|
39
|
+
this._endpoint = config.endpoint;
|
|
40
|
+
this._apiKey = config.apiKey;
|
|
41
|
+
this._isDebug = config.debug == "true";
|
|
42
|
+
console.log(config);
|
|
43
|
+
this._config = {
|
|
44
|
+
batchInterval: config.batchInterval ?? BATCH_INTERVAL,
|
|
45
|
+
maxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE
|
|
46
|
+
};
|
|
47
|
+
if (this._isDebug) {
|
|
48
|
+
console.log("[EventTracker] Modo DEBUG activado");
|
|
49
|
+
console.log("[EventTracker] Endpoint:", this._endpoint);
|
|
50
|
+
console.log("[EventTracker] API Key:", this._apiKey);
|
|
51
|
+
}
|
|
52
|
+
if (!this._isDebug) {
|
|
53
|
+
this._startFlushTimer();
|
|
54
|
+
}
|
|
55
|
+
this._setupVisibilityListener();
|
|
56
|
+
}
|
|
57
|
+
/** Agrega un evento al buffer */
|
|
58
|
+
add(event) {
|
|
59
|
+
try {
|
|
60
|
+
this._buffer.push(event);
|
|
61
|
+
if (this._isDebug) {
|
|
62
|
+
console.log("[EventTracker] Evento agregado:", event.eventType);
|
|
63
|
+
console.log("[EventTracker] Buffer size:", this._buffer.length);
|
|
64
|
+
console.log("[EventTracker] Event content:", JSON.stringify(event, null, 2));
|
|
65
|
+
}
|
|
66
|
+
if (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {
|
|
67
|
+
this.flush();
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Envía todos los eventos acumulados al servidor */
|
|
73
|
+
async flush() {
|
|
74
|
+
try {
|
|
75
|
+
if (this._buffer.length === 0) {
|
|
76
|
+
return { success: true };
|
|
77
|
+
}
|
|
78
|
+
const eventsToSend = [...this._buffer];
|
|
79
|
+
this._buffer = [];
|
|
80
|
+
if (this._isDebug) {
|
|
81
|
+
console.log("[EventTracker] DEBUG - Events:", JSON.stringify(eventsToSend, null, 2));
|
|
82
|
+
return { success: true };
|
|
83
|
+
}
|
|
84
|
+
const result = await this._sendEventsAsync(eventsToSend);
|
|
85
|
+
return result;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Obtiene la cantidad de eventos en el buffer */
|
|
94
|
+
getSize() {
|
|
95
|
+
return this._buffer.length;
|
|
96
|
+
}
|
|
97
|
+
/** Limpia el buffer sin enviar */
|
|
98
|
+
clear() {
|
|
99
|
+
this._buffer = [];
|
|
100
|
+
}
|
|
101
|
+
/** Destruye el buffer y limpia recursos */
|
|
102
|
+
destroy() {
|
|
103
|
+
if (this._flushTimer) {
|
|
104
|
+
clearInterval(this._flushTimer);
|
|
105
|
+
this._flushTimer = null;
|
|
106
|
+
}
|
|
107
|
+
this._buffer = [];
|
|
108
|
+
}
|
|
109
|
+
/** Inicia el temporizador de flush periódico */
|
|
110
|
+
_startFlushTimer() {
|
|
111
|
+
this._flushTimer = setInterval(() => {
|
|
112
|
+
if (this._buffer.length > 0) {
|
|
113
|
+
this.flush();
|
|
114
|
+
}
|
|
115
|
+
}, this._config.batchInterval);
|
|
116
|
+
}
|
|
117
|
+
/** Configura el listener de visibilitychange para envío de emergencia */
|
|
118
|
+
_setupVisibilityListener() {
|
|
119
|
+
if (typeof document === "undefined") {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const handleVisibilityChange = () => {
|
|
123
|
+
if (this._isDebug) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (document.visibilityState === "hidden" && this._buffer.length > 0) {
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
this.flush();
|
|
129
|
+
}, 0);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
133
|
+
}
|
|
134
|
+
/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */
|
|
135
|
+
async _sendEventsAsync(events) {
|
|
136
|
+
try {
|
|
137
|
+
const payload = JSON.stringify({
|
|
138
|
+
apiKey: this._apiKey,
|
|
139
|
+
events
|
|
140
|
+
});
|
|
141
|
+
const response = await fetch(this._endpoint, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: {
|
|
144
|
+
"Content-Type": "application/json"
|
|
145
|
+
},
|
|
146
|
+
body: payload,
|
|
147
|
+
keepalive: true
|
|
148
|
+
});
|
|
149
|
+
let message;
|
|
150
|
+
try {
|
|
151
|
+
const responseData = await response.json();
|
|
152
|
+
message = responseData.message;
|
|
153
|
+
} catch {
|
|
154
|
+
message = response.statusText;
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
success: response.ok,
|
|
158
|
+
statusCode: response.status,
|
|
159
|
+
message
|
|
160
|
+
};
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
error: error instanceof Error ? error.message : "Network error"
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/utils/uuid.ts
|
|
171
|
+
function generateUUID() {
|
|
172
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
173
|
+
const random = Math.random() * 16 | 0;
|
|
174
|
+
const value = char === "x" ? random : random & 3 | 8;
|
|
175
|
+
return value.toString(16);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/utils/truncate.ts
|
|
180
|
+
function truncate(text, maxLength) {
|
|
181
|
+
if (maxLength < 3) {
|
|
182
|
+
return "...";
|
|
183
|
+
}
|
|
184
|
+
if (text.length <= maxLength) {
|
|
185
|
+
return text;
|
|
186
|
+
}
|
|
187
|
+
return text.substring(0, maxLength - 3) + "...";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/utils/metadata.ts
|
|
191
|
+
function getOrCreateSessionId() {
|
|
192
|
+
try {
|
|
193
|
+
const storageKey = "event_tracker_session_id";
|
|
194
|
+
const existingSessionId = sessionStorage.getItem(storageKey);
|
|
195
|
+
if (existingSessionId) {
|
|
196
|
+
return existingSessionId;
|
|
197
|
+
}
|
|
198
|
+
const newSessionId = generateUUID();
|
|
199
|
+
sessionStorage.setItem(storageKey, newSessionId);
|
|
200
|
+
return newSessionId;
|
|
201
|
+
} catch {
|
|
202
|
+
return generateUUID();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function getScreenSize() {
|
|
206
|
+
if (typeof window === "undefined") {
|
|
207
|
+
return "0x0";
|
|
208
|
+
}
|
|
209
|
+
return `${window.innerWidth}x${window.innerHeight}`;
|
|
210
|
+
}
|
|
211
|
+
function getViewportSize() {
|
|
212
|
+
if (typeof window === "undefined") {
|
|
213
|
+
return "0x0";
|
|
214
|
+
}
|
|
215
|
+
return `${window.innerWidth}x${window.innerHeight}`;
|
|
216
|
+
}
|
|
217
|
+
function getDeviceType() {
|
|
218
|
+
if (typeof navigator === "undefined") {
|
|
219
|
+
return "unknown";
|
|
220
|
+
}
|
|
221
|
+
const ua = navigator.userAgent;
|
|
222
|
+
if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {
|
|
223
|
+
return "tablet";
|
|
224
|
+
}
|
|
225
|
+
if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {
|
|
226
|
+
return "mobile";
|
|
227
|
+
}
|
|
228
|
+
return "desktop";
|
|
229
|
+
}
|
|
230
|
+
function getBrowser() {
|
|
231
|
+
if (typeof navigator === "undefined") {
|
|
232
|
+
return "unknown";
|
|
233
|
+
}
|
|
234
|
+
const ua = navigator.userAgent;
|
|
235
|
+
if (ua.includes("Firefox")) {
|
|
236
|
+
return "Firefox";
|
|
237
|
+
}
|
|
238
|
+
if (ua.includes("Edg/")) {
|
|
239
|
+
return "Edge";
|
|
240
|
+
}
|
|
241
|
+
if (ua.includes("Chrome")) {
|
|
242
|
+
return "Chrome";
|
|
243
|
+
}
|
|
244
|
+
if (ua.includes("Safari")) {
|
|
245
|
+
return "Safari";
|
|
246
|
+
}
|
|
247
|
+
if (ua.includes("MSIE") || ua.includes("Trident/")) {
|
|
248
|
+
return "Internet Explorer";
|
|
249
|
+
}
|
|
250
|
+
return "unknown";
|
|
251
|
+
}
|
|
252
|
+
function getOS() {
|
|
253
|
+
if (typeof navigator === "undefined") {
|
|
254
|
+
return "unknown";
|
|
255
|
+
}
|
|
256
|
+
const ua = navigator.userAgent;
|
|
257
|
+
if (ua.includes("Windows")) {
|
|
258
|
+
return "Windows";
|
|
259
|
+
}
|
|
260
|
+
if (ua.includes("Mac OS")) {
|
|
261
|
+
return "macOS";
|
|
262
|
+
}
|
|
263
|
+
if (ua.includes("Linux")) {
|
|
264
|
+
return "Linux";
|
|
265
|
+
}
|
|
266
|
+
if (ua.includes("Android")) {
|
|
267
|
+
return "Android";
|
|
268
|
+
}
|
|
269
|
+
if (ua.includes("iOS") || ua.includes("iPhone") || ua.includes("iPad")) {
|
|
270
|
+
return "iOS";
|
|
271
|
+
}
|
|
272
|
+
return "unknown";
|
|
273
|
+
}
|
|
274
|
+
function getLanguage() {
|
|
275
|
+
if (typeof navigator === "undefined") {
|
|
276
|
+
return "unknown";
|
|
277
|
+
}
|
|
278
|
+
return navigator.language || "unknown";
|
|
279
|
+
}
|
|
280
|
+
function getReferrer() {
|
|
281
|
+
if (typeof window === "undefined") {
|
|
282
|
+
return "";
|
|
283
|
+
}
|
|
284
|
+
return document.referrer;
|
|
285
|
+
}
|
|
286
|
+
function getTimestamp() {
|
|
287
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
288
|
+
}
|
|
289
|
+
function getCurrentPath() {
|
|
290
|
+
if (typeof window === "undefined") {
|
|
291
|
+
return "";
|
|
292
|
+
}
|
|
293
|
+
return window.location.pathname;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/observers/ClickObserver.ts
|
|
297
|
+
var TRACKABLE_TAGS = ["BUTTON", "A", "INPUT", "I", "SPAN", "DIV"];
|
|
298
|
+
var TRACKABLE_INPUT_TYPES = ["submit"];
|
|
299
|
+
var SENSITIVE_INPUT_TYPES = ["password", "email", "tel", "number", "search", "url"];
|
|
300
|
+
var TRACKABLE_SELECTORS = [
|
|
301
|
+
"button",
|
|
302
|
+
"a",
|
|
303
|
+
'input[type="submit"]',
|
|
304
|
+
"[data-track]",
|
|
305
|
+
'[style*="cursor: pointer"]',
|
|
306
|
+
'i[class*="mdi-"]'
|
|
307
|
+
];
|
|
308
|
+
function _isSensitiveElement(element) {
|
|
309
|
+
if (element.tagName === "INPUT") {
|
|
310
|
+
const inputType = element.type?.toLowerCase() ?? "";
|
|
311
|
+
return SENSITIVE_INPUT_TYPES.includes(inputType);
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
function _isTrackableElement(element) {
|
|
316
|
+
const tagName = element.tagName.toUpperCase();
|
|
317
|
+
if (!TRACKABLE_TAGS.includes(tagName)) {
|
|
318
|
+
return element.hasAttribute("data-track") || _hasCursorPointer(element) || _isMdiIcon(element);
|
|
319
|
+
}
|
|
320
|
+
if (tagName === "INPUT") {
|
|
321
|
+
const inputType = element.type?.toLowerCase() ?? "";
|
|
322
|
+
return TRACKABLE_INPUT_TYPES.includes(inputType);
|
|
323
|
+
}
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
function _hasCursorPointer(element) {
|
|
327
|
+
try {
|
|
328
|
+
const style = window.getComputedStyle(element);
|
|
329
|
+
return style.cursor === "pointer";
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function _isMdiIcon(element) {
|
|
335
|
+
return element.tagName === "I" && element.classList.contains("mdi");
|
|
336
|
+
}
|
|
337
|
+
function _generateSelector(element) {
|
|
338
|
+
const parts = [];
|
|
339
|
+
let current = element;
|
|
340
|
+
while (current && current !== document.documentElement) {
|
|
341
|
+
let selector = current.tagName.toLowerCase();
|
|
342
|
+
if (current.id) {
|
|
343
|
+
selector += `#${current.id}`;
|
|
344
|
+
parts.unshift(selector);
|
|
345
|
+
break;
|
|
346
|
+
} else if (current.className && typeof current.className === "string") {
|
|
347
|
+
const classes = current.className.trim().split(/\s+/).filter(Boolean);
|
|
348
|
+
if (classes.length > 0) {
|
|
349
|
+
selector += `.${classes[0]}`;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
parts.unshift(selector);
|
|
353
|
+
current = current.parentElement;
|
|
354
|
+
}
|
|
355
|
+
return parts.slice(0, 4).join(" > ");
|
|
356
|
+
}
|
|
357
|
+
function _getAllClasses(element) {
|
|
358
|
+
if (!element.className || typeof element.className !== "string") {
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
const classes = element.className.trim().split(/\s+/).filter(Boolean);
|
|
362
|
+
return classes.length > 0 ? classes.join(" ") : void 0;
|
|
363
|
+
}
|
|
364
|
+
var ClickObserver = class {
|
|
365
|
+
constructor(tracker2) {
|
|
366
|
+
this._isActive = false;
|
|
367
|
+
this._tracker = tracker2;
|
|
368
|
+
this._boundHandleClick = this._handleClick.bind(this);
|
|
369
|
+
}
|
|
370
|
+
/** Inicia el observer */
|
|
371
|
+
start() {
|
|
372
|
+
if (this._isActive) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
window.addEventListener("click", this._boundHandleClick, true);
|
|
377
|
+
this._isActive = true;
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/** Detiene el observer */
|
|
382
|
+
stop() {
|
|
383
|
+
if (!this._isActive) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
window.removeEventListener("click", this._boundHandleClick, true);
|
|
388
|
+
this._isActive = false;
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/** Manejador de eventos de clic */
|
|
393
|
+
_handleClick(event) {
|
|
394
|
+
try {
|
|
395
|
+
const target = event.target;
|
|
396
|
+
if (!target) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (_isTrackableElement(target) && !_isSensitiveElement(target)) {
|
|
400
|
+
this._createClickEvent(target);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const trackableElement = target.closest(TRACKABLE_SELECTORS.join(","));
|
|
404
|
+
if (!trackableElement) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (_isSensitiveElement(trackableElement)) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (!_isTrackableElement(trackableElement)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
this._createClickEvent(trackableElement);
|
|
414
|
+
} catch {
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/** Crea un evento de clic */
|
|
418
|
+
_createClickEvent(element) {
|
|
419
|
+
const event = {
|
|
420
|
+
eventType: "user_click",
|
|
421
|
+
eventId: generateUUID(),
|
|
422
|
+
context: {
|
|
423
|
+
sessionId: "",
|
|
424
|
+
userId: null,
|
|
425
|
+
userName: null,
|
|
426
|
+
url: window.location.href,
|
|
427
|
+
referrer: getReferrer(),
|
|
428
|
+
screenSize: getScreenSize(),
|
|
429
|
+
viewportSize: getViewportSize(),
|
|
430
|
+
deviceType: getDeviceType(),
|
|
431
|
+
browser: getBrowser(),
|
|
432
|
+
os: getOS(),
|
|
433
|
+
language: getLanguage(),
|
|
434
|
+
timestamp: getTimestamp()
|
|
435
|
+
},
|
|
436
|
+
data: {
|
|
437
|
+
tagName: element.tagName,
|
|
438
|
+
id: element.id || void 0,
|
|
439
|
+
className: _getAllClasses(element),
|
|
440
|
+
title: element.getAttribute("title") || void 0,
|
|
441
|
+
text: element.textContent ? truncate(element.textContent, 30) : void 0,
|
|
442
|
+
selector: _generateSelector(element)
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
this._tracker.trackEvent(event);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// src/observers/HistoryObserver.ts
|
|
450
|
+
var HistoryObserver = class {
|
|
451
|
+
constructor(tracker2) {
|
|
452
|
+
this._isActive = false;
|
|
453
|
+
this._tracker = tracker2;
|
|
454
|
+
this._lastFocusTime = Date.now();
|
|
455
|
+
this._accumulatedFocusTime = 0;
|
|
456
|
+
this._boundHandlePopState = this._handlePopState.bind(this);
|
|
457
|
+
}
|
|
458
|
+
/** Inicia el observer */
|
|
459
|
+
start() {
|
|
460
|
+
if (this._isActive) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
this._patchHistoryMethods();
|
|
465
|
+
window.addEventListener("popstate", this._boundHandlePopState);
|
|
466
|
+
window.addEventListener("beforeunload", this._handleBeforeUnload);
|
|
467
|
+
window.addEventListener("focus", this._handleFocus);
|
|
468
|
+
window.addEventListener("blur", this._handleBlur);
|
|
469
|
+
this._isActive = true;
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/** Detiene el observer */
|
|
474
|
+
stop() {
|
|
475
|
+
if (!this._isActive) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
this._restoreHistoryMethods();
|
|
480
|
+
window.removeEventListener("popstate", this._boundHandlePopState);
|
|
481
|
+
window.removeEventListener("beforeunload", this._handleBeforeUnload);
|
|
482
|
+
window.removeEventListener("focus", this._handleFocus);
|
|
483
|
+
window.removeEventListener("blur", this._handleBlur);
|
|
484
|
+
this._isActive = false;
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/** Aplica monkey-patch a los métodos history */
|
|
489
|
+
_patchHistoryMethods() {
|
|
490
|
+
const historyWithTracking = history;
|
|
491
|
+
if (!historyWithTracking._originalPushState) {
|
|
492
|
+
historyWithTracking._originalPushState = history.pushState;
|
|
493
|
+
history.pushState = this._wrappedPushState.bind(this);
|
|
494
|
+
}
|
|
495
|
+
if (!historyWithTracking._originalReplaceState) {
|
|
496
|
+
historyWithTracking._originalReplaceState = history.replaceState;
|
|
497
|
+
history.replaceState = this._wrappedReplaceState.bind(this);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/** Restaura los métodos originales */
|
|
501
|
+
_restoreHistoryMethods() {
|
|
502
|
+
const historyWithTracking = history;
|
|
503
|
+
if (historyWithTracking._originalPushState) {
|
|
504
|
+
history.pushState = historyWithTracking._originalPushState;
|
|
505
|
+
historyWithTracking._originalPushState = void 0;
|
|
506
|
+
}
|
|
507
|
+
if (historyWithTracking._originalReplaceState) {
|
|
508
|
+
history.replaceState = historyWithTracking._originalReplaceState;
|
|
509
|
+
historyWithTracking._originalReplaceState = void 0;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/** Wrapper para pushState */
|
|
513
|
+
_wrappedPushState(state, title, url) {
|
|
514
|
+
try {
|
|
515
|
+
const historyWithTracking = history;
|
|
516
|
+
historyWithTracking._originalPushState?.call(history, state, title, url);
|
|
517
|
+
this._handleNavigationChange();
|
|
518
|
+
} catch {
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
/** Wrapper para replaceState */
|
|
522
|
+
_wrappedReplaceState(state, title, url) {
|
|
523
|
+
try {
|
|
524
|
+
const historyWithTracking = history;
|
|
525
|
+
historyWithTracking._originalReplaceState?.call(history, state, title, url);
|
|
526
|
+
this._handleNavigationChange();
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/** Manejador de popstate */
|
|
531
|
+
_handlePopState() {
|
|
532
|
+
try {
|
|
533
|
+
this._handleNavigationChange();
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/** Manejador de focus (la ventana gagne foco) */
|
|
538
|
+
_handleFocus() {
|
|
539
|
+
try {
|
|
540
|
+
this._lastFocusTime = Date.now();
|
|
541
|
+
} catch {
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/** Manejador de blur (la ventana pierde foco) */
|
|
545
|
+
_handleBlur() {
|
|
546
|
+
try {
|
|
547
|
+
this._accumulatedFocusTime += Date.now() - this._lastFocusTime;
|
|
548
|
+
this._lastFocusTime = Date.now();
|
|
549
|
+
} catch {
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/** Manejador de beforeunload (cuando el usuario sale de la página) */
|
|
553
|
+
_handleBeforeUnload() {
|
|
554
|
+
try {
|
|
555
|
+
const duration = this._calculateFocusedDuration();
|
|
556
|
+
this._tracker.trackPageLeave(duration);
|
|
557
|
+
} catch {
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/** Calcula la duración total con foco */
|
|
561
|
+
_calculateFocusedDuration() {
|
|
562
|
+
const currentTime = Date.now();
|
|
563
|
+
const hasFocus = document.hasFocus();
|
|
564
|
+
const focusedTime = this._accumulatedFocusTime + (hasFocus ? currentTime - this._lastFocusTime : 0);
|
|
565
|
+
return focusedTime / 1e3;
|
|
566
|
+
}
|
|
567
|
+
/** Maneja cambios de navegación (pushState, replaceState, popstate) */
|
|
568
|
+
_handleNavigationChange() {
|
|
569
|
+
try {
|
|
570
|
+
const duration = this._calculateFocusedDuration();
|
|
571
|
+
this._tracker.trackPageLeave(duration);
|
|
572
|
+
this._tracker.trackPageView();
|
|
573
|
+
this._lastFocusTime = Date.now();
|
|
574
|
+
this._accumulatedFocusTime = 0;
|
|
575
|
+
} catch {
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// src/core/Tracker.ts
|
|
581
|
+
var _Tracker = class _Tracker {
|
|
582
|
+
constructor() {
|
|
583
|
+
this._eventBuffer = null;
|
|
584
|
+
this._clickObserver = null;
|
|
585
|
+
this._historyObserver = null;
|
|
586
|
+
this._isInitialized = false;
|
|
587
|
+
this._userIdentity = {
|
|
588
|
+
userId: null,
|
|
589
|
+
userName: null
|
|
590
|
+
};
|
|
591
|
+
this._sessionId = getOrCreateSessionId();
|
|
592
|
+
}
|
|
593
|
+
/** Obtiene la instancia única del Tracker */
|
|
594
|
+
static getInstance() {
|
|
595
|
+
if (_Tracker._instance === null) {
|
|
596
|
+
_Tracker._instance = new _Tracker();
|
|
597
|
+
}
|
|
598
|
+
return _Tracker._instance;
|
|
599
|
+
}
|
|
600
|
+
/** Construye el contexto completo del evento */
|
|
601
|
+
_buildContext() {
|
|
602
|
+
return {
|
|
603
|
+
sessionId: this._sessionId,
|
|
604
|
+
userId: this._userIdentity.userId,
|
|
605
|
+
userName: this._userIdentity.userName,
|
|
606
|
+
url: window.location.href,
|
|
607
|
+
referrer: getReferrer(),
|
|
608
|
+
screenSize: getScreenSize(),
|
|
609
|
+
viewportSize: getViewportSize(),
|
|
610
|
+
deviceType: getDeviceType(),
|
|
611
|
+
browser: getBrowser(),
|
|
612
|
+
os: getOS(),
|
|
613
|
+
language: getLanguage(),
|
|
614
|
+
timestamp: getTimestamp()
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
/** Inicializa el tracker con la configuración */
|
|
618
|
+
init(config) {
|
|
619
|
+
try {
|
|
620
|
+
if (config.userId && config.userId != this._userIdentity.userId) {
|
|
621
|
+
this._userIdentity = {
|
|
622
|
+
userId: config.userId,
|
|
623
|
+
userName: config.userName ?? null
|
|
624
|
+
};
|
|
625
|
+
if (this._isInitialized && config.debug) {
|
|
626
|
+
console.log("[EventTracker] Usuario actualizado:", this._userIdentity);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (this._isInitialized) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
this._eventBuffer = new EventBuffer(config);
|
|
633
|
+
this._isInitialized = true;
|
|
634
|
+
this._clickObserver = new ClickObserver(this);
|
|
635
|
+
this._historyObserver = new HistoryObserver(this);
|
|
636
|
+
this._clickObserver.start();
|
|
637
|
+
this._historyObserver.start();
|
|
638
|
+
this.trackPageView();
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/** Identifica al usuario */
|
|
643
|
+
identify(userId, userName) {
|
|
644
|
+
try {
|
|
645
|
+
this._userIdentity = {
|
|
646
|
+
userId,
|
|
647
|
+
userName: userName ?? null
|
|
648
|
+
};
|
|
649
|
+
} catch {
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/** Registra un evento */
|
|
653
|
+
trackEvent(event) {
|
|
654
|
+
try {
|
|
655
|
+
if (!this._eventBuffer) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const eventWithContext = {
|
|
659
|
+
...event,
|
|
660
|
+
eventId: generateUUID(),
|
|
661
|
+
context: this._buildContext()
|
|
662
|
+
};
|
|
663
|
+
this._eventBuffer.add(eventWithContext);
|
|
664
|
+
} catch {
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/** Crea un evento de page view */
|
|
668
|
+
trackPageView() {
|
|
669
|
+
try {
|
|
670
|
+
const event = {
|
|
671
|
+
eventType: "page_view",
|
|
672
|
+
eventId: generateUUID(),
|
|
673
|
+
context: this._buildContext(),
|
|
674
|
+
data: {
|
|
675
|
+
path: getCurrentPath()
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
this.trackEvent(event);
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/** Crea un evento de page leave */
|
|
683
|
+
trackPageLeave(duration) {
|
|
684
|
+
try {
|
|
685
|
+
const event = {
|
|
686
|
+
eventType: "page_leave",
|
|
687
|
+
eventId: generateUUID(),
|
|
688
|
+
context: this._buildContext(),
|
|
689
|
+
data: {
|
|
690
|
+
duration,
|
|
691
|
+
path: getCurrentPath()
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
this.trackEvent(event);
|
|
695
|
+
} catch {
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/** Fuerza el envío de todos los eventos pendientes */
|
|
699
|
+
async flush() {
|
|
700
|
+
try {
|
|
701
|
+
if (this._eventBuffer) {
|
|
702
|
+
await this._eventBuffer.flush();
|
|
703
|
+
}
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/** Verifica si el tracker está inicializado */
|
|
708
|
+
isInitialized() {
|
|
709
|
+
return this._isInitialized;
|
|
710
|
+
}
|
|
711
|
+
/** Destruye el tracker */
|
|
712
|
+
destroy() {
|
|
713
|
+
try {
|
|
714
|
+
if (this._clickObserver) {
|
|
715
|
+
this._clickObserver.stop();
|
|
716
|
+
this._clickObserver = null;
|
|
717
|
+
}
|
|
718
|
+
if (this._historyObserver) {
|
|
719
|
+
this._historyObserver.stop();
|
|
720
|
+
this._historyObserver = null;
|
|
721
|
+
}
|
|
722
|
+
if (this._eventBuffer) {
|
|
723
|
+
this._eventBuffer.destroy();
|
|
724
|
+
this._eventBuffer = null;
|
|
725
|
+
}
|
|
726
|
+
this._isInitialized = false;
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
_Tracker._instance = null;
|
|
732
|
+
var Tracker2 = _Tracker;
|
|
733
|
+
|
|
734
|
+
// src/index.ts
|
|
735
|
+
var tracker = Tracker2.getInstance();
|
|
736
|
+
function init(config) {
|
|
737
|
+
tracker.init(config);
|
|
738
|
+
}
|
|
739
|
+
function identify(userId, userName) {
|
|
740
|
+
tracker.identify(userId, userName);
|
|
741
|
+
}
|
|
742
|
+
function flush() {
|
|
743
|
+
return tracker.flush();
|
|
744
|
+
}
|
|
745
|
+
function destroy() {
|
|
746
|
+
tracker.destroy();
|
|
747
|
+
}
|
|
748
|
+
// Generated by tsup
|
|
749
|
+
//# sourceMappingURL=index.js.map
|