@agroideas/event-tracker-sdk 1.0.7 → 1.0.8

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.
@@ -466,7 +466,7 @@
466
466
  };
467
467
  this._tracker = tracker2;
468
468
  this._pageStartTime = performance.now();
469
- this._lastUrl = typeof window !== "undefined" ? window.location.pathname : "";
469
+ this._lastUrl = typeof window !== "undefined" ? window.location.href : "";
470
470
  if (typeof window !== "undefined" && typeof history !== "undefined") {
471
471
  this._originalPushState = history.pushState.bind(history);
472
472
  this._originalReplaceState = history.replaceState.bind(history);
@@ -545,7 +545,7 @@
545
545
  if (typeof window === "undefined") {
546
546
  return;
547
547
  }
548
- const currentUrl = window.location.pathname;
548
+ const currentUrl = window.location.href;
549
549
  if (currentUrl !== this._lastUrl) {
550
550
  const duration = this._calculateWallClockDuration();
551
551
  if (duration >= MIN_DURATION) {
@@ -572,7 +572,7 @@
572
572
  if (typeof window === "undefined") {
573
573
  return;
574
574
  }
575
- const currentUrl = window.location.pathname;
575
+ const currentUrl = window.location.href;
576
576
  if (currentUrl !== this._lastUrl) {
577
577
  this._handleNavigationChange();
578
578
  }
@@ -645,7 +645,7 @@
645
645
  // package.json
646
646
  var package_default = {
647
647
  name: "@agroideas/event-tracker-sdk",
648
- version: "1.0.7",
648
+ version: "1.0.8",
649
649
  description: "A lightweight, framework-agnostic SDK for tracking frontend user behavior",
650
650
  license: "MIT",
651
651
  author: "Agroideas",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/EventBuffer.ts","../src/utils/uuid.ts","../src/utils/truncate.ts","../src/utils/metadata.ts","../src/observers/ClickObserver.ts","../src/observers/HistoryObserver.ts","../package.json","../src/version.ts","../src/core/Tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * EventBuffer: Maneja la acumulación, batching y envío de eventos\r\n */\r\n\r\nimport type { TrackerEvent, SendResult, TrackerConfig, TrackingResponse } from '../types';\r\n\r\nconst BATCH_INTERVAL = 10000;\r\nconst MAX_BUFFER_SIZE = 15;\r\n\r\nexport class EventBuffer {\r\n\tprivate _buffer: TrackerEvent[];\r\n\tprivate _config: Required<Omit<TrackerConfig, 'apiKey' | 'endpoint' | 'userId' | 'userName' | 'debug'>>;\r\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _endpoint: string;\r\n\tprivate _apiKey: string;\r\n\tprivate _isDebug: boolean;\r\n\r\n\tconstructor(config: TrackerConfig) {\r\n\t\tthis._buffer = [];\r\n\r\n\t\t// apiKey y endpoint son requeridos\r\n\t\tthis._endpoint = config.endpoint;\r\n\t\tthis._apiKey = config.apiKey;\r\n\r\n\t\t// DEBUG es opcional, default false\r\n\t\tthis._isDebug = config.debug == \"true\";\r\n\t\tconsole.log(config);\r\n\r\n\t\tthis._config = {\r\n\t\t\tbatchInterval: config.batchInterval ?? BATCH_INTERVAL,\r\n\t\t\tmaxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE,\r\n\t\t\texcludePatterns: config.excludePatterns ?? [],\r\n\t\t};\r\n\r\n\t\tif (this._isDebug) {\r\n\t\t\tconsole.log('[EventTracker] Modo DEBUG activado');\r\n\t\t\tconsole.log('[EventTracker] Endpoint:', this._endpoint);\r\n\t\t\tconsole.log('[EventTracker] API Key:', this._apiKey);\r\n\t\t}\r\n\r\n\t\t// En modo debug, no iniciar timer de flush\r\n\t\tif (!this._isDebug) {\r\n\t\t\tthis._startFlushTimer();\r\n\t\t}\r\n\r\n\t\tthis._setupVisibilityListener();\r\n\t}\r\n\r\n\t/** Agrega un evento al buffer */\r\n\tadd(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\tthis._buffer.push(event);\r\n\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] Evento agregado:', event.eventType);\r\n\t\t\t\tconsole.log('[EventTracker] Buffer size:', this._buffer.length);\r\n\t\t\t\tconsole.log('[EventTracker] Event content:', JSON.stringify(event, null, 2));\r\n\t\t\t}\r\n\r\n\t\t\t// En modo debug, no hacer flush automático\r\n\t\t\tif (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Silently ignore errors to never block the main app\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía todos los eventos acumulados al servidor */\r\n\tasync flush(): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tif (this._buffer.length === 0) {\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst eventsToSend = [...this._buffer];\r\n\t\t\tthis._buffer = [];\r\n\r\n\t\t\t// Modo debug: solo console.log, no fetch\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] DEBUG - Events:', JSON.stringify(eventsToSend, null, 2));\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst result = await this._sendEventsAsync(eventsToSend);\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\t/** Obtiene la cantidad de eventos en el buffer */\r\n\tgetSize(): number {\r\n\t\treturn this._buffer.length;\r\n\t}\r\n\r\n\t/** Limpia el buffer sin enviar */\r\n\tclear(): void {\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Destruye el buffer y limpia recursos */\r\n\tdestroy(): void {\r\n\t\tif (this._flushTimer) {\r\n\t\t\tclearInterval(this._flushTimer);\r\n\t\t\tthis._flushTimer = null;\r\n\t\t}\r\n\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Inicia el temporizador de flush periódico */\r\n\tprivate _startFlushTimer(): void {\r\n\t\tthis._flushTimer = setInterval(() => {\r\n\t\t\tif (this._buffer.length > 0) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t}, this._config.batchInterval);\r\n\t}\r\n\r\n\t/** Configura el listener de visibilitychange para envío de emergencia */\r\n\tprivate _setupVisibilityListener(): void {\r\n\t\tif (typeof document === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst handleVisibilityChange = (): void => {\r\n\t\t\t// En modo debug, no enviar eventos\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (document.visibilityState === 'hidden' && this._buffer.length > 0) {\r\n\t\t\t\t// Usar setTimeout para asegurar que el fetch se complete antes del unload\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tthis.flush();\r\n\t\t\t\t}, 0);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tdocument.addEventListener('visibilitychange', handleVisibilityChange);\r\n\t}\r\n\r\n\t/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */\r\n\tprivate async _sendEventsAsync(events: TrackerEvent[]): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tconst payload = JSON.stringify({\r\n\t\t\t\tevents: events,\r\n\t\t\t});\r\n\r\n\t\t\tconst response = await fetch(this._endpoint, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'X-API-Key': this._apiKey,\r\n\t\t\t\t},\r\n\t\t\t\tbody: payload,\r\n\t\t\t\tkeepalive: true,\r\n\t\t\t});\r\n\r\n\t\t\t// Intentar parsear el mensaje de respuesta del backend\r\n\t\t\tlet message: string | undefined;\r\n\t\t\ttry {\r\n\t\t\t\tconst responseData: TrackingResponse = await response.json();\r\n\t\t\t\tmessage = responseData.message;\r\n\t\t\t} catch {\r\n\t\t\t\t// Si no se puede parsear, usar el status text\r\n\t\t\t\tmessage = response.statusText;\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: response.ok,\r\n\t\t\t\tstatusCode: response.status,\r\n\t\t\t\tmessage,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Network error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Generador de UUID v4\r\n */\r\n\r\n/** Genera un UUID v4 aleatorio */\r\nexport function generateUUID(): string {\r\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\r\n\t\tconst random = Math.random() * 16 | 0;\r\n\t\tconst value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n\t\treturn value.toString(16);\r\n\t});\r\n}\r\n","/**\r\n * Utilidad para truncar texto\r\n */\r\n\r\n/**\r\n * Trunca un texto a la longitud máxima especificada\r\n * @param text - Texto a truncar\r\n * @param maxLength - Longitud máxima\r\n * @returns Texto truncado con '...' al final si excede la longitud\r\n */\r\nexport function truncate(text: string, maxLength: number): string {\r\n\tif (maxLength < 3) {\r\n\t\treturn '...';\r\n\t}\r\n\r\n\tif (text.length <= maxLength) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, maxLength - 3) + '...';\r\n}\r\n","/**\r\n * Utilidades para recolectar metadatos del navegador\r\n */\r\n\r\nimport { generateUUID } from './uuid';\r\n\r\n/** Obtiene o crea un sessionId en sessionStorage */\r\nexport function getOrCreateSessionId(): string {\r\n\ttry {\r\n\t\tconst storageKey = 'event_tracker_session_id';\r\n\t\tconst existingSessionId = sessionStorage.getItem(storageKey);\r\n\r\n\t\tif (existingSessionId) {\r\n\t\t\treturn existingSessionId;\r\n\t\t}\r\n\r\n\t\tconst newSessionId = generateUUID();\r\n\t\tsessionStorage.setItem(storageKey, newSessionId);\r\n\t\treturn newSessionId;\r\n\t} catch {\r\n\t\treturn generateUUID();\r\n\t}\r\n}\r\n\r\n/** Obtiene el tamaño de pantalla */\r\nexport function getScreenSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Obtiene el tamaño del viewport */\r\nexport function getViewportSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Detecta el tipo de dispositivo */\r\nexport function getDeviceType(): 'mobile' | 'desktop' | 'tablet' | 'unknown' {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\r\n\t\treturn 'tablet';\r\n\t}\r\n\r\n\tif (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\r\n\t\treturn 'mobile';\r\n\t}\r\n\r\n\treturn 'desktop';\r\n}\r\n\r\n/** Obtiene el navegador */\r\nexport function getBrowser(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Firefox')) {\r\n\t\treturn 'Firefox';\r\n\t}\r\n\r\n\tif (ua.includes('Edg/')) {\r\n\t\treturn 'Edge';\r\n\t}\r\n\r\n\tif (ua.includes('Chrome')) {\r\n\t\treturn 'Chrome';\r\n\t}\r\n\r\n\tif (ua.includes('Safari')) {\r\n\t\treturn 'Safari';\r\n\t}\r\n\r\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\r\n\t\treturn 'Internet Explorer';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el sistema operativo */\r\nexport function getOS(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Windows')) {\r\n\t\treturn 'Windows';\r\n\t}\r\n\r\n\tif (ua.includes('Mac OS')) {\r\n\t\treturn 'macOS';\r\n\t}\r\n\r\n\tif (ua.includes('Linux')) {\r\n\t\treturn 'Linux';\r\n\t}\r\n\r\n\tif (ua.includes('Android')) {\r\n\t\treturn 'Android';\r\n\t}\r\n\r\n\tif (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) {\r\n\t\treturn 'iOS';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el idioma del navegador */\r\nexport function getLanguage(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\treturn navigator.language || 'unknown';\r\n}\r\n\r\n/** Obtiene la URL actual */\r\nexport function getCurrentUrl(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.href;\r\n}\r\n\r\n/** Obtiene el referrer */\r\nexport function getReferrer(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn document.referrer;\r\n}\r\n\r\n/** Obtiene el timestamp actual en formato ISO */\r\nexport function getTimestamp(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\n/** Obtiene el path actual */\r\nexport function getCurrentPath(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.pathname;\r\n}\r\n","/**\r\n * ClickObserver: Captura clics globales en elementos interactivos\r\n */\r\n\r\nimport type { TrackerEvent } from '../types';\r\nimport { Tracker } from '../core/Tracker';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { truncate } from '../utils/truncate';\r\nimport {\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\n\r\n/** Tipos de elementos que deben ser rastreados */\r\nconst TRACKABLE_TAGS = ['BUTTON', 'A', 'INPUT', 'I', 'SPAN', 'DIV'];\r\n\r\n/** Tipos de input que deben ser rastreados */\r\nconst TRACKABLE_INPUT_TYPES = ['submit'];\r\n\r\n/** Tipos de input que deben ser excluidos (sensibles) */\r\nconst SENSITIVE_INPUT_TYPES = ['password', 'email', 'tel', 'number', 'search', 'url'];\r\n\r\n/** Selectores para elementos rastreables */\r\nconst TRACKABLE_SELECTORS = [\r\n\t'button',\r\n\t'a',\r\n\t'input[type=\"submit\"]',\r\n\t'[data-track]',\r\n\t'[style*=\"cursor: pointer\"]',\r\n\t'i[class*=\"mdi-\"]',\r\n];\r\n\r\n/** Verifica si un elemento es sensible (no capturar valores) */\r\nfunction _isSensitiveElement(element: Element): boolean {\r\n\tif (element.tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn SENSITIVE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n/** Verifica si un elemento es rastreable */\r\nfunction _isTrackableElement(element: Element): boolean {\r\n\tconst tagName = element.tagName.toUpperCase();\r\n\r\n\tif (!TRACKABLE_TAGS.includes(tagName)) {\r\n\t\treturn element.hasAttribute('data-track') ||\r\n\t\t\t_hasCursorPointer(element) ||\r\n\t\t\t_isMdiIcon(element);\r\n\t}\r\n\r\n\tif (tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn TRACKABLE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/** Verifica si el elemento tiene cursor: pointer */\r\nfunction _hasCursorPointer(element: Element): boolean {\r\n\ttry {\r\n\t\tconst style = window.getComputedStyle(element);\r\n\t\treturn style.cursor === 'pointer';\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/** Verifica si es un icono MDI */\r\nfunction _isMdiIcon(element: Element): boolean {\r\n\treturn element.tagName === 'I' && element.classList.contains('mdi');\r\n}\r\n\r\n/** Genera un selector CSS simple para el elemento */\r\nfunction _generateSelector(element: Element): string {\r\n\tconst parts: string[] = [];\r\n\tlet current: Element | null = element;\r\n\r\n\twhile (current && current !== document.documentElement) {\r\n\t\tlet selector = current.tagName.toLowerCase();\r\n\r\n\t\tif (current.id) {\r\n\t\t\tselector += `#${current.id}`;\r\n\t\t\tparts.unshift(selector);\r\n\t\t\tbreak;\r\n\t\t} else if (current.className && typeof current.className === 'string') {\r\n\t\t\tconst classes = current.className.trim().split(/\\s+/).filter(Boolean);\r\n\t\t\tif (classes.length > 0) {\r\n\t\t\t\tselector += `.${classes[0]}`;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tparts.unshift(selector);\r\n\t\tcurrent = current.parentElement;\r\n\t}\r\n\r\n\treturn parts.slice(0, 4).join(' > ');\r\n}\r\n\r\n/** Obtiene todas las clases de un elemento como string separado por espacios */\r\nfunction _getAllClasses(element: Element): string | undefined {\r\n\tif (!element.className || typeof element.className !== 'string') {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst classes = element.className.trim().split(/\\s+/).filter(Boolean);\r\n\treturn classes.length > 0 ? classes.join(' ') : undefined;\r\n}\r\n\r\nexport class ClickObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _boundHandleClick: (event: MouseEvent) => void;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._boundHandleClick = this._handleClick.bind(this);\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.addEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = true;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.removeEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de eventos de clic */\r\n\tprivate _handleClick(event: MouseEvent): void {\r\n\t\ttry {\r\n\t\t\tconst target = event.target as Element;\r\n\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 1: Si el elemento clickeado es rastreable, usarlo directamente\r\n\t\t\tif (_isTrackableElement(target) && !_isSensitiveElement(target)) {\r\n\t\t\t\tthis._createClickEvent(target);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 2: Buscar el ancestro rastreable más cercano\r\n\t\t\tconst trackableElement = target.closest(TRACKABLE_SELECTORS.join(','));\r\n\r\n\t\t\tif (!trackableElement) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isSensitiveElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isTrackableElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._createClickEvent(trackableElement);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de clic */\r\n\tprivate _createClickEvent(element: Element): void {\r\n\t\tconst event: TrackerEvent = {\r\n\t\t\teventType: 'user_click',\r\n\t\t\teventId: generateUUID(),\r\n\t\t\tcontext: {\r\n\t\t\t\tsessionId: '',\r\n\t\t\t\tuserId: null,\r\n\t\t\t\tuserName: null,\r\n\t\t\t\turl: window.location.href,\r\n\t\t\t\treferrer: getReferrer(),\r\n\t\t\t\tscreenSize: getScreenSize(),\r\n\t\t\t\tviewportSize: getViewportSize(),\r\n\t\t\t\tdeviceType: getDeviceType(),\r\n\t\t\t\tbrowser: getBrowser(),\r\n\t\t\t\tos: getOS(),\r\n\t\t\t\tlanguage: getLanguage(),\r\n\t\t\t\ttimestamp: getTimestamp(),\r\n\t\t\t},\r\n\t\t\tdata: {\r\n\t\t\t\ttagName: element.tagName,\r\n\t\t\t\tid: element.id || undefined,\r\n\t\t\t\tclassName: _getAllClasses(element),\r\n\t\t\t\ttitle: element.getAttribute('title') || undefined,\r\n\t\t\t\ttext: element.textContent ? truncate(element.textContent, 30) : undefined,\r\n\t\t\t\tselector: _generateSelector(element),\r\n\t\t\t},\r\n\t\t};\r\n\r\n\t\tthis._tracker.trackEvent(event);\r\n\t}\r\n}\r\n","/**\r\n * HistoryObserver: Monitorea cambios de navegación usando múltiples estrategias\r\n * - popstate: para botones atrás/adelante del navegador\r\n * - history intercept: para pushState/replaceState en SPA\r\n * - polling: fallback para detectar cambios\r\n * - sessionStorage: para manejar F5\r\n */\r\n\r\nimport type { Tracker } from '../core/Tracker';\r\n\r\nconst POLL_INTERVAL = 1000; // Verificar URL cada 1 segundo (fallback)\r\nconst HEARTBEAT_INTERVAL = 10000; // Enviar heartbeat cada 10 segundos\r\nconst MIN_DURATION = 0.5; // Ignorar page_leave con menos de 0.5 segundos\r\nconst STORAGE_KEY_URL = 'event_tracker_last_url';\r\nconst STORAGE_KEY_TIME = 'event_tracker_last_time';\r\n\r\nexport class HistoryObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _lastUrl: string = '';\r\n\tprivate _pageStartTime: number;\r\n\tprivate _originalPushState: typeof history.pushState = () => null;\r\n\tprivate _originalReplaceState: typeof history.replaceState = () => null;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._pageStartTime = performance.now();\r\n\t\tthis._lastUrl = typeof window !== 'undefined' ? window.location.pathname : '';\r\n\r\n\t\t// Guardar referencias originales de history\r\n\t\tif (typeof window !== 'undefined' && typeof history !== 'undefined') {\r\n\t\t\tthis._originalPushState = history.pushState.bind(history);\r\n\t\t\tthis._originalReplaceState = history.replaceState.bind(history);\r\n\t\t}\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// Configurar interceptación de history\r\n\t\t\tthis._interceptHistory();\r\n\r\n\t\t\t// Agregar listener de popstate (botones atrás/adelante)\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('popstate', this._handlePopState);\r\n\t\t\t}\r\n\r\n\t\t\t// Iniciar intervalo de polling (fallback)\r\n\t\t\tthis._intervalId = setInterval(() => this._checkUrlChange(), POLL_INTERVAL);\r\n\r\n\t\t\t// Iniciar intervalo de heartbeat\r\n\t\t\tthis._heartbeatIntervalId = setInterval(() => this._sendHeartbeat(), HEARTBEAT_INTERVAL);\r\n\r\n\t\t\t// Eventos para cierre de página\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.addEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = true;\r\n\r\n\t\t\t// Verificar si hay una página previa guardada (para F5)\r\n\t\t\t// Solo después de que el tracker esté inicializado\r\n\t\t\tsetTimeout(() => this._checkPreviousPage(), 0);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tif (this._intervalId) {\r\n\t\t\t\tclearInterval(this._intervalId);\r\n\t\t\t\tthis._intervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (this._heartbeatIntervalId) {\r\n\t\t\t\tclearInterval(this._heartbeatIntervalId);\r\n\t\t\t\tthis._heartbeatIntervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.removeEventListener('popstate', this._handlePopState);\r\n\t\t\t\twindow.removeEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.removeEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\t// Restaurar history original\r\n\t\t\tif (typeof history !== 'undefined') {\r\n\t\t\t\thistory.pushState = this._originalPushState;\r\n\t\t\t\thistory.replaceState = this._originalReplaceState;\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Intercepta history.pushState y history.replaceState */\r\n\tprivate _interceptHistory(): void {\r\n\t\tif (typeof window === 'undefined' || typeof history === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst self = this;\r\n\t\tconst originalPushState = history.pushState.bind(history) as (...args: unknown[]) => unknown;\r\n\t\tconst originalReplaceState = history.replaceState.bind(history) as (...args: unknown[]) => unknown;\r\n\r\n\t\t// Interceptar pushState\r\n\t\thistory.pushState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalPushState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.pushState;\r\n\r\n\t\t// Interceptar replaceState\r\n\t\thistory.replaceState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalReplaceState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.replaceState;\r\n\t}\r\n\r\n\t/** Manejador de popstate (botones atrás/adelante) */\r\n\tprivate _handlePopState = (): void => {\r\n\t\ttry {\r\n\t\t\tthis._handleNavigationChange();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejo centralizado de cambio de navegación */\r\n\tprivate _handleNavigationChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.pathname;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Calcular duración wall-clock de la página anterior\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t\t// Solo enviar page_leave si la duración es significativa\r\n\t\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\t\t// Enviar page_leave INMEDIATAMENTE para la página anterior\r\n\t\t\t\t\tthis._sendPageLeaveImmediate(this._lastUrl, duration);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Track page view de la nueva página\r\n\t\t\t\tthis._tracker.trackPageView();\r\n\r\n\t\t\t\t// Resetear contador para la nueva página\r\n\t\t\t\tthis._pageStartTime = performance.now();\r\n\r\n\t\t\t\t// Actualizar la última URL\r\n\t\t\t\tthis._lastUrl = currentUrl;\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía page_leave inmediatamente sin esperar el buffer */\r\n\tprivate _sendPageLeaveImmediate(path: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\t// Crear el evento directamente\r\n\t\t\tthis._tracker.trackPageLeave(duration, path);\r\n\r\n\t\t\t// También guardar en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(path, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de beforeunload (cuando el usuario sale de la página) */\r\n\tprivate _handleBeforeUnload = (): void => {\r\n\t\ttry {\r\n\t\t\t// Calcular duración wall-clock\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Solo enviar si la duración es significativa\r\n\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\tthis._tracker.trackPageLeave(duration, this._lastUrl);\r\n\t\t\t}\r\n\r\n\t\t\t// Guardar URL actual en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejador de visibilitychange (para detectar cuando la app pasa a segundo plano) */\r\n\tprivate _handleVisibilityChange = (): void => {\r\n\t\ttry {\r\n\t\t\tif (typeof document === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cuando la página se oculta, guardar el tiempo actual en sessionStorage\r\n\t\t\tif (document.visibilityState === 'hidden') {\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\t\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Verifica si la URL cambió (fallback de polling) */\r\n\tprivate _checkUrlChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.pathname;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Usar el manejo centralizado\r\n\t\t\t\tthis._handleNavigationChange();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Calcula la duración wall-clock (tiempo total desde que entró a la página) */\r\n\tprivate _calculateWallClockDuration(): number {\r\n\t\treturn (performance.now() - this._pageStartTime) / 1000;\r\n\t}\r\n\r\n\t/** Envía un heartbeat con la duración actual */\r\n\tprivate _sendHeartbeat(): void {\r\n\t\ttry {\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Usar trackPageLeave para mantener compatibilidad\r\n\t\t\t// El backend puede filtrar por eventType = 'page_heartbeat'\r\n\t\t\tthis._tracker.trackEvent({\r\n\t\t\t\teventType: 'page_heartbeat',\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath: this._lastUrl,\r\n\t\t\t\t\tduration: duration,\r\n\t\t\t\t\ttimestamp: new Date().toISOString(),\r\n\t\t\t\t},\r\n\t\t\t} as import('../types/index').TrackerEvent);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Guarda la URL y tiempo en sessionStorage (para F5) */\r\n\tprivate _saveToStorage(url: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_URL, url);\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_TIME, String(duration));\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Lee la URL y tiempo previos de sessionStorage (para F5) */\r\n\tprivate _getFromStorage(): { url: string; duration: number; } | null {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tconst url = sessionStorage.getItem(STORAGE_KEY_URL);\r\n\t\t\t\tconst duration = sessionStorage.getItem(STORAGE_KEY_TIME);\r\n\t\t\t\tif (url && duration) {\r\n\t\t\t\t\treturn { url, duration: parseFloat(duration) };\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/** Limpia sessionStorage */\r\n\tprivate _clearStorage(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_URL);\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_TIME);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si hay una página previa (para F5) */\r\n\tprivate _checkPreviousPage(): void {\r\n\t\tconst previous = this._getFromStorage();\r\n\t\tif (previous && previous.duration >= MIN_DURATION) {\r\n\t\t\t// La página anterior tenía una duración significativa, enviar page_leave\r\n\t\t\tthis._tracker.trackPageLeave(previous.duration, previous.url);\r\n\t\t}\r\n\t\t// Limpiar storage después de procesar\r\n\t\tthis._clearStorage();\r\n\t}\r\n}\r\n","{\r\n\t\"name\": \"@agroideas/event-tracker-sdk\",\r\n\t\"version\": \"1.0.7\",\r\n\t\"description\": \"A lightweight, framework-agnostic SDK for tracking frontend user behavior\",\r\n\t\"license\": \"MIT\",\r\n\t\"author\": \"Agroideas\",\r\n\t\"homepage\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"main\": \"./dist/index.cjs\",\r\n\t\"module\": \"./dist/index.mjs\",\r\n\t\"types\": \"./dist/index.d.ts\",\r\n\t\"exports\": {\r\n\t\t\".\": {\r\n\t\t\t\"types\": \"./dist/index.d.ts\",\r\n\t\t\t\"import\": \"./dist/index.mjs\",\r\n\t\t\t\"require\": \"./dist/index.cjs\"\r\n\t\t},\r\n\t\t\"./iife\": \"./dist/index.iife.js\"\r\n\t},\r\n\t\"files\": [\r\n\t\t\"dist\"\r\n\t],\r\n\t\"scripts\": {\r\n\t\t\"build\": \"tsup\",\r\n\t\t\"dev\": \"tsup --watch\",\r\n\t\t\"lint\": \"eslint src --ext .ts\",\r\n\t\t\"lint:fix\": \"eslint src --ext .ts --fix\",\r\n\t\t\"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\r\n\t\t\"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\r\n\t\t\"typecheck\": \"tsc --noEmit\",\r\n\t\t\"prepare\": \"husky install\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"@types/node\": \"^20.10.0\",\r\n\t\t\"eslint\": \"^8.55.0\",\r\n\t\t\"husky\": \"^8.0.3\",\r\n\t\t\"prettier\": \"^3.1.0\",\r\n\t\t\"tsup\": \"^8.0.1\",\r\n\t\t\"typescript\": \"^5.3.2\"\r\n\t},\r\n\t\"keywords\": [\r\n\t\t\"tracking\",\r\n\t\t\"analytics\",\r\n\t\t\"sdk\",\r\n\t\t\"events\",\r\n\t\t\"behavior\",\r\n\t\t\"frontend\"\r\n\t],\r\n\t\"engines\": {\r\n\t\t\"node\": \">=18.0.0\"\r\n\t}\r\n}","/**\n * Version constants for the Event Tracker SDK\n */\n\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION = pkg.version;\n","/**\r\n * Tracker: Singleton principal que coordina observers y EventBuffer\r\n */\r\n\r\nimport type { TrackerConfig, TrackerEvent, UserIdentity, EventContext } from '../types';\r\nimport { EventBuffer } from './EventBuffer';\r\nimport { ClickObserver } from '../observers/ClickObserver';\r\nimport { HistoryObserver } from '../observers/HistoryObserver';\r\nimport {\r\n\tgetCurrentPath,\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetOrCreateSessionId,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { VERSION } from '../version';\r\n\r\nexport class Tracker {\r\n\tprivate static _instance: Tracker | null = null;\r\n\tprivate _eventBuffer: EventBuffer | null = null;\r\n\tprivate _clickObserver: ClickObserver | null = null;\r\n\tprivate _historyObserver: HistoryObserver | null = null;\r\n\tprivate _userIdentity: UserIdentity;\r\n\tprivate _sessionId: string;\r\n\tprivate _isInitialized: boolean = false;\r\n\tprivate _isPaused: boolean = false;\r\n\tprivate _config: TrackerConfig | null = null;\r\n\r\n\tprivate constructor() {\r\n\t\tthis._userIdentity = {\r\n\t\t\tuserId: null,\r\n\t\t\tuserName: null,\r\n\t\t};\r\n\t\tthis._sessionId = getOrCreateSessionId();\r\n\t}\r\n\r\n\t/** Obtiene la instancia única del Tracker */\r\n\tstatic getInstance(): Tracker {\r\n\t\tif (Tracker._instance === null) {\r\n\t\t\tTracker._instance = new Tracker();\r\n\t\t}\r\n\t\treturn Tracker._instance;\r\n\t}\r\n\r\n\t/** Construye el contexto completo del evento */\r\n\tprivate _buildContext(): EventContext {\r\n\t\treturn {\r\n\t\t\tsessionId: this._sessionId,\r\n\t\t\tuserId: this._userIdentity.userId,\r\n\t\t\tuserName: this._userIdentity.userName,\r\n\t\t\turl: window.location.href,\r\n\t\t\treferrer: getReferrer(),\r\n\t\t\tscreenSize: getScreenSize(),\r\n\t\t\tviewportSize: getViewportSize(),\r\n\t\t\tdeviceType: getDeviceType(),\r\n\t\t\tbrowser: getBrowser(),\r\n\t\t\tos: getOS(),\r\n\t\t\tlanguage: getLanguage(),\r\n\t\t\ttimestamp: getTimestamp(),\r\n\t\t};\r\n\t}\r\n\r\n\t/** Verifica si una ruta debe ser excluida del tracking */\r\n\tprivate _shouldExclude(path: string): boolean {\r\n\t\tif (!this._config) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst { excludePatterns } = this._config;\r\n\r\n\t\t// Normalizar el path (quitar trailing slash para comparación consistente)\r\n\t\tconst normalizedPath = path.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;\r\n\r\n\t\t// Si hay patrones de exclusión, verificarlos\r\n\t\tif (excludePatterns && excludePatterns.length > 0) {\r\n\t\t\treturn excludePatterns.some((pattern) => {\r\n\t\t\t\tif (typeof pattern === 'string') {\r\n\t\t\t\t\t// Normalizar el patrón también\r\n\t\t\t\t\tconst normalizedPattern = pattern.endsWith('/') && pattern.length > 1 ? pattern.slice(0, -1) : pattern;\r\n\t\t\t\t\t// Coincidencia exacta o prefijo\r\n\t\t\t\t\treturn normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern);\r\n\t\t\t\t}\r\n\t\t\t\t// Es RegExp\r\n\t\t\t\treturn pattern.test(normalizedPath) || pattern.test(path);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/** Inicializa el tracker con la configuración */\r\n\tinit(config: TrackerConfig): void {\r\n\t\ttry {\r\n\t\t\t// Guardar configuración\r\n\t\t\tthis._config = config;\r\n\r\n\t\t\t// Actualizamos el usuario cada vez que se llama init (permite actualizar userId/userName)\r\n\t\t\tif (config.userId) {\r\n\t\t\t\tthis._userIdentity = {\r\n\t\t\t\t\tuserId: config.userId,\r\n\t\t\t\t\tuserName: config.userName ?? null,\r\n\t\t\t\t};\r\n\t\t\t\tif (this._isInitialized && config.debug) {\r\n\t\t\t\t\tconsole.log('[EventTracker] Usuario actualizado:', this._userIdentity);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Solo inicializar EventBuffer y observers la primera vez\r\n\t\t\tif (this._isInitialized) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._eventBuffer = new EventBuffer(config);\r\n\t\t\tthis._isInitialized = true;\r\n\r\n\t\t\t// Imprimir versión del tracker\r\n\t\t\tconsole.log(`🚀 [EventTracker] Inicializado v${VERSION}`);\r\n\r\n\t\t\t// Iniciar observers\r\n\t\t\tthis._clickObserver = new ClickObserver(this);\r\n\t\t\tthis._historyObserver = new HistoryObserver(this);\r\n\t\t\tthis._clickObserver.start();\r\n\t\t\tthis._historyObserver.start();\r\n\r\n\t\t\t// Enviar page_view inicial\r\n\t\t\tthis.trackPageView();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Identifica al usuario */\r\n\tidentify(userId: string, userName?: string): void {\r\n\t\ttry {\r\n\t\t\tthis._userIdentity = {\r\n\t\t\t\tuserId,\r\n\t\t\t\tuserName: userName ?? null,\r\n\t\t\t};\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Registra un evento */\r\n\ttrackEvent(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!this._eventBuffer) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Verificar si la URL actual debe ser excluida\r\n\t\t\tconst currentPath = getCurrentPath();\r\n\t\t\tif (this._shouldExclude(currentPath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst eventWithContext: TrackerEvent = {\r\n\t\t\t\t...event,\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t};\r\n\r\n\t\t\tthis._eventBuffer.add(eventWithContext);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page view */\r\n\ttrackPageView(): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst path = getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(path)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_view',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page leave */\r\n\ttrackPageLeave(duration: number, path?: string): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst pagePath = path ?? getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(pagePath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_leave',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tduration,\r\n\t\t\t\t\tpath: pagePath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Fuerza el envío de todos los eventos pendientes */\r\n\tasync flush(): Promise<void> {\r\n\t\ttry {\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tawait this._eventBuffer.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si el tracker está inicializado */\r\n\tisInitialized(): boolean {\r\n\t\treturn this._isInitialized;\r\n\t}\r\n\r\n\t/** Pausa el tracker (no registrará eventos) */\r\n\tpause(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = true;\r\n\t\t\tconsole.log('⏸️ [EventTracker] Tracker pausado - No se registrarán eventos');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Reanuda el tracker (volverá a registrar eventos) */\r\n\tresume(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = false;\r\n\t\t\tconsole.log('▶️ [EventTracker] Tracker reanudado - Eventos serán registrados');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Alterna el modo debug */\r\n\ttoggleDebug(): void {\r\n\t\ttry {\r\n\t\t\tif (!this._config) {\r\n\t\t\t\tconsole.log('⚠️ [EventTracker] Error: Tracker no inicializado');\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cambiar el valor de debug\r\n\t\t\tthis._config.debug = this._config.debug ? '' : 'true';\r\n\r\n\t\t\tif (this._config.debug) {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug ACTIVADO ✅');\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug DESACTIVADO ❌');\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Destruye el tracker */\r\n\tdestroy(): void {\r\n\t\ttry {\r\n\t\t\tif (this._clickObserver) {\r\n\t\t\t\tthis._clickObserver.stop();\r\n\t\t\t\tthis._clickObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._historyObserver) {\r\n\t\t\t\tthis._historyObserver.stop();\r\n\t\t\t\tthis._historyObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tthis._eventBuffer.destroy();\r\n\t\t\t\tthis._eventBuffer = null;\r\n\t\t\t}\r\n\t\t\tthis._isInitialized = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Event Tracker SDK\r\n *\r\n * SDK ligero y agnóstico al framework para tracking de comportamiento frontend.\r\n *\r\n * @example\r\n * import { tracker } from '@agroideas/event-tracker-sdk';\r\n *\r\n * tracker.init({\r\n * apiKey: 'tu-api-key',\r\n * endpoint: 'https://tu-api.com/events'\r\n * });\r\n *\r\n * // Identificar usuario\r\n * tracker.identify('user-123', 'Juan Pérez');\r\n */\r\n\r\nimport { Tracker } from './core/Tracker';\r\nimport type { TrackerConfig } from './types';\r\n\r\n/** Instancia única exportada del Tracker */\r\nexport const tracker = Tracker.getInstance();\r\n\r\n/**\r\n * Inicializa el tracker con configuración requerida (apiKey y endpoint).\r\n */\r\nexport function init(config: TrackerConfig): void {\r\n\ttracker.init(config);\r\n}\r\n\r\n/** Identifica al usuario */\r\nexport function identify(userId: string, userName?: string): void {\r\n\ttracker.identify(userId, userName);\r\n}\r\n\r\n/** Fuerza el envío de eventos pendientes */\r\nexport function flush(): Promise<void> {\r\n\treturn tracker.flush();\r\n}\r\n\r\n/** Destruye el tracker */\r\nexport function destroy(): void {\r\n\ttracker.destroy();\r\n}\r\n\r\nexport type { TrackerConfig };\r\n"],"mappings":";;;;AAMA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAEjB,MAAM,cAAN,MAAkB;AAAA,IAQxB,YAAY,QAAuB;AALnC,WAAQ,cAAqD;AAM5D,WAAK,UAAU,CAAC;AAGhB,WAAK,YAAY,OAAO;AACxB,WAAK,UAAU,OAAO;AAGtB,WAAK,WAAW,OAAO,SAAS;AAChC,cAAQ,IAAI,MAAM;AAElB,WAAK,UAAU;AAAA,QACd,eAAe,OAAO,iBAAiB;AAAA,QACvC,eAAe,OAAO,iBAAiB;AAAA,QACvC,iBAAiB,OAAO,mBAAmB,CAAC;AAAA,MAC7C;AAEA,UAAI,KAAK,UAAU;AAClB,gBAAQ,IAAI,oCAAoC;AAChD,gBAAQ,IAAI,4BAA4B,KAAK,SAAS;AACtD,gBAAQ,IAAI,2BAA2B,KAAK,OAAO;AAAA,MACpD;AAGA,UAAI,CAAC,KAAK,UAAU;AACnB,aAAK,iBAAiB;AAAA,MACvB;AAEA,WAAK,yBAAyB;AAAA,IAC/B;AAAA;AAAA,IAGA,IAAI,OAA2B;AAC9B,UAAI;AACH,aAAK,QAAQ,KAAK,KAAK;AAEvB,YAAI,KAAK,UAAU;AAClB,kBAAQ,IAAI,mCAAmC,MAAM,SAAS;AAC9D,kBAAQ,IAAI,+BAA+B,KAAK,QAAQ,MAAM;AAC9D,kBAAQ,IAAI,iCAAiC,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,QAC5E;AAGA,YAAI,CAAC,KAAK,YAAY,KAAK,QAAQ,UAAU,KAAK,QAAQ,eAAe;AACxE,eAAK,MAAM;AAAA,QACZ;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,MAAM,QAA6B;AAClC,UAAI;AACH,YAAI,KAAK,QAAQ,WAAW,GAAG;AAC9B,iBAAO,EAAE,SAAS,KAAK;AAAA,QACxB;AAEA,cAAM,eAAe,CAAC,GAAG,KAAK,OAAO;AACrC,aAAK,UAAU,CAAC;AAGhB,YAAI,KAAK,UAAU;AAClB,kBAAQ,IAAI,kCAAkC,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC;AACnF,iBAAO,EAAE,SAAS,KAAK;AAAA,QACxB;AAEA,cAAM,SAAS,MAAM,KAAK,iBAAiB,YAAY;AACvD,eAAO;AAAA,MACR,SAAS,OAAO;AACf,eAAO;AAAA,UACN,SAAS;AAAA,UACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACjD;AAAA,MACD;AAAA,IACD;AAAA;AAAA,IAGA,UAAkB;AACjB,aAAO,KAAK,QAAQ;AAAA,IACrB;AAAA;AAAA,IAGA,QAAc;AACb,WAAK,UAAU,CAAC;AAAA,IACjB;AAAA;AAAA,IAGA,UAAgB;AACf,UAAI,KAAK,aAAa;AACrB,sBAAc,KAAK,WAAW;AAC9B,aAAK,cAAc;AAAA,MACpB;AAEA,WAAK,UAAU,CAAC;AAAA,IACjB;AAAA;AAAA,IAGQ,mBAAyB;AAChC,WAAK,cAAc,YAAY,MAAM;AACpC,YAAI,KAAK,QAAQ,SAAS,GAAG;AAC5B,eAAK,MAAM;AAAA,QACZ;AAAA,MACD,GAAG,KAAK,QAAQ,aAAa;AAAA,IAC9B;AAAA;AAAA,IAGQ,2BAAiC;AACxC,UAAI,OAAO,aAAa,aAAa;AACpC;AAAA,MACD;AAEA,YAAM,yBAAyB,MAAY;AAE1C,YAAI,KAAK,UAAU;AAClB;AAAA,QACD;AAEA,YAAI,SAAS,oBAAoB,YAAY,KAAK,QAAQ,SAAS,GAAG;AAErE,qBAAW,MAAM;AAChB,iBAAK,MAAM;AAAA,UACZ,GAAG,CAAC;AAAA,QACL;AAAA,MACD;AAEA,eAAS,iBAAiB,oBAAoB,sBAAsB;AAAA,IACrE;AAAA;AAAA,IAGA,MAAc,iBAAiB,QAA6C;AAC3E,UAAI;AACH,cAAM,UAAU,KAAK,UAAU;AAAA,UAC9B;AAAA,QACD,CAAC;AAED,cAAM,WAAW,MAAM,MAAM,KAAK,WAAW;AAAA,UAC5C,QAAQ;AAAA,UACR,SAAS;AAAA,YACR,gBAAgB;AAAA,YAChB,aAAa,KAAK;AAAA,UACnB;AAAA,UACA,MAAM;AAAA,UACN,WAAW;AAAA,QACZ,CAAC;AAGD,YAAI;AACJ,YAAI;AACH,gBAAM,eAAiC,MAAM,SAAS,KAAK;AAC3D,oBAAU,aAAa;AAAA,QACxB,QAAQ;AAEP,oBAAU,SAAS;AAAA,QACpB;AAEA,eAAO;AAAA,UACN,SAAS,SAAS;AAAA,UAClB,YAAY,SAAS;AAAA,UACrB;AAAA,QACD;AAAA,MACD,SAAS,OAAO;AACf,eAAO;AAAA,UACN,SAAS;AAAA,UACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACjD;AAAA,MACD;AAAA,IACD;AAAA,EACD;;;ACpLO,WAAS,eAAuB;AACtC,WAAO,uCAAuC,QAAQ,SAAS,CAAC,SAAS;AACxE,YAAM,SAAS,KAAK,OAAO,IAAI,KAAK;AACpC,YAAM,QAAQ,SAAS,MAAM,SAAU,SAAS,IAAO;AACvD,aAAO,MAAM,SAAS,EAAE;AAAA,IACzB,CAAC;AAAA,EACF;;;ACDO,WAAS,SAAS,MAAc,WAA2B;AACjE,QAAI,YAAY,GAAG;AAClB,aAAO;AAAA,IACR;AAEA,QAAI,KAAK,UAAU,WAAW;AAC7B,aAAO;AAAA,IACR;AAEA,WAAO,KAAK,UAAU,GAAG,YAAY,CAAC,IAAI;AAAA,EAC3C;;;ACbO,WAAS,uBAA+B;AAC9C,QAAI;AACH,YAAM,aAAa;AACnB,YAAM,oBAAoB,eAAe,QAAQ,UAAU;AAE3D,UAAI,mBAAmB;AACtB,eAAO;AAAA,MACR;AAEA,YAAM,eAAe,aAAa;AAClC,qBAAe,QAAQ,YAAY,YAAY;AAC/C,aAAO;AAAA,IACR,QAAQ;AACP,aAAO,aAAa;AAAA,IACrB;AAAA,EACD;AAGO,WAAS,gBAAwB;AACvC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,GAAG,OAAO,UAAU,IAAI,OAAO,WAAW;AAAA,EAClD;AAGO,WAAS,kBAA0B;AACzC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,GAAG,OAAO,UAAU,IAAI,OAAO,WAAW;AAAA,EAClD;AAGO,WAAS,gBAA6D;AAC5E,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,UAAM,KAAK,UAAU;AAErB,QAAI,mDAAmD,KAAK,EAAE,GAAG;AAChE,aAAO;AAAA,IACR;AAEA,QAAI,sGAAsG,KAAK,EAAE,GAAG;AACnH,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAGO,WAAS,aAAqB;AACpC,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,UAAM,KAAK,UAAU;AAErB,QAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,MAAM,GAAG;AACxB,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,QAAQ,GAAG;AAC1B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,QAAQ,GAAG;AAC1B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,UAAU,GAAG;AACnD,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAGO,WAAS,QAAgB;AAC/B,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,UAAM,KAAK,UAAU;AAErB,QAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,QAAQ,GAAG;AAC1B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,OAAO,GAAG;AACzB,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,KAAK,KAAK,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,MAAM,GAAG;AACvE,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAGO,WAAS,cAAsB;AACrC,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,WAAO,UAAU,YAAY;AAAA,EAC9B;AAYO,WAAS,cAAsB;AACrC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,SAAS;AAAA,EACjB;AAGO,WAAS,eAAuB;AACtC,YAAO,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC/B;AAGO,WAAS,iBAAyB;AACxC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,OAAO,SAAS;AAAA,EACxB;;;AC9IA,MAAM,iBAAiB,CAAC,UAAU,KAAK,SAAS,KAAK,QAAQ,KAAK;AAGlE,MAAM,wBAAwB,CAAC,QAAQ;AAGvC,MAAM,wBAAwB,CAAC,YAAY,SAAS,OAAO,UAAU,UAAU,KAAK;AAGpF,MAAM,sBAAsB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAGA,WAAS,oBAAoB,SAA2B;AACvD,QAAI,QAAQ,YAAY,SAAS;AAChC,YAAM,YAAa,QAA6B,MAAM,YAAY,KAAK;AACvE,aAAO,sBAAsB,SAAS,SAAS;AAAA,IAChD;AACA,WAAO;AAAA,EACR;AAGA,WAAS,oBAAoB,SAA2B;AACvD,UAAM,UAAU,QAAQ,QAAQ,YAAY;AAE5C,QAAI,CAAC,eAAe,SAAS,OAAO,GAAG;AACtC,aAAO,QAAQ,aAAa,YAAY,KACvC,kBAAkB,OAAO,KACzB,WAAW,OAAO;AAAA,IACpB;AAEA,QAAI,YAAY,SAAS;AACxB,YAAM,YAAa,QAA6B,MAAM,YAAY,KAAK;AACvE,aAAO,sBAAsB,SAAS,SAAS;AAAA,IAChD;AAEA,WAAO;AAAA,EACR;AAGA,WAAS,kBAAkB,SAA2B;AACrD,QAAI;AACH,YAAM,QAAQ,OAAO,iBAAiB,OAAO;AAC7C,aAAO,MAAM,WAAW;AAAA,IACzB,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAGA,WAAS,WAAW,SAA2B;AAC9C,WAAO,QAAQ,YAAY,OAAO,QAAQ,UAAU,SAAS,KAAK;AAAA,EACnE;AAGA,WAAS,kBAAkB,SAA0B;AACpD,UAAM,QAAkB,CAAC;AACzB,QAAI,UAA0B;AAE9B,WAAO,WAAW,YAAY,SAAS,iBAAiB;AACvD,UAAI,WAAW,QAAQ,QAAQ,YAAY;AAE3C,UAAI,QAAQ,IAAI;AACf,oBAAY,IAAI,QAAQ,EAAE;AAC1B,cAAM,QAAQ,QAAQ;AACtB;AAAA,MACD,WAAW,QAAQ,aAAa,OAAO,QAAQ,cAAc,UAAU;AACtE,cAAM,UAAU,QAAQ,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AACpE,YAAI,QAAQ,SAAS,GAAG;AACvB,sBAAY,IAAI,QAAQ,CAAC,CAAC;AAAA,QAC3B;AAAA,MACD;AAEA,YAAM,QAAQ,QAAQ;AACtB,gBAAU,QAAQ;AAAA,IACnB;AAEA,WAAO,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,KAAK;AAAA,EACpC;AAGA,WAAS,eAAe,SAAsC;AAC7D,QAAI,CAAC,QAAQ,aAAa,OAAO,QAAQ,cAAc,UAAU;AAChE,aAAO;AAAA,IACR;AACA,UAAM,UAAU,QAAQ,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AACpE,WAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,GAAG,IAAI;AAAA,EACjD;AAEO,MAAM,gBAAN,MAAoB;AAAA,IAK1B,YAAYA,UAAkB;AAH9B,WAAQ,YAAqB;AAI5B,WAAK,WAAWA;AAChB,WAAK,oBAAoB,KAAK,aAAa,KAAK,IAAI;AAAA,IACrD;AAAA;AAAA,IAGA,QAAc;AACb,UAAI,KAAK,WAAW;AACnB;AAAA,MACD;AAEA,UAAI;AACH,eAAO,iBAAiB,SAAS,KAAK,mBAAmB,IAAI;AAC7D,aAAK,YAAY;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,OAAa;AACZ,UAAI,CAAC,KAAK,WAAW;AACpB;AAAA,MACD;AAEA,UAAI;AACH,eAAO,oBAAoB,SAAS,KAAK,mBAAmB,IAAI;AAChE,aAAK,YAAY;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,aAAa,OAAyB;AAC7C,UAAI;AACH,cAAM,SAAS,MAAM;AAErB,YAAI,CAAC,QAAQ;AACZ;AAAA,QACD;AAGA,YAAI,oBAAoB,MAAM,KAAK,CAAC,oBAAoB,MAAM,GAAG;AAChE,eAAK,kBAAkB,MAAM;AAC7B;AAAA,QACD;AAGA,cAAM,mBAAmB,OAAO,QAAQ,oBAAoB,KAAK,GAAG,CAAC;AAErE,YAAI,CAAC,kBAAkB;AACtB;AAAA,QACD;AAEA,YAAI,oBAAoB,gBAAgB,GAAG;AAC1C;AAAA,QACD;AAEA,YAAI,CAAC,oBAAoB,gBAAgB,GAAG;AAC3C;AAAA,QACD;AAEA,aAAK,kBAAkB,gBAAgB;AAAA,MACxC,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,kBAAkB,SAAwB;AACjD,YAAM,QAAsB;AAAA,QAC3B,WAAW;AAAA,QACX,SAAS,aAAa;AAAA,QACtB,SAAS;AAAA,UACR,WAAW;AAAA,UACX,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,KAAK,OAAO,SAAS;AAAA,UACrB,UAAU,YAAY;AAAA,UACtB,YAAY,cAAc;AAAA,UAC1B,cAAc,gBAAgB;AAAA,UAC9B,YAAY,cAAc;AAAA,UAC1B,SAAS,WAAW;AAAA,UACpB,IAAI,MAAM;AAAA,UACV,UAAU,YAAY;AAAA,UACtB,WAAW,aAAa;AAAA,QACzB;AAAA,QACA,MAAM;AAAA,UACL,SAAS,QAAQ;AAAA,UACjB,IAAI,QAAQ,MAAM;AAAA,UAClB,WAAW,eAAe,OAAO;AAAA,UACjC,OAAO,QAAQ,aAAa,OAAO,KAAK;AAAA,UACxC,MAAM,QAAQ,cAAc,SAAS,QAAQ,aAAa,EAAE,IAAI;AAAA,UAChE,UAAU,kBAAkB,OAAO;AAAA,QACpC;AAAA,MACD;AAEA,WAAK,SAAS,WAAW,KAAK;AAAA,IAC/B;AAAA,EACD;;;AClNA,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAC3B,MAAM,eAAe;AACrB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAElB,MAAM,kBAAN,MAAsB;AAAA,IAU5B,YAAYC,UAAkB;AAR9B,WAAQ,YAAqB;AAC7B,WAAQ,cAAqD;AAC7D,WAAQ,uBAA8D;AACtE,WAAQ,WAAmB;AAE3B,WAAQ,qBAA+C,MAAM;AAC7D,WAAQ,wBAAqD,MAAM;AAgHnE;AAAA,WAAQ,kBAAkB,MAAY;AACrC,YAAI;AACH,eAAK,wBAAwB;AAAA,QAC9B,QAAQ;AAAA,QAER;AAAA,MACD;AAkDA;AAAA,WAAQ,sBAAsB,MAAY;AACzC,YAAI;AAEH,gBAAM,WAAW,KAAK,4BAA4B;AAGlD,cAAI,YAAY,cAAc;AAC7B,iBAAK,SAAS,eAAe,UAAU,KAAK,QAAQ;AAAA,UACrD;AAGA,eAAK,eAAe,KAAK,UAAU,QAAQ;AAAA,QAC5C,QAAQ;AAAA,QAER;AAAA,MACD;AAGA;AAAA,WAAQ,0BAA0B,MAAY;AAC7C,YAAI;AACH,cAAI,OAAO,aAAa,aAAa;AACpC;AAAA,UACD;AAGA,cAAI,SAAS,oBAAoB,UAAU;AAC1C,kBAAM,WAAW,KAAK,4BAA4B;AAClD,iBAAK,eAAe,KAAK,UAAU,QAAQ;AAAA,UAC5C;AAAA,QACD,QAAQ;AAAA,QAER;AAAA,MACD;AArMC,WAAK,WAAWA;AAChB,WAAK,iBAAiB,YAAY,IAAI;AACtC,WAAK,WAAW,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAG3E,UAAI,OAAO,WAAW,eAAe,OAAO,YAAY,aAAa;AACpE,aAAK,qBAAqB,QAAQ,UAAU,KAAK,OAAO;AACxD,aAAK,wBAAwB,QAAQ,aAAa,KAAK,OAAO;AAAA,MAC/D;AAAA,IACD;AAAA;AAAA,IAGA,QAAc;AACb,UAAI,KAAK,WAAW;AACnB;AAAA,MACD;AAEA,UAAI;AAEH,aAAK,kBAAkB;AAGvB,YAAI,OAAO,WAAW,aAAa;AAClC,iBAAO,iBAAiB,YAAY,KAAK,eAAe;AAAA,QACzD;AAGA,aAAK,cAAc,YAAY,MAAM,KAAK,gBAAgB,GAAG,aAAa;AAG1E,aAAK,uBAAuB,YAAY,MAAM,KAAK,eAAe,GAAG,kBAAkB;AAGvF,YAAI,OAAO,WAAW,aAAa;AAClC,iBAAO,iBAAiB,gBAAgB,KAAK,mBAAmB;AAChE,mBAAS,iBAAiB,oBAAoB,KAAK,uBAAuB;AAAA,QAC3E;AAEA,aAAK,YAAY;AAIjB,mBAAW,MAAM,KAAK,mBAAmB,GAAG,CAAC;AAAA,MAC9C,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,OAAa;AACZ,UAAI,CAAC,KAAK,WAAW;AACpB;AAAA,MACD;AAEA,UAAI;AACH,YAAI,KAAK,aAAa;AACrB,wBAAc,KAAK,WAAW;AAC9B,eAAK,cAAc;AAAA,QACpB;AAEA,YAAI,KAAK,sBAAsB;AAC9B,wBAAc,KAAK,oBAAoB;AACvC,eAAK,uBAAuB;AAAA,QAC7B;AAEA,YAAI,OAAO,WAAW,aAAa;AAClC,iBAAO,oBAAoB,YAAY,KAAK,eAAe;AAC3D,iBAAO,oBAAoB,gBAAgB,KAAK,mBAAmB;AACnE,mBAAS,oBAAoB,oBAAoB,KAAK,uBAAuB;AAAA,QAC9E;AAGA,YAAI,OAAO,YAAY,aAAa;AACnC,kBAAQ,YAAY,KAAK;AACzB,kBAAQ,eAAe,KAAK;AAAA,QAC7B;AAEA,aAAK,YAAY;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,oBAA0B;AACjC,UAAI,OAAO,WAAW,eAAe,OAAO,YAAY,aAAa;AACpE;AAAA,MACD;AAEA,YAAM,OAAO;AACb,YAAM,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AACxD,YAAM,uBAAuB,QAAQ,aAAa,KAAK,OAAO;AAG9D,cAAQ,YAAY,YAAa,MAAiB;AACjD,cAAM,SAAS,kBAAkB,GAAG,IAAI;AACxC,aAAK,wBAAwB;AAC7B,eAAO;AAAA,MACR;AAGA,cAAQ,eAAe,YAAa,MAAiB;AACpD,cAAM,SAAS,qBAAqB,GAAG,IAAI;AAC3C,aAAK,wBAAwB;AAC7B,eAAO;AAAA,MACR;AAAA,IACD;AAAA;AAAA,IAYQ,0BAAgC;AACvC,UAAI;AACH,YAAI,OAAO,WAAW,aAAa;AAClC;AAAA,QACD;AAEA,cAAM,aAAa,OAAO,SAAS;AAGnC,YAAI,eAAe,KAAK,UAAU;AAEjC,gBAAM,WAAW,KAAK,4BAA4B;AAGlD,cAAI,YAAY,cAAc;AAE7B,iBAAK,wBAAwB,KAAK,UAAU,QAAQ;AAAA,UACrD;AAGA,eAAK,SAAS,cAAc;AAG5B,eAAK,iBAAiB,YAAY,IAAI;AAGtC,eAAK,WAAW;AAAA,QACjB;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,wBAAwB,MAAc,UAAwB;AACrE,UAAI;AAEH,aAAK,SAAS,eAAe,UAAU,IAAI;AAG3C,aAAK,eAAe,MAAM,QAAQ;AAAA,MACnC,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAsCQ,kBAAwB;AAC/B,UAAI;AACH,YAAI,OAAO,WAAW,aAAa;AAClC;AAAA,QACD;AAEA,cAAM,aAAa,OAAO,SAAS;AAGnC,YAAI,eAAe,KAAK,UAAU;AAEjC,eAAK,wBAAwB;AAAA,QAC9B;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,8BAAsC;AAC7C,cAAQ,YAAY,IAAI,IAAI,KAAK,kBAAkB;AAAA,IACpD;AAAA;AAAA,IAGQ,iBAAuB;AAC9B,UAAI;AACH,cAAM,WAAW,KAAK,4BAA4B;AAIlD,aAAK,SAAS,WAAW;AAAA,UACxB,WAAW;AAAA,UACX,MAAM;AAAA,YACL,MAAM,KAAK;AAAA,YACX;AAAA,YACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UACnC;AAAA,QACD,CAA0C;AAAA,MAC3C,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,eAAe,KAAa,UAAwB;AAC3D,UAAI;AACH,YAAI,OAAO,mBAAmB,aAAa;AAC1C,yBAAe,QAAQ,iBAAiB,GAAG;AAC3C,yBAAe,QAAQ,kBAAkB,OAAO,QAAQ,CAAC;AAAA,QAC1D;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,kBAA6D;AACpE,UAAI;AACH,YAAI,OAAO,mBAAmB,aAAa;AAC1C,gBAAM,MAAM,eAAe,QAAQ,eAAe;AAClD,gBAAM,WAAW,eAAe,QAAQ,gBAAgB;AACxD,cAAI,OAAO,UAAU;AACpB,mBAAO,EAAE,KAAK,UAAU,WAAW,QAAQ,EAAE;AAAA,UAC9C;AAAA,QACD;AAAA,MACD,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACR;AAAA;AAAA,IAGQ,gBAAsB;AAC7B,UAAI;AACH,YAAI,OAAO,mBAAmB,aAAa;AAC1C,yBAAe,WAAW,eAAe;AACzC,yBAAe,WAAW,gBAAgB;AAAA,QAC3C;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,qBAA2B;AAClC,YAAM,WAAW,KAAK,gBAAgB;AACtC,UAAI,YAAY,SAAS,YAAY,cAAc;AAElD,aAAK,SAAS,eAAe,SAAS,UAAU,SAAS,GAAG;AAAA,MAC7D;AAEA,WAAK,cAAc;AAAA,IACpB;AAAA,EACD;;;AChUA;AAAA,IACC,MAAQ;AAAA,IACR,SAAW;AAAA,IACX,aAAe;AAAA,IACf,SAAW;AAAA,IACX,QAAU;AAAA,IACV,UAAY;AAAA,IACZ,YAAc;AAAA,MACb,MAAQ;AAAA,MACR,KAAO;AAAA,IACR;AAAA,IACA,MAAQ;AAAA,MACP,KAAO;AAAA,IACR;AAAA,IACA,MAAQ;AAAA,IACR,QAAU;AAAA,IACV,OAAS;AAAA,IACT,SAAW;AAAA,MACV,KAAK;AAAA,QACJ,OAAS;AAAA,QACT,QAAU;AAAA,QACV,SAAW;AAAA,MACZ;AAAA,MACA,UAAU;AAAA,IACX;AAAA,IACA,OAAS;AAAA,MACR;AAAA,IACD;AAAA,IACA,SAAW;AAAA,MACV,OAAS;AAAA,MACT,KAAO;AAAA,MACP,MAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAU;AAAA,MACV,gBAAgB;AAAA,MAChB,WAAa;AAAA,MACb,SAAW;AAAA,IACZ;AAAA,IACA,iBAAmB;AAAA,MAClB,eAAe;AAAA,MACf,QAAU;AAAA,MACV,OAAS;AAAA,MACT,UAAY;AAAA,MACZ,MAAQ;AAAA,MACR,YAAc;AAAA,IACf;AAAA,IACA,UAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,SAAW;AAAA,MACV,MAAQ;AAAA,IACT;AAAA,EACD;;;ACnDO,MAAM,UAAU,gBAAI;;;ACiBpB,MAAM,WAAN,MAAM,SAAQ;AAAA,IAWZ,cAAc;AATtB,WAAQ,eAAmC;AAC3C,WAAQ,iBAAuC;AAC/C,WAAQ,mBAA2C;AAGnD,WAAQ,iBAA0B;AAClC,WAAQ,YAAqB;AAC7B,WAAQ,UAAgC;AAGvC,WAAK,gBAAgB;AAAA,QACpB,QAAQ;AAAA,QACR,UAAU;AAAA,MACX;AACA,WAAK,aAAa,qBAAqB;AAAA,IACxC;AAAA;AAAA,IAGA,OAAO,cAAuB;AAC7B,UAAI,SAAQ,cAAc,MAAM;AAC/B,iBAAQ,YAAY,IAAI,SAAQ;AAAA,MACjC;AACA,aAAO,SAAQ;AAAA,IAChB;AAAA;AAAA,IAGQ,gBAA8B;AACrC,aAAO;AAAA,QACN,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK,cAAc;AAAA,QAC3B,UAAU,KAAK,cAAc;AAAA,QAC7B,KAAK,OAAO,SAAS;AAAA,QACrB,UAAU,YAAY;AAAA,QACtB,YAAY,cAAc;AAAA,QAC1B,cAAc,gBAAgB;AAAA,QAC9B,YAAY,cAAc;AAAA,QAC1B,SAAS,WAAW;AAAA,QACpB,IAAI,MAAM;AAAA,QACV,UAAU,YAAY;AAAA,QACtB,WAAW,aAAa;AAAA,MACzB;AAAA,IACD;AAAA;AAAA,IAGQ,eAAe,MAAuB;AAC7C,UAAI,CAAC,KAAK,SAAS;AAClB,eAAO;AAAA,MACR;AAEA,YAAM,EAAE,gBAAgB,IAAI,KAAK;AAGjC,YAAM,iBAAiB,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AAGnF,UAAI,mBAAmB,gBAAgB,SAAS,GAAG;AAClD,eAAO,gBAAgB,KAAK,CAAC,YAAY;AACxC,cAAI,OAAO,YAAY,UAAU;AAEhC,kBAAM,oBAAoB,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AAE/F,mBAAO,mBAAmB,qBAAqB,eAAe,WAAW,iBAAiB;AAAA,UAC3F;AAEA,iBAAO,QAAQ,KAAK,cAAc,KAAK,QAAQ,KAAK,IAAI;AAAA,QACzD,CAAC;AAAA,MACF;AAEA,aAAO;AAAA,IACR;AAAA;AAAA,IAGA,KAAK,QAA6B;AACjC,UAAI;AAEH,aAAK,UAAU;AAGf,YAAI,OAAO,QAAQ;AAClB,eAAK,gBAAgB;AAAA,YACpB,QAAQ,OAAO;AAAA,YACf,UAAU,OAAO,YAAY;AAAA,UAC9B;AACA,cAAI,KAAK,kBAAkB,OAAO,OAAO;AACxC,oBAAQ,IAAI,uCAAuC,KAAK,aAAa;AAAA,UACtE;AAAA,QACD;AAGA,YAAI,KAAK,gBAAgB;AACxB;AAAA,QACD;AAEA,aAAK,eAAe,IAAI,YAAY,MAAM;AAC1C,aAAK,iBAAiB;AAGtB,gBAAQ,IAAI,0CAAmC,OAAO,EAAE;AAGxD,aAAK,iBAAiB,IAAI,cAAc,IAAI;AAC5C,aAAK,mBAAmB,IAAI,gBAAgB,IAAI;AAChD,aAAK,eAAe,MAAM;AAC1B,aAAK,iBAAiB,MAAM;AAG5B,aAAK,cAAc;AAAA,MACpB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,SAAS,QAAgB,UAAyB;AACjD,UAAI;AACH,aAAK,gBAAgB;AAAA,UACpB;AAAA,UACA,UAAU,YAAY;AAAA,QACvB;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,WAAW,OAA2B;AACrC,UAAI;AAEH,YAAI,KAAK,WAAW;AACnB;AAAA,QACD;AAEA,YAAI,CAAC,KAAK,cAAc;AACvB;AAAA,QACD;AAGA,cAAM,cAAc,eAAe;AACnC,YAAI,KAAK,eAAe,WAAW,GAAG;AACrC;AAAA,QACD;AAEA,cAAM,mBAAiC;AAAA,UACtC,GAAG;AAAA,UACH,SAAS,aAAa;AAAA,UACtB,SAAS,KAAK,cAAc;AAAA,QAC7B;AAEA,aAAK,aAAa,IAAI,gBAAgB;AAAA,MACvC,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,gBAAsB;AACrB,UAAI;AAEH,YAAI,KAAK,WAAW;AACnB;AAAA,QACD;AAEA,cAAM,OAAO,eAAe;AAG5B,YAAI,KAAK,eAAe,IAAI,GAAG;AAC9B;AAAA,QACD;AAEA,cAAM,QAAsB;AAAA,UAC3B,WAAW;AAAA,UACX,SAAS,aAAa;AAAA,UACtB,SAAS,KAAK,cAAc;AAAA,UAC5B,MAAM;AAAA,YACL;AAAA,UACD;AAAA,QACD;AAEA,aAAK,WAAW,KAAK;AAAA,MACtB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,eAAe,UAAkB,MAAqB;AACrD,UAAI;AAEH,YAAI,KAAK,WAAW;AACnB;AAAA,QACD;AAEA,cAAM,WAAW,QAAQ,eAAe;AAGxC,YAAI,KAAK,eAAe,QAAQ,GAAG;AAClC;AAAA,QACD;AAEA,cAAM,QAAsB;AAAA,UAC3B,WAAW;AAAA,UACX,SAAS,aAAa;AAAA,UACtB,SAAS,KAAK,cAAc;AAAA,UAC5B,MAAM;AAAA,YACL;AAAA,YACA,MAAM;AAAA,UACP;AAAA,QACD;AAEA,aAAK,WAAW,KAAK;AAAA,MACtB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,MAAM,QAAuB;AAC5B,UAAI;AACH,YAAI,KAAK,cAAc;AACtB,gBAAM,KAAK,aAAa,MAAM;AAAA,QAC/B;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,gBAAyB;AACxB,aAAO,KAAK;AAAA,IACb;AAAA;AAAA,IAGA,QAAc;AACb,UAAI;AACH,aAAK,YAAY;AACjB,gBAAQ,IAAI,4EAA+D;AAAA,MAC5E,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,SAAe;AACd,UAAI;AACH,aAAK,YAAY;AACjB,gBAAQ,IAAI,8EAAiE;AAAA,MAC9E,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,cAAoB;AACnB,UAAI;AACH,YAAI,CAAC,KAAK,SAAS;AAClB,kBAAQ,IAAI,4DAAkD;AAC9D;AAAA,QACD;AAGA,aAAK,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,KAAK;AAE/C,YAAI,KAAK,QAAQ,OAAO;AACvB,kBAAQ,IAAI,qDAAyC;AAAA,QACtD,OAAO;AACN,kBAAQ,IAAI,wDAA4C;AAAA,QACzD;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,UAAgB;AACf,UAAI;AACH,YAAI,KAAK,gBAAgB;AACxB,eAAK,eAAe,KAAK;AACzB,eAAK,iBAAiB;AAAA,QACvB;AACA,YAAI,KAAK,kBAAkB;AAC1B,eAAK,iBAAiB,KAAK;AAC3B,eAAK,mBAAmB;AAAA,QACzB;AACA,YAAI,KAAK,cAAc;AACtB,eAAK,aAAa,QAAQ;AAC1B,eAAK,eAAe;AAAA,QACrB;AACA,aAAK,iBAAiB;AAAA,MACvB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA,EACD;AArSC,EADY,SACG,YAA4B;AADrC,MAAMC,WAAN;;;ACFA,MAAM,UAAUC,SAAQ,YAAY;AAKpC,WAAS,KAAK,QAA6B;AACjD,YAAQ,KAAK,MAAM;AAAA,EACpB;AAGO,WAAS,SAAS,QAAgB,UAAyB;AACjE,YAAQ,SAAS,QAAQ,QAAQ;AAAA,EAClC;AAGO,WAAS,QAAuB;AACtC,WAAO,QAAQ,MAAM;AAAA,EACtB;AAGO,WAAS,UAAgB;AAC/B,YAAQ,QAAQ;AAAA,EACjB;","names":["tracker","tracker","Tracker","Tracker"]}
1
+ {"version":3,"sources":["../src/core/EventBuffer.ts","../src/utils/uuid.ts","../src/utils/truncate.ts","../src/utils/metadata.ts","../src/observers/ClickObserver.ts","../src/observers/HistoryObserver.ts","../package.json","../src/version.ts","../src/core/Tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * EventBuffer: Maneja la acumulación, batching y envío de eventos\r\n */\r\n\r\nimport type { TrackerEvent, SendResult, TrackerConfig, TrackingResponse } from '../types';\r\n\r\nconst BATCH_INTERVAL = 10000;\r\nconst MAX_BUFFER_SIZE = 15;\r\n\r\nexport class EventBuffer {\r\n\tprivate _buffer: TrackerEvent[];\r\n\tprivate _config: Required<Omit<TrackerConfig, 'apiKey' | 'endpoint' | 'userId' | 'userName' | 'debug'>>;\r\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _endpoint: string;\r\n\tprivate _apiKey: string;\r\n\tprivate _isDebug: boolean;\r\n\r\n\tconstructor(config: TrackerConfig) {\r\n\t\tthis._buffer = [];\r\n\r\n\t\t// apiKey y endpoint son requeridos\r\n\t\tthis._endpoint = config.endpoint;\r\n\t\tthis._apiKey = config.apiKey;\r\n\r\n\t\t// DEBUG es opcional, default false\r\n\t\tthis._isDebug = config.debug == \"true\";\r\n\t\tconsole.log(config);\r\n\r\n\t\tthis._config = {\r\n\t\t\tbatchInterval: config.batchInterval ?? BATCH_INTERVAL,\r\n\t\t\tmaxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE,\r\n\t\t\texcludePatterns: config.excludePatterns ?? [],\r\n\t\t};\r\n\r\n\t\tif (this._isDebug) {\r\n\t\t\tconsole.log('[EventTracker] Modo DEBUG activado');\r\n\t\t\tconsole.log('[EventTracker] Endpoint:', this._endpoint);\r\n\t\t\tconsole.log('[EventTracker] API Key:', this._apiKey);\r\n\t\t}\r\n\r\n\t\t// En modo debug, no iniciar timer de flush\r\n\t\tif (!this._isDebug) {\r\n\t\t\tthis._startFlushTimer();\r\n\t\t}\r\n\r\n\t\tthis._setupVisibilityListener();\r\n\t}\r\n\r\n\t/** Agrega un evento al buffer */\r\n\tadd(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\tthis._buffer.push(event);\r\n\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] Evento agregado:', event.eventType);\r\n\t\t\t\tconsole.log('[EventTracker] Buffer size:', this._buffer.length);\r\n\t\t\t\tconsole.log('[EventTracker] Event content:', JSON.stringify(event, null, 2));\r\n\t\t\t}\r\n\r\n\t\t\t// En modo debug, no hacer flush automático\r\n\t\t\tif (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Silently ignore errors to never block the main app\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía todos los eventos acumulados al servidor */\r\n\tasync flush(): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tif (this._buffer.length === 0) {\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst eventsToSend = [...this._buffer];\r\n\t\t\tthis._buffer = [];\r\n\r\n\t\t\t// Modo debug: solo console.log, no fetch\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] DEBUG - Events:', JSON.stringify(eventsToSend, null, 2));\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst result = await this._sendEventsAsync(eventsToSend);\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\t/** Obtiene la cantidad de eventos en el buffer */\r\n\tgetSize(): number {\r\n\t\treturn this._buffer.length;\r\n\t}\r\n\r\n\t/** Limpia el buffer sin enviar */\r\n\tclear(): void {\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Destruye el buffer y limpia recursos */\r\n\tdestroy(): void {\r\n\t\tif (this._flushTimer) {\r\n\t\t\tclearInterval(this._flushTimer);\r\n\t\t\tthis._flushTimer = null;\r\n\t\t}\r\n\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Inicia el temporizador de flush periódico */\r\n\tprivate _startFlushTimer(): void {\r\n\t\tthis._flushTimer = setInterval(() => {\r\n\t\t\tif (this._buffer.length > 0) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t}, this._config.batchInterval);\r\n\t}\r\n\r\n\t/** Configura el listener de visibilitychange para envío de emergencia */\r\n\tprivate _setupVisibilityListener(): void {\r\n\t\tif (typeof document === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst handleVisibilityChange = (): void => {\r\n\t\t\t// En modo debug, no enviar eventos\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (document.visibilityState === 'hidden' && this._buffer.length > 0) {\r\n\t\t\t\t// Usar setTimeout para asegurar que el fetch se complete antes del unload\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tthis.flush();\r\n\t\t\t\t}, 0);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tdocument.addEventListener('visibilitychange', handleVisibilityChange);\r\n\t}\r\n\r\n\t/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */\r\n\tprivate async _sendEventsAsync(events: TrackerEvent[]): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tconst payload = JSON.stringify({\r\n\t\t\t\tevents: events,\r\n\t\t\t});\r\n\r\n\t\t\tconst response = await fetch(this._endpoint, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'X-API-Key': this._apiKey,\r\n\t\t\t\t},\r\n\t\t\t\tbody: payload,\r\n\t\t\t\tkeepalive: true,\r\n\t\t\t});\r\n\r\n\t\t\t// Intentar parsear el mensaje de respuesta del backend\r\n\t\t\tlet message: string | undefined;\r\n\t\t\ttry {\r\n\t\t\t\tconst responseData: TrackingResponse = await response.json();\r\n\t\t\t\tmessage = responseData.message;\r\n\t\t\t} catch {\r\n\t\t\t\t// Si no se puede parsear, usar el status text\r\n\t\t\t\tmessage = response.statusText;\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: response.ok,\r\n\t\t\t\tstatusCode: response.status,\r\n\t\t\t\tmessage,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Network error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Generador de UUID v4\r\n */\r\n\r\n/** Genera un UUID v4 aleatorio */\r\nexport function generateUUID(): string {\r\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\r\n\t\tconst random = Math.random() * 16 | 0;\r\n\t\tconst value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n\t\treturn value.toString(16);\r\n\t});\r\n}\r\n","/**\r\n * Utilidad para truncar texto\r\n */\r\n\r\n/**\r\n * Trunca un texto a la longitud máxima especificada\r\n * @param text - Texto a truncar\r\n * @param maxLength - Longitud máxima\r\n * @returns Texto truncado con '...' al final si excede la longitud\r\n */\r\nexport function truncate(text: string, maxLength: number): string {\r\n\tif (maxLength < 3) {\r\n\t\treturn '...';\r\n\t}\r\n\r\n\tif (text.length <= maxLength) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, maxLength - 3) + '...';\r\n}\r\n","/**\r\n * Utilidades para recolectar metadatos del navegador\r\n */\r\n\r\nimport { generateUUID } from './uuid';\r\n\r\n/** Obtiene o crea un sessionId en sessionStorage */\r\nexport function getOrCreateSessionId(): string {\r\n\ttry {\r\n\t\tconst storageKey = 'event_tracker_session_id';\r\n\t\tconst existingSessionId = sessionStorage.getItem(storageKey);\r\n\r\n\t\tif (existingSessionId) {\r\n\t\t\treturn existingSessionId;\r\n\t\t}\r\n\r\n\t\tconst newSessionId = generateUUID();\r\n\t\tsessionStorage.setItem(storageKey, newSessionId);\r\n\t\treturn newSessionId;\r\n\t} catch {\r\n\t\treturn generateUUID();\r\n\t}\r\n}\r\n\r\n/** Obtiene el tamaño de pantalla */\r\nexport function getScreenSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Obtiene el tamaño del viewport */\r\nexport function getViewportSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Detecta el tipo de dispositivo */\r\nexport function getDeviceType(): 'mobile' | 'desktop' | 'tablet' | 'unknown' {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\r\n\t\treturn 'tablet';\r\n\t}\r\n\r\n\tif (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\r\n\t\treturn 'mobile';\r\n\t}\r\n\r\n\treturn 'desktop';\r\n}\r\n\r\n/** Obtiene el navegador */\r\nexport function getBrowser(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Firefox')) {\r\n\t\treturn 'Firefox';\r\n\t}\r\n\r\n\tif (ua.includes('Edg/')) {\r\n\t\treturn 'Edge';\r\n\t}\r\n\r\n\tif (ua.includes('Chrome')) {\r\n\t\treturn 'Chrome';\r\n\t}\r\n\r\n\tif (ua.includes('Safari')) {\r\n\t\treturn 'Safari';\r\n\t}\r\n\r\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\r\n\t\treturn 'Internet Explorer';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el sistema operativo */\r\nexport function getOS(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Windows')) {\r\n\t\treturn 'Windows';\r\n\t}\r\n\r\n\tif (ua.includes('Mac OS')) {\r\n\t\treturn 'macOS';\r\n\t}\r\n\r\n\tif (ua.includes('Linux')) {\r\n\t\treturn 'Linux';\r\n\t}\r\n\r\n\tif (ua.includes('Android')) {\r\n\t\treturn 'Android';\r\n\t}\r\n\r\n\tif (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) {\r\n\t\treturn 'iOS';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el idioma del navegador */\r\nexport function getLanguage(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\treturn navigator.language || 'unknown';\r\n}\r\n\r\n/** Obtiene la URL actual */\r\nexport function getCurrentUrl(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.href;\r\n}\r\n\r\n/** Obtiene el referrer */\r\nexport function getReferrer(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn document.referrer;\r\n}\r\n\r\n/** Obtiene el timestamp actual en formato ISO */\r\nexport function getTimestamp(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\n/** Obtiene el path actual */\r\nexport function getCurrentPath(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.pathname;\r\n}\r\n","/**\r\n * ClickObserver: Captura clics globales en elementos interactivos\r\n */\r\n\r\nimport type { TrackerEvent } from '../types';\r\nimport { Tracker } from '../core/Tracker';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { truncate } from '../utils/truncate';\r\nimport {\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\n\r\n/** Tipos de elementos que deben ser rastreados */\r\nconst TRACKABLE_TAGS = ['BUTTON', 'A', 'INPUT', 'I', 'SPAN', 'DIV'];\r\n\r\n/** Tipos de input que deben ser rastreados */\r\nconst TRACKABLE_INPUT_TYPES = ['submit'];\r\n\r\n/** Tipos de input que deben ser excluidos (sensibles) */\r\nconst SENSITIVE_INPUT_TYPES = ['password', 'email', 'tel', 'number', 'search', 'url'];\r\n\r\n/** Selectores para elementos rastreables */\r\nconst TRACKABLE_SELECTORS = [\r\n\t'button',\r\n\t'a',\r\n\t'input[type=\"submit\"]',\r\n\t'[data-track]',\r\n\t'[style*=\"cursor: pointer\"]',\r\n\t'i[class*=\"mdi-\"]',\r\n];\r\n\r\n/** Verifica si un elemento es sensible (no capturar valores) */\r\nfunction _isSensitiveElement(element: Element): boolean {\r\n\tif (element.tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn SENSITIVE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n/** Verifica si un elemento es rastreable */\r\nfunction _isTrackableElement(element: Element): boolean {\r\n\tconst tagName = element.tagName.toUpperCase();\r\n\r\n\tif (!TRACKABLE_TAGS.includes(tagName)) {\r\n\t\treturn element.hasAttribute('data-track') ||\r\n\t\t\t_hasCursorPointer(element) ||\r\n\t\t\t_isMdiIcon(element);\r\n\t}\r\n\r\n\tif (tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn TRACKABLE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/** Verifica si el elemento tiene cursor: pointer */\r\nfunction _hasCursorPointer(element: Element): boolean {\r\n\ttry {\r\n\t\tconst style = window.getComputedStyle(element);\r\n\t\treturn style.cursor === 'pointer';\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/** Verifica si es un icono MDI */\r\nfunction _isMdiIcon(element: Element): boolean {\r\n\treturn element.tagName === 'I' && element.classList.contains('mdi');\r\n}\r\n\r\n/** Genera un selector CSS simple para el elemento */\r\nfunction _generateSelector(element: Element): string {\r\n\tconst parts: string[] = [];\r\n\tlet current: Element | null = element;\r\n\r\n\twhile (current && current !== document.documentElement) {\r\n\t\tlet selector = current.tagName.toLowerCase();\r\n\r\n\t\tif (current.id) {\r\n\t\t\tselector += `#${current.id}`;\r\n\t\t\tparts.unshift(selector);\r\n\t\t\tbreak;\r\n\t\t} else if (current.className && typeof current.className === 'string') {\r\n\t\t\tconst classes = current.className.trim().split(/\\s+/).filter(Boolean);\r\n\t\t\tif (classes.length > 0) {\r\n\t\t\t\tselector += `.${classes[0]}`;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tparts.unshift(selector);\r\n\t\tcurrent = current.parentElement;\r\n\t}\r\n\r\n\treturn parts.slice(0, 4).join(' > ');\r\n}\r\n\r\n/** Obtiene todas las clases de un elemento como string separado por espacios */\r\nfunction _getAllClasses(element: Element): string | undefined {\r\n\tif (!element.className || typeof element.className !== 'string') {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst classes = element.className.trim().split(/\\s+/).filter(Boolean);\r\n\treturn classes.length > 0 ? classes.join(' ') : undefined;\r\n}\r\n\r\nexport class ClickObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _boundHandleClick: (event: MouseEvent) => void;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._boundHandleClick = this._handleClick.bind(this);\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.addEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = true;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.removeEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de eventos de clic */\r\n\tprivate _handleClick(event: MouseEvent): void {\r\n\t\ttry {\r\n\t\t\tconst target = event.target as Element;\r\n\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 1: Si el elemento clickeado es rastreable, usarlo directamente\r\n\t\t\tif (_isTrackableElement(target) && !_isSensitiveElement(target)) {\r\n\t\t\t\tthis._createClickEvent(target);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 2: Buscar el ancestro rastreable más cercano\r\n\t\t\tconst trackableElement = target.closest(TRACKABLE_SELECTORS.join(','));\r\n\r\n\t\t\tif (!trackableElement) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isSensitiveElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isTrackableElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._createClickEvent(trackableElement);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de clic */\r\n\tprivate _createClickEvent(element: Element): void {\r\n\t\tconst event: TrackerEvent = {\r\n\t\t\teventType: 'user_click',\r\n\t\t\teventId: generateUUID(),\r\n\t\t\tcontext: {\r\n\t\t\t\tsessionId: '',\r\n\t\t\t\tuserId: null,\r\n\t\t\t\tuserName: null,\r\n\t\t\t\turl: window.location.href,\r\n\t\t\t\treferrer: getReferrer(),\r\n\t\t\t\tscreenSize: getScreenSize(),\r\n\t\t\t\tviewportSize: getViewportSize(),\r\n\t\t\t\tdeviceType: getDeviceType(),\r\n\t\t\t\tbrowser: getBrowser(),\r\n\t\t\t\tos: getOS(),\r\n\t\t\t\tlanguage: getLanguage(),\r\n\t\t\t\ttimestamp: getTimestamp(),\r\n\t\t\t},\r\n\t\t\tdata: {\r\n\t\t\t\ttagName: element.tagName,\r\n\t\t\t\tid: element.id || undefined,\r\n\t\t\t\tclassName: _getAllClasses(element),\r\n\t\t\t\ttitle: element.getAttribute('title') || undefined,\r\n\t\t\t\ttext: element.textContent ? truncate(element.textContent, 30) : undefined,\r\n\t\t\t\tselector: _generateSelector(element),\r\n\t\t\t},\r\n\t\t};\r\n\r\n\t\tthis._tracker.trackEvent(event);\r\n\t}\r\n}\r\n","/**\r\n * HistoryObserver: Monitorea cambios de navegación usando múltiples estrategias\r\n * - popstate: para botones atrás/adelante del navegador\r\n * - history intercept: para pushState/replaceState en SPA\r\n * - polling: fallback para detectar cambios\r\n * - sessionStorage: para manejar F5\r\n */\r\n\r\nimport type { Tracker } from '../core/Tracker';\r\n\r\nconst POLL_INTERVAL = 1000; // Verificar URL cada 1 segundo (fallback)\r\nconst HEARTBEAT_INTERVAL = 10000; // Enviar heartbeat cada 10 segundos\r\nconst MIN_DURATION = 0.5; // Ignorar page_leave con menos de 0.5 segundos\r\nconst STORAGE_KEY_URL = 'event_tracker_last_url';\r\nconst STORAGE_KEY_TIME = 'event_tracker_last_time';\r\n\r\nexport class HistoryObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _lastUrl: string = '';\r\n\tprivate _pageStartTime: number;\r\n\tprivate _originalPushState: typeof history.pushState = () => null;\r\n\tprivate _originalReplaceState: typeof history.replaceState = () => null;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._pageStartTime = performance.now();\r\n\t\tthis._lastUrl = typeof window !== 'undefined' ? window.location.href : '';\r\n\r\n\t\t// Guardar referencias originales de history\r\n\t\tif (typeof window !== 'undefined' && typeof history !== 'undefined') {\r\n\t\t\tthis._originalPushState = history.pushState.bind(history);\r\n\t\t\tthis._originalReplaceState = history.replaceState.bind(history);\r\n\t\t}\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// Configurar interceptación de history\r\n\t\t\tthis._interceptHistory();\r\n\r\n\t\t\t// Agregar listener de popstate (botones atrás/adelante)\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('popstate', this._handlePopState);\r\n\t\t\t}\r\n\r\n\t\t\t// Iniciar intervalo de polling (fallback)\r\n\t\t\tthis._intervalId = setInterval(() => this._checkUrlChange(), POLL_INTERVAL);\r\n\r\n\t\t\t// Iniciar intervalo de heartbeat\r\n\t\t\tthis._heartbeatIntervalId = setInterval(() => this._sendHeartbeat(), HEARTBEAT_INTERVAL);\r\n\r\n\t\t\t// Eventos para cierre de página\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.addEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = true;\r\n\r\n\t\t\t// Verificar si hay una página previa guardada (para F5)\r\n\t\t\t// Solo después de que el tracker esté inicializado\r\n\t\t\tsetTimeout(() => this._checkPreviousPage(), 0);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tif (this._intervalId) {\r\n\t\t\t\tclearInterval(this._intervalId);\r\n\t\t\t\tthis._intervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (this._heartbeatIntervalId) {\r\n\t\t\t\tclearInterval(this._heartbeatIntervalId);\r\n\t\t\t\tthis._heartbeatIntervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.removeEventListener('popstate', this._handlePopState);\r\n\t\t\t\twindow.removeEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.removeEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\t// Restaurar history original\r\n\t\t\tif (typeof history !== 'undefined') {\r\n\t\t\t\thistory.pushState = this._originalPushState;\r\n\t\t\t\thistory.replaceState = this._originalReplaceState;\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Intercepta history.pushState y history.replaceState */\r\n\tprivate _interceptHistory(): void {\r\n\t\tif (typeof window === 'undefined' || typeof history === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst self = this;\r\n\t\tconst originalPushState = history.pushState.bind(history) as (...args: unknown[]) => unknown;\r\n\t\tconst originalReplaceState = history.replaceState.bind(history) as (...args: unknown[]) => unknown;\r\n\r\n\t\t// Interceptar pushState\r\n\t\thistory.pushState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalPushState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.pushState;\r\n\r\n\t\t// Interceptar replaceState\r\n\t\thistory.replaceState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalReplaceState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.replaceState;\r\n\t}\r\n\r\n\t/** Manejador de popstate (botones atrás/adelante) */\r\n\tprivate _handlePopState = (): void => {\r\n\t\ttry {\r\n\t\t\tthis._handleNavigationChange();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejo centralizado de cambio de navegación */\r\n\tprivate _handleNavigationChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.href;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Calcular duración wall-clock de la página anterior\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t\t// Solo enviar page_leave si la duración es significativa\r\n\t\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\t\t// Enviar page_leave INMEDIATAMENTE para la página anterior\r\n\t\t\t\t\tthis._sendPageLeaveImmediate(this._lastUrl, duration);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Track page view de la nueva página\r\n\t\t\t\tthis._tracker.trackPageView();\r\n\r\n\t\t\t\t// Resetear contador para la nueva página\r\n\t\t\t\tthis._pageStartTime = performance.now();\r\n\r\n\t\t\t\t// Actualizar la última URL\r\n\t\t\t\tthis._lastUrl = currentUrl;\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía page_leave inmediatamente sin esperar el buffer */\r\n\tprivate _sendPageLeaveImmediate(path: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\t// Crear el evento directamente\r\n\t\t\tthis._tracker.trackPageLeave(duration, path);\r\n\r\n\t\t\t// También guardar en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(path, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de beforeunload (cuando el usuario sale de la página) */\r\n\tprivate _handleBeforeUnload = (): void => {\r\n\t\ttry {\r\n\t\t\t// Calcular duración wall-clock\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Solo enviar si la duración es significativa\r\n\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\tthis._tracker.trackPageLeave(duration, this._lastUrl);\r\n\t\t\t}\r\n\r\n\t\t\t// Guardar URL actual en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejador de visibilitychange (para detectar cuando la app pasa a segundo plano) */\r\n\tprivate _handleVisibilityChange = (): void => {\r\n\t\ttry {\r\n\t\t\tif (typeof document === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cuando la página se oculta, guardar el tiempo actual en sessionStorage\r\n\t\t\tif (document.visibilityState === 'hidden') {\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\t\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Verifica si la URL cambió (fallback de polling) */\r\n\tprivate _checkUrlChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.href;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Usar el manejo centralizado\r\n\t\t\t\tthis._handleNavigationChange();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Calcula la duración wall-clock (tiempo total desde que entró a la página) */\r\n\tprivate _calculateWallClockDuration(): number {\r\n\t\treturn (performance.now() - this._pageStartTime) / 1000;\r\n\t}\r\n\r\n\t/** Envía un heartbeat con la duración actual */\r\n\tprivate _sendHeartbeat(): void {\r\n\t\ttry {\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Usar trackPageLeave para mantener compatibilidad\r\n\t\t\t// El backend puede filtrar por eventType = 'page_heartbeat'\r\n\t\t\tthis._tracker.trackEvent({\r\n\t\t\t\teventType: 'page_heartbeat',\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath: this._lastUrl,\r\n\t\t\t\t\tduration: duration,\r\n\t\t\t\t\ttimestamp: new Date().toISOString(),\r\n\t\t\t\t},\r\n\t\t\t} as import('../types/index').TrackerEvent);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Guarda la URL y tiempo en sessionStorage (para F5) */\r\n\tprivate _saveToStorage(url: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_URL, url);\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_TIME, String(duration));\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Lee la URL y tiempo previos de sessionStorage (para F5) */\r\n\tprivate _getFromStorage(): { url: string; duration: number; } | null {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tconst url = sessionStorage.getItem(STORAGE_KEY_URL);\r\n\t\t\t\tconst duration = sessionStorage.getItem(STORAGE_KEY_TIME);\r\n\t\t\t\tif (url && duration) {\r\n\t\t\t\t\treturn { url, duration: parseFloat(duration) };\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/** Limpia sessionStorage */\r\n\tprivate _clearStorage(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_URL);\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_TIME);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si hay una página previa (para F5) */\r\n\tprivate _checkPreviousPage(): void {\r\n\t\tconst previous = this._getFromStorage();\r\n\t\tif (previous && previous.duration >= MIN_DURATION) {\r\n\t\t\t// La página anterior tenía una duración significativa, enviar page_leave\r\n\t\t\tthis._tracker.trackPageLeave(previous.duration, previous.url);\r\n\t\t}\r\n\t\t// Limpiar storage después de procesar\r\n\t\tthis._clearStorage();\r\n\t}\r\n}\r\n","{\r\n\t\"name\": \"@agroideas/event-tracker-sdk\",\r\n\t\"version\": \"1.0.8\",\r\n\t\"description\": \"A lightweight, framework-agnostic SDK for tracking frontend user behavior\",\r\n\t\"license\": \"MIT\",\r\n\t\"author\": \"Agroideas\",\r\n\t\"homepage\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"main\": \"./dist/index.cjs\",\r\n\t\"module\": \"./dist/index.mjs\",\r\n\t\"types\": \"./dist/index.d.ts\",\r\n\t\"exports\": {\r\n\t\t\".\": {\r\n\t\t\t\"types\": \"./dist/index.d.ts\",\r\n\t\t\t\"import\": \"./dist/index.mjs\",\r\n\t\t\t\"require\": \"./dist/index.cjs\"\r\n\t\t},\r\n\t\t\"./iife\": \"./dist/index.iife.js\"\r\n\t},\r\n\t\"files\": [\r\n\t\t\"dist\"\r\n\t],\r\n\t\"scripts\": {\r\n\t\t\"build\": \"tsup\",\r\n\t\t\"dev\": \"tsup --watch\",\r\n\t\t\"lint\": \"eslint src --ext .ts\",\r\n\t\t\"lint:fix\": \"eslint src --ext .ts --fix\",\r\n\t\t\"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\r\n\t\t\"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\r\n\t\t\"typecheck\": \"tsc --noEmit\",\r\n\t\t\"prepare\": \"husky install\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"@types/node\": \"^20.10.0\",\r\n\t\t\"eslint\": \"^8.55.0\",\r\n\t\t\"husky\": \"^8.0.3\",\r\n\t\t\"prettier\": \"^3.1.0\",\r\n\t\t\"tsup\": \"^8.0.1\",\r\n\t\t\"typescript\": \"^5.3.2\"\r\n\t},\r\n\t\"keywords\": [\r\n\t\t\"tracking\",\r\n\t\t\"analytics\",\r\n\t\t\"sdk\",\r\n\t\t\"events\",\r\n\t\t\"behavior\",\r\n\t\t\"frontend\"\r\n\t],\r\n\t\"engines\": {\r\n\t\t\"node\": \">=18.0.0\"\r\n\t}\r\n}","/**\n * Version constants for the Event Tracker SDK\n */\n\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION = pkg.version;\n","/**\r\n * Tracker: Singleton principal que coordina observers y EventBuffer\r\n */\r\n\r\nimport type { TrackerConfig, TrackerEvent, UserIdentity, EventContext } from '../types';\r\nimport { EventBuffer } from './EventBuffer';\r\nimport { ClickObserver } from '../observers/ClickObserver';\r\nimport { HistoryObserver } from '../observers/HistoryObserver';\r\nimport {\r\n\tgetCurrentPath,\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetOrCreateSessionId,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { VERSION } from '../version';\r\n\r\nexport class Tracker {\r\n\tprivate static _instance: Tracker | null = null;\r\n\tprivate _eventBuffer: EventBuffer | null = null;\r\n\tprivate _clickObserver: ClickObserver | null = null;\r\n\tprivate _historyObserver: HistoryObserver | null = null;\r\n\tprivate _userIdentity: UserIdentity;\r\n\tprivate _sessionId: string;\r\n\tprivate _isInitialized: boolean = false;\r\n\tprivate _isPaused: boolean = false;\r\n\tprivate _config: TrackerConfig | null = null;\r\n\r\n\tprivate constructor() {\r\n\t\tthis._userIdentity = {\r\n\t\t\tuserId: null,\r\n\t\t\tuserName: null,\r\n\t\t};\r\n\t\tthis._sessionId = getOrCreateSessionId();\r\n\t}\r\n\r\n\t/** Obtiene la instancia única del Tracker */\r\n\tstatic getInstance(): Tracker {\r\n\t\tif (Tracker._instance === null) {\r\n\t\t\tTracker._instance = new Tracker();\r\n\t\t}\r\n\t\treturn Tracker._instance;\r\n\t}\r\n\r\n\t/** Construye el contexto completo del evento */\r\n\tprivate _buildContext(): EventContext {\r\n\t\treturn {\r\n\t\t\tsessionId: this._sessionId,\r\n\t\t\tuserId: this._userIdentity.userId,\r\n\t\t\tuserName: this._userIdentity.userName,\r\n\t\t\turl: window.location.href,\r\n\t\t\treferrer: getReferrer(),\r\n\t\t\tscreenSize: getScreenSize(),\r\n\t\t\tviewportSize: getViewportSize(),\r\n\t\t\tdeviceType: getDeviceType(),\r\n\t\t\tbrowser: getBrowser(),\r\n\t\t\tos: getOS(),\r\n\t\t\tlanguage: getLanguage(),\r\n\t\t\ttimestamp: getTimestamp(),\r\n\t\t};\r\n\t}\r\n\r\n\t/** Verifica si una ruta debe ser excluida del tracking */\r\n\tprivate _shouldExclude(path: string): boolean {\r\n\t\tif (!this._config) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst { excludePatterns } = this._config;\r\n\r\n\t\t// Normalizar el path (quitar trailing slash para comparación consistente)\r\n\t\tconst normalizedPath = path.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;\r\n\r\n\t\t// Si hay patrones de exclusión, verificarlos\r\n\t\tif (excludePatterns && excludePatterns.length > 0) {\r\n\t\t\treturn excludePatterns.some((pattern) => {\r\n\t\t\t\tif (typeof pattern === 'string') {\r\n\t\t\t\t\t// Normalizar el patrón también\r\n\t\t\t\t\tconst normalizedPattern = pattern.endsWith('/') && pattern.length > 1 ? pattern.slice(0, -1) : pattern;\r\n\t\t\t\t\t// Coincidencia exacta o prefijo\r\n\t\t\t\t\treturn normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern);\r\n\t\t\t\t}\r\n\t\t\t\t// Es RegExp\r\n\t\t\t\treturn pattern.test(normalizedPath) || pattern.test(path);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/** Inicializa el tracker con la configuración */\r\n\tinit(config: TrackerConfig): void {\r\n\t\ttry {\r\n\t\t\t// Guardar configuración\r\n\t\t\tthis._config = config;\r\n\r\n\t\t\t// Actualizamos el usuario cada vez que se llama init (permite actualizar userId/userName)\r\n\t\t\tif (config.userId) {\r\n\t\t\t\tthis._userIdentity = {\r\n\t\t\t\t\tuserId: config.userId,\r\n\t\t\t\t\tuserName: config.userName ?? null,\r\n\t\t\t\t};\r\n\t\t\t\tif (this._isInitialized && config.debug) {\r\n\t\t\t\t\tconsole.log('[EventTracker] Usuario actualizado:', this._userIdentity);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Solo inicializar EventBuffer y observers la primera vez\r\n\t\t\tif (this._isInitialized) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._eventBuffer = new EventBuffer(config);\r\n\t\t\tthis._isInitialized = true;\r\n\r\n\t\t\t// Imprimir versión del tracker\r\n\t\t\tconsole.log(`🚀 [EventTracker] Inicializado v${VERSION}`);\r\n\r\n\t\t\t// Iniciar observers\r\n\t\t\tthis._clickObserver = new ClickObserver(this);\r\n\t\t\tthis._historyObserver = new HistoryObserver(this);\r\n\t\t\tthis._clickObserver.start();\r\n\t\t\tthis._historyObserver.start();\r\n\r\n\t\t\t// Enviar page_view inicial\r\n\t\t\tthis.trackPageView();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Identifica al usuario */\r\n\tidentify(userId: string, userName?: string): void {\r\n\t\ttry {\r\n\t\t\tthis._userIdentity = {\r\n\t\t\t\tuserId,\r\n\t\t\t\tuserName: userName ?? null,\r\n\t\t\t};\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Registra un evento */\r\n\ttrackEvent(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!this._eventBuffer) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Verificar si la URL actual debe ser excluida\r\n\t\t\tconst currentPath = getCurrentPath();\r\n\t\t\tif (this._shouldExclude(currentPath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst eventWithContext: TrackerEvent = {\r\n\t\t\t\t...event,\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t};\r\n\r\n\t\t\tthis._eventBuffer.add(eventWithContext);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page view */\r\n\ttrackPageView(): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst path = getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(path)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_view',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page leave */\r\n\ttrackPageLeave(duration: number, path?: string): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst pagePath = path ?? getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(pagePath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_leave',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tduration,\r\n\t\t\t\t\tpath: pagePath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Fuerza el envío de todos los eventos pendientes */\r\n\tasync flush(): Promise<void> {\r\n\t\ttry {\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tawait this._eventBuffer.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si el tracker está inicializado */\r\n\tisInitialized(): boolean {\r\n\t\treturn this._isInitialized;\r\n\t}\r\n\r\n\t/** Pausa el tracker (no registrará eventos) */\r\n\tpause(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = true;\r\n\t\t\tconsole.log('⏸️ [EventTracker] Tracker pausado - No se registrarán eventos');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Reanuda el tracker (volverá a registrar eventos) */\r\n\tresume(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = false;\r\n\t\t\tconsole.log('▶️ [EventTracker] Tracker reanudado - Eventos serán registrados');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Alterna el modo debug */\r\n\ttoggleDebug(): void {\r\n\t\ttry {\r\n\t\t\tif (!this._config) {\r\n\t\t\t\tconsole.log('⚠️ [EventTracker] Error: Tracker no inicializado');\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cambiar el valor de debug\r\n\t\t\tthis._config.debug = this._config.debug ? '' : 'true';\r\n\r\n\t\t\tif (this._config.debug) {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug ACTIVADO ✅');\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug DESACTIVADO ❌');\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Destruye el tracker */\r\n\tdestroy(): void {\r\n\t\ttry {\r\n\t\t\tif (this._clickObserver) {\r\n\t\t\t\tthis._clickObserver.stop();\r\n\t\t\t\tthis._clickObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._historyObserver) {\r\n\t\t\t\tthis._historyObserver.stop();\r\n\t\t\t\tthis._historyObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tthis._eventBuffer.destroy();\r\n\t\t\t\tthis._eventBuffer = null;\r\n\t\t\t}\r\n\t\t\tthis._isInitialized = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Event Tracker SDK\r\n *\r\n * SDK ligero y agnóstico al framework para tracking de comportamiento frontend.\r\n *\r\n * @example\r\n * import { tracker } from '@agroideas/event-tracker-sdk';\r\n *\r\n * tracker.init({\r\n * apiKey: 'tu-api-key',\r\n * endpoint: 'https://tu-api.com/events'\r\n * });\r\n *\r\n * // Identificar usuario\r\n * tracker.identify('user-123', 'Juan Pérez');\r\n */\r\n\r\nimport { Tracker } from './core/Tracker';\r\nimport type { TrackerConfig } from './types';\r\n\r\n/** Instancia única exportada del Tracker */\r\nexport const tracker = Tracker.getInstance();\r\n\r\n/**\r\n * Inicializa el tracker con configuración requerida (apiKey y endpoint).\r\n */\r\nexport function init(config: TrackerConfig): void {\r\n\ttracker.init(config);\r\n}\r\n\r\n/** Identifica al usuario */\r\nexport function identify(userId: string, userName?: string): void {\r\n\ttracker.identify(userId, userName);\r\n}\r\n\r\n/** Fuerza el envío de eventos pendientes */\r\nexport function flush(): Promise<void> {\r\n\treturn tracker.flush();\r\n}\r\n\r\n/** Destruye el tracker */\r\nexport function destroy(): void {\r\n\ttracker.destroy();\r\n}\r\n\r\nexport type { TrackerConfig };\r\n"],"mappings":";;;;AAMA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAEjB,MAAM,cAAN,MAAkB;AAAA,IAQxB,YAAY,QAAuB;AALnC,WAAQ,cAAqD;AAM5D,WAAK,UAAU,CAAC;AAGhB,WAAK,YAAY,OAAO;AACxB,WAAK,UAAU,OAAO;AAGtB,WAAK,WAAW,OAAO,SAAS;AAChC,cAAQ,IAAI,MAAM;AAElB,WAAK,UAAU;AAAA,QACd,eAAe,OAAO,iBAAiB;AAAA,QACvC,eAAe,OAAO,iBAAiB;AAAA,QACvC,iBAAiB,OAAO,mBAAmB,CAAC;AAAA,MAC7C;AAEA,UAAI,KAAK,UAAU;AAClB,gBAAQ,IAAI,oCAAoC;AAChD,gBAAQ,IAAI,4BAA4B,KAAK,SAAS;AACtD,gBAAQ,IAAI,2BAA2B,KAAK,OAAO;AAAA,MACpD;AAGA,UAAI,CAAC,KAAK,UAAU;AACnB,aAAK,iBAAiB;AAAA,MACvB;AAEA,WAAK,yBAAyB;AAAA,IAC/B;AAAA;AAAA,IAGA,IAAI,OAA2B;AAC9B,UAAI;AACH,aAAK,QAAQ,KAAK,KAAK;AAEvB,YAAI,KAAK,UAAU;AAClB,kBAAQ,IAAI,mCAAmC,MAAM,SAAS;AAC9D,kBAAQ,IAAI,+BAA+B,KAAK,QAAQ,MAAM;AAC9D,kBAAQ,IAAI,iCAAiC,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,QAC5E;AAGA,YAAI,CAAC,KAAK,YAAY,KAAK,QAAQ,UAAU,KAAK,QAAQ,eAAe;AACxE,eAAK,MAAM;AAAA,QACZ;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,MAAM,QAA6B;AAClC,UAAI;AACH,YAAI,KAAK,QAAQ,WAAW,GAAG;AAC9B,iBAAO,EAAE,SAAS,KAAK;AAAA,QACxB;AAEA,cAAM,eAAe,CAAC,GAAG,KAAK,OAAO;AACrC,aAAK,UAAU,CAAC;AAGhB,YAAI,KAAK,UAAU;AAClB,kBAAQ,IAAI,kCAAkC,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC;AACnF,iBAAO,EAAE,SAAS,KAAK;AAAA,QACxB;AAEA,cAAM,SAAS,MAAM,KAAK,iBAAiB,YAAY;AACvD,eAAO;AAAA,MACR,SAAS,OAAO;AACf,eAAO;AAAA,UACN,SAAS;AAAA,UACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACjD;AAAA,MACD;AAAA,IACD;AAAA;AAAA,IAGA,UAAkB;AACjB,aAAO,KAAK,QAAQ;AAAA,IACrB;AAAA;AAAA,IAGA,QAAc;AACb,WAAK,UAAU,CAAC;AAAA,IACjB;AAAA;AAAA,IAGA,UAAgB;AACf,UAAI,KAAK,aAAa;AACrB,sBAAc,KAAK,WAAW;AAC9B,aAAK,cAAc;AAAA,MACpB;AAEA,WAAK,UAAU,CAAC;AAAA,IACjB;AAAA;AAAA,IAGQ,mBAAyB;AAChC,WAAK,cAAc,YAAY,MAAM;AACpC,YAAI,KAAK,QAAQ,SAAS,GAAG;AAC5B,eAAK,MAAM;AAAA,QACZ;AAAA,MACD,GAAG,KAAK,QAAQ,aAAa;AAAA,IAC9B;AAAA;AAAA,IAGQ,2BAAiC;AACxC,UAAI,OAAO,aAAa,aAAa;AACpC;AAAA,MACD;AAEA,YAAM,yBAAyB,MAAY;AAE1C,YAAI,KAAK,UAAU;AAClB;AAAA,QACD;AAEA,YAAI,SAAS,oBAAoB,YAAY,KAAK,QAAQ,SAAS,GAAG;AAErE,qBAAW,MAAM;AAChB,iBAAK,MAAM;AAAA,UACZ,GAAG,CAAC;AAAA,QACL;AAAA,MACD;AAEA,eAAS,iBAAiB,oBAAoB,sBAAsB;AAAA,IACrE;AAAA;AAAA,IAGA,MAAc,iBAAiB,QAA6C;AAC3E,UAAI;AACH,cAAM,UAAU,KAAK,UAAU;AAAA,UAC9B;AAAA,QACD,CAAC;AAED,cAAM,WAAW,MAAM,MAAM,KAAK,WAAW;AAAA,UAC5C,QAAQ;AAAA,UACR,SAAS;AAAA,YACR,gBAAgB;AAAA,YAChB,aAAa,KAAK;AAAA,UACnB;AAAA,UACA,MAAM;AAAA,UACN,WAAW;AAAA,QACZ,CAAC;AAGD,YAAI;AACJ,YAAI;AACH,gBAAM,eAAiC,MAAM,SAAS,KAAK;AAC3D,oBAAU,aAAa;AAAA,QACxB,QAAQ;AAEP,oBAAU,SAAS;AAAA,QACpB;AAEA,eAAO;AAAA,UACN,SAAS,SAAS;AAAA,UAClB,YAAY,SAAS;AAAA,UACrB;AAAA,QACD;AAAA,MACD,SAAS,OAAO;AACf,eAAO;AAAA,UACN,SAAS;AAAA,UACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACjD;AAAA,MACD;AAAA,IACD;AAAA,EACD;;;ACpLO,WAAS,eAAuB;AACtC,WAAO,uCAAuC,QAAQ,SAAS,CAAC,SAAS;AACxE,YAAM,SAAS,KAAK,OAAO,IAAI,KAAK;AACpC,YAAM,QAAQ,SAAS,MAAM,SAAU,SAAS,IAAO;AACvD,aAAO,MAAM,SAAS,EAAE;AAAA,IACzB,CAAC;AAAA,EACF;;;ACDO,WAAS,SAAS,MAAc,WAA2B;AACjE,QAAI,YAAY,GAAG;AAClB,aAAO;AAAA,IACR;AAEA,QAAI,KAAK,UAAU,WAAW;AAC7B,aAAO;AAAA,IACR;AAEA,WAAO,KAAK,UAAU,GAAG,YAAY,CAAC,IAAI;AAAA,EAC3C;;;ACbO,WAAS,uBAA+B;AAC9C,QAAI;AACH,YAAM,aAAa;AACnB,YAAM,oBAAoB,eAAe,QAAQ,UAAU;AAE3D,UAAI,mBAAmB;AACtB,eAAO;AAAA,MACR;AAEA,YAAM,eAAe,aAAa;AAClC,qBAAe,QAAQ,YAAY,YAAY;AAC/C,aAAO;AAAA,IACR,QAAQ;AACP,aAAO,aAAa;AAAA,IACrB;AAAA,EACD;AAGO,WAAS,gBAAwB;AACvC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,GAAG,OAAO,UAAU,IAAI,OAAO,WAAW;AAAA,EAClD;AAGO,WAAS,kBAA0B;AACzC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,GAAG,OAAO,UAAU,IAAI,OAAO,WAAW;AAAA,EAClD;AAGO,WAAS,gBAA6D;AAC5E,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,UAAM,KAAK,UAAU;AAErB,QAAI,mDAAmD,KAAK,EAAE,GAAG;AAChE,aAAO;AAAA,IACR;AAEA,QAAI,sGAAsG,KAAK,EAAE,GAAG;AACnH,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAGO,WAAS,aAAqB;AACpC,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,UAAM,KAAK,UAAU;AAErB,QAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,MAAM,GAAG;AACxB,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,QAAQ,GAAG;AAC1B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,QAAQ,GAAG;AAC1B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,UAAU,GAAG;AACnD,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAGO,WAAS,QAAgB;AAC/B,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,UAAM,KAAK,UAAU;AAErB,QAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,QAAQ,GAAG;AAC1B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,OAAO,GAAG;AACzB,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,SAAS,GAAG;AAC3B,aAAO;AAAA,IACR;AAEA,QAAI,GAAG,SAAS,KAAK,KAAK,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,MAAM,GAAG;AACvE,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAGO,WAAS,cAAsB;AACrC,QAAI,OAAO,cAAc,aAAa;AACrC,aAAO;AAAA,IACR;AAEA,WAAO,UAAU,YAAY;AAAA,EAC9B;AAYO,WAAS,cAAsB;AACrC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,SAAS;AAAA,EACjB;AAGO,WAAS,eAAuB;AACtC,YAAO,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC/B;AAGO,WAAS,iBAAyB;AACxC,QAAI,OAAO,WAAW,aAAa;AAClC,aAAO;AAAA,IACR;AAEA,WAAO,OAAO,SAAS;AAAA,EACxB;;;AC9IA,MAAM,iBAAiB,CAAC,UAAU,KAAK,SAAS,KAAK,QAAQ,KAAK;AAGlE,MAAM,wBAAwB,CAAC,QAAQ;AAGvC,MAAM,wBAAwB,CAAC,YAAY,SAAS,OAAO,UAAU,UAAU,KAAK;AAGpF,MAAM,sBAAsB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAGA,WAAS,oBAAoB,SAA2B;AACvD,QAAI,QAAQ,YAAY,SAAS;AAChC,YAAM,YAAa,QAA6B,MAAM,YAAY,KAAK;AACvE,aAAO,sBAAsB,SAAS,SAAS;AAAA,IAChD;AACA,WAAO;AAAA,EACR;AAGA,WAAS,oBAAoB,SAA2B;AACvD,UAAM,UAAU,QAAQ,QAAQ,YAAY;AAE5C,QAAI,CAAC,eAAe,SAAS,OAAO,GAAG;AACtC,aAAO,QAAQ,aAAa,YAAY,KACvC,kBAAkB,OAAO,KACzB,WAAW,OAAO;AAAA,IACpB;AAEA,QAAI,YAAY,SAAS;AACxB,YAAM,YAAa,QAA6B,MAAM,YAAY,KAAK;AACvE,aAAO,sBAAsB,SAAS,SAAS;AAAA,IAChD;AAEA,WAAO;AAAA,EACR;AAGA,WAAS,kBAAkB,SAA2B;AACrD,QAAI;AACH,YAAM,QAAQ,OAAO,iBAAiB,OAAO;AAC7C,aAAO,MAAM,WAAW;AAAA,IACzB,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AAGA,WAAS,WAAW,SAA2B;AAC9C,WAAO,QAAQ,YAAY,OAAO,QAAQ,UAAU,SAAS,KAAK;AAAA,EACnE;AAGA,WAAS,kBAAkB,SAA0B;AACpD,UAAM,QAAkB,CAAC;AACzB,QAAI,UAA0B;AAE9B,WAAO,WAAW,YAAY,SAAS,iBAAiB;AACvD,UAAI,WAAW,QAAQ,QAAQ,YAAY;AAE3C,UAAI,QAAQ,IAAI;AACf,oBAAY,IAAI,QAAQ,EAAE;AAC1B,cAAM,QAAQ,QAAQ;AACtB;AAAA,MACD,WAAW,QAAQ,aAAa,OAAO,QAAQ,cAAc,UAAU;AACtE,cAAM,UAAU,QAAQ,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AACpE,YAAI,QAAQ,SAAS,GAAG;AACvB,sBAAY,IAAI,QAAQ,CAAC,CAAC;AAAA,QAC3B;AAAA,MACD;AAEA,YAAM,QAAQ,QAAQ;AACtB,gBAAU,QAAQ;AAAA,IACnB;AAEA,WAAO,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,KAAK;AAAA,EACpC;AAGA,WAAS,eAAe,SAAsC;AAC7D,QAAI,CAAC,QAAQ,aAAa,OAAO,QAAQ,cAAc,UAAU;AAChE,aAAO;AAAA,IACR;AACA,UAAM,UAAU,QAAQ,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AACpE,WAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,GAAG,IAAI;AAAA,EACjD;AAEO,MAAM,gBAAN,MAAoB;AAAA,IAK1B,YAAYA,UAAkB;AAH9B,WAAQ,YAAqB;AAI5B,WAAK,WAAWA;AAChB,WAAK,oBAAoB,KAAK,aAAa,KAAK,IAAI;AAAA,IACrD;AAAA;AAAA,IAGA,QAAc;AACb,UAAI,KAAK,WAAW;AACnB;AAAA,MACD;AAEA,UAAI;AACH,eAAO,iBAAiB,SAAS,KAAK,mBAAmB,IAAI;AAC7D,aAAK,YAAY;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,OAAa;AACZ,UAAI,CAAC,KAAK,WAAW;AACpB;AAAA,MACD;AAEA,UAAI;AACH,eAAO,oBAAoB,SAAS,KAAK,mBAAmB,IAAI;AAChE,aAAK,YAAY;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,aAAa,OAAyB;AAC7C,UAAI;AACH,cAAM,SAAS,MAAM;AAErB,YAAI,CAAC,QAAQ;AACZ;AAAA,QACD;AAGA,YAAI,oBAAoB,MAAM,KAAK,CAAC,oBAAoB,MAAM,GAAG;AAChE,eAAK,kBAAkB,MAAM;AAC7B;AAAA,QACD;AAGA,cAAM,mBAAmB,OAAO,QAAQ,oBAAoB,KAAK,GAAG,CAAC;AAErE,YAAI,CAAC,kBAAkB;AACtB;AAAA,QACD;AAEA,YAAI,oBAAoB,gBAAgB,GAAG;AAC1C;AAAA,QACD;AAEA,YAAI,CAAC,oBAAoB,gBAAgB,GAAG;AAC3C;AAAA,QACD;AAEA,aAAK,kBAAkB,gBAAgB;AAAA,MACxC,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,kBAAkB,SAAwB;AACjD,YAAM,QAAsB;AAAA,QAC3B,WAAW;AAAA,QACX,SAAS,aAAa;AAAA,QACtB,SAAS;AAAA,UACR,WAAW;AAAA,UACX,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,KAAK,OAAO,SAAS;AAAA,UACrB,UAAU,YAAY;AAAA,UACtB,YAAY,cAAc;AAAA,UAC1B,cAAc,gBAAgB;AAAA,UAC9B,YAAY,cAAc;AAAA,UAC1B,SAAS,WAAW;AAAA,UACpB,IAAI,MAAM;AAAA,UACV,UAAU,YAAY;AAAA,UACtB,WAAW,aAAa;AAAA,QACzB;AAAA,QACA,MAAM;AAAA,UACL,SAAS,QAAQ;AAAA,UACjB,IAAI,QAAQ,MAAM;AAAA,UAClB,WAAW,eAAe,OAAO;AAAA,UACjC,OAAO,QAAQ,aAAa,OAAO,KAAK;AAAA,UACxC,MAAM,QAAQ,cAAc,SAAS,QAAQ,aAAa,EAAE,IAAI;AAAA,UAChE,UAAU,kBAAkB,OAAO;AAAA,QACpC;AAAA,MACD;AAEA,WAAK,SAAS,WAAW,KAAK;AAAA,IAC/B;AAAA,EACD;;;AClNA,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAC3B,MAAM,eAAe;AACrB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAElB,MAAM,kBAAN,MAAsB;AAAA,IAU5B,YAAYC,UAAkB;AAR9B,WAAQ,YAAqB;AAC7B,WAAQ,cAAqD;AAC7D,WAAQ,uBAA8D;AACtE,WAAQ,WAAmB;AAE3B,WAAQ,qBAA+C,MAAM;AAC7D,WAAQ,wBAAqD,MAAM;AAgHnE;AAAA,WAAQ,kBAAkB,MAAY;AACrC,YAAI;AACH,eAAK,wBAAwB;AAAA,QAC9B,QAAQ;AAAA,QAER;AAAA,MACD;AAkDA;AAAA,WAAQ,sBAAsB,MAAY;AACzC,YAAI;AAEH,gBAAM,WAAW,KAAK,4BAA4B;AAGlD,cAAI,YAAY,cAAc;AAC7B,iBAAK,SAAS,eAAe,UAAU,KAAK,QAAQ;AAAA,UACrD;AAGA,eAAK,eAAe,KAAK,UAAU,QAAQ;AAAA,QAC5C,QAAQ;AAAA,QAER;AAAA,MACD;AAGA;AAAA,WAAQ,0BAA0B,MAAY;AAC7C,YAAI;AACH,cAAI,OAAO,aAAa,aAAa;AACpC;AAAA,UACD;AAGA,cAAI,SAAS,oBAAoB,UAAU;AAC1C,kBAAM,WAAW,KAAK,4BAA4B;AAClD,iBAAK,eAAe,KAAK,UAAU,QAAQ;AAAA,UAC5C;AAAA,QACD,QAAQ;AAAA,QAER;AAAA,MACD;AArMC,WAAK,WAAWA;AAChB,WAAK,iBAAiB,YAAY,IAAI;AACtC,WAAK,WAAW,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAGvE,UAAI,OAAO,WAAW,eAAe,OAAO,YAAY,aAAa;AACpE,aAAK,qBAAqB,QAAQ,UAAU,KAAK,OAAO;AACxD,aAAK,wBAAwB,QAAQ,aAAa,KAAK,OAAO;AAAA,MAC/D;AAAA,IACD;AAAA;AAAA,IAGA,QAAc;AACb,UAAI,KAAK,WAAW;AACnB;AAAA,MACD;AAEA,UAAI;AAEH,aAAK,kBAAkB;AAGvB,YAAI,OAAO,WAAW,aAAa;AAClC,iBAAO,iBAAiB,YAAY,KAAK,eAAe;AAAA,QACzD;AAGA,aAAK,cAAc,YAAY,MAAM,KAAK,gBAAgB,GAAG,aAAa;AAG1E,aAAK,uBAAuB,YAAY,MAAM,KAAK,eAAe,GAAG,kBAAkB;AAGvF,YAAI,OAAO,WAAW,aAAa;AAClC,iBAAO,iBAAiB,gBAAgB,KAAK,mBAAmB;AAChE,mBAAS,iBAAiB,oBAAoB,KAAK,uBAAuB;AAAA,QAC3E;AAEA,aAAK,YAAY;AAIjB,mBAAW,MAAM,KAAK,mBAAmB,GAAG,CAAC;AAAA,MAC9C,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,OAAa;AACZ,UAAI,CAAC,KAAK,WAAW;AACpB;AAAA,MACD;AAEA,UAAI;AACH,YAAI,KAAK,aAAa;AACrB,wBAAc,KAAK,WAAW;AAC9B,eAAK,cAAc;AAAA,QACpB;AAEA,YAAI,KAAK,sBAAsB;AAC9B,wBAAc,KAAK,oBAAoB;AACvC,eAAK,uBAAuB;AAAA,QAC7B;AAEA,YAAI,OAAO,WAAW,aAAa;AAClC,iBAAO,oBAAoB,YAAY,KAAK,eAAe;AAC3D,iBAAO,oBAAoB,gBAAgB,KAAK,mBAAmB;AACnE,mBAAS,oBAAoB,oBAAoB,KAAK,uBAAuB;AAAA,QAC9E;AAGA,YAAI,OAAO,YAAY,aAAa;AACnC,kBAAQ,YAAY,KAAK;AACzB,kBAAQ,eAAe,KAAK;AAAA,QAC7B;AAEA,aAAK,YAAY;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,oBAA0B;AACjC,UAAI,OAAO,WAAW,eAAe,OAAO,YAAY,aAAa;AACpE;AAAA,MACD;AAEA,YAAM,OAAO;AACb,YAAM,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AACxD,YAAM,uBAAuB,QAAQ,aAAa,KAAK,OAAO;AAG9D,cAAQ,YAAY,YAAa,MAAiB;AACjD,cAAM,SAAS,kBAAkB,GAAG,IAAI;AACxC,aAAK,wBAAwB;AAC7B,eAAO;AAAA,MACR;AAGA,cAAQ,eAAe,YAAa,MAAiB;AACpD,cAAM,SAAS,qBAAqB,GAAG,IAAI;AAC3C,aAAK,wBAAwB;AAC7B,eAAO;AAAA,MACR;AAAA,IACD;AAAA;AAAA,IAYQ,0BAAgC;AACvC,UAAI;AACH,YAAI,OAAO,WAAW,aAAa;AAClC;AAAA,QACD;AAEA,cAAM,aAAa,OAAO,SAAS;AAGnC,YAAI,eAAe,KAAK,UAAU;AAEjC,gBAAM,WAAW,KAAK,4BAA4B;AAGlD,cAAI,YAAY,cAAc;AAE7B,iBAAK,wBAAwB,KAAK,UAAU,QAAQ;AAAA,UACrD;AAGA,eAAK,SAAS,cAAc;AAG5B,eAAK,iBAAiB,YAAY,IAAI;AAGtC,eAAK,WAAW;AAAA,QACjB;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,wBAAwB,MAAc,UAAwB;AACrE,UAAI;AAEH,aAAK,SAAS,eAAe,UAAU,IAAI;AAG3C,aAAK,eAAe,MAAM,QAAQ;AAAA,MACnC,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAsCQ,kBAAwB;AAC/B,UAAI;AACH,YAAI,OAAO,WAAW,aAAa;AAClC;AAAA,QACD;AAEA,cAAM,aAAa,OAAO,SAAS;AAGnC,YAAI,eAAe,KAAK,UAAU;AAEjC,eAAK,wBAAwB;AAAA,QAC9B;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,8BAAsC;AAC7C,cAAQ,YAAY,IAAI,IAAI,KAAK,kBAAkB;AAAA,IACpD;AAAA;AAAA,IAGQ,iBAAuB;AAC9B,UAAI;AACH,cAAM,WAAW,KAAK,4BAA4B;AAIlD,aAAK,SAAS,WAAW;AAAA,UACxB,WAAW;AAAA,UACX,MAAM;AAAA,YACL,MAAM,KAAK;AAAA,YACX;AAAA,YACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UACnC;AAAA,QACD,CAA0C;AAAA,MAC3C,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,eAAe,KAAa,UAAwB;AAC3D,UAAI;AACH,YAAI,OAAO,mBAAmB,aAAa;AAC1C,yBAAe,QAAQ,iBAAiB,GAAG;AAC3C,yBAAe,QAAQ,kBAAkB,OAAO,QAAQ,CAAC;AAAA,QAC1D;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,kBAA6D;AACpE,UAAI;AACH,YAAI,OAAO,mBAAmB,aAAa;AAC1C,gBAAM,MAAM,eAAe,QAAQ,eAAe;AAClD,gBAAM,WAAW,eAAe,QAAQ,gBAAgB;AACxD,cAAI,OAAO,UAAU;AACpB,mBAAO,EAAE,KAAK,UAAU,WAAW,QAAQ,EAAE;AAAA,UAC9C;AAAA,QACD;AAAA,MACD,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACR;AAAA;AAAA,IAGQ,gBAAsB;AAC7B,UAAI;AACH,YAAI,OAAO,mBAAmB,aAAa;AAC1C,yBAAe,WAAW,eAAe;AACzC,yBAAe,WAAW,gBAAgB;AAAA,QAC3C;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGQ,qBAA2B;AAClC,YAAM,WAAW,KAAK,gBAAgB;AACtC,UAAI,YAAY,SAAS,YAAY,cAAc;AAElD,aAAK,SAAS,eAAe,SAAS,UAAU,SAAS,GAAG;AAAA,MAC7D;AAEA,WAAK,cAAc;AAAA,IACpB;AAAA,EACD;;;AChUA;AAAA,IACC,MAAQ;AAAA,IACR,SAAW;AAAA,IACX,aAAe;AAAA,IACf,SAAW;AAAA,IACX,QAAU;AAAA,IACV,UAAY;AAAA,IACZ,YAAc;AAAA,MACb,MAAQ;AAAA,MACR,KAAO;AAAA,IACR;AAAA,IACA,MAAQ;AAAA,MACP,KAAO;AAAA,IACR;AAAA,IACA,MAAQ;AAAA,IACR,QAAU;AAAA,IACV,OAAS;AAAA,IACT,SAAW;AAAA,MACV,KAAK;AAAA,QACJ,OAAS;AAAA,QACT,QAAU;AAAA,QACV,SAAW;AAAA,MACZ;AAAA,MACA,UAAU;AAAA,IACX;AAAA,IACA,OAAS;AAAA,MACR;AAAA,IACD;AAAA,IACA,SAAW;AAAA,MACV,OAAS;AAAA,MACT,KAAO;AAAA,MACP,MAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAU;AAAA,MACV,gBAAgB;AAAA,MAChB,WAAa;AAAA,MACb,SAAW;AAAA,IACZ;AAAA,IACA,iBAAmB;AAAA,MAClB,eAAe;AAAA,MACf,QAAU;AAAA,MACV,OAAS;AAAA,MACT,UAAY;AAAA,MACZ,MAAQ;AAAA,MACR,YAAc;AAAA,IACf;AAAA,IACA,UAAY;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,SAAW;AAAA,MACV,MAAQ;AAAA,IACT;AAAA,EACD;;;ACnDO,MAAM,UAAU,gBAAI;;;ACiBpB,MAAM,WAAN,MAAM,SAAQ;AAAA,IAWZ,cAAc;AATtB,WAAQ,eAAmC;AAC3C,WAAQ,iBAAuC;AAC/C,WAAQ,mBAA2C;AAGnD,WAAQ,iBAA0B;AAClC,WAAQ,YAAqB;AAC7B,WAAQ,UAAgC;AAGvC,WAAK,gBAAgB;AAAA,QACpB,QAAQ;AAAA,QACR,UAAU;AAAA,MACX;AACA,WAAK,aAAa,qBAAqB;AAAA,IACxC;AAAA;AAAA,IAGA,OAAO,cAAuB;AAC7B,UAAI,SAAQ,cAAc,MAAM;AAC/B,iBAAQ,YAAY,IAAI,SAAQ;AAAA,MACjC;AACA,aAAO,SAAQ;AAAA,IAChB;AAAA;AAAA,IAGQ,gBAA8B;AACrC,aAAO;AAAA,QACN,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK,cAAc;AAAA,QAC3B,UAAU,KAAK,cAAc;AAAA,QAC7B,KAAK,OAAO,SAAS;AAAA,QACrB,UAAU,YAAY;AAAA,QACtB,YAAY,cAAc;AAAA,QAC1B,cAAc,gBAAgB;AAAA,QAC9B,YAAY,cAAc;AAAA,QAC1B,SAAS,WAAW;AAAA,QACpB,IAAI,MAAM;AAAA,QACV,UAAU,YAAY;AAAA,QACtB,WAAW,aAAa;AAAA,MACzB;AAAA,IACD;AAAA;AAAA,IAGQ,eAAe,MAAuB;AAC7C,UAAI,CAAC,KAAK,SAAS;AAClB,eAAO;AAAA,MACR;AAEA,YAAM,EAAE,gBAAgB,IAAI,KAAK;AAGjC,YAAM,iBAAiB,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AAGnF,UAAI,mBAAmB,gBAAgB,SAAS,GAAG;AAClD,eAAO,gBAAgB,KAAK,CAAC,YAAY;AACxC,cAAI,OAAO,YAAY,UAAU;AAEhC,kBAAM,oBAAoB,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AAE/F,mBAAO,mBAAmB,qBAAqB,eAAe,WAAW,iBAAiB;AAAA,UAC3F;AAEA,iBAAO,QAAQ,KAAK,cAAc,KAAK,QAAQ,KAAK,IAAI;AAAA,QACzD,CAAC;AAAA,MACF;AAEA,aAAO;AAAA,IACR;AAAA;AAAA,IAGA,KAAK,QAA6B;AACjC,UAAI;AAEH,aAAK,UAAU;AAGf,YAAI,OAAO,QAAQ;AAClB,eAAK,gBAAgB;AAAA,YACpB,QAAQ,OAAO;AAAA,YACf,UAAU,OAAO,YAAY;AAAA,UAC9B;AACA,cAAI,KAAK,kBAAkB,OAAO,OAAO;AACxC,oBAAQ,IAAI,uCAAuC,KAAK,aAAa;AAAA,UACtE;AAAA,QACD;AAGA,YAAI,KAAK,gBAAgB;AACxB;AAAA,QACD;AAEA,aAAK,eAAe,IAAI,YAAY,MAAM;AAC1C,aAAK,iBAAiB;AAGtB,gBAAQ,IAAI,0CAAmC,OAAO,EAAE;AAGxD,aAAK,iBAAiB,IAAI,cAAc,IAAI;AAC5C,aAAK,mBAAmB,IAAI,gBAAgB,IAAI;AAChD,aAAK,eAAe,MAAM;AAC1B,aAAK,iBAAiB,MAAM;AAG5B,aAAK,cAAc;AAAA,MACpB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,SAAS,QAAgB,UAAyB;AACjD,UAAI;AACH,aAAK,gBAAgB;AAAA,UACpB;AAAA,UACA,UAAU,YAAY;AAAA,QACvB;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,WAAW,OAA2B;AACrC,UAAI;AAEH,YAAI,KAAK,WAAW;AACnB;AAAA,QACD;AAEA,YAAI,CAAC,KAAK,cAAc;AACvB;AAAA,QACD;AAGA,cAAM,cAAc,eAAe;AACnC,YAAI,KAAK,eAAe,WAAW,GAAG;AACrC;AAAA,QACD;AAEA,cAAM,mBAAiC;AAAA,UACtC,GAAG;AAAA,UACH,SAAS,aAAa;AAAA,UACtB,SAAS,KAAK,cAAc;AAAA,QAC7B;AAEA,aAAK,aAAa,IAAI,gBAAgB;AAAA,MACvC,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,gBAAsB;AACrB,UAAI;AAEH,YAAI,KAAK,WAAW;AACnB;AAAA,QACD;AAEA,cAAM,OAAO,eAAe;AAG5B,YAAI,KAAK,eAAe,IAAI,GAAG;AAC9B;AAAA,QACD;AAEA,cAAM,QAAsB;AAAA,UAC3B,WAAW;AAAA,UACX,SAAS,aAAa;AAAA,UACtB,SAAS,KAAK,cAAc;AAAA,UAC5B,MAAM;AAAA,YACL;AAAA,UACD;AAAA,QACD;AAEA,aAAK,WAAW,KAAK;AAAA,MACtB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,eAAe,UAAkB,MAAqB;AACrD,UAAI;AAEH,YAAI,KAAK,WAAW;AACnB;AAAA,QACD;AAEA,cAAM,WAAW,QAAQ,eAAe;AAGxC,YAAI,KAAK,eAAe,QAAQ,GAAG;AAClC;AAAA,QACD;AAEA,cAAM,QAAsB;AAAA,UAC3B,WAAW;AAAA,UACX,SAAS,aAAa;AAAA,UACtB,SAAS,KAAK,cAAc;AAAA,UAC5B,MAAM;AAAA,YACL;AAAA,YACA,MAAM;AAAA,UACP;AAAA,QACD;AAEA,aAAK,WAAW,KAAK;AAAA,MACtB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,MAAM,QAAuB;AAC5B,UAAI;AACH,YAAI,KAAK,cAAc;AACtB,gBAAM,KAAK,aAAa,MAAM;AAAA,QAC/B;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,gBAAyB;AACxB,aAAO,KAAK;AAAA,IACb;AAAA;AAAA,IAGA,QAAc;AACb,UAAI;AACH,aAAK,YAAY;AACjB,gBAAQ,IAAI,4EAA+D;AAAA,MAC5E,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,SAAe;AACd,UAAI;AACH,aAAK,YAAY;AACjB,gBAAQ,IAAI,8EAAiE;AAAA,MAC9E,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,cAAoB;AACnB,UAAI;AACH,YAAI,CAAC,KAAK,SAAS;AAClB,kBAAQ,IAAI,4DAAkD;AAC9D;AAAA,QACD;AAGA,aAAK,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,KAAK;AAE/C,YAAI,KAAK,QAAQ,OAAO;AACvB,kBAAQ,IAAI,qDAAyC;AAAA,QACtD,OAAO;AACN,kBAAQ,IAAI,wDAA4C;AAAA,QACzD;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AAAA;AAAA,IAGA,UAAgB;AACf,UAAI;AACH,YAAI,KAAK,gBAAgB;AACxB,eAAK,eAAe,KAAK;AACzB,eAAK,iBAAiB;AAAA,QACvB;AACA,YAAI,KAAK,kBAAkB;AAC1B,eAAK,iBAAiB,KAAK;AAC3B,eAAK,mBAAmB;AAAA,QACzB;AACA,YAAI,KAAK,cAAc;AACtB,eAAK,aAAa,QAAQ;AAC1B,eAAK,eAAe;AAAA,QACrB;AACA,aAAK,iBAAiB;AAAA,MACvB,QAAQ;AAAA,MAER;AAAA,IACD;AAAA,EACD;AArSC,EADY,SACG,YAA4B;AADrC,MAAMC,WAAN;;;ACFA,MAAM,UAAUC,SAAQ,YAAY;AAKpC,WAAS,KAAK,QAA6B;AACjD,YAAQ,KAAK,MAAM;AAAA,EACpB;AAGO,WAAS,SAAS,QAAgB,UAAyB;AACjE,YAAQ,SAAS,QAAQ,QAAQ;AAAA,EAClC;AAGO,WAAS,QAAuB;AACtC,WAAO,QAAQ,MAAM;AAAA,EACtB;AAGO,WAAS,UAAgB;AAC/B,YAAQ,QAAQ;AAAA,EACjB;","names":["tracker","tracker","Tracker","Tracker"]}
package/dist/index.js CHANGED
@@ -494,7 +494,7 @@ var HistoryObserver = class {
494
494
  };
495
495
  this._tracker = tracker2;
496
496
  this._pageStartTime = performance.now();
497
- this._lastUrl = typeof window !== "undefined" ? window.location.pathname : "";
497
+ this._lastUrl = typeof window !== "undefined" ? window.location.href : "";
498
498
  if (typeof window !== "undefined" && typeof history !== "undefined") {
499
499
  this._originalPushState = history.pushState.bind(history);
500
500
  this._originalReplaceState = history.replaceState.bind(history);
@@ -573,7 +573,7 @@ var HistoryObserver = class {
573
573
  if (typeof window === "undefined") {
574
574
  return;
575
575
  }
576
- const currentUrl = window.location.pathname;
576
+ const currentUrl = window.location.href;
577
577
  if (currentUrl !== this._lastUrl) {
578
578
  const duration = this._calculateWallClockDuration();
579
579
  if (duration >= MIN_DURATION) {
@@ -600,7 +600,7 @@ var HistoryObserver = class {
600
600
  if (typeof window === "undefined") {
601
601
  return;
602
602
  }
603
- const currentUrl = window.location.pathname;
603
+ const currentUrl = window.location.href;
604
604
  if (currentUrl !== this._lastUrl) {
605
605
  this._handleNavigationChange();
606
606
  }
@@ -673,7 +673,7 @@ var HistoryObserver = class {
673
673
  // package.json
674
674
  var package_default = {
675
675
  name: "@agroideas/event-tracker-sdk",
676
- version: "1.0.7",
676
+ version: "1.0.8",
677
677
  description: "A lightweight, framework-agnostic SDK for tracking frontend user behavior",
678
678
  license: "MIT",
679
679
  author: "Agroideas",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/EventBuffer.ts","../src/utils/uuid.ts","../src/utils/truncate.ts","../src/utils/metadata.ts","../src/observers/ClickObserver.ts","../src/observers/HistoryObserver.ts","../package.json","../src/version.ts","../src/core/Tracker.ts"],"sourcesContent":["/**\r\n * Event Tracker SDK\r\n *\r\n * SDK ligero y agnóstico al framework para tracking de comportamiento frontend.\r\n *\r\n * @example\r\n * import { tracker } from '@agroideas/event-tracker-sdk';\r\n *\r\n * tracker.init({\r\n * apiKey: 'tu-api-key',\r\n * endpoint: 'https://tu-api.com/events'\r\n * });\r\n *\r\n * // Identificar usuario\r\n * tracker.identify('user-123', 'Juan Pérez');\r\n */\r\n\r\nimport { Tracker } from './core/Tracker';\r\nimport type { TrackerConfig } from './types';\r\n\r\n/** Instancia única exportada del Tracker */\r\nexport const tracker = Tracker.getInstance();\r\n\r\n/**\r\n * Inicializa el tracker con configuración requerida (apiKey y endpoint).\r\n */\r\nexport function init(config: TrackerConfig): void {\r\n\ttracker.init(config);\r\n}\r\n\r\n/** Identifica al usuario */\r\nexport function identify(userId: string, userName?: string): void {\r\n\ttracker.identify(userId, userName);\r\n}\r\n\r\n/** Fuerza el envío de eventos pendientes */\r\nexport function flush(): Promise<void> {\r\n\treturn tracker.flush();\r\n}\r\n\r\n/** Destruye el tracker */\r\nexport function destroy(): void {\r\n\ttracker.destroy();\r\n}\r\n\r\nexport type { TrackerConfig };\r\n","/**\r\n * EventBuffer: Maneja la acumulación, batching y envío de eventos\r\n */\r\n\r\nimport type { TrackerEvent, SendResult, TrackerConfig, TrackingResponse } from '../types';\r\n\r\nconst BATCH_INTERVAL = 10000;\r\nconst MAX_BUFFER_SIZE = 15;\r\n\r\nexport class EventBuffer {\r\n\tprivate _buffer: TrackerEvent[];\r\n\tprivate _config: Required<Omit<TrackerConfig, 'apiKey' | 'endpoint' | 'userId' | 'userName' | 'debug'>>;\r\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _endpoint: string;\r\n\tprivate _apiKey: string;\r\n\tprivate _isDebug: boolean;\r\n\r\n\tconstructor(config: TrackerConfig) {\r\n\t\tthis._buffer = [];\r\n\r\n\t\t// apiKey y endpoint son requeridos\r\n\t\tthis._endpoint = config.endpoint;\r\n\t\tthis._apiKey = config.apiKey;\r\n\r\n\t\t// DEBUG es opcional, default false\r\n\t\tthis._isDebug = config.debug == \"true\";\r\n\t\tconsole.log(config);\r\n\r\n\t\tthis._config = {\r\n\t\t\tbatchInterval: config.batchInterval ?? BATCH_INTERVAL,\r\n\t\t\tmaxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE,\r\n\t\t\texcludePatterns: config.excludePatterns ?? [],\r\n\t\t};\r\n\r\n\t\tif (this._isDebug) {\r\n\t\t\tconsole.log('[EventTracker] Modo DEBUG activado');\r\n\t\t\tconsole.log('[EventTracker] Endpoint:', this._endpoint);\r\n\t\t\tconsole.log('[EventTracker] API Key:', this._apiKey);\r\n\t\t}\r\n\r\n\t\t// En modo debug, no iniciar timer de flush\r\n\t\tif (!this._isDebug) {\r\n\t\t\tthis._startFlushTimer();\r\n\t\t}\r\n\r\n\t\tthis._setupVisibilityListener();\r\n\t}\r\n\r\n\t/** Agrega un evento al buffer */\r\n\tadd(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\tthis._buffer.push(event);\r\n\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] Evento agregado:', event.eventType);\r\n\t\t\t\tconsole.log('[EventTracker] Buffer size:', this._buffer.length);\r\n\t\t\t\tconsole.log('[EventTracker] Event content:', JSON.stringify(event, null, 2));\r\n\t\t\t}\r\n\r\n\t\t\t// En modo debug, no hacer flush automático\r\n\t\t\tif (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Silently ignore errors to never block the main app\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía todos los eventos acumulados al servidor */\r\n\tasync flush(): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tif (this._buffer.length === 0) {\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst eventsToSend = [...this._buffer];\r\n\t\t\tthis._buffer = [];\r\n\r\n\t\t\t// Modo debug: solo console.log, no fetch\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] DEBUG - Events:', JSON.stringify(eventsToSend, null, 2));\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst result = await this._sendEventsAsync(eventsToSend);\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\t/** Obtiene la cantidad de eventos en el buffer */\r\n\tgetSize(): number {\r\n\t\treturn this._buffer.length;\r\n\t}\r\n\r\n\t/** Limpia el buffer sin enviar */\r\n\tclear(): void {\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Destruye el buffer y limpia recursos */\r\n\tdestroy(): void {\r\n\t\tif (this._flushTimer) {\r\n\t\t\tclearInterval(this._flushTimer);\r\n\t\t\tthis._flushTimer = null;\r\n\t\t}\r\n\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Inicia el temporizador de flush periódico */\r\n\tprivate _startFlushTimer(): void {\r\n\t\tthis._flushTimer = setInterval(() => {\r\n\t\t\tif (this._buffer.length > 0) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t}, this._config.batchInterval);\r\n\t}\r\n\r\n\t/** Configura el listener de visibilitychange para envío de emergencia */\r\n\tprivate _setupVisibilityListener(): void {\r\n\t\tif (typeof document === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst handleVisibilityChange = (): void => {\r\n\t\t\t// En modo debug, no enviar eventos\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (document.visibilityState === 'hidden' && this._buffer.length > 0) {\r\n\t\t\t\t// Usar setTimeout para asegurar que el fetch se complete antes del unload\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tthis.flush();\r\n\t\t\t\t}, 0);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tdocument.addEventListener('visibilitychange', handleVisibilityChange);\r\n\t}\r\n\r\n\t/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */\r\n\tprivate async _sendEventsAsync(events: TrackerEvent[]): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tconst payload = JSON.stringify({\r\n\t\t\t\tevents: events,\r\n\t\t\t});\r\n\r\n\t\t\tconst response = await fetch(this._endpoint, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'X-API-Key': this._apiKey,\r\n\t\t\t\t},\r\n\t\t\t\tbody: payload,\r\n\t\t\t\tkeepalive: true,\r\n\t\t\t});\r\n\r\n\t\t\t// Intentar parsear el mensaje de respuesta del backend\r\n\t\t\tlet message: string | undefined;\r\n\t\t\ttry {\r\n\t\t\t\tconst responseData: TrackingResponse = await response.json();\r\n\t\t\t\tmessage = responseData.message;\r\n\t\t\t} catch {\r\n\t\t\t\t// Si no se puede parsear, usar el status text\r\n\t\t\t\tmessage = response.statusText;\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: response.ok,\r\n\t\t\t\tstatusCode: response.status,\r\n\t\t\t\tmessage,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Network error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Generador de UUID v4\r\n */\r\n\r\n/** Genera un UUID v4 aleatorio */\r\nexport function generateUUID(): string {\r\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\r\n\t\tconst random = Math.random() * 16 | 0;\r\n\t\tconst value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n\t\treturn value.toString(16);\r\n\t});\r\n}\r\n","/**\r\n * Utilidad para truncar texto\r\n */\r\n\r\n/**\r\n * Trunca un texto a la longitud máxima especificada\r\n * @param text - Texto a truncar\r\n * @param maxLength - Longitud máxima\r\n * @returns Texto truncado con '...' al final si excede la longitud\r\n */\r\nexport function truncate(text: string, maxLength: number): string {\r\n\tif (maxLength < 3) {\r\n\t\treturn '...';\r\n\t}\r\n\r\n\tif (text.length <= maxLength) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, maxLength - 3) + '...';\r\n}\r\n","/**\r\n * Utilidades para recolectar metadatos del navegador\r\n */\r\n\r\nimport { generateUUID } from './uuid';\r\n\r\n/** Obtiene o crea un sessionId en sessionStorage */\r\nexport function getOrCreateSessionId(): string {\r\n\ttry {\r\n\t\tconst storageKey = 'event_tracker_session_id';\r\n\t\tconst existingSessionId = sessionStorage.getItem(storageKey);\r\n\r\n\t\tif (existingSessionId) {\r\n\t\t\treturn existingSessionId;\r\n\t\t}\r\n\r\n\t\tconst newSessionId = generateUUID();\r\n\t\tsessionStorage.setItem(storageKey, newSessionId);\r\n\t\treturn newSessionId;\r\n\t} catch {\r\n\t\treturn generateUUID();\r\n\t}\r\n}\r\n\r\n/** Obtiene el tamaño de pantalla */\r\nexport function getScreenSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Obtiene el tamaño del viewport */\r\nexport function getViewportSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Detecta el tipo de dispositivo */\r\nexport function getDeviceType(): 'mobile' | 'desktop' | 'tablet' | 'unknown' {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\r\n\t\treturn 'tablet';\r\n\t}\r\n\r\n\tif (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\r\n\t\treturn 'mobile';\r\n\t}\r\n\r\n\treturn 'desktop';\r\n}\r\n\r\n/** Obtiene el navegador */\r\nexport function getBrowser(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Firefox')) {\r\n\t\treturn 'Firefox';\r\n\t}\r\n\r\n\tif (ua.includes('Edg/')) {\r\n\t\treturn 'Edge';\r\n\t}\r\n\r\n\tif (ua.includes('Chrome')) {\r\n\t\treturn 'Chrome';\r\n\t}\r\n\r\n\tif (ua.includes('Safari')) {\r\n\t\treturn 'Safari';\r\n\t}\r\n\r\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\r\n\t\treturn 'Internet Explorer';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el sistema operativo */\r\nexport function getOS(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Windows')) {\r\n\t\treturn 'Windows';\r\n\t}\r\n\r\n\tif (ua.includes('Mac OS')) {\r\n\t\treturn 'macOS';\r\n\t}\r\n\r\n\tif (ua.includes('Linux')) {\r\n\t\treturn 'Linux';\r\n\t}\r\n\r\n\tif (ua.includes('Android')) {\r\n\t\treturn 'Android';\r\n\t}\r\n\r\n\tif (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) {\r\n\t\treturn 'iOS';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el idioma del navegador */\r\nexport function getLanguage(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\treturn navigator.language || 'unknown';\r\n}\r\n\r\n/** Obtiene la URL actual */\r\nexport function getCurrentUrl(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.href;\r\n}\r\n\r\n/** Obtiene el referrer */\r\nexport function getReferrer(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn document.referrer;\r\n}\r\n\r\n/** Obtiene el timestamp actual en formato ISO */\r\nexport function getTimestamp(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\n/** Obtiene el path actual */\r\nexport function getCurrentPath(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.pathname;\r\n}\r\n","/**\r\n * ClickObserver: Captura clics globales en elementos interactivos\r\n */\r\n\r\nimport type { TrackerEvent } from '../types';\r\nimport { Tracker } from '../core/Tracker';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { truncate } from '../utils/truncate';\r\nimport {\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\n\r\n/** Tipos de elementos que deben ser rastreados */\r\nconst TRACKABLE_TAGS = ['BUTTON', 'A', 'INPUT', 'I', 'SPAN', 'DIV'];\r\n\r\n/** Tipos de input que deben ser rastreados */\r\nconst TRACKABLE_INPUT_TYPES = ['submit'];\r\n\r\n/** Tipos de input que deben ser excluidos (sensibles) */\r\nconst SENSITIVE_INPUT_TYPES = ['password', 'email', 'tel', 'number', 'search', 'url'];\r\n\r\n/** Selectores para elementos rastreables */\r\nconst TRACKABLE_SELECTORS = [\r\n\t'button',\r\n\t'a',\r\n\t'input[type=\"submit\"]',\r\n\t'[data-track]',\r\n\t'[style*=\"cursor: pointer\"]',\r\n\t'i[class*=\"mdi-\"]',\r\n];\r\n\r\n/** Verifica si un elemento es sensible (no capturar valores) */\r\nfunction _isSensitiveElement(element: Element): boolean {\r\n\tif (element.tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn SENSITIVE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n/** Verifica si un elemento es rastreable */\r\nfunction _isTrackableElement(element: Element): boolean {\r\n\tconst tagName = element.tagName.toUpperCase();\r\n\r\n\tif (!TRACKABLE_TAGS.includes(tagName)) {\r\n\t\treturn element.hasAttribute('data-track') ||\r\n\t\t\t_hasCursorPointer(element) ||\r\n\t\t\t_isMdiIcon(element);\r\n\t}\r\n\r\n\tif (tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn TRACKABLE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/** Verifica si el elemento tiene cursor: pointer */\r\nfunction _hasCursorPointer(element: Element): boolean {\r\n\ttry {\r\n\t\tconst style = window.getComputedStyle(element);\r\n\t\treturn style.cursor === 'pointer';\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/** Verifica si es un icono MDI */\r\nfunction _isMdiIcon(element: Element): boolean {\r\n\treturn element.tagName === 'I' && element.classList.contains('mdi');\r\n}\r\n\r\n/** Genera un selector CSS simple para el elemento */\r\nfunction _generateSelector(element: Element): string {\r\n\tconst parts: string[] = [];\r\n\tlet current: Element | null = element;\r\n\r\n\twhile (current && current !== document.documentElement) {\r\n\t\tlet selector = current.tagName.toLowerCase();\r\n\r\n\t\tif (current.id) {\r\n\t\t\tselector += `#${current.id}`;\r\n\t\t\tparts.unshift(selector);\r\n\t\t\tbreak;\r\n\t\t} else if (current.className && typeof current.className === 'string') {\r\n\t\t\tconst classes = current.className.trim().split(/\\s+/).filter(Boolean);\r\n\t\t\tif (classes.length > 0) {\r\n\t\t\t\tselector += `.${classes[0]}`;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tparts.unshift(selector);\r\n\t\tcurrent = current.parentElement;\r\n\t}\r\n\r\n\treturn parts.slice(0, 4).join(' > ');\r\n}\r\n\r\n/** Obtiene todas las clases de un elemento como string separado por espacios */\r\nfunction _getAllClasses(element: Element): string | undefined {\r\n\tif (!element.className || typeof element.className !== 'string') {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst classes = element.className.trim().split(/\\s+/).filter(Boolean);\r\n\treturn classes.length > 0 ? classes.join(' ') : undefined;\r\n}\r\n\r\nexport class ClickObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _boundHandleClick: (event: MouseEvent) => void;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._boundHandleClick = this._handleClick.bind(this);\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.addEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = true;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.removeEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de eventos de clic */\r\n\tprivate _handleClick(event: MouseEvent): void {\r\n\t\ttry {\r\n\t\t\tconst target = event.target as Element;\r\n\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 1: Si el elemento clickeado es rastreable, usarlo directamente\r\n\t\t\tif (_isTrackableElement(target) && !_isSensitiveElement(target)) {\r\n\t\t\t\tthis._createClickEvent(target);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 2: Buscar el ancestro rastreable más cercano\r\n\t\t\tconst trackableElement = target.closest(TRACKABLE_SELECTORS.join(','));\r\n\r\n\t\t\tif (!trackableElement) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isSensitiveElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isTrackableElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._createClickEvent(trackableElement);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de clic */\r\n\tprivate _createClickEvent(element: Element): void {\r\n\t\tconst event: TrackerEvent = {\r\n\t\t\teventType: 'user_click',\r\n\t\t\teventId: generateUUID(),\r\n\t\t\tcontext: {\r\n\t\t\t\tsessionId: '',\r\n\t\t\t\tuserId: null,\r\n\t\t\t\tuserName: null,\r\n\t\t\t\turl: window.location.href,\r\n\t\t\t\treferrer: getReferrer(),\r\n\t\t\t\tscreenSize: getScreenSize(),\r\n\t\t\t\tviewportSize: getViewportSize(),\r\n\t\t\t\tdeviceType: getDeviceType(),\r\n\t\t\t\tbrowser: getBrowser(),\r\n\t\t\t\tos: getOS(),\r\n\t\t\t\tlanguage: getLanguage(),\r\n\t\t\t\ttimestamp: getTimestamp(),\r\n\t\t\t},\r\n\t\t\tdata: {\r\n\t\t\t\ttagName: element.tagName,\r\n\t\t\t\tid: element.id || undefined,\r\n\t\t\t\tclassName: _getAllClasses(element),\r\n\t\t\t\ttitle: element.getAttribute('title') || undefined,\r\n\t\t\t\ttext: element.textContent ? truncate(element.textContent, 30) : undefined,\r\n\t\t\t\tselector: _generateSelector(element),\r\n\t\t\t},\r\n\t\t};\r\n\r\n\t\tthis._tracker.trackEvent(event);\r\n\t}\r\n}\r\n","/**\r\n * HistoryObserver: Monitorea cambios de navegación usando múltiples estrategias\r\n * - popstate: para botones atrás/adelante del navegador\r\n * - history intercept: para pushState/replaceState en SPA\r\n * - polling: fallback para detectar cambios\r\n * - sessionStorage: para manejar F5\r\n */\r\n\r\nimport type { Tracker } from '../core/Tracker';\r\n\r\nconst POLL_INTERVAL = 1000; // Verificar URL cada 1 segundo (fallback)\r\nconst HEARTBEAT_INTERVAL = 10000; // Enviar heartbeat cada 10 segundos\r\nconst MIN_DURATION = 0.5; // Ignorar page_leave con menos de 0.5 segundos\r\nconst STORAGE_KEY_URL = 'event_tracker_last_url';\r\nconst STORAGE_KEY_TIME = 'event_tracker_last_time';\r\n\r\nexport class HistoryObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _lastUrl: string = '';\r\n\tprivate _pageStartTime: number;\r\n\tprivate _originalPushState: typeof history.pushState = () => null;\r\n\tprivate _originalReplaceState: typeof history.replaceState = () => null;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._pageStartTime = performance.now();\r\n\t\tthis._lastUrl = typeof window !== 'undefined' ? window.location.pathname : '';\r\n\r\n\t\t// Guardar referencias originales de history\r\n\t\tif (typeof window !== 'undefined' && typeof history !== 'undefined') {\r\n\t\t\tthis._originalPushState = history.pushState.bind(history);\r\n\t\t\tthis._originalReplaceState = history.replaceState.bind(history);\r\n\t\t}\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// Configurar interceptación de history\r\n\t\t\tthis._interceptHistory();\r\n\r\n\t\t\t// Agregar listener de popstate (botones atrás/adelante)\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('popstate', this._handlePopState);\r\n\t\t\t}\r\n\r\n\t\t\t// Iniciar intervalo de polling (fallback)\r\n\t\t\tthis._intervalId = setInterval(() => this._checkUrlChange(), POLL_INTERVAL);\r\n\r\n\t\t\t// Iniciar intervalo de heartbeat\r\n\t\t\tthis._heartbeatIntervalId = setInterval(() => this._sendHeartbeat(), HEARTBEAT_INTERVAL);\r\n\r\n\t\t\t// Eventos para cierre de página\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.addEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = true;\r\n\r\n\t\t\t// Verificar si hay una página previa guardada (para F5)\r\n\t\t\t// Solo después de que el tracker esté inicializado\r\n\t\t\tsetTimeout(() => this._checkPreviousPage(), 0);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tif (this._intervalId) {\r\n\t\t\t\tclearInterval(this._intervalId);\r\n\t\t\t\tthis._intervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (this._heartbeatIntervalId) {\r\n\t\t\t\tclearInterval(this._heartbeatIntervalId);\r\n\t\t\t\tthis._heartbeatIntervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.removeEventListener('popstate', this._handlePopState);\r\n\t\t\t\twindow.removeEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.removeEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\t// Restaurar history original\r\n\t\t\tif (typeof history !== 'undefined') {\r\n\t\t\t\thistory.pushState = this._originalPushState;\r\n\t\t\t\thistory.replaceState = this._originalReplaceState;\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Intercepta history.pushState y history.replaceState */\r\n\tprivate _interceptHistory(): void {\r\n\t\tif (typeof window === 'undefined' || typeof history === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst self = this;\r\n\t\tconst originalPushState = history.pushState.bind(history) as (...args: unknown[]) => unknown;\r\n\t\tconst originalReplaceState = history.replaceState.bind(history) as (...args: unknown[]) => unknown;\r\n\r\n\t\t// Interceptar pushState\r\n\t\thistory.pushState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalPushState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.pushState;\r\n\r\n\t\t// Interceptar replaceState\r\n\t\thistory.replaceState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalReplaceState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.replaceState;\r\n\t}\r\n\r\n\t/** Manejador de popstate (botones atrás/adelante) */\r\n\tprivate _handlePopState = (): void => {\r\n\t\ttry {\r\n\t\t\tthis._handleNavigationChange();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejo centralizado de cambio de navegación */\r\n\tprivate _handleNavigationChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.pathname;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Calcular duración wall-clock de la página anterior\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t\t// Solo enviar page_leave si la duración es significativa\r\n\t\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\t\t// Enviar page_leave INMEDIATAMENTE para la página anterior\r\n\t\t\t\t\tthis._sendPageLeaveImmediate(this._lastUrl, duration);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Track page view de la nueva página\r\n\t\t\t\tthis._tracker.trackPageView();\r\n\r\n\t\t\t\t// Resetear contador para la nueva página\r\n\t\t\t\tthis._pageStartTime = performance.now();\r\n\r\n\t\t\t\t// Actualizar la última URL\r\n\t\t\t\tthis._lastUrl = currentUrl;\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía page_leave inmediatamente sin esperar el buffer */\r\n\tprivate _sendPageLeaveImmediate(path: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\t// Crear el evento directamente\r\n\t\t\tthis._tracker.trackPageLeave(duration, path);\r\n\r\n\t\t\t// También guardar en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(path, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de beforeunload (cuando el usuario sale de la página) */\r\n\tprivate _handleBeforeUnload = (): void => {\r\n\t\ttry {\r\n\t\t\t// Calcular duración wall-clock\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Solo enviar si la duración es significativa\r\n\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\tthis._tracker.trackPageLeave(duration, this._lastUrl);\r\n\t\t\t}\r\n\r\n\t\t\t// Guardar URL actual en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejador de visibilitychange (para detectar cuando la app pasa a segundo plano) */\r\n\tprivate _handleVisibilityChange = (): void => {\r\n\t\ttry {\r\n\t\t\tif (typeof document === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cuando la página se oculta, guardar el tiempo actual en sessionStorage\r\n\t\t\tif (document.visibilityState === 'hidden') {\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\t\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Verifica si la URL cambió (fallback de polling) */\r\n\tprivate _checkUrlChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.pathname;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Usar el manejo centralizado\r\n\t\t\t\tthis._handleNavigationChange();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Calcula la duración wall-clock (tiempo total desde que entró a la página) */\r\n\tprivate _calculateWallClockDuration(): number {\r\n\t\treturn (performance.now() - this._pageStartTime) / 1000;\r\n\t}\r\n\r\n\t/** Envía un heartbeat con la duración actual */\r\n\tprivate _sendHeartbeat(): void {\r\n\t\ttry {\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Usar trackPageLeave para mantener compatibilidad\r\n\t\t\t// El backend puede filtrar por eventType = 'page_heartbeat'\r\n\t\t\tthis._tracker.trackEvent({\r\n\t\t\t\teventType: 'page_heartbeat',\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath: this._lastUrl,\r\n\t\t\t\t\tduration: duration,\r\n\t\t\t\t\ttimestamp: new Date().toISOString(),\r\n\t\t\t\t},\r\n\t\t\t} as import('../types/index').TrackerEvent);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Guarda la URL y tiempo en sessionStorage (para F5) */\r\n\tprivate _saveToStorage(url: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_URL, url);\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_TIME, String(duration));\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Lee la URL y tiempo previos de sessionStorage (para F5) */\r\n\tprivate _getFromStorage(): { url: string; duration: number; } | null {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tconst url = sessionStorage.getItem(STORAGE_KEY_URL);\r\n\t\t\t\tconst duration = sessionStorage.getItem(STORAGE_KEY_TIME);\r\n\t\t\t\tif (url && duration) {\r\n\t\t\t\t\treturn { url, duration: parseFloat(duration) };\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/** Limpia sessionStorage */\r\n\tprivate _clearStorage(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_URL);\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_TIME);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si hay una página previa (para F5) */\r\n\tprivate _checkPreviousPage(): void {\r\n\t\tconst previous = this._getFromStorage();\r\n\t\tif (previous && previous.duration >= MIN_DURATION) {\r\n\t\t\t// La página anterior tenía una duración significativa, enviar page_leave\r\n\t\t\tthis._tracker.trackPageLeave(previous.duration, previous.url);\r\n\t\t}\r\n\t\t// Limpiar storage después de procesar\r\n\t\tthis._clearStorage();\r\n\t}\r\n}\r\n","{\r\n\t\"name\": \"@agroideas/event-tracker-sdk\",\r\n\t\"version\": \"1.0.7\",\r\n\t\"description\": \"A lightweight, framework-agnostic SDK for tracking frontend user behavior\",\r\n\t\"license\": \"MIT\",\r\n\t\"author\": \"Agroideas\",\r\n\t\"homepage\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"main\": \"./dist/index.cjs\",\r\n\t\"module\": \"./dist/index.mjs\",\r\n\t\"types\": \"./dist/index.d.ts\",\r\n\t\"exports\": {\r\n\t\t\".\": {\r\n\t\t\t\"types\": \"./dist/index.d.ts\",\r\n\t\t\t\"import\": \"./dist/index.mjs\",\r\n\t\t\t\"require\": \"./dist/index.cjs\"\r\n\t\t},\r\n\t\t\"./iife\": \"./dist/index.iife.js\"\r\n\t},\r\n\t\"files\": [\r\n\t\t\"dist\"\r\n\t],\r\n\t\"scripts\": {\r\n\t\t\"build\": \"tsup\",\r\n\t\t\"dev\": \"tsup --watch\",\r\n\t\t\"lint\": \"eslint src --ext .ts\",\r\n\t\t\"lint:fix\": \"eslint src --ext .ts --fix\",\r\n\t\t\"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\r\n\t\t\"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\r\n\t\t\"typecheck\": \"tsc --noEmit\",\r\n\t\t\"prepare\": \"husky install\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"@types/node\": \"^20.10.0\",\r\n\t\t\"eslint\": \"^8.55.0\",\r\n\t\t\"husky\": \"^8.0.3\",\r\n\t\t\"prettier\": \"^3.1.0\",\r\n\t\t\"tsup\": \"^8.0.1\",\r\n\t\t\"typescript\": \"^5.3.2\"\r\n\t},\r\n\t\"keywords\": [\r\n\t\t\"tracking\",\r\n\t\t\"analytics\",\r\n\t\t\"sdk\",\r\n\t\t\"events\",\r\n\t\t\"behavior\",\r\n\t\t\"frontend\"\r\n\t],\r\n\t\"engines\": {\r\n\t\t\"node\": \">=18.0.0\"\r\n\t}\r\n}","/**\n * Version constants for the Event Tracker SDK\n */\n\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION = pkg.version;\n","/**\r\n * Tracker: Singleton principal que coordina observers y EventBuffer\r\n */\r\n\r\nimport type { TrackerConfig, TrackerEvent, UserIdentity, EventContext } from '../types';\r\nimport { EventBuffer } from './EventBuffer';\r\nimport { ClickObserver } from '../observers/ClickObserver';\r\nimport { HistoryObserver } from '../observers/HistoryObserver';\r\nimport {\r\n\tgetCurrentPath,\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetOrCreateSessionId,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { VERSION } from '../version';\r\n\r\nexport class Tracker {\r\n\tprivate static _instance: Tracker | null = null;\r\n\tprivate _eventBuffer: EventBuffer | null = null;\r\n\tprivate _clickObserver: ClickObserver | null = null;\r\n\tprivate _historyObserver: HistoryObserver | null = null;\r\n\tprivate _userIdentity: UserIdentity;\r\n\tprivate _sessionId: string;\r\n\tprivate _isInitialized: boolean = false;\r\n\tprivate _isPaused: boolean = false;\r\n\tprivate _config: TrackerConfig | null = null;\r\n\r\n\tprivate constructor() {\r\n\t\tthis._userIdentity = {\r\n\t\t\tuserId: null,\r\n\t\t\tuserName: null,\r\n\t\t};\r\n\t\tthis._sessionId = getOrCreateSessionId();\r\n\t}\r\n\r\n\t/** Obtiene la instancia única del Tracker */\r\n\tstatic getInstance(): Tracker {\r\n\t\tif (Tracker._instance === null) {\r\n\t\t\tTracker._instance = new Tracker();\r\n\t\t}\r\n\t\treturn Tracker._instance;\r\n\t}\r\n\r\n\t/** Construye el contexto completo del evento */\r\n\tprivate _buildContext(): EventContext {\r\n\t\treturn {\r\n\t\t\tsessionId: this._sessionId,\r\n\t\t\tuserId: this._userIdentity.userId,\r\n\t\t\tuserName: this._userIdentity.userName,\r\n\t\t\turl: window.location.href,\r\n\t\t\treferrer: getReferrer(),\r\n\t\t\tscreenSize: getScreenSize(),\r\n\t\t\tviewportSize: getViewportSize(),\r\n\t\t\tdeviceType: getDeviceType(),\r\n\t\t\tbrowser: getBrowser(),\r\n\t\t\tos: getOS(),\r\n\t\t\tlanguage: getLanguage(),\r\n\t\t\ttimestamp: getTimestamp(),\r\n\t\t};\r\n\t}\r\n\r\n\t/** Verifica si una ruta debe ser excluida del tracking */\r\n\tprivate _shouldExclude(path: string): boolean {\r\n\t\tif (!this._config) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst { excludePatterns } = this._config;\r\n\r\n\t\t// Normalizar el path (quitar trailing slash para comparación consistente)\r\n\t\tconst normalizedPath = path.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;\r\n\r\n\t\t// Si hay patrones de exclusión, verificarlos\r\n\t\tif (excludePatterns && excludePatterns.length > 0) {\r\n\t\t\treturn excludePatterns.some((pattern) => {\r\n\t\t\t\tif (typeof pattern === 'string') {\r\n\t\t\t\t\t// Normalizar el patrón también\r\n\t\t\t\t\tconst normalizedPattern = pattern.endsWith('/') && pattern.length > 1 ? pattern.slice(0, -1) : pattern;\r\n\t\t\t\t\t// Coincidencia exacta o prefijo\r\n\t\t\t\t\treturn normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern);\r\n\t\t\t\t}\r\n\t\t\t\t// Es RegExp\r\n\t\t\t\treturn pattern.test(normalizedPath) || pattern.test(path);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/** Inicializa el tracker con la configuración */\r\n\tinit(config: TrackerConfig): void {\r\n\t\ttry {\r\n\t\t\t// Guardar configuración\r\n\t\t\tthis._config = config;\r\n\r\n\t\t\t// Actualizamos el usuario cada vez que se llama init (permite actualizar userId/userName)\r\n\t\t\tif (config.userId) {\r\n\t\t\t\tthis._userIdentity = {\r\n\t\t\t\t\tuserId: config.userId,\r\n\t\t\t\t\tuserName: config.userName ?? null,\r\n\t\t\t\t};\r\n\t\t\t\tif (this._isInitialized && config.debug) {\r\n\t\t\t\t\tconsole.log('[EventTracker] Usuario actualizado:', this._userIdentity);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Solo inicializar EventBuffer y observers la primera vez\r\n\t\t\tif (this._isInitialized) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._eventBuffer = new EventBuffer(config);\r\n\t\t\tthis._isInitialized = true;\r\n\r\n\t\t\t// Imprimir versión del tracker\r\n\t\t\tconsole.log(`🚀 [EventTracker] Inicializado v${VERSION}`);\r\n\r\n\t\t\t// Iniciar observers\r\n\t\t\tthis._clickObserver = new ClickObserver(this);\r\n\t\t\tthis._historyObserver = new HistoryObserver(this);\r\n\t\t\tthis._clickObserver.start();\r\n\t\t\tthis._historyObserver.start();\r\n\r\n\t\t\t// Enviar page_view inicial\r\n\t\t\tthis.trackPageView();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Identifica al usuario */\r\n\tidentify(userId: string, userName?: string): void {\r\n\t\ttry {\r\n\t\t\tthis._userIdentity = {\r\n\t\t\t\tuserId,\r\n\t\t\t\tuserName: userName ?? null,\r\n\t\t\t};\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Registra un evento */\r\n\ttrackEvent(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!this._eventBuffer) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Verificar si la URL actual debe ser excluida\r\n\t\t\tconst currentPath = getCurrentPath();\r\n\t\t\tif (this._shouldExclude(currentPath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst eventWithContext: TrackerEvent = {\r\n\t\t\t\t...event,\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t};\r\n\r\n\t\t\tthis._eventBuffer.add(eventWithContext);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page view */\r\n\ttrackPageView(): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst path = getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(path)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_view',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page leave */\r\n\ttrackPageLeave(duration: number, path?: string): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst pagePath = path ?? getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(pagePath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_leave',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tduration,\r\n\t\t\t\t\tpath: pagePath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Fuerza el envío de todos los eventos pendientes */\r\n\tasync flush(): Promise<void> {\r\n\t\ttry {\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tawait this._eventBuffer.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si el tracker está inicializado */\r\n\tisInitialized(): boolean {\r\n\t\treturn this._isInitialized;\r\n\t}\r\n\r\n\t/** Pausa el tracker (no registrará eventos) */\r\n\tpause(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = true;\r\n\t\t\tconsole.log('⏸️ [EventTracker] Tracker pausado - No se registrarán eventos');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Reanuda el tracker (volverá a registrar eventos) */\r\n\tresume(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = false;\r\n\t\t\tconsole.log('▶️ [EventTracker] Tracker reanudado - Eventos serán registrados');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Alterna el modo debug */\r\n\ttoggleDebug(): void {\r\n\t\ttry {\r\n\t\t\tif (!this._config) {\r\n\t\t\t\tconsole.log('⚠️ [EventTracker] Error: Tracker no inicializado');\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cambiar el valor de debug\r\n\t\t\tthis._config.debug = this._config.debug ? '' : 'true';\r\n\r\n\t\t\tif (this._config.debug) {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug ACTIVADO ✅');\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug DESACTIVADO ❌');\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Destruye el tracker */\r\n\tdestroy(): void {\r\n\t\ttry {\r\n\t\t\tif (this._clickObserver) {\r\n\t\t\t\tthis._clickObserver.stop();\r\n\t\t\t\tthis._clickObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._historyObserver) {\r\n\t\t\t\tthis._historyObserver.stop();\r\n\t\t\t\tthis._historyObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tthis._eventBuffer.destroy();\r\n\t\t\t\tthis._eventBuffer = null;\r\n\t\t\t}\r\n\t\t\tthis._isInitialized = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n}\r\n"],"mappings":"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","names":["tracker","tracker","Tracker","Tracker"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/EventBuffer.ts","../src/utils/uuid.ts","../src/utils/truncate.ts","../src/utils/metadata.ts","../src/observers/ClickObserver.ts","../src/observers/HistoryObserver.ts","../package.json","../src/version.ts","../src/core/Tracker.ts"],"sourcesContent":["/**\r\n * Event Tracker SDK\r\n *\r\n * SDK ligero y agnóstico al framework para tracking de comportamiento frontend.\r\n *\r\n * @example\r\n * import { tracker } from '@agroideas/event-tracker-sdk';\r\n *\r\n * tracker.init({\r\n * apiKey: 'tu-api-key',\r\n * endpoint: 'https://tu-api.com/events'\r\n * });\r\n *\r\n * // Identificar usuario\r\n * tracker.identify('user-123', 'Juan Pérez');\r\n */\r\n\r\nimport { Tracker } from './core/Tracker';\r\nimport type { TrackerConfig } from './types';\r\n\r\n/** Instancia única exportada del Tracker */\r\nexport const tracker = Tracker.getInstance();\r\n\r\n/**\r\n * Inicializa el tracker con configuración requerida (apiKey y endpoint).\r\n */\r\nexport function init(config: TrackerConfig): void {\r\n\ttracker.init(config);\r\n}\r\n\r\n/** Identifica al usuario */\r\nexport function identify(userId: string, userName?: string): void {\r\n\ttracker.identify(userId, userName);\r\n}\r\n\r\n/** Fuerza el envío de eventos pendientes */\r\nexport function flush(): Promise<void> {\r\n\treturn tracker.flush();\r\n}\r\n\r\n/** Destruye el tracker */\r\nexport function destroy(): void {\r\n\ttracker.destroy();\r\n}\r\n\r\nexport type { TrackerConfig };\r\n","/**\r\n * EventBuffer: Maneja la acumulación, batching y envío de eventos\r\n */\r\n\r\nimport type { TrackerEvent, SendResult, TrackerConfig, TrackingResponse } from '../types';\r\n\r\nconst BATCH_INTERVAL = 10000;\r\nconst MAX_BUFFER_SIZE = 15;\r\n\r\nexport class EventBuffer {\r\n\tprivate _buffer: TrackerEvent[];\r\n\tprivate _config: Required<Omit<TrackerConfig, 'apiKey' | 'endpoint' | 'userId' | 'userName' | 'debug'>>;\r\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _endpoint: string;\r\n\tprivate _apiKey: string;\r\n\tprivate _isDebug: boolean;\r\n\r\n\tconstructor(config: TrackerConfig) {\r\n\t\tthis._buffer = [];\r\n\r\n\t\t// apiKey y endpoint son requeridos\r\n\t\tthis._endpoint = config.endpoint;\r\n\t\tthis._apiKey = config.apiKey;\r\n\r\n\t\t// DEBUG es opcional, default false\r\n\t\tthis._isDebug = config.debug == \"true\";\r\n\t\tconsole.log(config);\r\n\r\n\t\tthis._config = {\r\n\t\t\tbatchInterval: config.batchInterval ?? BATCH_INTERVAL,\r\n\t\t\tmaxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE,\r\n\t\t\texcludePatterns: config.excludePatterns ?? [],\r\n\t\t};\r\n\r\n\t\tif (this._isDebug) {\r\n\t\t\tconsole.log('[EventTracker] Modo DEBUG activado');\r\n\t\t\tconsole.log('[EventTracker] Endpoint:', this._endpoint);\r\n\t\t\tconsole.log('[EventTracker] API Key:', this._apiKey);\r\n\t\t}\r\n\r\n\t\t// En modo debug, no iniciar timer de flush\r\n\t\tif (!this._isDebug) {\r\n\t\t\tthis._startFlushTimer();\r\n\t\t}\r\n\r\n\t\tthis._setupVisibilityListener();\r\n\t}\r\n\r\n\t/** Agrega un evento al buffer */\r\n\tadd(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\tthis._buffer.push(event);\r\n\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] Evento agregado:', event.eventType);\r\n\t\t\t\tconsole.log('[EventTracker] Buffer size:', this._buffer.length);\r\n\t\t\t\tconsole.log('[EventTracker] Event content:', JSON.stringify(event, null, 2));\r\n\t\t\t}\r\n\r\n\t\t\t// En modo debug, no hacer flush automático\r\n\t\t\tif (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Silently ignore errors to never block the main app\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía todos los eventos acumulados al servidor */\r\n\tasync flush(): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tif (this._buffer.length === 0) {\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst eventsToSend = [...this._buffer];\r\n\t\t\tthis._buffer = [];\r\n\r\n\t\t\t// Modo debug: solo console.log, no fetch\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] DEBUG - Events:', JSON.stringify(eventsToSend, null, 2));\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst result = await this._sendEventsAsync(eventsToSend);\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\t/** Obtiene la cantidad de eventos en el buffer */\r\n\tgetSize(): number {\r\n\t\treturn this._buffer.length;\r\n\t}\r\n\r\n\t/** Limpia el buffer sin enviar */\r\n\tclear(): void {\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Destruye el buffer y limpia recursos */\r\n\tdestroy(): void {\r\n\t\tif (this._flushTimer) {\r\n\t\t\tclearInterval(this._flushTimer);\r\n\t\t\tthis._flushTimer = null;\r\n\t\t}\r\n\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Inicia el temporizador de flush periódico */\r\n\tprivate _startFlushTimer(): void {\r\n\t\tthis._flushTimer = setInterval(() => {\r\n\t\t\tif (this._buffer.length > 0) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t}, this._config.batchInterval);\r\n\t}\r\n\r\n\t/** Configura el listener de visibilitychange para envío de emergencia */\r\n\tprivate _setupVisibilityListener(): void {\r\n\t\tif (typeof document === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst handleVisibilityChange = (): void => {\r\n\t\t\t// En modo debug, no enviar eventos\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (document.visibilityState === 'hidden' && this._buffer.length > 0) {\r\n\t\t\t\t// Usar setTimeout para asegurar que el fetch se complete antes del unload\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tthis.flush();\r\n\t\t\t\t}, 0);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tdocument.addEventListener('visibilitychange', handleVisibilityChange);\r\n\t}\r\n\r\n\t/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */\r\n\tprivate async _sendEventsAsync(events: TrackerEvent[]): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tconst payload = JSON.stringify({\r\n\t\t\t\tevents: events,\r\n\t\t\t});\r\n\r\n\t\t\tconst response = await fetch(this._endpoint, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'X-API-Key': this._apiKey,\r\n\t\t\t\t},\r\n\t\t\t\tbody: payload,\r\n\t\t\t\tkeepalive: true,\r\n\t\t\t});\r\n\r\n\t\t\t// Intentar parsear el mensaje de respuesta del backend\r\n\t\t\tlet message: string | undefined;\r\n\t\t\ttry {\r\n\t\t\t\tconst responseData: TrackingResponse = await response.json();\r\n\t\t\t\tmessage = responseData.message;\r\n\t\t\t} catch {\r\n\t\t\t\t// Si no se puede parsear, usar el status text\r\n\t\t\t\tmessage = response.statusText;\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: response.ok,\r\n\t\t\t\tstatusCode: response.status,\r\n\t\t\t\tmessage,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Network error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Generador de UUID v4\r\n */\r\n\r\n/** Genera un UUID v4 aleatorio */\r\nexport function generateUUID(): string {\r\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\r\n\t\tconst random = Math.random() * 16 | 0;\r\n\t\tconst value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n\t\treturn value.toString(16);\r\n\t});\r\n}\r\n","/**\r\n * Utilidad para truncar texto\r\n */\r\n\r\n/**\r\n * Trunca un texto a la longitud máxima especificada\r\n * @param text - Texto a truncar\r\n * @param maxLength - Longitud máxima\r\n * @returns Texto truncado con '...' al final si excede la longitud\r\n */\r\nexport function truncate(text: string, maxLength: number): string {\r\n\tif (maxLength < 3) {\r\n\t\treturn '...';\r\n\t}\r\n\r\n\tif (text.length <= maxLength) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, maxLength - 3) + '...';\r\n}\r\n","/**\r\n * Utilidades para recolectar metadatos del navegador\r\n */\r\n\r\nimport { generateUUID } from './uuid';\r\n\r\n/** Obtiene o crea un sessionId en sessionStorage */\r\nexport function getOrCreateSessionId(): string {\r\n\ttry {\r\n\t\tconst storageKey = 'event_tracker_session_id';\r\n\t\tconst existingSessionId = sessionStorage.getItem(storageKey);\r\n\r\n\t\tif (existingSessionId) {\r\n\t\t\treturn existingSessionId;\r\n\t\t}\r\n\r\n\t\tconst newSessionId = generateUUID();\r\n\t\tsessionStorage.setItem(storageKey, newSessionId);\r\n\t\treturn newSessionId;\r\n\t} catch {\r\n\t\treturn generateUUID();\r\n\t}\r\n}\r\n\r\n/** Obtiene el tamaño de pantalla */\r\nexport function getScreenSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Obtiene el tamaño del viewport */\r\nexport function getViewportSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Detecta el tipo de dispositivo */\r\nexport function getDeviceType(): 'mobile' | 'desktop' | 'tablet' | 'unknown' {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\r\n\t\treturn 'tablet';\r\n\t}\r\n\r\n\tif (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\r\n\t\treturn 'mobile';\r\n\t}\r\n\r\n\treturn 'desktop';\r\n}\r\n\r\n/** Obtiene el navegador */\r\nexport function getBrowser(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Firefox')) {\r\n\t\treturn 'Firefox';\r\n\t}\r\n\r\n\tif (ua.includes('Edg/')) {\r\n\t\treturn 'Edge';\r\n\t}\r\n\r\n\tif (ua.includes('Chrome')) {\r\n\t\treturn 'Chrome';\r\n\t}\r\n\r\n\tif (ua.includes('Safari')) {\r\n\t\treturn 'Safari';\r\n\t}\r\n\r\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\r\n\t\treturn 'Internet Explorer';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el sistema operativo */\r\nexport function getOS(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Windows')) {\r\n\t\treturn 'Windows';\r\n\t}\r\n\r\n\tif (ua.includes('Mac OS')) {\r\n\t\treturn 'macOS';\r\n\t}\r\n\r\n\tif (ua.includes('Linux')) {\r\n\t\treturn 'Linux';\r\n\t}\r\n\r\n\tif (ua.includes('Android')) {\r\n\t\treturn 'Android';\r\n\t}\r\n\r\n\tif (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) {\r\n\t\treturn 'iOS';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el idioma del navegador */\r\nexport function getLanguage(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\treturn navigator.language || 'unknown';\r\n}\r\n\r\n/** Obtiene la URL actual */\r\nexport function getCurrentUrl(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.href;\r\n}\r\n\r\n/** Obtiene el referrer */\r\nexport function getReferrer(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn document.referrer;\r\n}\r\n\r\n/** Obtiene el timestamp actual en formato ISO */\r\nexport function getTimestamp(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\n/** Obtiene el path actual */\r\nexport function getCurrentPath(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.pathname;\r\n}\r\n","/**\r\n * ClickObserver: Captura clics globales en elementos interactivos\r\n */\r\n\r\nimport type { TrackerEvent } from '../types';\r\nimport { Tracker } from '../core/Tracker';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { truncate } from '../utils/truncate';\r\nimport {\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\n\r\n/** Tipos de elementos que deben ser rastreados */\r\nconst TRACKABLE_TAGS = ['BUTTON', 'A', 'INPUT', 'I', 'SPAN', 'DIV'];\r\n\r\n/** Tipos de input que deben ser rastreados */\r\nconst TRACKABLE_INPUT_TYPES = ['submit'];\r\n\r\n/** Tipos de input que deben ser excluidos (sensibles) */\r\nconst SENSITIVE_INPUT_TYPES = ['password', 'email', 'tel', 'number', 'search', 'url'];\r\n\r\n/** Selectores para elementos rastreables */\r\nconst TRACKABLE_SELECTORS = [\r\n\t'button',\r\n\t'a',\r\n\t'input[type=\"submit\"]',\r\n\t'[data-track]',\r\n\t'[style*=\"cursor: pointer\"]',\r\n\t'i[class*=\"mdi-\"]',\r\n];\r\n\r\n/** Verifica si un elemento es sensible (no capturar valores) */\r\nfunction _isSensitiveElement(element: Element): boolean {\r\n\tif (element.tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn SENSITIVE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n/** Verifica si un elemento es rastreable */\r\nfunction _isTrackableElement(element: Element): boolean {\r\n\tconst tagName = element.tagName.toUpperCase();\r\n\r\n\tif (!TRACKABLE_TAGS.includes(tagName)) {\r\n\t\treturn element.hasAttribute('data-track') ||\r\n\t\t\t_hasCursorPointer(element) ||\r\n\t\t\t_isMdiIcon(element);\r\n\t}\r\n\r\n\tif (tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn TRACKABLE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/** Verifica si el elemento tiene cursor: pointer */\r\nfunction _hasCursorPointer(element: Element): boolean {\r\n\ttry {\r\n\t\tconst style = window.getComputedStyle(element);\r\n\t\treturn style.cursor === 'pointer';\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/** Verifica si es un icono MDI */\r\nfunction _isMdiIcon(element: Element): boolean {\r\n\treturn element.tagName === 'I' && element.classList.contains('mdi');\r\n}\r\n\r\n/** Genera un selector CSS simple para el elemento */\r\nfunction _generateSelector(element: Element): string {\r\n\tconst parts: string[] = [];\r\n\tlet current: Element | null = element;\r\n\r\n\twhile (current && current !== document.documentElement) {\r\n\t\tlet selector = current.tagName.toLowerCase();\r\n\r\n\t\tif (current.id) {\r\n\t\t\tselector += `#${current.id}`;\r\n\t\t\tparts.unshift(selector);\r\n\t\t\tbreak;\r\n\t\t} else if (current.className && typeof current.className === 'string') {\r\n\t\t\tconst classes = current.className.trim().split(/\\s+/).filter(Boolean);\r\n\t\t\tif (classes.length > 0) {\r\n\t\t\t\tselector += `.${classes[0]}`;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tparts.unshift(selector);\r\n\t\tcurrent = current.parentElement;\r\n\t}\r\n\r\n\treturn parts.slice(0, 4).join(' > ');\r\n}\r\n\r\n/** Obtiene todas las clases de un elemento como string separado por espacios */\r\nfunction _getAllClasses(element: Element): string | undefined {\r\n\tif (!element.className || typeof element.className !== 'string') {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst classes = element.className.trim().split(/\\s+/).filter(Boolean);\r\n\treturn classes.length > 0 ? classes.join(' ') : undefined;\r\n}\r\n\r\nexport class ClickObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _boundHandleClick: (event: MouseEvent) => void;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._boundHandleClick = this._handleClick.bind(this);\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.addEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = true;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.removeEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de eventos de clic */\r\n\tprivate _handleClick(event: MouseEvent): void {\r\n\t\ttry {\r\n\t\t\tconst target = event.target as Element;\r\n\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 1: Si el elemento clickeado es rastreable, usarlo directamente\r\n\t\t\tif (_isTrackableElement(target) && !_isSensitiveElement(target)) {\r\n\t\t\t\tthis._createClickEvent(target);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 2: Buscar el ancestro rastreable más cercano\r\n\t\t\tconst trackableElement = target.closest(TRACKABLE_SELECTORS.join(','));\r\n\r\n\t\t\tif (!trackableElement) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isSensitiveElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isTrackableElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._createClickEvent(trackableElement);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de clic */\r\n\tprivate _createClickEvent(element: Element): void {\r\n\t\tconst event: TrackerEvent = {\r\n\t\t\teventType: 'user_click',\r\n\t\t\teventId: generateUUID(),\r\n\t\t\tcontext: {\r\n\t\t\t\tsessionId: '',\r\n\t\t\t\tuserId: null,\r\n\t\t\t\tuserName: null,\r\n\t\t\t\turl: window.location.href,\r\n\t\t\t\treferrer: getReferrer(),\r\n\t\t\t\tscreenSize: getScreenSize(),\r\n\t\t\t\tviewportSize: getViewportSize(),\r\n\t\t\t\tdeviceType: getDeviceType(),\r\n\t\t\t\tbrowser: getBrowser(),\r\n\t\t\t\tos: getOS(),\r\n\t\t\t\tlanguage: getLanguage(),\r\n\t\t\t\ttimestamp: getTimestamp(),\r\n\t\t\t},\r\n\t\t\tdata: {\r\n\t\t\t\ttagName: element.tagName,\r\n\t\t\t\tid: element.id || undefined,\r\n\t\t\t\tclassName: _getAllClasses(element),\r\n\t\t\t\ttitle: element.getAttribute('title') || undefined,\r\n\t\t\t\ttext: element.textContent ? truncate(element.textContent, 30) : undefined,\r\n\t\t\t\tselector: _generateSelector(element),\r\n\t\t\t},\r\n\t\t};\r\n\r\n\t\tthis._tracker.trackEvent(event);\r\n\t}\r\n}\r\n","/**\r\n * HistoryObserver: Monitorea cambios de navegación usando múltiples estrategias\r\n * - popstate: para botones atrás/adelante del navegador\r\n * - history intercept: para pushState/replaceState en SPA\r\n * - polling: fallback para detectar cambios\r\n * - sessionStorage: para manejar F5\r\n */\r\n\r\nimport type { Tracker } from '../core/Tracker';\r\n\r\nconst POLL_INTERVAL = 1000; // Verificar URL cada 1 segundo (fallback)\r\nconst HEARTBEAT_INTERVAL = 10000; // Enviar heartbeat cada 10 segundos\r\nconst MIN_DURATION = 0.5; // Ignorar page_leave con menos de 0.5 segundos\r\nconst STORAGE_KEY_URL = 'event_tracker_last_url';\r\nconst STORAGE_KEY_TIME = 'event_tracker_last_time';\r\n\r\nexport class HistoryObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _lastUrl: string = '';\r\n\tprivate _pageStartTime: number;\r\n\tprivate _originalPushState: typeof history.pushState = () => null;\r\n\tprivate _originalReplaceState: typeof history.replaceState = () => null;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._pageStartTime = performance.now();\r\n\t\tthis._lastUrl = typeof window !== 'undefined' ? window.location.href : '';\r\n\r\n\t\t// Guardar referencias originales de history\r\n\t\tif (typeof window !== 'undefined' && typeof history !== 'undefined') {\r\n\t\t\tthis._originalPushState = history.pushState.bind(history);\r\n\t\t\tthis._originalReplaceState = history.replaceState.bind(history);\r\n\t\t}\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// Configurar interceptación de history\r\n\t\t\tthis._interceptHistory();\r\n\r\n\t\t\t// Agregar listener de popstate (botones atrás/adelante)\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('popstate', this._handlePopState);\r\n\t\t\t}\r\n\r\n\t\t\t// Iniciar intervalo de polling (fallback)\r\n\t\t\tthis._intervalId = setInterval(() => this._checkUrlChange(), POLL_INTERVAL);\r\n\r\n\t\t\t// Iniciar intervalo de heartbeat\r\n\t\t\tthis._heartbeatIntervalId = setInterval(() => this._sendHeartbeat(), HEARTBEAT_INTERVAL);\r\n\r\n\t\t\t// Eventos para cierre de página\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.addEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = true;\r\n\r\n\t\t\t// Verificar si hay una página previa guardada (para F5)\r\n\t\t\t// Solo después de que el tracker esté inicializado\r\n\t\t\tsetTimeout(() => this._checkPreviousPage(), 0);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tif (this._intervalId) {\r\n\t\t\t\tclearInterval(this._intervalId);\r\n\t\t\t\tthis._intervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (this._heartbeatIntervalId) {\r\n\t\t\t\tclearInterval(this._heartbeatIntervalId);\r\n\t\t\t\tthis._heartbeatIntervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.removeEventListener('popstate', this._handlePopState);\r\n\t\t\t\twindow.removeEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.removeEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\t// Restaurar history original\r\n\t\t\tif (typeof history !== 'undefined') {\r\n\t\t\t\thistory.pushState = this._originalPushState;\r\n\t\t\t\thistory.replaceState = this._originalReplaceState;\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Intercepta history.pushState y history.replaceState */\r\n\tprivate _interceptHistory(): void {\r\n\t\tif (typeof window === 'undefined' || typeof history === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst self = this;\r\n\t\tconst originalPushState = history.pushState.bind(history) as (...args: unknown[]) => unknown;\r\n\t\tconst originalReplaceState = history.replaceState.bind(history) as (...args: unknown[]) => unknown;\r\n\r\n\t\t// Interceptar pushState\r\n\t\thistory.pushState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalPushState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.pushState;\r\n\r\n\t\t// Interceptar replaceState\r\n\t\thistory.replaceState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalReplaceState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.replaceState;\r\n\t}\r\n\r\n\t/** Manejador de popstate (botones atrás/adelante) */\r\n\tprivate _handlePopState = (): void => {\r\n\t\ttry {\r\n\t\t\tthis._handleNavigationChange();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejo centralizado de cambio de navegación */\r\n\tprivate _handleNavigationChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.href;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Calcular duración wall-clock de la página anterior\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t\t// Solo enviar page_leave si la duración es significativa\r\n\t\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\t\t// Enviar page_leave INMEDIATAMENTE para la página anterior\r\n\t\t\t\t\tthis._sendPageLeaveImmediate(this._lastUrl, duration);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Track page view de la nueva página\r\n\t\t\t\tthis._tracker.trackPageView();\r\n\r\n\t\t\t\t// Resetear contador para la nueva página\r\n\t\t\t\tthis._pageStartTime = performance.now();\r\n\r\n\t\t\t\t// Actualizar la última URL\r\n\t\t\t\tthis._lastUrl = currentUrl;\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía page_leave inmediatamente sin esperar el buffer */\r\n\tprivate _sendPageLeaveImmediate(path: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\t// Crear el evento directamente\r\n\t\t\tthis._tracker.trackPageLeave(duration, path);\r\n\r\n\t\t\t// También guardar en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(path, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de beforeunload (cuando el usuario sale de la página) */\r\n\tprivate _handleBeforeUnload = (): void => {\r\n\t\ttry {\r\n\t\t\t// Calcular duración wall-clock\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Solo enviar si la duración es significativa\r\n\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\tthis._tracker.trackPageLeave(duration, this._lastUrl);\r\n\t\t\t}\r\n\r\n\t\t\t// Guardar URL actual en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejador de visibilitychange (para detectar cuando la app pasa a segundo plano) */\r\n\tprivate _handleVisibilityChange = (): void => {\r\n\t\ttry {\r\n\t\t\tif (typeof document === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cuando la página se oculta, guardar el tiempo actual en sessionStorage\r\n\t\t\tif (document.visibilityState === 'hidden') {\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\t\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Verifica si la URL cambió (fallback de polling) */\r\n\tprivate _checkUrlChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.href;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Usar el manejo centralizado\r\n\t\t\t\tthis._handleNavigationChange();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Calcula la duración wall-clock (tiempo total desde que entró a la página) */\r\n\tprivate _calculateWallClockDuration(): number {\r\n\t\treturn (performance.now() - this._pageStartTime) / 1000;\r\n\t}\r\n\r\n\t/** Envía un heartbeat con la duración actual */\r\n\tprivate _sendHeartbeat(): void {\r\n\t\ttry {\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Usar trackPageLeave para mantener compatibilidad\r\n\t\t\t// El backend puede filtrar por eventType = 'page_heartbeat'\r\n\t\t\tthis._tracker.trackEvent({\r\n\t\t\t\teventType: 'page_heartbeat',\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath: this._lastUrl,\r\n\t\t\t\t\tduration: duration,\r\n\t\t\t\t\ttimestamp: new Date().toISOString(),\r\n\t\t\t\t},\r\n\t\t\t} as import('../types/index').TrackerEvent);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Guarda la URL y tiempo en sessionStorage (para F5) */\r\n\tprivate _saveToStorage(url: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_URL, url);\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_TIME, String(duration));\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Lee la URL y tiempo previos de sessionStorage (para F5) */\r\n\tprivate _getFromStorage(): { url: string; duration: number; } | null {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tconst url = sessionStorage.getItem(STORAGE_KEY_URL);\r\n\t\t\t\tconst duration = sessionStorage.getItem(STORAGE_KEY_TIME);\r\n\t\t\t\tif (url && duration) {\r\n\t\t\t\t\treturn { url, duration: parseFloat(duration) };\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/** Limpia sessionStorage */\r\n\tprivate _clearStorage(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_URL);\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_TIME);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si hay una página previa (para F5) */\r\n\tprivate _checkPreviousPage(): void {\r\n\t\tconst previous = this._getFromStorage();\r\n\t\tif (previous && previous.duration >= MIN_DURATION) {\r\n\t\t\t// La página anterior tenía una duración significativa, enviar page_leave\r\n\t\t\tthis._tracker.trackPageLeave(previous.duration, previous.url);\r\n\t\t}\r\n\t\t// Limpiar storage después de procesar\r\n\t\tthis._clearStorage();\r\n\t}\r\n}\r\n","{\r\n\t\"name\": \"@agroideas/event-tracker-sdk\",\r\n\t\"version\": \"1.0.8\",\r\n\t\"description\": \"A lightweight, framework-agnostic SDK for tracking frontend user behavior\",\r\n\t\"license\": \"MIT\",\r\n\t\"author\": \"Agroideas\",\r\n\t\"homepage\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"main\": \"./dist/index.cjs\",\r\n\t\"module\": \"./dist/index.mjs\",\r\n\t\"types\": \"./dist/index.d.ts\",\r\n\t\"exports\": {\r\n\t\t\".\": {\r\n\t\t\t\"types\": \"./dist/index.d.ts\",\r\n\t\t\t\"import\": \"./dist/index.mjs\",\r\n\t\t\t\"require\": \"./dist/index.cjs\"\r\n\t\t},\r\n\t\t\"./iife\": \"./dist/index.iife.js\"\r\n\t},\r\n\t\"files\": [\r\n\t\t\"dist\"\r\n\t],\r\n\t\"scripts\": {\r\n\t\t\"build\": \"tsup\",\r\n\t\t\"dev\": \"tsup --watch\",\r\n\t\t\"lint\": \"eslint src --ext .ts\",\r\n\t\t\"lint:fix\": \"eslint src --ext .ts --fix\",\r\n\t\t\"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\r\n\t\t\"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\r\n\t\t\"typecheck\": \"tsc --noEmit\",\r\n\t\t\"prepare\": \"husky install\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"@types/node\": \"^20.10.0\",\r\n\t\t\"eslint\": \"^8.55.0\",\r\n\t\t\"husky\": \"^8.0.3\",\r\n\t\t\"prettier\": \"^3.1.0\",\r\n\t\t\"tsup\": \"^8.0.1\",\r\n\t\t\"typescript\": \"^5.3.2\"\r\n\t},\r\n\t\"keywords\": [\r\n\t\t\"tracking\",\r\n\t\t\"analytics\",\r\n\t\t\"sdk\",\r\n\t\t\"events\",\r\n\t\t\"behavior\",\r\n\t\t\"frontend\"\r\n\t],\r\n\t\"engines\": {\r\n\t\t\"node\": \">=18.0.0\"\r\n\t}\r\n}","/**\n * Version constants for the Event Tracker SDK\n */\n\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION = pkg.version;\n","/**\r\n * Tracker: Singleton principal que coordina observers y EventBuffer\r\n */\r\n\r\nimport type { TrackerConfig, TrackerEvent, UserIdentity, EventContext } from '../types';\r\nimport { EventBuffer } from './EventBuffer';\r\nimport { ClickObserver } from '../observers/ClickObserver';\r\nimport { HistoryObserver } from '../observers/HistoryObserver';\r\nimport {\r\n\tgetCurrentPath,\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetOrCreateSessionId,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { VERSION } from '../version';\r\n\r\nexport class Tracker {\r\n\tprivate static _instance: Tracker | null = null;\r\n\tprivate _eventBuffer: EventBuffer | null = null;\r\n\tprivate _clickObserver: ClickObserver | null = null;\r\n\tprivate _historyObserver: HistoryObserver | null = null;\r\n\tprivate _userIdentity: UserIdentity;\r\n\tprivate _sessionId: string;\r\n\tprivate _isInitialized: boolean = false;\r\n\tprivate _isPaused: boolean = false;\r\n\tprivate _config: TrackerConfig | null = null;\r\n\r\n\tprivate constructor() {\r\n\t\tthis._userIdentity = {\r\n\t\t\tuserId: null,\r\n\t\t\tuserName: null,\r\n\t\t};\r\n\t\tthis._sessionId = getOrCreateSessionId();\r\n\t}\r\n\r\n\t/** Obtiene la instancia única del Tracker */\r\n\tstatic getInstance(): Tracker {\r\n\t\tif (Tracker._instance === null) {\r\n\t\t\tTracker._instance = new Tracker();\r\n\t\t}\r\n\t\treturn Tracker._instance;\r\n\t}\r\n\r\n\t/** Construye el contexto completo del evento */\r\n\tprivate _buildContext(): EventContext {\r\n\t\treturn {\r\n\t\t\tsessionId: this._sessionId,\r\n\t\t\tuserId: this._userIdentity.userId,\r\n\t\t\tuserName: this._userIdentity.userName,\r\n\t\t\turl: window.location.href,\r\n\t\t\treferrer: getReferrer(),\r\n\t\t\tscreenSize: getScreenSize(),\r\n\t\t\tviewportSize: getViewportSize(),\r\n\t\t\tdeviceType: getDeviceType(),\r\n\t\t\tbrowser: getBrowser(),\r\n\t\t\tos: getOS(),\r\n\t\t\tlanguage: getLanguage(),\r\n\t\t\ttimestamp: getTimestamp(),\r\n\t\t};\r\n\t}\r\n\r\n\t/** Verifica si una ruta debe ser excluida del tracking */\r\n\tprivate _shouldExclude(path: string): boolean {\r\n\t\tif (!this._config) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst { excludePatterns } = this._config;\r\n\r\n\t\t// Normalizar el path (quitar trailing slash para comparación consistente)\r\n\t\tconst normalizedPath = path.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;\r\n\r\n\t\t// Si hay patrones de exclusión, verificarlos\r\n\t\tif (excludePatterns && excludePatterns.length > 0) {\r\n\t\t\treturn excludePatterns.some((pattern) => {\r\n\t\t\t\tif (typeof pattern === 'string') {\r\n\t\t\t\t\t// Normalizar el patrón también\r\n\t\t\t\t\tconst normalizedPattern = pattern.endsWith('/') && pattern.length > 1 ? pattern.slice(0, -1) : pattern;\r\n\t\t\t\t\t// Coincidencia exacta o prefijo\r\n\t\t\t\t\treturn normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern);\r\n\t\t\t\t}\r\n\t\t\t\t// Es RegExp\r\n\t\t\t\treturn pattern.test(normalizedPath) || pattern.test(path);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/** Inicializa el tracker con la configuración */\r\n\tinit(config: TrackerConfig): void {\r\n\t\ttry {\r\n\t\t\t// Guardar configuración\r\n\t\t\tthis._config = config;\r\n\r\n\t\t\t// Actualizamos el usuario cada vez que se llama init (permite actualizar userId/userName)\r\n\t\t\tif (config.userId) {\r\n\t\t\t\tthis._userIdentity = {\r\n\t\t\t\t\tuserId: config.userId,\r\n\t\t\t\t\tuserName: config.userName ?? null,\r\n\t\t\t\t};\r\n\t\t\t\tif (this._isInitialized && config.debug) {\r\n\t\t\t\t\tconsole.log('[EventTracker] Usuario actualizado:', this._userIdentity);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Solo inicializar EventBuffer y observers la primera vez\r\n\t\t\tif (this._isInitialized) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._eventBuffer = new EventBuffer(config);\r\n\t\t\tthis._isInitialized = true;\r\n\r\n\t\t\t// Imprimir versión del tracker\r\n\t\t\tconsole.log(`🚀 [EventTracker] Inicializado v${VERSION}`);\r\n\r\n\t\t\t// Iniciar observers\r\n\t\t\tthis._clickObserver = new ClickObserver(this);\r\n\t\t\tthis._historyObserver = new HistoryObserver(this);\r\n\t\t\tthis._clickObserver.start();\r\n\t\t\tthis._historyObserver.start();\r\n\r\n\t\t\t// Enviar page_view inicial\r\n\t\t\tthis.trackPageView();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Identifica al usuario */\r\n\tidentify(userId: string, userName?: string): void {\r\n\t\ttry {\r\n\t\t\tthis._userIdentity = {\r\n\t\t\t\tuserId,\r\n\t\t\t\tuserName: userName ?? null,\r\n\t\t\t};\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Registra un evento */\r\n\ttrackEvent(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!this._eventBuffer) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Verificar si la URL actual debe ser excluida\r\n\t\t\tconst currentPath = getCurrentPath();\r\n\t\t\tif (this._shouldExclude(currentPath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst eventWithContext: TrackerEvent = {\r\n\t\t\t\t...event,\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t};\r\n\r\n\t\t\tthis._eventBuffer.add(eventWithContext);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page view */\r\n\ttrackPageView(): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst path = getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(path)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_view',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page leave */\r\n\ttrackPageLeave(duration: number, path?: string): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst pagePath = path ?? getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(pagePath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_leave',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tduration,\r\n\t\t\t\t\tpath: pagePath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Fuerza el envío de todos los eventos pendientes */\r\n\tasync flush(): Promise<void> {\r\n\t\ttry {\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tawait this._eventBuffer.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si el tracker está inicializado */\r\n\tisInitialized(): boolean {\r\n\t\treturn this._isInitialized;\r\n\t}\r\n\r\n\t/** Pausa el tracker (no registrará eventos) */\r\n\tpause(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = true;\r\n\t\t\tconsole.log('⏸️ [EventTracker] Tracker pausado - No se registrarán eventos');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Reanuda el tracker (volverá a registrar eventos) */\r\n\tresume(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = false;\r\n\t\t\tconsole.log('▶️ [EventTracker] Tracker reanudado - Eventos serán registrados');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Alterna el modo debug */\r\n\ttoggleDebug(): void {\r\n\t\ttry {\r\n\t\t\tif (!this._config) {\r\n\t\t\t\tconsole.log('⚠️ [EventTracker] Error: Tracker no inicializado');\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cambiar el valor de debug\r\n\t\t\tthis._config.debug = this._config.debug ? '' : 'true';\r\n\r\n\t\t\tif (this._config.debug) {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug ACTIVADO ✅');\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug DESACTIVADO ❌');\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Destruye el tracker */\r\n\tdestroy(): void {\r\n\t\ttry {\r\n\t\t\tif (this._clickObserver) {\r\n\t\t\t\tthis._clickObserver.stop();\r\n\t\t\t\tthis._clickObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._historyObserver) {\r\n\t\t\t\tthis._historyObserver.stop();\r\n\t\t\t\tthis._historyObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tthis._eventBuffer.destroy();\r\n\t\t\t\tthis._eventBuffer = null;\r\n\t\t\t}\r\n\t\t\tthis._isInitialized = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n}\r\n"],"mappings":"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","names":["tracker","tracker","Tracker","Tracker"]}
package/dist/index.mjs CHANGED
@@ -465,7 +465,7 @@ var HistoryObserver = class {
465
465
  };
466
466
  this._tracker = tracker2;
467
467
  this._pageStartTime = performance.now();
468
- this._lastUrl = typeof window !== "undefined" ? window.location.pathname : "";
468
+ this._lastUrl = typeof window !== "undefined" ? window.location.href : "";
469
469
  if (typeof window !== "undefined" && typeof history !== "undefined") {
470
470
  this._originalPushState = history.pushState.bind(history);
471
471
  this._originalReplaceState = history.replaceState.bind(history);
@@ -544,7 +544,7 @@ var HistoryObserver = class {
544
544
  if (typeof window === "undefined") {
545
545
  return;
546
546
  }
547
- const currentUrl = window.location.pathname;
547
+ const currentUrl = window.location.href;
548
548
  if (currentUrl !== this._lastUrl) {
549
549
  const duration = this._calculateWallClockDuration();
550
550
  if (duration >= MIN_DURATION) {
@@ -571,7 +571,7 @@ var HistoryObserver = class {
571
571
  if (typeof window === "undefined") {
572
572
  return;
573
573
  }
574
- const currentUrl = window.location.pathname;
574
+ const currentUrl = window.location.href;
575
575
  if (currentUrl !== this._lastUrl) {
576
576
  this._handleNavigationChange();
577
577
  }
@@ -644,7 +644,7 @@ var HistoryObserver = class {
644
644
  // package.json
645
645
  var package_default = {
646
646
  name: "@agroideas/event-tracker-sdk",
647
- version: "1.0.7",
647
+ version: "1.0.8",
648
648
  description: "A lightweight, framework-agnostic SDK for tracking frontend user behavior",
649
649
  license: "MIT",
650
650
  author: "Agroideas",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/EventBuffer.ts","../src/utils/uuid.ts","../src/utils/truncate.ts","../src/utils/metadata.ts","../src/observers/ClickObserver.ts","../src/observers/HistoryObserver.ts","../package.json","../src/version.ts","../src/core/Tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * EventBuffer: Maneja la acumulación, batching y envío de eventos\r\n */\r\n\r\nimport type { TrackerEvent, SendResult, TrackerConfig, TrackingResponse } from '../types';\r\n\r\nconst BATCH_INTERVAL = 10000;\r\nconst MAX_BUFFER_SIZE = 15;\r\n\r\nexport class EventBuffer {\r\n\tprivate _buffer: TrackerEvent[];\r\n\tprivate _config: Required<Omit<TrackerConfig, 'apiKey' | 'endpoint' | 'userId' | 'userName' | 'debug'>>;\r\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _endpoint: string;\r\n\tprivate _apiKey: string;\r\n\tprivate _isDebug: boolean;\r\n\r\n\tconstructor(config: TrackerConfig) {\r\n\t\tthis._buffer = [];\r\n\r\n\t\t// apiKey y endpoint son requeridos\r\n\t\tthis._endpoint = config.endpoint;\r\n\t\tthis._apiKey = config.apiKey;\r\n\r\n\t\t// DEBUG es opcional, default false\r\n\t\tthis._isDebug = config.debug == \"true\";\r\n\t\tconsole.log(config);\r\n\r\n\t\tthis._config = {\r\n\t\t\tbatchInterval: config.batchInterval ?? BATCH_INTERVAL,\r\n\t\t\tmaxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE,\r\n\t\t\texcludePatterns: config.excludePatterns ?? [],\r\n\t\t};\r\n\r\n\t\tif (this._isDebug) {\r\n\t\t\tconsole.log('[EventTracker] Modo DEBUG activado');\r\n\t\t\tconsole.log('[EventTracker] Endpoint:', this._endpoint);\r\n\t\t\tconsole.log('[EventTracker] API Key:', this._apiKey);\r\n\t\t}\r\n\r\n\t\t// En modo debug, no iniciar timer de flush\r\n\t\tif (!this._isDebug) {\r\n\t\t\tthis._startFlushTimer();\r\n\t\t}\r\n\r\n\t\tthis._setupVisibilityListener();\r\n\t}\r\n\r\n\t/** Agrega un evento al buffer */\r\n\tadd(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\tthis._buffer.push(event);\r\n\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] Evento agregado:', event.eventType);\r\n\t\t\t\tconsole.log('[EventTracker] Buffer size:', this._buffer.length);\r\n\t\t\t\tconsole.log('[EventTracker] Event content:', JSON.stringify(event, null, 2));\r\n\t\t\t}\r\n\r\n\t\t\t// En modo debug, no hacer flush automático\r\n\t\t\tif (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Silently ignore errors to never block the main app\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía todos los eventos acumulados al servidor */\r\n\tasync flush(): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tif (this._buffer.length === 0) {\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst eventsToSend = [...this._buffer];\r\n\t\t\tthis._buffer = [];\r\n\r\n\t\t\t// Modo debug: solo console.log, no fetch\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] DEBUG - Events:', JSON.stringify(eventsToSend, null, 2));\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst result = await this._sendEventsAsync(eventsToSend);\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\t/** Obtiene la cantidad de eventos en el buffer */\r\n\tgetSize(): number {\r\n\t\treturn this._buffer.length;\r\n\t}\r\n\r\n\t/** Limpia el buffer sin enviar */\r\n\tclear(): void {\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Destruye el buffer y limpia recursos */\r\n\tdestroy(): void {\r\n\t\tif (this._flushTimer) {\r\n\t\t\tclearInterval(this._flushTimer);\r\n\t\t\tthis._flushTimer = null;\r\n\t\t}\r\n\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Inicia el temporizador de flush periódico */\r\n\tprivate _startFlushTimer(): void {\r\n\t\tthis._flushTimer = setInterval(() => {\r\n\t\t\tif (this._buffer.length > 0) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t}, this._config.batchInterval);\r\n\t}\r\n\r\n\t/** Configura el listener de visibilitychange para envío de emergencia */\r\n\tprivate _setupVisibilityListener(): void {\r\n\t\tif (typeof document === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst handleVisibilityChange = (): void => {\r\n\t\t\t// En modo debug, no enviar eventos\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (document.visibilityState === 'hidden' && this._buffer.length > 0) {\r\n\t\t\t\t// Usar setTimeout para asegurar que el fetch se complete antes del unload\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tthis.flush();\r\n\t\t\t\t}, 0);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tdocument.addEventListener('visibilitychange', handleVisibilityChange);\r\n\t}\r\n\r\n\t/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */\r\n\tprivate async _sendEventsAsync(events: TrackerEvent[]): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tconst payload = JSON.stringify({\r\n\t\t\t\tevents: events,\r\n\t\t\t});\r\n\r\n\t\t\tconst response = await fetch(this._endpoint, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'X-API-Key': this._apiKey,\r\n\t\t\t\t},\r\n\t\t\t\tbody: payload,\r\n\t\t\t\tkeepalive: true,\r\n\t\t\t});\r\n\r\n\t\t\t// Intentar parsear el mensaje de respuesta del backend\r\n\t\t\tlet message: string | undefined;\r\n\t\t\ttry {\r\n\t\t\t\tconst responseData: TrackingResponse = await response.json();\r\n\t\t\t\tmessage = responseData.message;\r\n\t\t\t} catch {\r\n\t\t\t\t// Si no se puede parsear, usar el status text\r\n\t\t\t\tmessage = response.statusText;\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: response.ok,\r\n\t\t\t\tstatusCode: response.status,\r\n\t\t\t\tmessage,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Network error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Generador de UUID v4\r\n */\r\n\r\n/** Genera un UUID v4 aleatorio */\r\nexport function generateUUID(): string {\r\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\r\n\t\tconst random = Math.random() * 16 | 0;\r\n\t\tconst value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n\t\treturn value.toString(16);\r\n\t});\r\n}\r\n","/**\r\n * Utilidad para truncar texto\r\n */\r\n\r\n/**\r\n * Trunca un texto a la longitud máxima especificada\r\n * @param text - Texto a truncar\r\n * @param maxLength - Longitud máxima\r\n * @returns Texto truncado con '...' al final si excede la longitud\r\n */\r\nexport function truncate(text: string, maxLength: number): string {\r\n\tif (maxLength < 3) {\r\n\t\treturn '...';\r\n\t}\r\n\r\n\tif (text.length <= maxLength) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, maxLength - 3) + '...';\r\n}\r\n","/**\r\n * Utilidades para recolectar metadatos del navegador\r\n */\r\n\r\nimport { generateUUID } from './uuid';\r\n\r\n/** Obtiene o crea un sessionId en sessionStorage */\r\nexport function getOrCreateSessionId(): string {\r\n\ttry {\r\n\t\tconst storageKey = 'event_tracker_session_id';\r\n\t\tconst existingSessionId = sessionStorage.getItem(storageKey);\r\n\r\n\t\tif (existingSessionId) {\r\n\t\t\treturn existingSessionId;\r\n\t\t}\r\n\r\n\t\tconst newSessionId = generateUUID();\r\n\t\tsessionStorage.setItem(storageKey, newSessionId);\r\n\t\treturn newSessionId;\r\n\t} catch {\r\n\t\treturn generateUUID();\r\n\t}\r\n}\r\n\r\n/** Obtiene el tamaño de pantalla */\r\nexport function getScreenSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Obtiene el tamaño del viewport */\r\nexport function getViewportSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Detecta el tipo de dispositivo */\r\nexport function getDeviceType(): 'mobile' | 'desktop' | 'tablet' | 'unknown' {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\r\n\t\treturn 'tablet';\r\n\t}\r\n\r\n\tif (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\r\n\t\treturn 'mobile';\r\n\t}\r\n\r\n\treturn 'desktop';\r\n}\r\n\r\n/** Obtiene el navegador */\r\nexport function getBrowser(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Firefox')) {\r\n\t\treturn 'Firefox';\r\n\t}\r\n\r\n\tif (ua.includes('Edg/')) {\r\n\t\treturn 'Edge';\r\n\t}\r\n\r\n\tif (ua.includes('Chrome')) {\r\n\t\treturn 'Chrome';\r\n\t}\r\n\r\n\tif (ua.includes('Safari')) {\r\n\t\treturn 'Safari';\r\n\t}\r\n\r\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\r\n\t\treturn 'Internet Explorer';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el sistema operativo */\r\nexport function getOS(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Windows')) {\r\n\t\treturn 'Windows';\r\n\t}\r\n\r\n\tif (ua.includes('Mac OS')) {\r\n\t\treturn 'macOS';\r\n\t}\r\n\r\n\tif (ua.includes('Linux')) {\r\n\t\treturn 'Linux';\r\n\t}\r\n\r\n\tif (ua.includes('Android')) {\r\n\t\treturn 'Android';\r\n\t}\r\n\r\n\tif (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) {\r\n\t\treturn 'iOS';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el idioma del navegador */\r\nexport function getLanguage(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\treturn navigator.language || 'unknown';\r\n}\r\n\r\n/** Obtiene la URL actual */\r\nexport function getCurrentUrl(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.href;\r\n}\r\n\r\n/** Obtiene el referrer */\r\nexport function getReferrer(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn document.referrer;\r\n}\r\n\r\n/** Obtiene el timestamp actual en formato ISO */\r\nexport function getTimestamp(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\n/** Obtiene el path actual */\r\nexport function getCurrentPath(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.pathname;\r\n}\r\n","/**\r\n * ClickObserver: Captura clics globales en elementos interactivos\r\n */\r\n\r\nimport type { TrackerEvent } from '../types';\r\nimport { Tracker } from '../core/Tracker';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { truncate } from '../utils/truncate';\r\nimport {\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\n\r\n/** Tipos de elementos que deben ser rastreados */\r\nconst TRACKABLE_TAGS = ['BUTTON', 'A', 'INPUT', 'I', 'SPAN', 'DIV'];\r\n\r\n/** Tipos de input que deben ser rastreados */\r\nconst TRACKABLE_INPUT_TYPES = ['submit'];\r\n\r\n/** Tipos de input que deben ser excluidos (sensibles) */\r\nconst SENSITIVE_INPUT_TYPES = ['password', 'email', 'tel', 'number', 'search', 'url'];\r\n\r\n/** Selectores para elementos rastreables */\r\nconst TRACKABLE_SELECTORS = [\r\n\t'button',\r\n\t'a',\r\n\t'input[type=\"submit\"]',\r\n\t'[data-track]',\r\n\t'[style*=\"cursor: pointer\"]',\r\n\t'i[class*=\"mdi-\"]',\r\n];\r\n\r\n/** Verifica si un elemento es sensible (no capturar valores) */\r\nfunction _isSensitiveElement(element: Element): boolean {\r\n\tif (element.tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn SENSITIVE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n/** Verifica si un elemento es rastreable */\r\nfunction _isTrackableElement(element: Element): boolean {\r\n\tconst tagName = element.tagName.toUpperCase();\r\n\r\n\tif (!TRACKABLE_TAGS.includes(tagName)) {\r\n\t\treturn element.hasAttribute('data-track') ||\r\n\t\t\t_hasCursorPointer(element) ||\r\n\t\t\t_isMdiIcon(element);\r\n\t}\r\n\r\n\tif (tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn TRACKABLE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/** Verifica si el elemento tiene cursor: pointer */\r\nfunction _hasCursorPointer(element: Element): boolean {\r\n\ttry {\r\n\t\tconst style = window.getComputedStyle(element);\r\n\t\treturn style.cursor === 'pointer';\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/** Verifica si es un icono MDI */\r\nfunction _isMdiIcon(element: Element): boolean {\r\n\treturn element.tagName === 'I' && element.classList.contains('mdi');\r\n}\r\n\r\n/** Genera un selector CSS simple para el elemento */\r\nfunction _generateSelector(element: Element): string {\r\n\tconst parts: string[] = [];\r\n\tlet current: Element | null = element;\r\n\r\n\twhile (current && current !== document.documentElement) {\r\n\t\tlet selector = current.tagName.toLowerCase();\r\n\r\n\t\tif (current.id) {\r\n\t\t\tselector += `#${current.id}`;\r\n\t\t\tparts.unshift(selector);\r\n\t\t\tbreak;\r\n\t\t} else if (current.className && typeof current.className === 'string') {\r\n\t\t\tconst classes = current.className.trim().split(/\\s+/).filter(Boolean);\r\n\t\t\tif (classes.length > 0) {\r\n\t\t\t\tselector += `.${classes[0]}`;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tparts.unshift(selector);\r\n\t\tcurrent = current.parentElement;\r\n\t}\r\n\r\n\treturn parts.slice(0, 4).join(' > ');\r\n}\r\n\r\n/** Obtiene todas las clases de un elemento como string separado por espacios */\r\nfunction _getAllClasses(element: Element): string | undefined {\r\n\tif (!element.className || typeof element.className !== 'string') {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst classes = element.className.trim().split(/\\s+/).filter(Boolean);\r\n\treturn classes.length > 0 ? classes.join(' ') : undefined;\r\n}\r\n\r\nexport class ClickObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _boundHandleClick: (event: MouseEvent) => void;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._boundHandleClick = this._handleClick.bind(this);\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.addEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = true;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.removeEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de eventos de clic */\r\n\tprivate _handleClick(event: MouseEvent): void {\r\n\t\ttry {\r\n\t\t\tconst target = event.target as Element;\r\n\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 1: Si el elemento clickeado es rastreable, usarlo directamente\r\n\t\t\tif (_isTrackableElement(target) && !_isSensitiveElement(target)) {\r\n\t\t\t\tthis._createClickEvent(target);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 2: Buscar el ancestro rastreable más cercano\r\n\t\t\tconst trackableElement = target.closest(TRACKABLE_SELECTORS.join(','));\r\n\r\n\t\t\tif (!trackableElement) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isSensitiveElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isTrackableElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._createClickEvent(trackableElement);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de clic */\r\n\tprivate _createClickEvent(element: Element): void {\r\n\t\tconst event: TrackerEvent = {\r\n\t\t\teventType: 'user_click',\r\n\t\t\teventId: generateUUID(),\r\n\t\t\tcontext: {\r\n\t\t\t\tsessionId: '',\r\n\t\t\t\tuserId: null,\r\n\t\t\t\tuserName: null,\r\n\t\t\t\turl: window.location.href,\r\n\t\t\t\treferrer: getReferrer(),\r\n\t\t\t\tscreenSize: getScreenSize(),\r\n\t\t\t\tviewportSize: getViewportSize(),\r\n\t\t\t\tdeviceType: getDeviceType(),\r\n\t\t\t\tbrowser: getBrowser(),\r\n\t\t\t\tos: getOS(),\r\n\t\t\t\tlanguage: getLanguage(),\r\n\t\t\t\ttimestamp: getTimestamp(),\r\n\t\t\t},\r\n\t\t\tdata: {\r\n\t\t\t\ttagName: element.tagName,\r\n\t\t\t\tid: element.id || undefined,\r\n\t\t\t\tclassName: _getAllClasses(element),\r\n\t\t\t\ttitle: element.getAttribute('title') || undefined,\r\n\t\t\t\ttext: element.textContent ? truncate(element.textContent, 30) : undefined,\r\n\t\t\t\tselector: _generateSelector(element),\r\n\t\t\t},\r\n\t\t};\r\n\r\n\t\tthis._tracker.trackEvent(event);\r\n\t}\r\n}\r\n","/**\r\n * HistoryObserver: Monitorea cambios de navegación usando múltiples estrategias\r\n * - popstate: para botones atrás/adelante del navegador\r\n * - history intercept: para pushState/replaceState en SPA\r\n * - polling: fallback para detectar cambios\r\n * - sessionStorage: para manejar F5\r\n */\r\n\r\nimport type { Tracker } from '../core/Tracker';\r\n\r\nconst POLL_INTERVAL = 1000; // Verificar URL cada 1 segundo (fallback)\r\nconst HEARTBEAT_INTERVAL = 10000; // Enviar heartbeat cada 10 segundos\r\nconst MIN_DURATION = 0.5; // Ignorar page_leave con menos de 0.5 segundos\r\nconst STORAGE_KEY_URL = 'event_tracker_last_url';\r\nconst STORAGE_KEY_TIME = 'event_tracker_last_time';\r\n\r\nexport class HistoryObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _lastUrl: string = '';\r\n\tprivate _pageStartTime: number;\r\n\tprivate _originalPushState: typeof history.pushState = () => null;\r\n\tprivate _originalReplaceState: typeof history.replaceState = () => null;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._pageStartTime = performance.now();\r\n\t\tthis._lastUrl = typeof window !== 'undefined' ? window.location.pathname : '';\r\n\r\n\t\t// Guardar referencias originales de history\r\n\t\tif (typeof window !== 'undefined' && typeof history !== 'undefined') {\r\n\t\t\tthis._originalPushState = history.pushState.bind(history);\r\n\t\t\tthis._originalReplaceState = history.replaceState.bind(history);\r\n\t\t}\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// Configurar interceptación de history\r\n\t\t\tthis._interceptHistory();\r\n\r\n\t\t\t// Agregar listener de popstate (botones atrás/adelante)\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('popstate', this._handlePopState);\r\n\t\t\t}\r\n\r\n\t\t\t// Iniciar intervalo de polling (fallback)\r\n\t\t\tthis._intervalId = setInterval(() => this._checkUrlChange(), POLL_INTERVAL);\r\n\r\n\t\t\t// Iniciar intervalo de heartbeat\r\n\t\t\tthis._heartbeatIntervalId = setInterval(() => this._sendHeartbeat(), HEARTBEAT_INTERVAL);\r\n\r\n\t\t\t// Eventos para cierre de página\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.addEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = true;\r\n\r\n\t\t\t// Verificar si hay una página previa guardada (para F5)\r\n\t\t\t// Solo después de que el tracker esté inicializado\r\n\t\t\tsetTimeout(() => this._checkPreviousPage(), 0);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tif (this._intervalId) {\r\n\t\t\t\tclearInterval(this._intervalId);\r\n\t\t\t\tthis._intervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (this._heartbeatIntervalId) {\r\n\t\t\t\tclearInterval(this._heartbeatIntervalId);\r\n\t\t\t\tthis._heartbeatIntervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.removeEventListener('popstate', this._handlePopState);\r\n\t\t\t\twindow.removeEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.removeEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\t// Restaurar history original\r\n\t\t\tif (typeof history !== 'undefined') {\r\n\t\t\t\thistory.pushState = this._originalPushState;\r\n\t\t\t\thistory.replaceState = this._originalReplaceState;\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Intercepta history.pushState y history.replaceState */\r\n\tprivate _interceptHistory(): void {\r\n\t\tif (typeof window === 'undefined' || typeof history === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst self = this;\r\n\t\tconst originalPushState = history.pushState.bind(history) as (...args: unknown[]) => unknown;\r\n\t\tconst originalReplaceState = history.replaceState.bind(history) as (...args: unknown[]) => unknown;\r\n\r\n\t\t// Interceptar pushState\r\n\t\thistory.pushState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalPushState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.pushState;\r\n\r\n\t\t// Interceptar replaceState\r\n\t\thistory.replaceState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalReplaceState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.replaceState;\r\n\t}\r\n\r\n\t/** Manejador de popstate (botones atrás/adelante) */\r\n\tprivate _handlePopState = (): void => {\r\n\t\ttry {\r\n\t\t\tthis._handleNavigationChange();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejo centralizado de cambio de navegación */\r\n\tprivate _handleNavigationChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.pathname;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Calcular duración wall-clock de la página anterior\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t\t// Solo enviar page_leave si la duración es significativa\r\n\t\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\t\t// Enviar page_leave INMEDIATAMENTE para la página anterior\r\n\t\t\t\t\tthis._sendPageLeaveImmediate(this._lastUrl, duration);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Track page view de la nueva página\r\n\t\t\t\tthis._tracker.trackPageView();\r\n\r\n\t\t\t\t// Resetear contador para la nueva página\r\n\t\t\t\tthis._pageStartTime = performance.now();\r\n\r\n\t\t\t\t// Actualizar la última URL\r\n\t\t\t\tthis._lastUrl = currentUrl;\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía page_leave inmediatamente sin esperar el buffer */\r\n\tprivate _sendPageLeaveImmediate(path: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\t// Crear el evento directamente\r\n\t\t\tthis._tracker.trackPageLeave(duration, path);\r\n\r\n\t\t\t// También guardar en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(path, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de beforeunload (cuando el usuario sale de la página) */\r\n\tprivate _handleBeforeUnload = (): void => {\r\n\t\ttry {\r\n\t\t\t// Calcular duración wall-clock\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Solo enviar si la duración es significativa\r\n\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\tthis._tracker.trackPageLeave(duration, this._lastUrl);\r\n\t\t\t}\r\n\r\n\t\t\t// Guardar URL actual en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejador de visibilitychange (para detectar cuando la app pasa a segundo plano) */\r\n\tprivate _handleVisibilityChange = (): void => {\r\n\t\ttry {\r\n\t\t\tif (typeof document === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cuando la página se oculta, guardar el tiempo actual en sessionStorage\r\n\t\t\tif (document.visibilityState === 'hidden') {\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\t\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Verifica si la URL cambió (fallback de polling) */\r\n\tprivate _checkUrlChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.pathname;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Usar el manejo centralizado\r\n\t\t\t\tthis._handleNavigationChange();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Calcula la duración wall-clock (tiempo total desde que entró a la página) */\r\n\tprivate _calculateWallClockDuration(): number {\r\n\t\treturn (performance.now() - this._pageStartTime) / 1000;\r\n\t}\r\n\r\n\t/** Envía un heartbeat con la duración actual */\r\n\tprivate _sendHeartbeat(): void {\r\n\t\ttry {\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Usar trackPageLeave para mantener compatibilidad\r\n\t\t\t// El backend puede filtrar por eventType = 'page_heartbeat'\r\n\t\t\tthis._tracker.trackEvent({\r\n\t\t\t\teventType: 'page_heartbeat',\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath: this._lastUrl,\r\n\t\t\t\t\tduration: duration,\r\n\t\t\t\t\ttimestamp: new Date().toISOString(),\r\n\t\t\t\t},\r\n\t\t\t} as import('../types/index').TrackerEvent);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Guarda la URL y tiempo en sessionStorage (para F5) */\r\n\tprivate _saveToStorage(url: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_URL, url);\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_TIME, String(duration));\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Lee la URL y tiempo previos de sessionStorage (para F5) */\r\n\tprivate _getFromStorage(): { url: string; duration: number; } | null {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tconst url = sessionStorage.getItem(STORAGE_KEY_URL);\r\n\t\t\t\tconst duration = sessionStorage.getItem(STORAGE_KEY_TIME);\r\n\t\t\t\tif (url && duration) {\r\n\t\t\t\t\treturn { url, duration: parseFloat(duration) };\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/** Limpia sessionStorage */\r\n\tprivate _clearStorage(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_URL);\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_TIME);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si hay una página previa (para F5) */\r\n\tprivate _checkPreviousPage(): void {\r\n\t\tconst previous = this._getFromStorage();\r\n\t\tif (previous && previous.duration >= MIN_DURATION) {\r\n\t\t\t// La página anterior tenía una duración significativa, enviar page_leave\r\n\t\t\tthis._tracker.trackPageLeave(previous.duration, previous.url);\r\n\t\t}\r\n\t\t// Limpiar storage después de procesar\r\n\t\tthis._clearStorage();\r\n\t}\r\n}\r\n","{\r\n\t\"name\": \"@agroideas/event-tracker-sdk\",\r\n\t\"version\": \"1.0.7\",\r\n\t\"description\": \"A lightweight, framework-agnostic SDK for tracking frontend user behavior\",\r\n\t\"license\": \"MIT\",\r\n\t\"author\": \"Agroideas\",\r\n\t\"homepage\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"main\": \"./dist/index.cjs\",\r\n\t\"module\": \"./dist/index.mjs\",\r\n\t\"types\": \"./dist/index.d.ts\",\r\n\t\"exports\": {\r\n\t\t\".\": {\r\n\t\t\t\"types\": \"./dist/index.d.ts\",\r\n\t\t\t\"import\": \"./dist/index.mjs\",\r\n\t\t\t\"require\": \"./dist/index.cjs\"\r\n\t\t},\r\n\t\t\"./iife\": \"./dist/index.iife.js\"\r\n\t},\r\n\t\"files\": [\r\n\t\t\"dist\"\r\n\t],\r\n\t\"scripts\": {\r\n\t\t\"build\": \"tsup\",\r\n\t\t\"dev\": \"tsup --watch\",\r\n\t\t\"lint\": \"eslint src --ext .ts\",\r\n\t\t\"lint:fix\": \"eslint src --ext .ts --fix\",\r\n\t\t\"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\r\n\t\t\"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\r\n\t\t\"typecheck\": \"tsc --noEmit\",\r\n\t\t\"prepare\": \"husky install\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"@types/node\": \"^20.10.0\",\r\n\t\t\"eslint\": \"^8.55.0\",\r\n\t\t\"husky\": \"^8.0.3\",\r\n\t\t\"prettier\": \"^3.1.0\",\r\n\t\t\"tsup\": \"^8.0.1\",\r\n\t\t\"typescript\": \"^5.3.2\"\r\n\t},\r\n\t\"keywords\": [\r\n\t\t\"tracking\",\r\n\t\t\"analytics\",\r\n\t\t\"sdk\",\r\n\t\t\"events\",\r\n\t\t\"behavior\",\r\n\t\t\"frontend\"\r\n\t],\r\n\t\"engines\": {\r\n\t\t\"node\": \">=18.0.0\"\r\n\t}\r\n}","/**\n * Version constants for the Event Tracker SDK\n */\n\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION = pkg.version;\n","/**\r\n * Tracker: Singleton principal que coordina observers y EventBuffer\r\n */\r\n\r\nimport type { TrackerConfig, TrackerEvent, UserIdentity, EventContext } from '../types';\r\nimport { EventBuffer } from './EventBuffer';\r\nimport { ClickObserver } from '../observers/ClickObserver';\r\nimport { HistoryObserver } from '../observers/HistoryObserver';\r\nimport {\r\n\tgetCurrentPath,\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetOrCreateSessionId,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { VERSION } from '../version';\r\n\r\nexport class Tracker {\r\n\tprivate static _instance: Tracker | null = null;\r\n\tprivate _eventBuffer: EventBuffer | null = null;\r\n\tprivate _clickObserver: ClickObserver | null = null;\r\n\tprivate _historyObserver: HistoryObserver | null = null;\r\n\tprivate _userIdentity: UserIdentity;\r\n\tprivate _sessionId: string;\r\n\tprivate _isInitialized: boolean = false;\r\n\tprivate _isPaused: boolean = false;\r\n\tprivate _config: TrackerConfig | null = null;\r\n\r\n\tprivate constructor() {\r\n\t\tthis._userIdentity = {\r\n\t\t\tuserId: null,\r\n\t\t\tuserName: null,\r\n\t\t};\r\n\t\tthis._sessionId = getOrCreateSessionId();\r\n\t}\r\n\r\n\t/** Obtiene la instancia única del Tracker */\r\n\tstatic getInstance(): Tracker {\r\n\t\tif (Tracker._instance === null) {\r\n\t\t\tTracker._instance = new Tracker();\r\n\t\t}\r\n\t\treturn Tracker._instance;\r\n\t}\r\n\r\n\t/** Construye el contexto completo del evento */\r\n\tprivate _buildContext(): EventContext {\r\n\t\treturn {\r\n\t\t\tsessionId: this._sessionId,\r\n\t\t\tuserId: this._userIdentity.userId,\r\n\t\t\tuserName: this._userIdentity.userName,\r\n\t\t\turl: window.location.href,\r\n\t\t\treferrer: getReferrer(),\r\n\t\t\tscreenSize: getScreenSize(),\r\n\t\t\tviewportSize: getViewportSize(),\r\n\t\t\tdeviceType: getDeviceType(),\r\n\t\t\tbrowser: getBrowser(),\r\n\t\t\tos: getOS(),\r\n\t\t\tlanguage: getLanguage(),\r\n\t\t\ttimestamp: getTimestamp(),\r\n\t\t};\r\n\t}\r\n\r\n\t/** Verifica si una ruta debe ser excluida del tracking */\r\n\tprivate _shouldExclude(path: string): boolean {\r\n\t\tif (!this._config) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst { excludePatterns } = this._config;\r\n\r\n\t\t// Normalizar el path (quitar trailing slash para comparación consistente)\r\n\t\tconst normalizedPath = path.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;\r\n\r\n\t\t// Si hay patrones de exclusión, verificarlos\r\n\t\tif (excludePatterns && excludePatterns.length > 0) {\r\n\t\t\treturn excludePatterns.some((pattern) => {\r\n\t\t\t\tif (typeof pattern === 'string') {\r\n\t\t\t\t\t// Normalizar el patrón también\r\n\t\t\t\t\tconst normalizedPattern = pattern.endsWith('/') && pattern.length > 1 ? pattern.slice(0, -1) : pattern;\r\n\t\t\t\t\t// Coincidencia exacta o prefijo\r\n\t\t\t\t\treturn normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern);\r\n\t\t\t\t}\r\n\t\t\t\t// Es RegExp\r\n\t\t\t\treturn pattern.test(normalizedPath) || pattern.test(path);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/** Inicializa el tracker con la configuración */\r\n\tinit(config: TrackerConfig): void {\r\n\t\ttry {\r\n\t\t\t// Guardar configuración\r\n\t\t\tthis._config = config;\r\n\r\n\t\t\t// Actualizamos el usuario cada vez que se llama init (permite actualizar userId/userName)\r\n\t\t\tif (config.userId) {\r\n\t\t\t\tthis._userIdentity = {\r\n\t\t\t\t\tuserId: config.userId,\r\n\t\t\t\t\tuserName: config.userName ?? null,\r\n\t\t\t\t};\r\n\t\t\t\tif (this._isInitialized && config.debug) {\r\n\t\t\t\t\tconsole.log('[EventTracker] Usuario actualizado:', this._userIdentity);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Solo inicializar EventBuffer y observers la primera vez\r\n\t\t\tif (this._isInitialized) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._eventBuffer = new EventBuffer(config);\r\n\t\t\tthis._isInitialized = true;\r\n\r\n\t\t\t// Imprimir versión del tracker\r\n\t\t\tconsole.log(`🚀 [EventTracker] Inicializado v${VERSION}`);\r\n\r\n\t\t\t// Iniciar observers\r\n\t\t\tthis._clickObserver = new ClickObserver(this);\r\n\t\t\tthis._historyObserver = new HistoryObserver(this);\r\n\t\t\tthis._clickObserver.start();\r\n\t\t\tthis._historyObserver.start();\r\n\r\n\t\t\t// Enviar page_view inicial\r\n\t\t\tthis.trackPageView();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Identifica al usuario */\r\n\tidentify(userId: string, userName?: string): void {\r\n\t\ttry {\r\n\t\t\tthis._userIdentity = {\r\n\t\t\t\tuserId,\r\n\t\t\t\tuserName: userName ?? null,\r\n\t\t\t};\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Registra un evento */\r\n\ttrackEvent(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!this._eventBuffer) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Verificar si la URL actual debe ser excluida\r\n\t\t\tconst currentPath = getCurrentPath();\r\n\t\t\tif (this._shouldExclude(currentPath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst eventWithContext: TrackerEvent = {\r\n\t\t\t\t...event,\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t};\r\n\r\n\t\t\tthis._eventBuffer.add(eventWithContext);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page view */\r\n\ttrackPageView(): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst path = getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(path)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_view',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page leave */\r\n\ttrackPageLeave(duration: number, path?: string): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst pagePath = path ?? getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(pagePath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_leave',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tduration,\r\n\t\t\t\t\tpath: pagePath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Fuerza el envío de todos los eventos pendientes */\r\n\tasync flush(): Promise<void> {\r\n\t\ttry {\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tawait this._eventBuffer.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si el tracker está inicializado */\r\n\tisInitialized(): boolean {\r\n\t\treturn this._isInitialized;\r\n\t}\r\n\r\n\t/** Pausa el tracker (no registrará eventos) */\r\n\tpause(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = true;\r\n\t\t\tconsole.log('⏸️ [EventTracker] Tracker pausado - No se registrarán eventos');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Reanuda el tracker (volverá a registrar eventos) */\r\n\tresume(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = false;\r\n\t\t\tconsole.log('▶️ [EventTracker] Tracker reanudado - Eventos serán registrados');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Alterna el modo debug */\r\n\ttoggleDebug(): void {\r\n\t\ttry {\r\n\t\t\tif (!this._config) {\r\n\t\t\t\tconsole.log('⚠️ [EventTracker] Error: Tracker no inicializado');\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cambiar el valor de debug\r\n\t\t\tthis._config.debug = this._config.debug ? '' : 'true';\r\n\r\n\t\t\tif (this._config.debug) {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug ACTIVADO ✅');\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug DESACTIVADO ❌');\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Destruye el tracker */\r\n\tdestroy(): void {\r\n\t\ttry {\r\n\t\t\tif (this._clickObserver) {\r\n\t\t\t\tthis._clickObserver.stop();\r\n\t\t\t\tthis._clickObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._historyObserver) {\r\n\t\t\t\tthis._historyObserver.stop();\r\n\t\t\t\tthis._historyObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tthis._eventBuffer.destroy();\r\n\t\t\t\tthis._eventBuffer = null;\r\n\t\t\t}\r\n\t\t\tthis._isInitialized = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Event Tracker SDK\r\n *\r\n * SDK ligero y agnóstico al framework para tracking de comportamiento frontend.\r\n *\r\n * @example\r\n * import { tracker } from '@agroideas/event-tracker-sdk';\r\n *\r\n * tracker.init({\r\n * apiKey: 'tu-api-key',\r\n * endpoint: 'https://tu-api.com/events'\r\n * });\r\n *\r\n * // Identificar usuario\r\n * tracker.identify('user-123', 'Juan Pérez');\r\n */\r\n\r\nimport { Tracker } from './core/Tracker';\r\nimport type { TrackerConfig } from './types';\r\n\r\n/** Instancia única exportada del Tracker */\r\nexport const tracker = Tracker.getInstance();\r\n\r\n/**\r\n * Inicializa el tracker con configuración requerida (apiKey y endpoint).\r\n */\r\nexport function init(config: TrackerConfig): void {\r\n\ttracker.init(config);\r\n}\r\n\r\n/** Identifica al usuario */\r\nexport function identify(userId: string, userName?: string): void {\r\n\ttracker.identify(userId, userName);\r\n}\r\n\r\n/** Fuerza el envío de eventos pendientes */\r\nexport function flush(): Promise<void> {\r\n\treturn tracker.flush();\r\n}\r\n\r\n/** Destruye el tracker */\r\nexport function destroy(): void {\r\n\ttracker.destroy();\r\n}\r\n\r\nexport type { TrackerConfig };\r\n"],"mappings":"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","names":["tracker","tracker","Tracker","Tracker"]}
1
+ {"version":3,"sources":["../src/core/EventBuffer.ts","../src/utils/uuid.ts","../src/utils/truncate.ts","../src/utils/metadata.ts","../src/observers/ClickObserver.ts","../src/observers/HistoryObserver.ts","../package.json","../src/version.ts","../src/core/Tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * EventBuffer: Maneja la acumulación, batching y envío de eventos\r\n */\r\n\r\nimport type { TrackerEvent, SendResult, TrackerConfig, TrackingResponse } from '../types';\r\n\r\nconst BATCH_INTERVAL = 10000;\r\nconst MAX_BUFFER_SIZE = 15;\r\n\r\nexport class EventBuffer {\r\n\tprivate _buffer: TrackerEvent[];\r\n\tprivate _config: Required<Omit<TrackerConfig, 'apiKey' | 'endpoint' | 'userId' | 'userName' | 'debug'>>;\r\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _endpoint: string;\r\n\tprivate _apiKey: string;\r\n\tprivate _isDebug: boolean;\r\n\r\n\tconstructor(config: TrackerConfig) {\r\n\t\tthis._buffer = [];\r\n\r\n\t\t// apiKey y endpoint son requeridos\r\n\t\tthis._endpoint = config.endpoint;\r\n\t\tthis._apiKey = config.apiKey;\r\n\r\n\t\t// DEBUG es opcional, default false\r\n\t\tthis._isDebug = config.debug == \"true\";\r\n\t\tconsole.log(config);\r\n\r\n\t\tthis._config = {\r\n\t\t\tbatchInterval: config.batchInterval ?? BATCH_INTERVAL,\r\n\t\t\tmaxBufferSize: config.maxBufferSize ?? MAX_BUFFER_SIZE,\r\n\t\t\texcludePatterns: config.excludePatterns ?? [],\r\n\t\t};\r\n\r\n\t\tif (this._isDebug) {\r\n\t\t\tconsole.log('[EventTracker] Modo DEBUG activado');\r\n\t\t\tconsole.log('[EventTracker] Endpoint:', this._endpoint);\r\n\t\t\tconsole.log('[EventTracker] API Key:', this._apiKey);\r\n\t\t}\r\n\r\n\t\t// En modo debug, no iniciar timer de flush\r\n\t\tif (!this._isDebug) {\r\n\t\t\tthis._startFlushTimer();\r\n\t\t}\r\n\r\n\t\tthis._setupVisibilityListener();\r\n\t}\r\n\r\n\t/** Agrega un evento al buffer */\r\n\tadd(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\tthis._buffer.push(event);\r\n\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] Evento agregado:', event.eventType);\r\n\t\t\t\tconsole.log('[EventTracker] Buffer size:', this._buffer.length);\r\n\t\t\t\tconsole.log('[EventTracker] Event content:', JSON.stringify(event, null, 2));\r\n\t\t\t}\r\n\r\n\t\t\t// En modo debug, no hacer flush automático\r\n\t\t\tif (!this._isDebug && this._buffer.length >= this._config.maxBufferSize) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Silently ignore errors to never block the main app\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía todos los eventos acumulados al servidor */\r\n\tasync flush(): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tif (this._buffer.length === 0) {\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst eventsToSend = [...this._buffer];\r\n\t\t\tthis._buffer = [];\r\n\r\n\t\t\t// Modo debug: solo console.log, no fetch\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\tconsole.log('[EventTracker] DEBUG - Events:', JSON.stringify(eventsToSend, null, 2));\r\n\t\t\t\treturn { success: true };\r\n\t\t\t}\r\n\r\n\t\t\tconst result = await this._sendEventsAsync(eventsToSend);\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\t/** Obtiene la cantidad de eventos en el buffer */\r\n\tgetSize(): number {\r\n\t\treturn this._buffer.length;\r\n\t}\r\n\r\n\t/** Limpia el buffer sin enviar */\r\n\tclear(): void {\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Destruye el buffer y limpia recursos */\r\n\tdestroy(): void {\r\n\t\tif (this._flushTimer) {\r\n\t\t\tclearInterval(this._flushTimer);\r\n\t\t\tthis._flushTimer = null;\r\n\t\t}\r\n\r\n\t\tthis._buffer = [];\r\n\t}\r\n\r\n\t/** Inicia el temporizador de flush periódico */\r\n\tprivate _startFlushTimer(): void {\r\n\t\tthis._flushTimer = setInterval(() => {\r\n\t\t\tif (this._buffer.length > 0) {\r\n\t\t\t\tthis.flush();\r\n\t\t\t}\r\n\t\t}, this._config.batchInterval);\r\n\t}\r\n\r\n\t/** Configura el listener de visibilitychange para envío de emergencia */\r\n\tprivate _setupVisibilityListener(): void {\r\n\t\tif (typeof document === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst handleVisibilityChange = (): void => {\r\n\t\t\t// En modo debug, no enviar eventos\r\n\t\t\tif (this._isDebug) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (document.visibilityState === 'hidden' && this._buffer.length > 0) {\r\n\t\t\t\t// Usar setTimeout para asegurar que el fetch se complete antes del unload\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tthis.flush();\r\n\t\t\t\t}, 0);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tdocument.addEventListener('visibilitychange', handleVisibilityChange);\r\n\t}\r\n\r\n\t/** Envía eventos usando fetch con keepalive (funciona incluso en page unload) */\r\n\tprivate async _sendEventsAsync(events: TrackerEvent[]): Promise<SendResult> {\r\n\t\ttry {\r\n\t\t\tconst payload = JSON.stringify({\r\n\t\t\t\tevents: events,\r\n\t\t\t});\r\n\r\n\t\t\tconst response = await fetch(this._endpoint, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'X-API-Key': this._apiKey,\r\n\t\t\t\t},\r\n\t\t\t\tbody: payload,\r\n\t\t\t\tkeepalive: true,\r\n\t\t\t});\r\n\r\n\t\t\t// Intentar parsear el mensaje de respuesta del backend\r\n\t\t\tlet message: string | undefined;\r\n\t\t\ttry {\r\n\t\t\t\tconst responseData: TrackingResponse = await response.json();\r\n\t\t\t\tmessage = responseData.message;\r\n\t\t\t} catch {\r\n\t\t\t\t// Si no se puede parsear, usar el status text\r\n\t\t\t\tmessage = response.statusText;\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: response.ok,\r\n\t\t\t\tstatusCode: response.status,\r\n\t\t\t\tmessage,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\treturn {\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: error instanceof Error ? error.message : 'Network error',\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Generador de UUID v4\r\n */\r\n\r\n/** Genera un UUID v4 aleatorio */\r\nexport function generateUUID(): string {\r\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\r\n\t\tconst random = Math.random() * 16 | 0;\r\n\t\tconst value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n\t\treturn value.toString(16);\r\n\t});\r\n}\r\n","/**\r\n * Utilidad para truncar texto\r\n */\r\n\r\n/**\r\n * Trunca un texto a la longitud máxima especificada\r\n * @param text - Texto a truncar\r\n * @param maxLength - Longitud máxima\r\n * @returns Texto truncado con '...' al final si excede la longitud\r\n */\r\nexport function truncate(text: string, maxLength: number): string {\r\n\tif (maxLength < 3) {\r\n\t\treturn '...';\r\n\t}\r\n\r\n\tif (text.length <= maxLength) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, maxLength - 3) + '...';\r\n}\r\n","/**\r\n * Utilidades para recolectar metadatos del navegador\r\n */\r\n\r\nimport { generateUUID } from './uuid';\r\n\r\n/** Obtiene o crea un sessionId en sessionStorage */\r\nexport function getOrCreateSessionId(): string {\r\n\ttry {\r\n\t\tconst storageKey = 'event_tracker_session_id';\r\n\t\tconst existingSessionId = sessionStorage.getItem(storageKey);\r\n\r\n\t\tif (existingSessionId) {\r\n\t\t\treturn existingSessionId;\r\n\t\t}\r\n\r\n\t\tconst newSessionId = generateUUID();\r\n\t\tsessionStorage.setItem(storageKey, newSessionId);\r\n\t\treturn newSessionId;\r\n\t} catch {\r\n\t\treturn generateUUID();\r\n\t}\r\n}\r\n\r\n/** Obtiene el tamaño de pantalla */\r\nexport function getScreenSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Obtiene el tamaño del viewport */\r\nexport function getViewportSize(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '0x0';\r\n\t}\r\n\r\n\treturn `${window.innerWidth}x${window.innerHeight}`;\r\n}\r\n\r\n/** Detecta el tipo de dispositivo */\r\nexport function getDeviceType(): 'mobile' | 'desktop' | 'tablet' | 'unknown' {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\r\n\t\treturn 'tablet';\r\n\t}\r\n\r\n\tif (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\r\n\t\treturn 'mobile';\r\n\t}\r\n\r\n\treturn 'desktop';\r\n}\r\n\r\n/** Obtiene el navegador */\r\nexport function getBrowser(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Firefox')) {\r\n\t\treturn 'Firefox';\r\n\t}\r\n\r\n\tif (ua.includes('Edg/')) {\r\n\t\treturn 'Edge';\r\n\t}\r\n\r\n\tif (ua.includes('Chrome')) {\r\n\t\treturn 'Chrome';\r\n\t}\r\n\r\n\tif (ua.includes('Safari')) {\r\n\t\treturn 'Safari';\r\n\t}\r\n\r\n\tif (ua.includes('MSIE') || ua.includes('Trident/')) {\r\n\t\treturn 'Internet Explorer';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el sistema operativo */\r\nexport function getOS(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\tconst ua = navigator.userAgent;\r\n\r\n\tif (ua.includes('Windows')) {\r\n\t\treturn 'Windows';\r\n\t}\r\n\r\n\tif (ua.includes('Mac OS')) {\r\n\t\treturn 'macOS';\r\n\t}\r\n\r\n\tif (ua.includes('Linux')) {\r\n\t\treturn 'Linux';\r\n\t}\r\n\r\n\tif (ua.includes('Android')) {\r\n\t\treturn 'Android';\r\n\t}\r\n\r\n\tif (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) {\r\n\t\treturn 'iOS';\r\n\t}\r\n\r\n\treturn 'unknown';\r\n}\r\n\r\n/** Obtiene el idioma del navegador */\r\nexport function getLanguage(): string {\r\n\tif (typeof navigator === 'undefined') {\r\n\t\treturn 'unknown';\r\n\t}\r\n\r\n\treturn navigator.language || 'unknown';\r\n}\r\n\r\n/** Obtiene la URL actual */\r\nexport function getCurrentUrl(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.href;\r\n}\r\n\r\n/** Obtiene el referrer */\r\nexport function getReferrer(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn document.referrer;\r\n}\r\n\r\n/** Obtiene el timestamp actual en formato ISO */\r\nexport function getTimestamp(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\n/** Obtiene el path actual */\r\nexport function getCurrentPath(): string {\r\n\tif (typeof window === 'undefined') {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn window.location.pathname;\r\n}\r\n","/**\r\n * ClickObserver: Captura clics globales en elementos interactivos\r\n */\r\n\r\nimport type { TrackerEvent } from '../types';\r\nimport { Tracker } from '../core/Tracker';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { truncate } from '../utils/truncate';\r\nimport {\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\n\r\n/** Tipos de elementos que deben ser rastreados */\r\nconst TRACKABLE_TAGS = ['BUTTON', 'A', 'INPUT', 'I', 'SPAN', 'DIV'];\r\n\r\n/** Tipos de input que deben ser rastreados */\r\nconst TRACKABLE_INPUT_TYPES = ['submit'];\r\n\r\n/** Tipos de input que deben ser excluidos (sensibles) */\r\nconst SENSITIVE_INPUT_TYPES = ['password', 'email', 'tel', 'number', 'search', 'url'];\r\n\r\n/** Selectores para elementos rastreables */\r\nconst TRACKABLE_SELECTORS = [\r\n\t'button',\r\n\t'a',\r\n\t'input[type=\"submit\"]',\r\n\t'[data-track]',\r\n\t'[style*=\"cursor: pointer\"]',\r\n\t'i[class*=\"mdi-\"]',\r\n];\r\n\r\n/** Verifica si un elemento es sensible (no capturar valores) */\r\nfunction _isSensitiveElement(element: Element): boolean {\r\n\tif (element.tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn SENSITIVE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n/** Verifica si un elemento es rastreable */\r\nfunction _isTrackableElement(element: Element): boolean {\r\n\tconst tagName = element.tagName.toUpperCase();\r\n\r\n\tif (!TRACKABLE_TAGS.includes(tagName)) {\r\n\t\treturn element.hasAttribute('data-track') ||\r\n\t\t\t_hasCursorPointer(element) ||\r\n\t\t\t_isMdiIcon(element);\r\n\t}\r\n\r\n\tif (tagName === 'INPUT') {\r\n\t\tconst inputType = (element as HTMLInputElement).type?.toLowerCase() ?? '';\r\n\t\treturn TRACKABLE_INPUT_TYPES.includes(inputType);\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/** Verifica si el elemento tiene cursor: pointer */\r\nfunction _hasCursorPointer(element: Element): boolean {\r\n\ttry {\r\n\t\tconst style = window.getComputedStyle(element);\r\n\t\treturn style.cursor === 'pointer';\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/** Verifica si es un icono MDI */\r\nfunction _isMdiIcon(element: Element): boolean {\r\n\treturn element.tagName === 'I' && element.classList.contains('mdi');\r\n}\r\n\r\n/** Genera un selector CSS simple para el elemento */\r\nfunction _generateSelector(element: Element): string {\r\n\tconst parts: string[] = [];\r\n\tlet current: Element | null = element;\r\n\r\n\twhile (current && current !== document.documentElement) {\r\n\t\tlet selector = current.tagName.toLowerCase();\r\n\r\n\t\tif (current.id) {\r\n\t\t\tselector += `#${current.id}`;\r\n\t\t\tparts.unshift(selector);\r\n\t\t\tbreak;\r\n\t\t} else if (current.className && typeof current.className === 'string') {\r\n\t\t\tconst classes = current.className.trim().split(/\\s+/).filter(Boolean);\r\n\t\t\tif (classes.length > 0) {\r\n\t\t\t\tselector += `.${classes[0]}`;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tparts.unshift(selector);\r\n\t\tcurrent = current.parentElement;\r\n\t}\r\n\r\n\treturn parts.slice(0, 4).join(' > ');\r\n}\r\n\r\n/** Obtiene todas las clases de un elemento como string separado por espacios */\r\nfunction _getAllClasses(element: Element): string | undefined {\r\n\tif (!element.className || typeof element.className !== 'string') {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst classes = element.className.trim().split(/\\s+/).filter(Boolean);\r\n\treturn classes.length > 0 ? classes.join(' ') : undefined;\r\n}\r\n\r\nexport class ClickObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _boundHandleClick: (event: MouseEvent) => void;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._boundHandleClick = this._handleClick.bind(this);\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.addEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = true;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\twindow.removeEventListener('click', this._boundHandleClick, true);\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de eventos de clic */\r\n\tprivate _handleClick(event: MouseEvent): void {\r\n\t\ttry {\r\n\t\t\tconst target = event.target as Element;\r\n\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 1: Si el elemento clickeado es rastreable, usarlo directamente\r\n\t\t\tif (_isTrackableElement(target) && !_isSensitiveElement(target)) {\r\n\t\t\t\tthis._createClickEvent(target);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// PRIORIDAD 2: Buscar el ancestro rastreable más cercano\r\n\t\t\tconst trackableElement = target.closest(TRACKABLE_SELECTORS.join(','));\r\n\r\n\t\t\tif (!trackableElement) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (_isSensitiveElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!_isTrackableElement(trackableElement)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._createClickEvent(trackableElement);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de clic */\r\n\tprivate _createClickEvent(element: Element): void {\r\n\t\tconst event: TrackerEvent = {\r\n\t\t\teventType: 'user_click',\r\n\t\t\teventId: generateUUID(),\r\n\t\t\tcontext: {\r\n\t\t\t\tsessionId: '',\r\n\t\t\t\tuserId: null,\r\n\t\t\t\tuserName: null,\r\n\t\t\t\turl: window.location.href,\r\n\t\t\t\treferrer: getReferrer(),\r\n\t\t\t\tscreenSize: getScreenSize(),\r\n\t\t\t\tviewportSize: getViewportSize(),\r\n\t\t\t\tdeviceType: getDeviceType(),\r\n\t\t\t\tbrowser: getBrowser(),\r\n\t\t\t\tos: getOS(),\r\n\t\t\t\tlanguage: getLanguage(),\r\n\t\t\t\ttimestamp: getTimestamp(),\r\n\t\t\t},\r\n\t\t\tdata: {\r\n\t\t\t\ttagName: element.tagName,\r\n\t\t\t\tid: element.id || undefined,\r\n\t\t\t\tclassName: _getAllClasses(element),\r\n\t\t\t\ttitle: element.getAttribute('title') || undefined,\r\n\t\t\t\ttext: element.textContent ? truncate(element.textContent, 30) : undefined,\r\n\t\t\t\tselector: _generateSelector(element),\r\n\t\t\t},\r\n\t\t};\r\n\r\n\t\tthis._tracker.trackEvent(event);\r\n\t}\r\n}\r\n","/**\r\n * HistoryObserver: Monitorea cambios de navegación usando múltiples estrategias\r\n * - popstate: para botones atrás/adelante del navegador\r\n * - history intercept: para pushState/replaceState en SPA\r\n * - polling: fallback para detectar cambios\r\n * - sessionStorage: para manejar F5\r\n */\r\n\r\nimport type { Tracker } from '../core/Tracker';\r\n\r\nconst POLL_INTERVAL = 1000; // Verificar URL cada 1 segundo (fallback)\r\nconst HEARTBEAT_INTERVAL = 10000; // Enviar heartbeat cada 10 segundos\r\nconst MIN_DURATION = 0.5; // Ignorar page_leave con menos de 0.5 segundos\r\nconst STORAGE_KEY_URL = 'event_tracker_last_url';\r\nconst STORAGE_KEY_TIME = 'event_tracker_last_time';\r\n\r\nexport class HistoryObserver {\r\n\tprivate _tracker: Tracker;\r\n\tprivate _isActive: boolean = false;\r\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;\r\n\tprivate _lastUrl: string = '';\r\n\tprivate _pageStartTime: number;\r\n\tprivate _originalPushState: typeof history.pushState = () => null;\r\n\tprivate _originalReplaceState: typeof history.replaceState = () => null;\r\n\r\n\tconstructor(tracker: Tracker) {\r\n\t\tthis._tracker = tracker;\r\n\t\tthis._pageStartTime = performance.now();\r\n\t\tthis._lastUrl = typeof window !== 'undefined' ? window.location.href : '';\r\n\r\n\t\t// Guardar referencias originales de history\r\n\t\tif (typeof window !== 'undefined' && typeof history !== 'undefined') {\r\n\t\t\tthis._originalPushState = history.pushState.bind(history);\r\n\t\t\tthis._originalReplaceState = history.replaceState.bind(history);\r\n\t\t}\r\n\t}\r\n\r\n\t/** Inicia el observer */\r\n\tstart(): void {\r\n\t\tif (this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// Configurar interceptación de history\r\n\t\t\tthis._interceptHistory();\r\n\r\n\t\t\t// Agregar listener de popstate (botones atrás/adelante)\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('popstate', this._handlePopState);\r\n\t\t\t}\r\n\r\n\t\t\t// Iniciar intervalo de polling (fallback)\r\n\t\t\tthis._intervalId = setInterval(() => this._checkUrlChange(), POLL_INTERVAL);\r\n\r\n\t\t\t// Iniciar intervalo de heartbeat\r\n\t\t\tthis._heartbeatIntervalId = setInterval(() => this._sendHeartbeat(), HEARTBEAT_INTERVAL);\r\n\r\n\t\t\t// Eventos para cierre de página\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.addEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.addEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = true;\r\n\r\n\t\t\t// Verificar si hay una página previa guardada (para F5)\r\n\t\t\t// Solo después de que el tracker esté inicializado\r\n\t\t\tsetTimeout(() => this._checkPreviousPage(), 0);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Detiene el observer */\r\n\tstop(): void {\r\n\t\tif (!this._isActive) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tif (this._intervalId) {\r\n\t\t\t\tclearInterval(this._intervalId);\r\n\t\t\t\tthis._intervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (this._heartbeatIntervalId) {\r\n\t\t\t\tclearInterval(this._heartbeatIntervalId);\r\n\t\t\t\tthis._heartbeatIntervalId = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (typeof window !== 'undefined') {\r\n\t\t\t\twindow.removeEventListener('popstate', this._handlePopState);\r\n\t\t\t\twindow.removeEventListener('beforeunload', this._handleBeforeUnload);\r\n\t\t\t\tdocument.removeEventListener('visibilitychange', this._handleVisibilityChange);\r\n\t\t\t}\r\n\r\n\t\t\t// Restaurar history original\r\n\t\t\tif (typeof history !== 'undefined') {\r\n\t\t\t\thistory.pushState = this._originalPushState;\r\n\t\t\t\thistory.replaceState = this._originalReplaceState;\r\n\t\t\t}\r\n\r\n\t\t\tthis._isActive = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Intercepta history.pushState y history.replaceState */\r\n\tprivate _interceptHistory(): void {\r\n\t\tif (typeof window === 'undefined' || typeof history === 'undefined') {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst self = this;\r\n\t\tconst originalPushState = history.pushState.bind(history) as (...args: unknown[]) => unknown;\r\n\t\tconst originalReplaceState = history.replaceState.bind(history) as (...args: unknown[]) => unknown;\r\n\r\n\t\t// Interceptar pushState\r\n\t\thistory.pushState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalPushState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.pushState;\r\n\r\n\t\t// Interceptar replaceState\r\n\t\thistory.replaceState = function (...args: unknown[]) {\r\n\t\t\tconst result = originalReplaceState(...args);\r\n\t\t\tself._handleNavigationChange();\r\n\t\t\treturn result;\r\n\t\t} as typeof history.replaceState;\r\n\t}\r\n\r\n\t/** Manejador de popstate (botones atrás/adelante) */\r\n\tprivate _handlePopState = (): void => {\r\n\t\ttry {\r\n\t\t\tthis._handleNavigationChange();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejo centralizado de cambio de navegación */\r\n\tprivate _handleNavigationChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.href;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Calcular duración wall-clock de la página anterior\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t\t// Solo enviar page_leave si la duración es significativa\r\n\t\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\t\t// Enviar page_leave INMEDIATAMENTE para la página anterior\r\n\t\t\t\t\tthis._sendPageLeaveImmediate(this._lastUrl, duration);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Track page view de la nueva página\r\n\t\t\t\tthis._tracker.trackPageView();\r\n\r\n\t\t\t\t// Resetear contador para la nueva página\r\n\t\t\t\tthis._pageStartTime = performance.now();\r\n\r\n\t\t\t\t// Actualizar la última URL\r\n\t\t\t\tthis._lastUrl = currentUrl;\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Envía page_leave inmediatamente sin esperar el buffer */\r\n\tprivate _sendPageLeaveImmediate(path: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\t// Crear el evento directamente\r\n\t\t\tthis._tracker.trackPageLeave(duration, path);\r\n\r\n\t\t\t// También guardar en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(path, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Manejador de beforeunload (cuando el usuario sale de la página) */\r\n\tprivate _handleBeforeUnload = (): void => {\r\n\t\ttry {\r\n\t\t\t// Calcular duración wall-clock\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Solo enviar si la duración es significativa\r\n\t\t\tif (duration >= MIN_DURATION) {\r\n\t\t\t\tthis._tracker.trackPageLeave(duration, this._lastUrl);\r\n\t\t\t}\r\n\r\n\t\t\t// Guardar URL actual en sessionStorage para F5\r\n\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Manejador de visibilitychange (para detectar cuando la app pasa a segundo plano) */\r\n\tprivate _handleVisibilityChange = (): void => {\r\n\t\ttry {\r\n\t\t\tif (typeof document === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cuando la página se oculta, guardar el tiempo actual en sessionStorage\r\n\t\t\tif (document.visibilityState === 'hidden') {\r\n\t\t\t\tconst duration = this._calculateWallClockDuration();\r\n\t\t\t\tthis._saveToStorage(this._lastUrl, duration);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t};\r\n\r\n\t/** Verifica si la URL cambió (fallback de polling) */\r\n\tprivate _checkUrlChange(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof window === 'undefined') {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst currentUrl = window.location.href;\r\n\r\n\t\t\t// Si la URL cambió\r\n\t\t\tif (currentUrl !== this._lastUrl) {\r\n\t\t\t\t// Usar el manejo centralizado\r\n\t\t\t\tthis._handleNavigationChange();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Calcula la duración wall-clock (tiempo total desde que entró a la página) */\r\n\tprivate _calculateWallClockDuration(): number {\r\n\t\treturn (performance.now() - this._pageStartTime) / 1000;\r\n\t}\r\n\r\n\t/** Envía un heartbeat con la duración actual */\r\n\tprivate _sendHeartbeat(): void {\r\n\t\ttry {\r\n\t\t\tconst duration = this._calculateWallClockDuration();\r\n\r\n\t\t\t// Usar trackPageLeave para mantener compatibilidad\r\n\t\t\t// El backend puede filtrar por eventType = 'page_heartbeat'\r\n\t\t\tthis._tracker.trackEvent({\r\n\t\t\t\teventType: 'page_heartbeat',\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath: this._lastUrl,\r\n\t\t\t\t\tduration: duration,\r\n\t\t\t\t\ttimestamp: new Date().toISOString(),\r\n\t\t\t\t},\r\n\t\t\t} as import('../types/index').TrackerEvent);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Guarda la URL y tiempo en sessionStorage (para F5) */\r\n\tprivate _saveToStorage(url: string, duration: number): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_URL, url);\r\n\t\t\t\tsessionStorage.setItem(STORAGE_KEY_TIME, String(duration));\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Lee la URL y tiempo previos de sessionStorage (para F5) */\r\n\tprivate _getFromStorage(): { url: string; duration: number; } | null {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tconst url = sessionStorage.getItem(STORAGE_KEY_URL);\r\n\t\t\t\tconst duration = sessionStorage.getItem(STORAGE_KEY_TIME);\r\n\t\t\t\tif (url && duration) {\r\n\t\t\t\t\treturn { url, duration: parseFloat(duration) };\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/** Limpia sessionStorage */\r\n\tprivate _clearStorage(): void {\r\n\t\ttry {\r\n\t\t\tif (typeof sessionStorage !== 'undefined') {\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_URL);\r\n\t\t\t\tsessionStorage.removeItem(STORAGE_KEY_TIME);\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Ignore storage errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si hay una página previa (para F5) */\r\n\tprivate _checkPreviousPage(): void {\r\n\t\tconst previous = this._getFromStorage();\r\n\t\tif (previous && previous.duration >= MIN_DURATION) {\r\n\t\t\t// La página anterior tenía una duración significativa, enviar page_leave\r\n\t\t\tthis._tracker.trackPageLeave(previous.duration, previous.url);\r\n\t\t}\r\n\t\t// Limpiar storage después de procesar\r\n\t\tthis._clearStorage();\r\n\t}\r\n}\r\n","{\r\n\t\"name\": \"@agroideas/event-tracker-sdk\",\r\n\t\"version\": \"1.0.8\",\r\n\t\"description\": \"A lightweight, framework-agnostic SDK for tracking frontend user behavior\",\r\n\t\"license\": \"MIT\",\r\n\t\"author\": \"Agroideas\",\r\n\t\"homepage\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://dev.azure.com/agroideassa/Sistemas/_git/event-tracker-front\"\r\n\t},\r\n\t\"main\": \"./dist/index.cjs\",\r\n\t\"module\": \"./dist/index.mjs\",\r\n\t\"types\": \"./dist/index.d.ts\",\r\n\t\"exports\": {\r\n\t\t\".\": {\r\n\t\t\t\"types\": \"./dist/index.d.ts\",\r\n\t\t\t\"import\": \"./dist/index.mjs\",\r\n\t\t\t\"require\": \"./dist/index.cjs\"\r\n\t\t},\r\n\t\t\"./iife\": \"./dist/index.iife.js\"\r\n\t},\r\n\t\"files\": [\r\n\t\t\"dist\"\r\n\t],\r\n\t\"scripts\": {\r\n\t\t\"build\": \"tsup\",\r\n\t\t\"dev\": \"tsup --watch\",\r\n\t\t\"lint\": \"eslint src --ext .ts\",\r\n\t\t\"lint:fix\": \"eslint src --ext .ts --fix\",\r\n\t\t\"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\r\n\t\t\"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\r\n\t\t\"typecheck\": \"tsc --noEmit\",\r\n\t\t\"prepare\": \"husky install\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"@types/node\": \"^20.10.0\",\r\n\t\t\"eslint\": \"^8.55.0\",\r\n\t\t\"husky\": \"^8.0.3\",\r\n\t\t\"prettier\": \"^3.1.0\",\r\n\t\t\"tsup\": \"^8.0.1\",\r\n\t\t\"typescript\": \"^5.3.2\"\r\n\t},\r\n\t\"keywords\": [\r\n\t\t\"tracking\",\r\n\t\t\"analytics\",\r\n\t\t\"sdk\",\r\n\t\t\"events\",\r\n\t\t\"behavior\",\r\n\t\t\"frontend\"\r\n\t],\r\n\t\"engines\": {\r\n\t\t\"node\": \">=18.0.0\"\r\n\t}\r\n}","/**\n * Version constants for the Event Tracker SDK\n */\n\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION = pkg.version;\n","/**\r\n * Tracker: Singleton principal que coordina observers y EventBuffer\r\n */\r\n\r\nimport type { TrackerConfig, TrackerEvent, UserIdentity, EventContext } from '../types';\r\nimport { EventBuffer } from './EventBuffer';\r\nimport { ClickObserver } from '../observers/ClickObserver';\r\nimport { HistoryObserver } from '../observers/HistoryObserver';\r\nimport {\r\n\tgetCurrentPath,\r\n\tgetReferrer,\r\n\tgetScreenSize,\r\n\tgetTimestamp,\r\n\tgetOrCreateSessionId,\r\n\tgetViewportSize,\r\n\tgetDeviceType,\r\n\tgetBrowser,\r\n\tgetOS,\r\n\tgetLanguage,\r\n} from '../utils/metadata';\r\nimport { generateUUID } from '../utils/uuid';\r\nimport { VERSION } from '../version';\r\n\r\nexport class Tracker {\r\n\tprivate static _instance: Tracker | null = null;\r\n\tprivate _eventBuffer: EventBuffer | null = null;\r\n\tprivate _clickObserver: ClickObserver | null = null;\r\n\tprivate _historyObserver: HistoryObserver | null = null;\r\n\tprivate _userIdentity: UserIdentity;\r\n\tprivate _sessionId: string;\r\n\tprivate _isInitialized: boolean = false;\r\n\tprivate _isPaused: boolean = false;\r\n\tprivate _config: TrackerConfig | null = null;\r\n\r\n\tprivate constructor() {\r\n\t\tthis._userIdentity = {\r\n\t\t\tuserId: null,\r\n\t\t\tuserName: null,\r\n\t\t};\r\n\t\tthis._sessionId = getOrCreateSessionId();\r\n\t}\r\n\r\n\t/** Obtiene la instancia única del Tracker */\r\n\tstatic getInstance(): Tracker {\r\n\t\tif (Tracker._instance === null) {\r\n\t\t\tTracker._instance = new Tracker();\r\n\t\t}\r\n\t\treturn Tracker._instance;\r\n\t}\r\n\r\n\t/** Construye el contexto completo del evento */\r\n\tprivate _buildContext(): EventContext {\r\n\t\treturn {\r\n\t\t\tsessionId: this._sessionId,\r\n\t\t\tuserId: this._userIdentity.userId,\r\n\t\t\tuserName: this._userIdentity.userName,\r\n\t\t\turl: window.location.href,\r\n\t\t\treferrer: getReferrer(),\r\n\t\t\tscreenSize: getScreenSize(),\r\n\t\t\tviewportSize: getViewportSize(),\r\n\t\t\tdeviceType: getDeviceType(),\r\n\t\t\tbrowser: getBrowser(),\r\n\t\t\tos: getOS(),\r\n\t\t\tlanguage: getLanguage(),\r\n\t\t\ttimestamp: getTimestamp(),\r\n\t\t};\r\n\t}\r\n\r\n\t/** Verifica si una ruta debe ser excluida del tracking */\r\n\tprivate _shouldExclude(path: string): boolean {\r\n\t\tif (!this._config) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst { excludePatterns } = this._config;\r\n\r\n\t\t// Normalizar el path (quitar trailing slash para comparación consistente)\r\n\t\tconst normalizedPath = path.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;\r\n\r\n\t\t// Si hay patrones de exclusión, verificarlos\r\n\t\tif (excludePatterns && excludePatterns.length > 0) {\r\n\t\t\treturn excludePatterns.some((pattern) => {\r\n\t\t\t\tif (typeof pattern === 'string') {\r\n\t\t\t\t\t// Normalizar el patrón también\r\n\t\t\t\t\tconst normalizedPattern = pattern.endsWith('/') && pattern.length > 1 ? pattern.slice(0, -1) : pattern;\r\n\t\t\t\t\t// Coincidencia exacta o prefijo\r\n\t\t\t\t\treturn normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern);\r\n\t\t\t\t}\r\n\t\t\t\t// Es RegExp\r\n\t\t\t\treturn pattern.test(normalizedPath) || pattern.test(path);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/** Inicializa el tracker con la configuración */\r\n\tinit(config: TrackerConfig): void {\r\n\t\ttry {\r\n\t\t\t// Guardar configuración\r\n\t\t\tthis._config = config;\r\n\r\n\t\t\t// Actualizamos el usuario cada vez que se llama init (permite actualizar userId/userName)\r\n\t\t\tif (config.userId) {\r\n\t\t\t\tthis._userIdentity = {\r\n\t\t\t\t\tuserId: config.userId,\r\n\t\t\t\t\tuserName: config.userName ?? null,\r\n\t\t\t\t};\r\n\t\t\t\tif (this._isInitialized && config.debug) {\r\n\t\t\t\t\tconsole.log('[EventTracker] Usuario actualizado:', this._userIdentity);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Solo inicializar EventBuffer y observers la primera vez\r\n\t\t\tif (this._isInitialized) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tthis._eventBuffer = new EventBuffer(config);\r\n\t\t\tthis._isInitialized = true;\r\n\r\n\t\t\t// Imprimir versión del tracker\r\n\t\t\tconsole.log(`🚀 [EventTracker] Inicializado v${VERSION}`);\r\n\r\n\t\t\t// Iniciar observers\r\n\t\t\tthis._clickObserver = new ClickObserver(this);\r\n\t\t\tthis._historyObserver = new HistoryObserver(this);\r\n\t\t\tthis._clickObserver.start();\r\n\t\t\tthis._historyObserver.start();\r\n\r\n\t\t\t// Enviar page_view inicial\r\n\t\t\tthis.trackPageView();\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Identifica al usuario */\r\n\tidentify(userId: string, userName?: string): void {\r\n\t\ttry {\r\n\t\t\tthis._userIdentity = {\r\n\t\t\t\tuserId,\r\n\t\t\t\tuserName: userName ?? null,\r\n\t\t\t};\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Registra un evento */\r\n\ttrackEvent(event: TrackerEvent): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!this._eventBuffer) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Verificar si la URL actual debe ser excluida\r\n\t\t\tconst currentPath = getCurrentPath();\r\n\t\t\tif (this._shouldExclude(currentPath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst eventWithContext: TrackerEvent = {\r\n\t\t\t\t...event,\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t};\r\n\r\n\t\t\tthis._eventBuffer.add(eventWithContext);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page view */\r\n\ttrackPageView(): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst path = getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(path)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_view',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tpath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Crea un evento de page leave */\r\n\ttrackPageLeave(duration: number, path?: string): void {\r\n\t\ttry {\r\n\t\t\t// Verificar si el tracker está pausado\r\n\t\t\tif (this._isPaused) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst pagePath = path ?? getCurrentPath();\r\n\r\n\t\t\t// Verificar si la URL debe ser excluida\r\n\t\t\tif (this._shouldExclude(pagePath)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tconst event: TrackerEvent = {\r\n\t\t\t\teventType: 'page_leave',\r\n\t\t\t\teventId: generateUUID(),\r\n\t\t\t\tcontext: this._buildContext(),\r\n\t\t\t\tdata: {\r\n\t\t\t\t\tduration,\r\n\t\t\t\t\tpath: pagePath,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\r\n\t\t\tthis.trackEvent(event);\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Fuerza el envío de todos los eventos pendientes */\r\n\tasync flush(): Promise<void> {\r\n\t\ttry {\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tawait this._eventBuffer.flush();\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Verifica si el tracker está inicializado */\r\n\tisInitialized(): boolean {\r\n\t\treturn this._isInitialized;\r\n\t}\r\n\r\n\t/** Pausa el tracker (no registrará eventos) */\r\n\tpause(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = true;\r\n\t\t\tconsole.log('⏸️ [EventTracker] Tracker pausado - No se registrarán eventos');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Reanuda el tracker (volverá a registrar eventos) */\r\n\tresume(): void {\r\n\t\ttry {\r\n\t\t\tthis._isPaused = false;\r\n\t\t\tconsole.log('▶️ [EventTracker] Tracker reanudado - Eventos serán registrados');\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Alterna el modo debug */\r\n\ttoggleDebug(): void {\r\n\t\ttry {\r\n\t\t\tif (!this._config) {\r\n\t\t\t\tconsole.log('⚠️ [EventTracker] Error: Tracker no inicializado');\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Cambiar el valor de debug\r\n\t\t\tthis._config.debug = this._config.debug ? '' : 'true';\r\n\r\n\t\t\tif (this._config.debug) {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug ACTIVADO ✅');\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('🔍 [EventTracker] Modo debug DESACTIVADO ❌');\r\n\t\t\t}\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n\r\n\t/** Destruye el tracker */\r\n\tdestroy(): void {\r\n\t\ttry {\r\n\t\t\tif (this._clickObserver) {\r\n\t\t\t\tthis._clickObserver.stop();\r\n\t\t\t\tthis._clickObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._historyObserver) {\r\n\t\t\t\tthis._historyObserver.stop();\r\n\t\t\t\tthis._historyObserver = null;\r\n\t\t\t}\r\n\t\t\tif (this._eventBuffer) {\r\n\t\t\t\tthis._eventBuffer.destroy();\r\n\t\t\t\tthis._eventBuffer = null;\r\n\t\t\t}\r\n\t\t\tthis._isInitialized = false;\r\n\t\t} catch {\r\n\t\t\t// Never throw errors\r\n\t\t}\r\n\t}\r\n}\r\n","/**\r\n * Event Tracker SDK\r\n *\r\n * SDK ligero y agnóstico al framework para tracking de comportamiento frontend.\r\n *\r\n * @example\r\n * import { tracker } from '@agroideas/event-tracker-sdk';\r\n *\r\n * tracker.init({\r\n * apiKey: 'tu-api-key',\r\n * endpoint: 'https://tu-api.com/events'\r\n * });\r\n *\r\n * // Identificar usuario\r\n * tracker.identify('user-123', 'Juan Pérez');\r\n */\r\n\r\nimport { Tracker } from './core/Tracker';\r\nimport type { TrackerConfig } from './types';\r\n\r\n/** Instancia única exportada del Tracker */\r\nexport const tracker = Tracker.getInstance();\r\n\r\n/**\r\n * Inicializa el tracker con configuración requerida (apiKey y endpoint).\r\n */\r\nexport function init(config: TrackerConfig): void {\r\n\ttracker.init(config);\r\n}\r\n\r\n/** Identifica al usuario */\r\nexport function identify(userId: string, userName?: string): void {\r\n\ttracker.identify(userId, userName);\r\n}\r\n\r\n/** Fuerza el envío de eventos pendientes */\r\nexport function flush(): Promise<void> {\r\n\treturn tracker.flush();\r\n}\r\n\r\n/** Destruye el tracker */\r\nexport function destroy(): void {\r\n\ttracker.destroy();\r\n}\r\n\r\nexport type { TrackerConfig };\r\n"],"mappings":"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","names":["tracker","tracker","Tracker","Tracker"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agroideas/event-tracker-sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "A lightweight, framework-agnostic SDK for tracking frontend user behavior",
5
5
  "license": "MIT",
6
6
  "author": "Agroideas",