@litemetrics/tracker 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @litemetrics/tracker
2
+
3
+ Lightweight browser analytics tracker for Litemetrics. **~3.5KB gzipped.**
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @litemetrics/tracker
9
+ ```
10
+
11
+ Or use the script tag (no build step needed):
12
+
13
+ ```html
14
+ <script src="https://your-server.com/litemetrics.js"></script>
15
+ <script>
16
+ Litemetrics.createTracker({
17
+ siteId: 'your-site-id',
18
+ endpoint: 'https://your-server.com/api/collect',
19
+ });
20
+ </script>
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import { createTracker } from '@litemetrics/tracker';
27
+
28
+ const tracker = createTracker({
29
+ siteId: 'your-site-id',
30
+ endpoint: 'https://your-server.com/api/collect',
31
+ });
32
+
33
+ // Track custom events
34
+ tracker.track('Signup', { plan: 'pro' });
35
+
36
+ // Identify users
37
+ tracker.identify('user-123', { name: 'John', email: 'john@example.com' });
38
+
39
+ // Manual page tracking
40
+ tracker.page();
41
+ ```
42
+
43
+ ## Auto-Tracking
44
+
45
+ When `autoTrack` is enabled (default), the tracker automatically captures:
46
+
47
+ - **Pageviews** - Initial page load
48
+ - **SPA Navigation** - Route changes via History API
49
+ - **Data Attributes** - Clicks on `data-litemetrics-event="EventName"` elements
50
+ - **Outbound Links** - Clicks on external links
51
+ - **File Downloads** - Clicks on links to `.pdf`, `.zip`, `.doc`, `.csv`, etc.
52
+ - **Scroll Depth** - Milestones at 25%, 50%, 75%, 90%
53
+ - **Rage Clicks** - 3+ rapid clicks in the same area
54
+
55
+ ## Configuration
56
+
57
+ ```ts
58
+ createTracker({
59
+ siteId: 'your-site-id', // Required
60
+ endpoint: '/api/collect', // Required
61
+ autoTrack: true, // Auto-track pageviews (default: true)
62
+ autoSpa: true, // Auto-track SPA navigation (default: true)
63
+ autoOutbound: true, // Track outbound link clicks (default: true)
64
+ autoFileDownloads: true, // Track file downloads (default: true)
65
+ autoScrollDepth: true, // Track scroll depth milestones (default: true)
66
+ autoRageClicks: true, // Detect rage clicks (default: true)
67
+ batchSize: 10, // Events per batch (default: 10)
68
+ flushInterval: 5000, // Flush interval in ms (default: 5000)
69
+ respectDnt: true, // Respect Do Not Track (default: true)
70
+ debug: false, // Console logging (default: false)
71
+ });
72
+ ```
73
+
74
+ ## Data Attribute Tracking
75
+
76
+ Track events declaratively with HTML attributes:
77
+
78
+ ```html
79
+ <button
80
+ data-litemetrics-event="Add to Cart"
81
+ data-litemetrics-event-product="T-Shirt"
82
+ data-litemetrics-event-price="29.99"
83
+ >
84
+ Add to Cart
85
+ </button>
86
+ ```
87
+
88
+ ## Privacy
89
+
90
+ - Respects `Do Not Track` browser setting
91
+ - No cookies - uses `localStorage` for session/visitor IDs
92
+ - `opt_out()` / `opt_in()` methods for user consent
93
+ - All data sent to your own server (self-hosted)
94
+
95
+ ## License
96
+
97
+ MIT
@@ -307,6 +307,106 @@ var AutoTracker = class {
307
307
  }, 0);
308
308
  }
309
309
  };
310
+ var FILE_EXTENSIONS = /\.(pdf|zip|doc|docx|xls|xlsx|csv|mp3|mp4|dmg|exe|rar|7z|gz|tar)$/i;
311
+ function initOutboundTracking(instance) {
312
+ function handleClick(e) {
313
+ const link = e.target?.closest?.("a");
314
+ if (!link) return;
315
+ const href = link.href;
316
+ if (!href || href.startsWith("javascript:") || href.startsWith("#")) return;
317
+ try {
318
+ const url = new URL(href, location.href);
319
+ if (url.hostname && url.hostname !== location.hostname) {
320
+ instance.track("Outbound Link", { url: href });
321
+ }
322
+ } catch {
323
+ }
324
+ }
325
+ document.addEventListener("click", handleClick, true);
326
+ return () => document.removeEventListener("click", handleClick, true);
327
+ }
328
+ function initFileDownloadTracking(instance) {
329
+ function handleClick(e) {
330
+ const link = e.target?.closest?.("a");
331
+ if (!link) return;
332
+ const href = link.href;
333
+ if (!href) return;
334
+ try {
335
+ const url = new URL(href, location.href);
336
+ const match = url.pathname.match(FILE_EXTENSIONS);
337
+ if (match) {
338
+ instance.track("File Download", { url: href, extension: match[1].toLowerCase() });
339
+ }
340
+ } catch {
341
+ }
342
+ }
343
+ document.addEventListener("click", handleClick, true);
344
+ return () => document.removeEventListener("click", handleClick, true);
345
+ }
346
+ function initScrollDepthTracking(instance) {
347
+ const milestones = [25, 50, 75, 90];
348
+ const reached = /* @__PURE__ */ new Set();
349
+ let lastPath = location.pathname;
350
+ let ticking = false;
351
+ function check() {
352
+ ticking = false;
353
+ if (location.pathname !== lastPath) {
354
+ lastPath = location.pathname;
355
+ reached.clear();
356
+ }
357
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
358
+ const docHeight = Math.max(
359
+ document.body.scrollHeight,
360
+ document.documentElement.scrollHeight
361
+ );
362
+ const viewHeight = window.innerHeight;
363
+ const scrollable = docHeight - viewHeight;
364
+ if (scrollable <= 0) return;
365
+ const pct = scrollTop / scrollable * 100;
366
+ for (const m of milestones) {
367
+ if (pct >= m && !reached.has(m)) {
368
+ reached.add(m);
369
+ instance.track("Scroll Depth", { depth: `${m}%` });
370
+ }
371
+ }
372
+ }
373
+ function onScroll() {
374
+ if (!ticking) {
375
+ ticking = true;
376
+ requestAnimationFrame(check);
377
+ }
378
+ }
379
+ window.addEventListener("scroll", onScroll, { passive: true });
380
+ return () => window.removeEventListener("scroll", onScroll);
381
+ }
382
+ function initRageClickTracking(instance) {
383
+ const clicks = [];
384
+ const THRESHOLD = 3;
385
+ const TIME_WINDOW = 800;
386
+ const DISTANCE = 30;
387
+ function handleClick(e) {
388
+ const now2 = Date.now();
389
+ clicks.push({ time: now2, x: e.clientX, y: e.clientY });
390
+ while (clicks.length > 0 && now2 - clicks[0].time > TIME_WINDOW) {
391
+ clicks.shift();
392
+ }
393
+ if (clicks.length >= THRESHOLD) {
394
+ const first = clicks[0];
395
+ const allClose = clicks.every(
396
+ (c) => Math.abs(c.x - first.x) < DISTANCE && Math.abs(c.y - first.y) < DISTANCE
397
+ );
398
+ if (allClose) {
399
+ const target = e.target;
400
+ const tagName = target?.tagName?.toLowerCase() || "unknown";
401
+ const text = (target?.innerText || "").slice(0, 50).trim();
402
+ instance.track("Rage Click", { element: tagName, text: text || void 0 });
403
+ clicks.length = 0;
404
+ }
405
+ }
406
+ }
407
+ document.addEventListener("click", handleClick, true);
408
+ return () => document.removeEventListener("click", handleClick, true);
409
+ }
310
410
 
311
411
  // src/attributes.ts
312
412
  var ATTR_EVENT = "data-litemetrics-event";
@@ -362,6 +462,12 @@ function createTracker(config) {
362
462
  }
363
463
  } catch {
364
464
  }
465
+ const {
466
+ autoOutbound = true,
467
+ autoFileDownloads = true,
468
+ autoScrollDepth = true,
469
+ autoRageClicks = true
470
+ } = config;
365
471
  const session = new SessionManager();
366
472
  const transport = new Transport({
367
473
  endpoint,
@@ -371,6 +477,7 @@ function createTracker(config) {
371
477
  });
372
478
  let autoTracker = null;
373
479
  let cleanupAttributes = null;
480
+ const autoCleanups = [];
374
481
  let optedOut = false;
375
482
  function getContext() {
376
483
  const ctx = {};
@@ -477,12 +584,17 @@ function createTracker(config) {
477
584
  },
478
585
  destroy() {
479
586
  cleanupAttributes?.();
587
+ autoCleanups.forEach((fn) => fn());
480
588
  autoTracker?.stop();
481
589
  transport.destroy();
482
590
  }
483
591
  };
484
592
  if (autoTrack && typeof document !== "undefined") {
485
593
  cleanupAttributes = initAttributeTracking(instance);
594
+ if (autoOutbound) autoCleanups.push(initOutboundTracking(instance));
595
+ if (autoFileDownloads) autoCleanups.push(initFileDownloadTracking(instance));
596
+ if (autoScrollDepth) autoCleanups.push(initScrollDepthTracking(instance));
597
+ if (autoRageClicks) autoCleanups.push(initRageClickTracking(instance));
486
598
  }
487
599
  return instance;
488
600
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/tracker.ts","../src/session.ts","../src/utils.ts","../src/transport.ts","../src/auto.ts","../src/attributes.ts"],"sourcesContent":["export { createTracker } from './tracker';\nexport type { LitemetricsInstance } from './tracker';\nexport type { TrackerConfig } from '@litemetrics/core';\n","import type {\n TrackerConfig,\n ClientEvent,\n PageviewEvent,\n CustomEvent,\n IdentifyEvent,\n ClientContext,\n} from '@litemetrics/core';\nimport { STORAGE_KEY_OPTOUT } from '@litemetrics/core';\nimport { SessionManager } from './session';\nimport { Transport } from './transport';\nimport { AutoTracker } from './auto';\nimport { parseUTM, now } from './utils';\nimport { initAttributeTracking } from './attributes';\n\nexport interface LitemetricsInstance {\n track(name: string, properties?: Record<string, unknown>): void;\n identify(userId: string, traits?: Record<string, unknown>): void;\n page(url?: string, title?: string): void;\n reset(): void;\n opt_out(): void;\n opt_in(): void;\n destroy(): void;\n}\n\nexport function createTracker(config: TrackerConfig): LitemetricsInstance {\n const {\n siteId,\n endpoint,\n autoTrack = true,\n autoSpa = true,\n debug = false,\n respectDnt = true,\n } = config;\n\n // Check Do Not Track\n if (respectDnt && typeof navigator !== 'undefined' && navigator.doNotTrack === '1') {\n return createNoopTracker();\n }\n\n // Check opt-out\n try {\n if (localStorage.getItem(STORAGE_KEY_OPTOUT) === '1') {\n return createNoopTracker();\n }\n } catch {\n // ignore\n }\n\n const session = new SessionManager();\n const transport = new Transport({\n endpoint,\n batchSize: config.batchSize,\n flushInterval: config.flushInterval,\n debug,\n });\n let autoTracker: AutoTracker | null = null;\n let cleanupAttributes: (() => void) | null = null;\n let optedOut = false;\n\n function getContext(): ClientContext {\n const ctx: ClientContext = {};\n if (typeof screen !== 'undefined') {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n if (typeof navigator !== 'undefined') {\n ctx.language = navigator.language;\n // Network Information API\n const conn = (navigator as any).connection;\n if (conn) {\n ctx.connection = {\n type: conn.type,\n downlink: conn.downlink,\n effectiveType: conn.effectiveType,\n rtt: conn.rtt,\n };\n }\n }\n if (typeof Intl !== 'undefined') {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n const utm = parseUTM();\n if (utm) ctx.utm = utm;\n return ctx;\n }\n\n async function sendEvent(event: ClientEvent): Promise<void> {\n if (optedOut) return;\n session.touch();\n transport.send(event);\n }\n\n async function trackPage(url?: string, title?: string, referrer?: string): Promise<void> {\n const visitorId = await session.getVisitorId();\n const event: PageviewEvent & ClientContext = {\n type: 'pageview',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n url: url || (typeof location !== 'undefined' ? location.href : ''),\n referrer: referrer || (typeof document !== 'undefined' ? document.referrer : undefined),\n title: title || (typeof document !== 'undefined' ? document.title : undefined),\n ...getContext(),\n };\n sendEvent(event);\n }\n\n // Auto-track initial page view\n if (autoTrack) {\n trackPage();\n }\n\n // Auto-track SPA navigation\n if (autoSpa) {\n autoTracker = new AutoTracker((url, ref) => trackPage(url, undefined, ref));\n autoTracker.startSPA();\n }\n\n // We need a reference to the instance for attribute tracking\n // so we create it first then init attributes\n const instance: LitemetricsInstance = {\n track(name: string, properties?: Record<string, unknown>): void {\n session.getVisitorId().then((visitorId) => {\n const event: CustomEvent & ClientContext = {\n type: 'event',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n name,\n properties,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n session.identify(userId);\n session.getVisitorId().then((visitorId) => {\n const event: IdentifyEvent & ClientContext = {\n type: 'identify',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n userId,\n traits,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n page(url?: string, title?: string): void {\n trackPage(url, title);\n },\n\n reset(): void {\n session.reset();\n },\n\n opt_out(): void {\n optedOut = true;\n try {\n localStorage.setItem(STORAGE_KEY_OPTOUT, '1');\n } catch {\n // ignore\n }\n },\n\n opt_in(): void {\n optedOut = false;\n try {\n localStorage.removeItem(STORAGE_KEY_OPTOUT);\n } catch {\n // ignore\n }\n },\n\n destroy(): void {\n cleanupAttributes?.();\n autoTracker?.stop();\n transport.destroy();\n },\n };\n\n // Initialize data-attribute event tracking\n if (autoTrack && typeof document !== 'undefined') {\n cleanupAttributes = initAttributeTracking(instance);\n }\n\n return instance;\n}\n\nfunction createNoopTracker(): LitemetricsInstance {\n return {\n track() {},\n identify() {},\n page() {},\n reset() {},\n opt_out() {},\n opt_in() {},\n destroy() {},\n };\n}\n","import {\n SESSION_TIMEOUT,\n STORAGE_KEY_SESSION,\n STORAGE_KEY_VISITOR,\n STORAGE_KEY_LAST_ACTIVE,\n STORAGE_KEY_USER,\n} from '@litemetrics/core';\nimport { generateId, hashString, getDayString, now } from './utils';\n\nfunction storageGet(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction storageSet(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n // localStorage unavailable (private browsing, etc.)\n }\n}\n\nfunction storageRemove(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n // noop\n }\n}\n\nexport class SessionManager {\n private _sessionId: string;\n private _visitorId: string | null = null;\n private _userId: string | null = null;\n private _hostname: string;\n\n constructor(hostname?: string) {\n this._hostname = hostname || (typeof location !== 'undefined' ? location.hostname : 'unknown');\n this._sessionId = this._getOrCreateSession();\n this._userId = storageGet(STORAGE_KEY_USER);\n }\n\n get sessionId(): string {\n return this._sessionId;\n }\n\n get userId(): string | null {\n return this._userId;\n }\n\n async getVisitorId(): Promise<string> {\n if (this._visitorId) return this._visitorId;\n\n const cached = storageGet(STORAGE_KEY_VISITOR);\n const today = getDayString();\n\n // Visitor ID rotates daily for privacy\n if (cached) {\n const [id, date] = cached.split('|');\n if (date === today) {\n this._visitorId = id;\n return id;\n }\n }\n\n const id = await this._generateVisitorId();\n this._visitorId = id;\n storageSet(STORAGE_KEY_VISITOR, `${id}|${today}`);\n return id;\n }\n\n touch(): void {\n storageSet(STORAGE_KEY_LAST_ACTIVE, now().toString());\n }\n\n identify(userId: string): void {\n this._userId = userId;\n storageSet(STORAGE_KEY_USER, userId);\n }\n\n reset(): void {\n this._sessionId = generateId();\n this._visitorId = null;\n this._userId = null;\n storageRemove(STORAGE_KEY_SESSION);\n storageRemove(STORAGE_KEY_VISITOR);\n storageRemove(STORAGE_KEY_USER);\n storageRemove(STORAGE_KEY_LAST_ACTIVE);\n }\n\n private _getOrCreateSession(): string {\n const stored = storageGet(STORAGE_KEY_SESSION);\n const lastActive = storageGet(STORAGE_KEY_LAST_ACTIVE);\n\n if (stored && lastActive) {\n const elapsed = now() - parseInt(lastActive, 10);\n if (elapsed < SESSION_TIMEOUT) {\n this.touch();\n return stored;\n }\n }\n\n const id = generateId();\n storageSet(STORAGE_KEY_SESSION, id);\n this.touch();\n return id;\n }\n\n private async _generateVisitorId(): Promise<string> {\n const components = [\n this._hostname,\n getDayString(),\n typeof navigator !== 'undefined' ? navigator.userAgent : '',\n typeof navigator !== 'undefined' ? navigator.language : '',\n typeof Intl !== 'undefined'\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : '',\n typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : '',\n ];\n\n const raw = components.join('|');\n const hash = await hashString(raw);\n return hash.slice(0, 16);\n }\n}\n","import type { UTMParams } from '@litemetrics/core';\n\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport async function hashString(input: string): Promise<string> {\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const array = Array.from(new Uint8Array(hash));\n return array.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n // Simple fallback hash\n let h = 0;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) - h + input.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(16).padStart(8, '0');\n}\n\nexport function parseUTM(): UTMParams | undefined {\n if (typeof location === 'undefined') return undefined;\n const params = new URLSearchParams(location.search);\n const utm: UTMParams = {};\n let hasUtm = false;\n\n for (const [key, field] of [\n ['utm_source', 'source'],\n ['utm_medium', 'medium'],\n ['utm_campaign', 'campaign'],\n ['utm_term', 'term'],\n ['utm_content', 'content'],\n ] as const) {\n const val = params.get(key);\n if (val) {\n (utm as Record<string, string>)[field] = val;\n hasUtm = true;\n }\n }\n\n return hasUtm ? utm : undefined;\n}\n\nexport function getDayString(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function now(): number {\n return Date.now();\n}\n","import type { ClientEvent, CollectPayload } from '@litemetrics/core';\nimport { DEFAULT_BATCH_SIZE, DEFAULT_FLUSH_INTERVAL } from '@litemetrics/core';\n\nexport interface TransportOptions {\n endpoint: string;\n batchSize?: number;\n flushInterval?: number;\n debug?: boolean;\n}\n\nexport class Transport {\n private queue: ClientEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private endpoint: string;\n private batchSize: number;\n private flushInterval: number;\n private debug: boolean;\n\n constructor(options: TransportOptions) {\n this.endpoint = options.endpoint;\n this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.debug = options.debug ?? false;\n\n this._startTimer();\n this._setupUnload();\n }\n\n send(event: ClientEvent): void {\n this.queue.push(event);\n if (this.queue.length >= this.batchSize) {\n this.flush();\n }\n }\n\n flush(): void {\n if (this.queue.length === 0) return;\n const events = this.queue.splice(0);\n this._dispatch(events);\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n }\n\n private _dispatch(events: ClientEvent[]): void {\n const payload: CollectPayload = { events };\n const body = JSON.stringify(payload);\n\n if (this.debug) {\n console.log('[litemetrics] sending', events.length, 'events', events);\n }\n\n // Try fetch first, fall back to sendBeacon\n if (typeof fetch !== 'undefined') {\n fetch(this.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n keepalive: true,\n }).catch(() => {\n // Retry once with sendBeacon\n this._beacon(body);\n });\n } else {\n this._beacon(body);\n }\n }\n\n private _beacon(body: string): void {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob([body], { type: 'application/json' });\n navigator.sendBeacon(this.endpoint, blob);\n }\n }\n\n private _startTimer(): void {\n if (typeof setInterval !== 'undefined') {\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n }\n }\n\n private _setupUnload(): void {\n if (typeof document === 'undefined') return;\n\n const onUnload = () => {\n if (this.queue.length === 0) return;\n const payload: CollectPayload = { events: this.queue.splice(0) };\n const body = JSON.stringify(payload);\n // sendBeacon is more reliable during page unload\n this._beacon(body);\n };\n\n // visibilitychange + pagehide is the most reliable combo\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n onUnload();\n }\n });\n\n if (typeof addEventListener !== 'undefined') {\n addEventListener('pagehide', onUnload);\n }\n }\n}\n","export type PageCallback = (url: string, referrer?: string) => void;\n\nexport class AutoTracker {\n private onPage: PageCallback;\n private lastUrl: string = '';\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n\n constructor(onPage: PageCallback) {\n this.onPage = onPage;\n }\n\n startSPA(): void {\n if (typeof history === 'undefined' || typeof addEventListener === 'undefined') return;\n\n this.lastUrl = location.href;\n\n // Monkey-patch pushState and replaceState\n this.originalPushState = history.pushState.bind(history);\n this.originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args);\n this._onNavigation();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args);\n this._onNavigation();\n };\n\n addEventListener('popstate', () => this._onNavigation());\n }\n\n stop(): void {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n }\n }\n\n private _onNavigation(): void {\n // Small delay to let the URL update\n setTimeout(() => {\n const current = location.href;\n if (current !== this.lastUrl) {\n const referrer = this.lastUrl;\n this.lastUrl = current;\n this.onPage(current, referrer);\n }\n }, 0);\n }\n}\n","import type { LitemetricsInstance } from './tracker';\n\nconst ATTR_EVENT = 'data-litemetrics-event';\nconst ATTR_PREFIX = 'data-litemetrics-event-';\n\n/**\n * Initialize data-attribute event tracking.\n * Clicks on elements with `data-litemetrics-event=\"EventName\"` will be auto-tracked.\n * Additional properties via `data-litemetrics-event-*` attributes.\n *\n * Example:\n * <button data-litemetrics-event=\"Signup\" data-litemetrics-event-plan=\"pro\">\n * → tracks event \"Signup\" with { plan: \"pro\" }\n */\nexport function initAttributeTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: Event) {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n\n // Walk up the DOM to find an element with data-litemetrics-event\n let el: HTMLElement | null = target;\n while (el) {\n const eventName = el.getAttribute(ATTR_EVENT);\n if (eventName) {\n // Collect data-litemetrics-event-* properties\n const properties: Record<string, string> = {};\n const attrs = el.attributes;\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n if (attr.name.startsWith(ATTR_PREFIX)) {\n const key = attr.name.slice(ATTR_PREFIX.length);\n properties[key] = attr.value;\n }\n }\n\n instance.track(\n eventName,\n Object.keys(properties).length > 0 ? properties : undefined\n );\n return;\n }\n el = el.parentElement;\n }\n }\n\n document.addEventListener('click', handleClick, true);\n\n return () => {\n document.removeEventListener('click', handleClick, true);\n };\n}\n"],"mappings":"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","names":["import_core","id","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/tracker.ts","../src/session.ts","../src/utils.ts","../src/transport.ts","../src/auto.ts","../src/attributes.ts"],"sourcesContent":["export { createTracker } from './tracker';\nexport type { LitemetricsInstance } from './tracker';\nexport type { TrackerConfig } from '@litemetrics/core';\n","import type {\n TrackerConfig,\n ClientEvent,\n PageviewEvent,\n CustomEvent,\n IdentifyEvent,\n ClientContext,\n} from '@litemetrics/core';\nimport { STORAGE_KEY_OPTOUT } from '@litemetrics/core';\nimport { SessionManager } from './session';\nimport { Transport } from './transport';\nimport { AutoTracker, initOutboundTracking, initFileDownloadTracking, initScrollDepthTracking, initRageClickTracking } from './auto';\nimport { parseUTM, now } from './utils';\nimport { initAttributeTracking } from './attributes';\n\nexport interface LitemetricsInstance {\n track(name: string, properties?: Record<string, unknown>): void;\n identify(userId: string, traits?: Record<string, unknown>): void;\n page(url?: string, title?: string): void;\n reset(): void;\n opt_out(): void;\n opt_in(): void;\n destroy(): void;\n}\n\nexport function createTracker(config: TrackerConfig): LitemetricsInstance {\n const {\n siteId,\n endpoint,\n autoTrack = true,\n autoSpa = true,\n debug = false,\n respectDnt = true,\n } = config;\n\n // Check Do Not Track\n if (respectDnt && typeof navigator !== 'undefined' && navigator.doNotTrack === '1') {\n return createNoopTracker();\n }\n\n // Check opt-out\n try {\n if (localStorage.getItem(STORAGE_KEY_OPTOUT) === '1') {\n return createNoopTracker();\n }\n } catch {\n // ignore\n }\n\n const {\n autoOutbound = true,\n autoFileDownloads = true,\n autoScrollDepth = true,\n autoRageClicks = true,\n } = config;\n\n const session = new SessionManager();\n const transport = new Transport({\n endpoint,\n batchSize: config.batchSize,\n flushInterval: config.flushInterval,\n debug,\n });\n let autoTracker: AutoTracker | null = null;\n let cleanupAttributes: (() => void) | null = null;\n const autoCleanups: (() => void)[] = [];\n let optedOut = false;\n\n function getContext(): ClientContext {\n const ctx: ClientContext = {};\n if (typeof screen !== 'undefined') {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n if (typeof navigator !== 'undefined') {\n ctx.language = navigator.language;\n // Network Information API\n const conn = (navigator as any).connection;\n if (conn) {\n ctx.connection = {\n type: conn.type,\n downlink: conn.downlink,\n effectiveType: conn.effectiveType,\n rtt: conn.rtt,\n };\n }\n }\n if (typeof Intl !== 'undefined') {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n const utm = parseUTM();\n if (utm) ctx.utm = utm;\n return ctx;\n }\n\n async function sendEvent(event: ClientEvent): Promise<void> {\n if (optedOut) return;\n session.touch();\n transport.send(event);\n }\n\n async function trackPage(url?: string, title?: string, referrer?: string): Promise<void> {\n const visitorId = await session.getVisitorId();\n const event: PageviewEvent & ClientContext = {\n type: 'pageview',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n url: url || (typeof location !== 'undefined' ? location.href : ''),\n referrer: referrer || (typeof document !== 'undefined' ? document.referrer : undefined),\n title: title || (typeof document !== 'undefined' ? document.title : undefined),\n ...getContext(),\n };\n sendEvent(event);\n }\n\n // Auto-track initial page view\n if (autoTrack) {\n trackPage();\n }\n\n // Auto-track SPA navigation\n if (autoSpa) {\n autoTracker = new AutoTracker((url, ref) => trackPage(url, undefined, ref));\n autoTracker.startSPA();\n }\n\n // We need a reference to the instance for attribute tracking\n // so we create it first then init attributes\n const instance: LitemetricsInstance = {\n track(name: string, properties?: Record<string, unknown>): void {\n session.getVisitorId().then((visitorId) => {\n const event: CustomEvent & ClientContext = {\n type: 'event',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n name,\n properties,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n session.identify(userId);\n session.getVisitorId().then((visitorId) => {\n const event: IdentifyEvent & ClientContext = {\n type: 'identify',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n userId,\n traits,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n page(url?: string, title?: string): void {\n trackPage(url, title);\n },\n\n reset(): void {\n session.reset();\n },\n\n opt_out(): void {\n optedOut = true;\n try {\n localStorage.setItem(STORAGE_KEY_OPTOUT, '1');\n } catch {\n // ignore\n }\n },\n\n opt_in(): void {\n optedOut = false;\n try {\n localStorage.removeItem(STORAGE_KEY_OPTOUT);\n } catch {\n // ignore\n }\n },\n\n destroy(): void {\n cleanupAttributes?.();\n autoCleanups.forEach((fn) => fn());\n autoTracker?.stop();\n transport.destroy();\n },\n };\n\n // Initialize data-attribute event tracking\n if (autoTrack && typeof document !== 'undefined') {\n cleanupAttributes = initAttributeTracking(instance);\n\n // Enhanced auto-tracking\n if (autoOutbound) autoCleanups.push(initOutboundTracking(instance));\n if (autoFileDownloads) autoCleanups.push(initFileDownloadTracking(instance));\n if (autoScrollDepth) autoCleanups.push(initScrollDepthTracking(instance));\n if (autoRageClicks) autoCleanups.push(initRageClickTracking(instance));\n }\n\n return instance;\n}\n\nfunction createNoopTracker(): LitemetricsInstance {\n return {\n track() {},\n identify() {},\n page() {},\n reset() {},\n opt_out() {},\n opt_in() {},\n destroy() {},\n };\n}\n","import {\n SESSION_TIMEOUT,\n STORAGE_KEY_SESSION,\n STORAGE_KEY_VISITOR,\n STORAGE_KEY_LAST_ACTIVE,\n STORAGE_KEY_USER,\n} from '@litemetrics/core';\nimport { generateId, hashString, getDayString, now } from './utils';\n\nfunction storageGet(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction storageSet(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n // localStorage unavailable (private browsing, etc.)\n }\n}\n\nfunction storageRemove(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n // noop\n }\n}\n\nexport class SessionManager {\n private _sessionId: string;\n private _visitorId: string | null = null;\n private _userId: string | null = null;\n private _hostname: string;\n\n constructor(hostname?: string) {\n this._hostname = hostname || (typeof location !== 'undefined' ? location.hostname : 'unknown');\n this._sessionId = this._getOrCreateSession();\n this._userId = storageGet(STORAGE_KEY_USER);\n }\n\n get sessionId(): string {\n return this._sessionId;\n }\n\n get userId(): string | null {\n return this._userId;\n }\n\n async getVisitorId(): Promise<string> {\n if (this._visitorId) return this._visitorId;\n\n const cached = storageGet(STORAGE_KEY_VISITOR);\n const today = getDayString();\n\n // Visitor ID rotates daily for privacy\n if (cached) {\n const [id, date] = cached.split('|');\n if (date === today) {\n this._visitorId = id;\n return id;\n }\n }\n\n const id = await this._generateVisitorId();\n this._visitorId = id;\n storageSet(STORAGE_KEY_VISITOR, `${id}|${today}`);\n return id;\n }\n\n touch(): void {\n storageSet(STORAGE_KEY_LAST_ACTIVE, now().toString());\n }\n\n identify(userId: string): void {\n this._userId = userId;\n storageSet(STORAGE_KEY_USER, userId);\n }\n\n reset(): void {\n this._sessionId = generateId();\n this._visitorId = null;\n this._userId = null;\n storageRemove(STORAGE_KEY_SESSION);\n storageRemove(STORAGE_KEY_VISITOR);\n storageRemove(STORAGE_KEY_USER);\n storageRemove(STORAGE_KEY_LAST_ACTIVE);\n }\n\n private _getOrCreateSession(): string {\n const stored = storageGet(STORAGE_KEY_SESSION);\n const lastActive = storageGet(STORAGE_KEY_LAST_ACTIVE);\n\n if (stored && lastActive) {\n const elapsed = now() - parseInt(lastActive, 10);\n if (elapsed < SESSION_TIMEOUT) {\n this.touch();\n return stored;\n }\n }\n\n const id = generateId();\n storageSet(STORAGE_KEY_SESSION, id);\n this.touch();\n return id;\n }\n\n private async _generateVisitorId(): Promise<string> {\n const components = [\n this._hostname,\n getDayString(),\n typeof navigator !== 'undefined' ? navigator.userAgent : '',\n typeof navigator !== 'undefined' ? navigator.language : '',\n typeof Intl !== 'undefined'\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : '',\n typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : '',\n ];\n\n const raw = components.join('|');\n const hash = await hashString(raw);\n return hash.slice(0, 16);\n }\n}\n","import type { UTMParams } from '@litemetrics/core';\n\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport async function hashString(input: string): Promise<string> {\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const array = Array.from(new Uint8Array(hash));\n return array.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n // Simple fallback hash\n let h = 0;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) - h + input.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(16).padStart(8, '0');\n}\n\nexport function parseUTM(): UTMParams | undefined {\n if (typeof location === 'undefined') return undefined;\n const params = new URLSearchParams(location.search);\n const utm: UTMParams = {};\n let hasUtm = false;\n\n for (const [key, field] of [\n ['utm_source', 'source'],\n ['utm_medium', 'medium'],\n ['utm_campaign', 'campaign'],\n ['utm_term', 'term'],\n ['utm_content', 'content'],\n ] as const) {\n const val = params.get(key);\n if (val) {\n (utm as Record<string, string>)[field] = val;\n hasUtm = true;\n }\n }\n\n return hasUtm ? utm : undefined;\n}\n\nexport function getDayString(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function now(): number {\n return Date.now();\n}\n","import type { ClientEvent, CollectPayload } from '@litemetrics/core';\nimport { DEFAULT_BATCH_SIZE, DEFAULT_FLUSH_INTERVAL } from '@litemetrics/core';\n\nexport interface TransportOptions {\n endpoint: string;\n batchSize?: number;\n flushInterval?: number;\n debug?: boolean;\n}\n\nexport class Transport {\n private queue: ClientEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private endpoint: string;\n private batchSize: number;\n private flushInterval: number;\n private debug: boolean;\n\n constructor(options: TransportOptions) {\n this.endpoint = options.endpoint;\n this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.debug = options.debug ?? false;\n\n this._startTimer();\n this._setupUnload();\n }\n\n send(event: ClientEvent): void {\n this.queue.push(event);\n if (this.queue.length >= this.batchSize) {\n this.flush();\n }\n }\n\n flush(): void {\n if (this.queue.length === 0) return;\n const events = this.queue.splice(0);\n this._dispatch(events);\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n }\n\n private _dispatch(events: ClientEvent[]): void {\n const payload: CollectPayload = { events };\n const body = JSON.stringify(payload);\n\n if (this.debug) {\n console.log('[litemetrics] sending', events.length, 'events', events);\n }\n\n // Try fetch first, fall back to sendBeacon\n if (typeof fetch !== 'undefined') {\n fetch(this.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n keepalive: true,\n }).catch(() => {\n // Retry once with sendBeacon\n this._beacon(body);\n });\n } else {\n this._beacon(body);\n }\n }\n\n private _beacon(body: string): void {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob([body], { type: 'application/json' });\n navigator.sendBeacon(this.endpoint, blob);\n }\n }\n\n private _startTimer(): void {\n if (typeof setInterval !== 'undefined') {\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n }\n }\n\n private _setupUnload(): void {\n if (typeof document === 'undefined') return;\n\n const onUnload = () => {\n if (this.queue.length === 0) return;\n const payload: CollectPayload = { events: this.queue.splice(0) };\n const body = JSON.stringify(payload);\n // sendBeacon is more reliable during page unload\n this._beacon(body);\n };\n\n // visibilitychange + pagehide is the most reliable combo\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n onUnload();\n }\n });\n\n if (typeof addEventListener !== 'undefined') {\n addEventListener('pagehide', onUnload);\n }\n }\n}\n","import type { LitemetricsInstance } from './tracker';\n\nexport type PageCallback = (url: string, referrer?: string) => void;\n\nexport class AutoTracker {\n private onPage: PageCallback;\n private lastUrl: string = '';\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n\n constructor(onPage: PageCallback) {\n this.onPage = onPage;\n }\n\n startSPA(): void {\n if (typeof history === 'undefined' || typeof addEventListener === 'undefined') return;\n\n this.lastUrl = location.href;\n\n // Monkey-patch pushState and replaceState\n this.originalPushState = history.pushState.bind(history);\n this.originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args);\n this._onNavigation();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args);\n this._onNavigation();\n };\n\n addEventListener('popstate', () => this._onNavigation());\n }\n\n stop(): void {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n }\n }\n\n private _onNavigation(): void {\n // Small delay to let the URL update\n setTimeout(() => {\n const current = location.href;\n if (current !== this.lastUrl) {\n const referrer = this.lastUrl;\n this.lastUrl = current;\n this.onPage(current, referrer);\n }\n }, 0);\n }\n}\n\n// ─── Outbound Link Tracking ─────────────────────────────────\n\nconst FILE_EXTENSIONS = /\\.(pdf|zip|doc|docx|xls|xlsx|csv|mp3|mp4|dmg|exe|rar|7z|gz|tar)$/i;\n\nexport function initOutboundTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: MouseEvent) {\n const link = (e.target as HTMLElement)?.closest?.('a');\n if (!link) return;\n\n const href = link.href;\n if (!href || href.startsWith('javascript:') || href.startsWith('#')) return;\n\n try {\n const url = new URL(href, location.href);\n if (url.hostname && url.hostname !== location.hostname) {\n instance.track('Outbound Link', { url: href });\n }\n } catch {\n // ignore malformed URLs\n }\n }\n\n document.addEventListener('click', handleClick, true);\n return () => document.removeEventListener('click', handleClick, true);\n}\n\n// ─── File Download Tracking ─────────────────────────────────\n\nexport function initFileDownloadTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: MouseEvent) {\n const link = (e.target as HTMLElement)?.closest?.('a');\n if (!link) return;\n\n const href = link.href;\n if (!href) return;\n\n try {\n const url = new URL(href, location.href);\n const match = url.pathname.match(FILE_EXTENSIONS);\n if (match) {\n instance.track('File Download', { url: href, extension: match[1].toLowerCase() });\n }\n } catch {\n // ignore malformed URLs\n }\n }\n\n document.addEventListener('click', handleClick, true);\n return () => document.removeEventListener('click', handleClick, true);\n}\n\n// ─── Scroll Depth Tracking ─────────────────────────────────\n\nexport function initScrollDepthTracking(instance: LitemetricsInstance): () => void {\n const milestones = [25, 50, 75, 90];\n const reached = new Set<number>();\n let lastPath = location.pathname;\n let ticking = false;\n\n function check() {\n ticking = false;\n\n // Reset on navigation\n if (location.pathname !== lastPath) {\n lastPath = location.pathname;\n reached.clear();\n }\n\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const docHeight = Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n );\n const viewHeight = window.innerHeight;\n const scrollable = docHeight - viewHeight;\n if (scrollable <= 0) return;\n\n const pct = (scrollTop / scrollable) * 100;\n for (const m of milestones) {\n if (pct >= m && !reached.has(m)) {\n reached.add(m);\n instance.track('Scroll Depth', { depth: `${m}%` });\n }\n }\n }\n\n function onScroll() {\n if (!ticking) {\n ticking = true;\n requestAnimationFrame(check);\n }\n }\n\n window.addEventListener('scroll', onScroll, { passive: true });\n return () => window.removeEventListener('scroll', onScroll);\n}\n\n// ─── Rage Click Detection ──────────────────────────────────\n\nexport function initRageClickTracking(instance: LitemetricsInstance): () => void {\n const clicks: { time: number; x: number; y: number }[] = [];\n const THRESHOLD = 3;\n const TIME_WINDOW = 800;\n const DISTANCE = 30; // pixels\n\n function handleClick(e: MouseEvent) {\n const now = Date.now();\n clicks.push({ time: now, x: e.clientX, y: e.clientY });\n\n // Remove old clicks outside window\n while (clicks.length > 0 && now - clicks[0].time > TIME_WINDOW) {\n clicks.shift();\n }\n\n if (clicks.length >= THRESHOLD) {\n // Check if all clicks are close together\n const first = clicks[0];\n const allClose = clicks.every(\n (c) => Math.abs(c.x - first.x) < DISTANCE && Math.abs(c.y - first.y) < DISTANCE\n );\n\n if (allClose) {\n const target = e.target as HTMLElement | null;\n const tagName = target?.tagName?.toLowerCase() || 'unknown';\n const text = (target?.innerText || '').slice(0, 50).trim();\n instance.track('Rage Click', { element: tagName, text: text || undefined });\n clicks.length = 0; // Reset after detection\n }\n }\n }\n\n document.addEventListener('click', handleClick, true);\n return () => document.removeEventListener('click', handleClick, true);\n}\n","import type { LitemetricsInstance } from './tracker';\n\nconst ATTR_EVENT = 'data-litemetrics-event';\nconst ATTR_PREFIX = 'data-litemetrics-event-';\n\n/**\n * Initialize data-attribute event tracking.\n * Clicks on elements with `data-litemetrics-event=\"EventName\"` will be auto-tracked.\n * Additional properties via `data-litemetrics-event-*` attributes.\n *\n * Example:\n * <button data-litemetrics-event=\"Signup\" data-litemetrics-event-plan=\"pro\">\n * → tracks event \"Signup\" with { plan: \"pro\" }\n */\nexport function initAttributeTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: Event) {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n\n // Walk up the DOM to find an element with data-litemetrics-event\n let el: HTMLElement | null = target;\n while (el) {\n const eventName = el.getAttribute(ATTR_EVENT);\n if (eventName) {\n // Collect data-litemetrics-event-* properties\n const properties: Record<string, string> = {};\n const attrs = el.attributes;\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n if (attr.name.startsWith(ATTR_PREFIX)) {\n const key = attr.name.slice(ATTR_PREFIX.length);\n properties[key] = attr.value;\n }\n }\n\n instance.track(\n eventName,\n Object.keys(properties).length > 0 ? properties : undefined\n );\n return;\n }\n el = el.parentElement;\n }\n }\n\n document.addEventListener('click', handleClick, true);\n\n return () => {\n document.removeEventListener('click', handleClick, true);\n };\n}\n"],"mappings":"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","names":["import_core","id","import_core","now"]}
@@ -1 +1 @@
1
- "use strict";var Litemetrics=(()=>{var R=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var Z=(n,t)=>{for(var e in t)R(n,e,{get:t[e],enumerable:!0})},$=(n,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of j(t))!B.call(n,r)&&r!==e&&R(n,r,{get:()=>t[r],enumerable:!(i=H(t,r))||i.enumerable});return n};var J=n=>$(R({},"__esModule",{value:!0}),n);var W={};Z(W,{createTracker:()=>q});var N=10,D=5e3,V=1800*1e3,v="__litemetrics_sid",m="__litemetrics_vid";var y="__litemetrics_optout",_="__litemetrics_uid",S="__litemetrics_la";function U(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let t=Math.random()*16|0;return(n==="x"?t:t&3|8).toString(16)})}async function G(n){if(typeof crypto<"u"&&crypto.subtle){let i=new TextEncoder().encode(n),r=await crypto.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(r)).map(l=>l.toString(16).padStart(2,"0")).join("")}let t=0;for(let e=0;e<n.length;e++)t=(t<<5)-t+n.charCodeAt(e)|0;return Math.abs(t).toString(16).padStart(8,"0")}function K(){if(typeof location>"u")return;let n=new URLSearchParams(location.search),t={},e=!1;for(let[i,r]of[["utm_source","source"],["utm_medium","medium"],["utm_campaign","campaign"],["utm_term","term"],["utm_content","content"]]){let d=n.get(i);d&&(t[r]=d,e=!0)}return e?t:void 0}function w(){return new Date().toISOString().slice(0,10)}function p(){return Date.now()}function I(n){try{return localStorage.getItem(n)}catch{return null}}function T(n,t){try{localStorage.setItem(n,t)}catch{}}function E(n){try{localStorage.removeItem(n)}catch{}}var x=class{_sessionId;_visitorId=null;_userId=null;_hostname;constructor(t){this._hostname=t||(typeof location<"u"?location.hostname:"unknown"),this._sessionId=this._getOrCreateSession(),this._userId=I(_)}get sessionId(){return this._sessionId}get userId(){return this._userId}async getVisitorId(){if(this._visitorId)return this._visitorId;let t=I(m),e=w();if(t){let[r,d]=t.split("|");if(d===e)return this._visitorId=r,r}let i=await this._generateVisitorId();return this._visitorId=i,T(m,`${i}|${e}`),i}touch(){T(S,p().toString())}identify(t){this._userId=t,T(_,t)}reset(){this._sessionId=U(),this._visitorId=null,this._userId=null,E(v),E(m),E(_),E(S)}_getOrCreateSession(){let t=I(v),e=I(S);if(t&&e&&p()-parseInt(e,10)<V)return this.touch(),t;let i=U();return T(v,i),this.touch(),i}async _generateVisitorId(){let e=[this._hostname,w(),typeof navigator<"u"?navigator.userAgent:"",typeof navigator<"u"?navigator.language:"",typeof Intl<"u"?Intl.DateTimeFormat().resolvedOptions().timeZone:"",typeof screen<"u"?`${screen.width}x${screen.height}`:""].join("|");return(await G(e)).slice(0,16)}};var b=class{queue=[];timer=null;endpoint;batchSize;flushInterval;debug;constructor(t){this.endpoint=t.endpoint,this.batchSize=t.batchSize??N,this.flushInterval=t.flushInterval??D,this.debug=t.debug??!1,this._startTimer(),this._setupUnload()}send(t){this.queue.push(t),this.queue.length>=this.batchSize&&this.flush()}flush(){if(this.queue.length===0)return;let t=this.queue.splice(0);this._dispatch(t)}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flush()}_dispatch(t){let i=JSON.stringify({events:t});this.debug&&console.log("[litemetrics] sending",t.length,"events",t),typeof fetch<"u"?fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:i,keepalive:!0}).catch(()=>{this._beacon(i)}):this._beacon(i)}_beacon(t){if(typeof navigator<"u"&&navigator.sendBeacon){let e=new Blob([t],{type:"application/json"});navigator.sendBeacon(this.endpoint,e)}}_startTimer(){typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),this.flushInterval))}_setupUnload(){if(typeof document>"u")return;let t=()=>{if(this.queue.length===0)return;let e={events:this.queue.splice(0)},i=JSON.stringify(e);this._beacon(i)};document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&t()}),typeof addEventListener<"u"&&addEventListener("pagehide",t)}};var C=class{onPage;lastUrl="";originalPushState=null;originalReplaceState=null;constructor(t){this.onPage=t}startSPA(){typeof history>"u"||typeof addEventListener>"u"||(this.lastUrl=location.href,this.originalPushState=history.pushState.bind(history),this.originalReplaceState=history.replaceState.bind(history),history.pushState=(...t)=>{this.originalPushState(...t),this._onNavigation()},history.replaceState=(...t)=>{this.originalReplaceState(...t),this._onNavigation()},addEventListener("popstate",()=>this._onNavigation()))}stop(){this.originalPushState&&(history.pushState=this.originalPushState),this.originalReplaceState&&(history.replaceState=this.originalReplaceState)}_onNavigation(){setTimeout(()=>{let t=location.href;if(t!==this.lastUrl){let e=this.lastUrl;this.lastUrl=t,this.onPage(t,e)}},0)}};var Q="data-litemetrics-event",M="data-litemetrics-event-";function Y(n){function t(e){let i=e.target;if(!i)return;let r=i;for(;r;){let d=r.getAttribute(Q);if(d){let l={},s=r.attributes;for(let f=0;f<s.length;f++){let u=s[f];if(u.name.startsWith(M)){let g=u.name.slice(M.length);l[g]=u.value}}n.track(d,Object.keys(l).length>0?l:void 0);return}r=r.parentElement}}return document.addEventListener("click",t,!0),()=>{document.removeEventListener("click",t,!0)}}function q(n){let{siteId:t,endpoint:e,autoTrack:i=!0,autoSpa:r=!0,debug:d=!1,respectDnt:l=!0}=n;if(l&&typeof navigator<"u"&&navigator.doNotTrack==="1")return F();try{if(localStorage.getItem(y)==="1")return F()}catch{}let s=new x,f=new b({endpoint:e,batchSize:n.batchSize,flushInterval:n.flushInterval,debug:d}),u=null,g=null,A=!1;function k(){let o={};if(typeof screen<"u"&&(o.screen={width:screen.width,height:screen.height}),typeof navigator<"u"){o.language=navigator.language;let c=navigator.connection;c&&(o.connection={type:c.type,downlink:c.downlink,effectiveType:c.effectiveType,rtt:c.rtt})}typeof Intl<"u"&&(o.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone);let a=K();return a&&(o.utm=a),o}async function O(o){A||(s.touch(),f.send(o))}async function P(o,a,c){let h=await s.getVisitorId(),z={type:"pageview",siteId:t,timestamp:p(),sessionId:s.sessionId,visitorId:h,url:o||(typeof location<"u"?location.href:""),referrer:c||(typeof document<"u"?document.referrer:void 0),title:a||(typeof document<"u"?document.title:void 0),...k()};O(z)}i&&P(),r&&(u=new C((o,a)=>P(o,void 0,a)),u.startSPA());let L={track(o,a){s.getVisitorId().then(c=>{let h={type:"event",siteId:t,timestamp:p(),sessionId:s.sessionId,visitorId:c,name:o,properties:a,...k()};O(h)})},identify(o,a){s.identify(o),s.getVisitorId().then(c=>{let h={type:"identify",siteId:t,timestamp:p(),sessionId:s.sessionId,visitorId:c,userId:o,traits:a,...k()};O(h)})},page(o,a){P(o,a)},reset(){s.reset()},opt_out(){A=!0;try{localStorage.setItem(y,"1")}catch{}},opt_in(){A=!1;try{localStorage.removeItem(y)}catch{}},destroy(){g?.(),u?.stop(),f.destroy()}};return i&&typeof document<"u"&&(g=Y(L)),L}function F(){return{track(){},identify(){},page(){},reset(){},opt_out(){},opt_in(){},destroy(){}}}return J(W);})();
1
+ "use strict";var Litemetrics=(()=>{var N=Object.defineProperty;var tt=Object.getOwnPropertyDescriptor;var et=Object.getOwnPropertyNames;var nt=Object.prototype.hasOwnProperty;var it=(n,t)=>{for(var e in t)N(n,e,{get:t[e],enumerable:!0})},rt=(n,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of et(t))!nt.call(n,r)&&r!==e&&N(n,r,{get:()=>t[r],enumerable:!(i=tt(t,r))||i.enumerable});return n};var ot=n=>rt(N({},"__esModule",{value:!0}),n);var ct={};it(ct,{createTracker:()=>J});var V=10,Y=5e3,G=1800*1e3,S="__litemetrics_sid",E="__litemetrics_vid";var I="__litemetrics_optout",T="__litemetrics_uid",x="__litemetrics_la";function M(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let t=Math.random()*16|0;return(n==="x"?t:t&3|8).toString(16)})}async function z(n){if(typeof crypto<"u"&&crypto.subtle){let i=new TextEncoder().encode(n),r=await crypto.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(r)).map(a=>a.toString(16).padStart(2,"0")).join("")}let t=0;for(let e=0;e<n.length;e++)t=(t<<5)-t+n.charCodeAt(e)|0;return Math.abs(t).toString(16).padStart(8,"0")}function K(){if(typeof location>"u")return;let n=new URLSearchParams(location.search),t={},e=!1;for(let[i,r]of[["utm_source","source"],["utm_medium","medium"],["utm_campaign","campaign"],["utm_term","term"],["utm_content","content"]]){let s=n.get(i);s&&(t[r]=s,e=!0)}return e?t:void 0}function H(){return new Date().toISOString().slice(0,10)}function g(){return Date.now()}function k(n){try{return localStorage.getItem(n)}catch{return null}}function b(n,t){try{localStorage.setItem(n,t)}catch{}}function C(n){try{localStorage.removeItem(n)}catch{}}var w=class{_sessionId;_visitorId=null;_userId=null;_hostname;constructor(t){this._hostname=t||(typeof location<"u"?location.hostname:"unknown"),this._sessionId=this._getOrCreateSession(),this._userId=k(T)}get sessionId(){return this._sessionId}get userId(){return this._userId}async getVisitorId(){if(this._visitorId)return this._visitorId;let t=k(E),e=H();if(t){let[r,s]=t.split("|");if(s===e)return this._visitorId=r,r}let i=await this._generateVisitorId();return this._visitorId=i,b(E,`${i}|${e}`),i}touch(){b(x,g().toString())}identify(t){this._userId=t,b(T,t)}reset(){this._sessionId=M(),this._visitorId=null,this._userId=null,C(S),C(E),C(T),C(x)}_getOrCreateSession(){let t=k(S),e=k(x);if(t&&e&&g()-parseInt(e,10)<G)return this.touch(),t;let i=M();return b(S,i),this.touch(),i}async _generateVisitorId(){let e=[this._hostname,H(),typeof navigator<"u"?navigator.userAgent:"",typeof navigator<"u"?navigator.language:"",typeof Intl<"u"?Intl.DateTimeFormat().resolvedOptions().timeZone:"",typeof screen<"u"?`${screen.width}x${screen.height}`:""].join("|");return(await z(e)).slice(0,16)}};var L=class{queue=[];timer=null;endpoint;batchSize;flushInterval;debug;constructor(t){this.endpoint=t.endpoint,this.batchSize=t.batchSize??V,this.flushInterval=t.flushInterval??Y,this.debug=t.debug??!1,this._startTimer(),this._setupUnload()}send(t){this.queue.push(t),this.queue.length>=this.batchSize&&this.flush()}flush(){if(this.queue.length===0)return;let t=this.queue.splice(0);this._dispatch(t)}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flush()}_dispatch(t){let i=JSON.stringify({events:t});this.debug&&console.log("[litemetrics] sending",t.length,"events",t),typeof fetch<"u"?fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:i,keepalive:!0}).catch(()=>{this._beacon(i)}):this._beacon(i)}_beacon(t){if(typeof navigator<"u"&&navigator.sendBeacon){let e=new Blob([t],{type:"application/json"});navigator.sendBeacon(this.endpoint,e)}}_startTimer(){typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),this.flushInterval))}_setupUnload(){if(typeof document>"u")return;let t=()=>{if(this.queue.length===0)return;let e={events:this.queue.splice(0)},i=JSON.stringify(e);this._beacon(i)};document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&t()}),typeof addEventListener<"u"&&addEventListener("pagehide",t)}};var O=class{onPage;lastUrl="";originalPushState=null;originalReplaceState=null;constructor(t){this.onPage=t}startSPA(){typeof history>"u"||typeof addEventListener>"u"||(this.lastUrl=location.href,this.originalPushState=history.pushState.bind(history),this.originalReplaceState=history.replaceState.bind(history),history.pushState=(...t)=>{this.originalPushState(...t),this._onNavigation()},history.replaceState=(...t)=>{this.originalReplaceState(...t),this._onNavigation()},addEventListener("popstate",()=>this._onNavigation()))}stop(){this.originalPushState&&(history.pushState=this.originalPushState),this.originalReplaceState&&(history.replaceState=this.originalReplaceState)}_onNavigation(){setTimeout(()=>{let t=location.href;if(t!==this.lastUrl){let e=this.lastUrl;this.lastUrl=t,this.onPage(t,e)}},0)}},st=/\.(pdf|zip|doc|docx|xls|xlsx|csv|mp3|mp4|dmg|exe|rar|7z|gz|tar)$/i;function q(n){function t(e){let i=e.target?.closest?.("a");if(!i)return;let r=i.href;if(!(!r||r.startsWith("javascript:")||r.startsWith("#")))try{let s=new URL(r,location.href);s.hostname&&s.hostname!==location.hostname&&n.track("Outbound Link",{url:r})}catch{}}return document.addEventListener("click",t,!0),()=>document.removeEventListener("click",t,!0)}function W(n){function t(e){let i=e.target?.closest?.("a");if(!i)return;let r=i.href;if(r)try{let a=new URL(r,location.href).pathname.match(st);a&&n.track("File Download",{url:r,extension:a[1].toLowerCase()})}catch{}}return document.addEventListener("click",t,!0),()=>document.removeEventListener("click",t,!0)}function j(n){let t=[25,50,75,90],e=new Set,i=location.pathname,r=!1;function s(){r=!1,location.pathname!==i&&(i=location.pathname,e.clear());let p=window.scrollY||document.documentElement.scrollTop,h=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight),m=window.innerHeight,l=h-m;if(l<=0)return;let c=p/l*100;for(let f of t)c>=f&&!e.has(f)&&(e.add(f),n.track("Scroll Depth",{depth:`${f}%`}))}function a(){r||(r=!0,requestAnimationFrame(s))}return window.addEventListener("scroll",a,{passive:!0}),()=>window.removeEventListener("scroll",a)}function $(n){let t=[];function s(a){let p=Date.now();for(t.push({time:p,x:a.clientX,y:a.clientY});t.length>0&&p-t[0].time>800;)t.shift();if(t.length>=3){let h=t[0];if(t.every(l=>Math.abs(l.x-h.x)<30&&Math.abs(l.y-h.y)<30)){let l=a.target,c=l?.tagName?.toLowerCase()||"unknown",f=(l?.innerText||"").slice(0,50).trim();n.track("Rage Click",{element:c,text:f||void 0}),t.length=0}}}return document.addEventListener("click",s,!0),()=>document.removeEventListener("click",s,!0)}var at="data-litemetrics-event",B="data-litemetrics-event-";function Z(n){function t(e){let i=e.target;if(!i)return;let r=i;for(;r;){let s=r.getAttribute(at);if(s){let a={},p=r.attributes;for(let h=0;h<p.length;h++){let m=p[h];if(m.name.startsWith(B)){let l=m.name.slice(B.length);a[l]=m.value}}n.track(s,Object.keys(a).length>0?a:void 0);return}r=r.parentElement}}return document.addEventListener("click",t,!0),()=>{document.removeEventListener("click",t,!0)}}function J(n){let{siteId:t,endpoint:e,autoTrack:i=!0,autoSpa:r=!0,debug:s=!1,respectDnt:a=!0}=n;if(a&&typeof navigator<"u"&&navigator.doNotTrack==="1")return X();try{if(localStorage.getItem(I)==="1")return X()}catch{}let{autoOutbound:p=!0,autoFileDownloads:h=!0,autoScrollDepth:m=!0,autoRageClicks:l=!0}=n,c=new w,f=new L({endpoint:e,batchSize:n.batchSize,flushInterval:n.flushInterval,debug:s}),A=null,F=null,y=[],R=!1;function P(){let o={};if(typeof screen<"u"&&(o.screen={width:screen.width,height:screen.height}),typeof navigator<"u"){o.language=navigator.language;let d=navigator.connection;d&&(o.connection={type:d.type,downlink:d.downlink,effectiveType:d.effectiveType,rtt:d.rtt})}typeof Intl<"u"&&(o.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone);let u=K();return u&&(o.utm=u),o}async function U(o){R||(c.touch(),f.send(o))}async function D(o,u,d){let _=await c.getVisitorId(),Q={type:"pageview",siteId:t,timestamp:g(),sessionId:c.sessionId,visitorId:_,url:o||(typeof location<"u"?location.href:""),referrer:d||(typeof document<"u"?document.referrer:void 0),title:u||(typeof document<"u"?document.title:void 0),...P()};U(Q)}i&&D(),r&&(A=new O((o,u)=>D(o,void 0,u)),A.startSPA());let v={track(o,u){c.getVisitorId().then(d=>{let _={type:"event",siteId:t,timestamp:g(),sessionId:c.sessionId,visitorId:d,name:o,properties:u,...P()};U(_)})},identify(o,u){c.identify(o),c.getVisitorId().then(d=>{let _={type:"identify",siteId:t,timestamp:g(),sessionId:c.sessionId,visitorId:d,userId:o,traits:u,...P()};U(_)})},page(o,u){D(o,u)},reset(){c.reset()},opt_out(){R=!0;try{localStorage.setItem(I,"1")}catch{}},opt_in(){R=!1;try{localStorage.removeItem(I)}catch{}},destroy(){F?.(),y.forEach(o=>o()),A?.stop(),f.destroy()}};return i&&typeof document<"u"&&(F=Z(v),p&&y.push(q(v)),h&&y.push(W(v)),m&&y.push(j(v)),l&&y.push($(v))),v}function X(){return{track(){},identify(){},page(){},reset(){},opt_out(){},opt_in(){},destroy(){}}}return ot(ct);})();
@@ -287,6 +287,106 @@ var AutoTracker = class {
287
287
  }, 0);
288
288
  }
289
289
  };
290
+ var FILE_EXTENSIONS = /\.(pdf|zip|doc|docx|xls|xlsx|csv|mp3|mp4|dmg|exe|rar|7z|gz|tar)$/i;
291
+ function initOutboundTracking(instance) {
292
+ function handleClick(e) {
293
+ const link = e.target?.closest?.("a");
294
+ if (!link) return;
295
+ const href = link.href;
296
+ if (!href || href.startsWith("javascript:") || href.startsWith("#")) return;
297
+ try {
298
+ const url = new URL(href, location.href);
299
+ if (url.hostname && url.hostname !== location.hostname) {
300
+ instance.track("Outbound Link", { url: href });
301
+ }
302
+ } catch {
303
+ }
304
+ }
305
+ document.addEventListener("click", handleClick, true);
306
+ return () => document.removeEventListener("click", handleClick, true);
307
+ }
308
+ function initFileDownloadTracking(instance) {
309
+ function handleClick(e) {
310
+ const link = e.target?.closest?.("a");
311
+ if (!link) return;
312
+ const href = link.href;
313
+ if (!href) return;
314
+ try {
315
+ const url = new URL(href, location.href);
316
+ const match = url.pathname.match(FILE_EXTENSIONS);
317
+ if (match) {
318
+ instance.track("File Download", { url: href, extension: match[1].toLowerCase() });
319
+ }
320
+ } catch {
321
+ }
322
+ }
323
+ document.addEventListener("click", handleClick, true);
324
+ return () => document.removeEventListener("click", handleClick, true);
325
+ }
326
+ function initScrollDepthTracking(instance) {
327
+ const milestones = [25, 50, 75, 90];
328
+ const reached = /* @__PURE__ */ new Set();
329
+ let lastPath = location.pathname;
330
+ let ticking = false;
331
+ function check() {
332
+ ticking = false;
333
+ if (location.pathname !== lastPath) {
334
+ lastPath = location.pathname;
335
+ reached.clear();
336
+ }
337
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
338
+ const docHeight = Math.max(
339
+ document.body.scrollHeight,
340
+ document.documentElement.scrollHeight
341
+ );
342
+ const viewHeight = window.innerHeight;
343
+ const scrollable = docHeight - viewHeight;
344
+ if (scrollable <= 0) return;
345
+ const pct = scrollTop / scrollable * 100;
346
+ for (const m of milestones) {
347
+ if (pct >= m && !reached.has(m)) {
348
+ reached.add(m);
349
+ instance.track("Scroll Depth", { depth: `${m}%` });
350
+ }
351
+ }
352
+ }
353
+ function onScroll() {
354
+ if (!ticking) {
355
+ ticking = true;
356
+ requestAnimationFrame(check);
357
+ }
358
+ }
359
+ window.addEventListener("scroll", onScroll, { passive: true });
360
+ return () => window.removeEventListener("scroll", onScroll);
361
+ }
362
+ function initRageClickTracking(instance) {
363
+ const clicks = [];
364
+ const THRESHOLD = 3;
365
+ const TIME_WINDOW = 800;
366
+ const DISTANCE = 30;
367
+ function handleClick(e) {
368
+ const now2 = Date.now();
369
+ clicks.push({ time: now2, x: e.clientX, y: e.clientY });
370
+ while (clicks.length > 0 && now2 - clicks[0].time > TIME_WINDOW) {
371
+ clicks.shift();
372
+ }
373
+ if (clicks.length >= THRESHOLD) {
374
+ const first = clicks[0];
375
+ const allClose = clicks.every(
376
+ (c) => Math.abs(c.x - first.x) < DISTANCE && Math.abs(c.y - first.y) < DISTANCE
377
+ );
378
+ if (allClose) {
379
+ const target = e.target;
380
+ const tagName = target?.tagName?.toLowerCase() || "unknown";
381
+ const text = (target?.innerText || "").slice(0, 50).trim();
382
+ instance.track("Rage Click", { element: tagName, text: text || void 0 });
383
+ clicks.length = 0;
384
+ }
385
+ }
386
+ }
387
+ document.addEventListener("click", handleClick, true);
388
+ return () => document.removeEventListener("click", handleClick, true);
389
+ }
290
390
 
291
391
  // src/attributes.ts
292
392
  var ATTR_EVENT = "data-litemetrics-event";
@@ -342,6 +442,12 @@ function createTracker(config) {
342
442
  }
343
443
  } catch {
344
444
  }
445
+ const {
446
+ autoOutbound = true,
447
+ autoFileDownloads = true,
448
+ autoScrollDepth = true,
449
+ autoRageClicks = true
450
+ } = config;
345
451
  const session = new SessionManager();
346
452
  const transport = new Transport({
347
453
  endpoint,
@@ -351,6 +457,7 @@ function createTracker(config) {
351
457
  });
352
458
  let autoTracker = null;
353
459
  let cleanupAttributes = null;
460
+ const autoCleanups = [];
354
461
  let optedOut = false;
355
462
  function getContext() {
356
463
  const ctx = {};
@@ -457,12 +564,17 @@ function createTracker(config) {
457
564
  },
458
565
  destroy() {
459
566
  cleanupAttributes?.();
567
+ autoCleanups.forEach((fn) => fn());
460
568
  autoTracker?.stop();
461
569
  transport.destroy();
462
570
  }
463
571
  };
464
572
  if (autoTrack && typeof document !== "undefined") {
465
573
  cleanupAttributes = initAttributeTracking(instance);
574
+ if (autoOutbound) autoCleanups.push(initOutboundTracking(instance));
575
+ if (autoFileDownloads) autoCleanups.push(initFileDownloadTracking(instance));
576
+ if (autoScrollDepth) autoCleanups.push(initScrollDepthTracking(instance));
577
+ if (autoRageClicks) autoCleanups.push(initRageClickTracking(instance));
466
578
  }
467
579
  return instance;
468
580
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracker.ts","../src/session.ts","../src/utils.ts","../src/transport.ts","../src/auto.ts","../src/attributes.ts"],"sourcesContent":["import type {\n TrackerConfig,\n ClientEvent,\n PageviewEvent,\n CustomEvent,\n IdentifyEvent,\n ClientContext,\n} from '@litemetrics/core';\nimport { STORAGE_KEY_OPTOUT } from '@litemetrics/core';\nimport { SessionManager } from './session';\nimport { Transport } from './transport';\nimport { AutoTracker } from './auto';\nimport { parseUTM, now } from './utils';\nimport { initAttributeTracking } from './attributes';\n\nexport interface LitemetricsInstance {\n track(name: string, properties?: Record<string, unknown>): void;\n identify(userId: string, traits?: Record<string, unknown>): void;\n page(url?: string, title?: string): void;\n reset(): void;\n opt_out(): void;\n opt_in(): void;\n destroy(): void;\n}\n\nexport function createTracker(config: TrackerConfig): LitemetricsInstance {\n const {\n siteId,\n endpoint,\n autoTrack = true,\n autoSpa = true,\n debug = false,\n respectDnt = true,\n } = config;\n\n // Check Do Not Track\n if (respectDnt && typeof navigator !== 'undefined' && navigator.doNotTrack === '1') {\n return createNoopTracker();\n }\n\n // Check opt-out\n try {\n if (localStorage.getItem(STORAGE_KEY_OPTOUT) === '1') {\n return createNoopTracker();\n }\n } catch {\n // ignore\n }\n\n const session = new SessionManager();\n const transport = new Transport({\n endpoint,\n batchSize: config.batchSize,\n flushInterval: config.flushInterval,\n debug,\n });\n let autoTracker: AutoTracker | null = null;\n let cleanupAttributes: (() => void) | null = null;\n let optedOut = false;\n\n function getContext(): ClientContext {\n const ctx: ClientContext = {};\n if (typeof screen !== 'undefined') {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n if (typeof navigator !== 'undefined') {\n ctx.language = navigator.language;\n // Network Information API\n const conn = (navigator as any).connection;\n if (conn) {\n ctx.connection = {\n type: conn.type,\n downlink: conn.downlink,\n effectiveType: conn.effectiveType,\n rtt: conn.rtt,\n };\n }\n }\n if (typeof Intl !== 'undefined') {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n const utm = parseUTM();\n if (utm) ctx.utm = utm;\n return ctx;\n }\n\n async function sendEvent(event: ClientEvent): Promise<void> {\n if (optedOut) return;\n session.touch();\n transport.send(event);\n }\n\n async function trackPage(url?: string, title?: string, referrer?: string): Promise<void> {\n const visitorId = await session.getVisitorId();\n const event: PageviewEvent & ClientContext = {\n type: 'pageview',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n url: url || (typeof location !== 'undefined' ? location.href : ''),\n referrer: referrer || (typeof document !== 'undefined' ? document.referrer : undefined),\n title: title || (typeof document !== 'undefined' ? document.title : undefined),\n ...getContext(),\n };\n sendEvent(event);\n }\n\n // Auto-track initial page view\n if (autoTrack) {\n trackPage();\n }\n\n // Auto-track SPA navigation\n if (autoSpa) {\n autoTracker = new AutoTracker((url, ref) => trackPage(url, undefined, ref));\n autoTracker.startSPA();\n }\n\n // We need a reference to the instance for attribute tracking\n // so we create it first then init attributes\n const instance: LitemetricsInstance = {\n track(name: string, properties?: Record<string, unknown>): void {\n session.getVisitorId().then((visitorId) => {\n const event: CustomEvent & ClientContext = {\n type: 'event',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n name,\n properties,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n session.identify(userId);\n session.getVisitorId().then((visitorId) => {\n const event: IdentifyEvent & ClientContext = {\n type: 'identify',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n userId,\n traits,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n page(url?: string, title?: string): void {\n trackPage(url, title);\n },\n\n reset(): void {\n session.reset();\n },\n\n opt_out(): void {\n optedOut = true;\n try {\n localStorage.setItem(STORAGE_KEY_OPTOUT, '1');\n } catch {\n // ignore\n }\n },\n\n opt_in(): void {\n optedOut = false;\n try {\n localStorage.removeItem(STORAGE_KEY_OPTOUT);\n } catch {\n // ignore\n }\n },\n\n destroy(): void {\n cleanupAttributes?.();\n autoTracker?.stop();\n transport.destroy();\n },\n };\n\n // Initialize data-attribute event tracking\n if (autoTrack && typeof document !== 'undefined') {\n cleanupAttributes = initAttributeTracking(instance);\n }\n\n return instance;\n}\n\nfunction createNoopTracker(): LitemetricsInstance {\n return {\n track() {},\n identify() {},\n page() {},\n reset() {},\n opt_out() {},\n opt_in() {},\n destroy() {},\n };\n}\n","import {\n SESSION_TIMEOUT,\n STORAGE_KEY_SESSION,\n STORAGE_KEY_VISITOR,\n STORAGE_KEY_LAST_ACTIVE,\n STORAGE_KEY_USER,\n} from '@litemetrics/core';\nimport { generateId, hashString, getDayString, now } from './utils';\n\nfunction storageGet(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction storageSet(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n // localStorage unavailable (private browsing, etc.)\n }\n}\n\nfunction storageRemove(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n // noop\n }\n}\n\nexport class SessionManager {\n private _sessionId: string;\n private _visitorId: string | null = null;\n private _userId: string | null = null;\n private _hostname: string;\n\n constructor(hostname?: string) {\n this._hostname = hostname || (typeof location !== 'undefined' ? location.hostname : 'unknown');\n this._sessionId = this._getOrCreateSession();\n this._userId = storageGet(STORAGE_KEY_USER);\n }\n\n get sessionId(): string {\n return this._sessionId;\n }\n\n get userId(): string | null {\n return this._userId;\n }\n\n async getVisitorId(): Promise<string> {\n if (this._visitorId) return this._visitorId;\n\n const cached = storageGet(STORAGE_KEY_VISITOR);\n const today = getDayString();\n\n // Visitor ID rotates daily for privacy\n if (cached) {\n const [id, date] = cached.split('|');\n if (date === today) {\n this._visitorId = id;\n return id;\n }\n }\n\n const id = await this._generateVisitorId();\n this._visitorId = id;\n storageSet(STORAGE_KEY_VISITOR, `${id}|${today}`);\n return id;\n }\n\n touch(): void {\n storageSet(STORAGE_KEY_LAST_ACTIVE, now().toString());\n }\n\n identify(userId: string): void {\n this._userId = userId;\n storageSet(STORAGE_KEY_USER, userId);\n }\n\n reset(): void {\n this._sessionId = generateId();\n this._visitorId = null;\n this._userId = null;\n storageRemove(STORAGE_KEY_SESSION);\n storageRemove(STORAGE_KEY_VISITOR);\n storageRemove(STORAGE_KEY_USER);\n storageRemove(STORAGE_KEY_LAST_ACTIVE);\n }\n\n private _getOrCreateSession(): string {\n const stored = storageGet(STORAGE_KEY_SESSION);\n const lastActive = storageGet(STORAGE_KEY_LAST_ACTIVE);\n\n if (stored && lastActive) {\n const elapsed = now() - parseInt(lastActive, 10);\n if (elapsed < SESSION_TIMEOUT) {\n this.touch();\n return stored;\n }\n }\n\n const id = generateId();\n storageSet(STORAGE_KEY_SESSION, id);\n this.touch();\n return id;\n }\n\n private async _generateVisitorId(): Promise<string> {\n const components = [\n this._hostname,\n getDayString(),\n typeof navigator !== 'undefined' ? navigator.userAgent : '',\n typeof navigator !== 'undefined' ? navigator.language : '',\n typeof Intl !== 'undefined'\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : '',\n typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : '',\n ];\n\n const raw = components.join('|');\n const hash = await hashString(raw);\n return hash.slice(0, 16);\n }\n}\n","import type { UTMParams } from '@litemetrics/core';\n\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport async function hashString(input: string): Promise<string> {\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const array = Array.from(new Uint8Array(hash));\n return array.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n // Simple fallback hash\n let h = 0;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) - h + input.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(16).padStart(8, '0');\n}\n\nexport function parseUTM(): UTMParams | undefined {\n if (typeof location === 'undefined') return undefined;\n const params = new URLSearchParams(location.search);\n const utm: UTMParams = {};\n let hasUtm = false;\n\n for (const [key, field] of [\n ['utm_source', 'source'],\n ['utm_medium', 'medium'],\n ['utm_campaign', 'campaign'],\n ['utm_term', 'term'],\n ['utm_content', 'content'],\n ] as const) {\n const val = params.get(key);\n if (val) {\n (utm as Record<string, string>)[field] = val;\n hasUtm = true;\n }\n }\n\n return hasUtm ? utm : undefined;\n}\n\nexport function getDayString(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function now(): number {\n return Date.now();\n}\n","import type { ClientEvent, CollectPayload } from '@litemetrics/core';\nimport { DEFAULT_BATCH_SIZE, DEFAULT_FLUSH_INTERVAL } from '@litemetrics/core';\n\nexport interface TransportOptions {\n endpoint: string;\n batchSize?: number;\n flushInterval?: number;\n debug?: boolean;\n}\n\nexport class Transport {\n private queue: ClientEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private endpoint: string;\n private batchSize: number;\n private flushInterval: number;\n private debug: boolean;\n\n constructor(options: TransportOptions) {\n this.endpoint = options.endpoint;\n this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.debug = options.debug ?? false;\n\n this._startTimer();\n this._setupUnload();\n }\n\n send(event: ClientEvent): void {\n this.queue.push(event);\n if (this.queue.length >= this.batchSize) {\n this.flush();\n }\n }\n\n flush(): void {\n if (this.queue.length === 0) return;\n const events = this.queue.splice(0);\n this._dispatch(events);\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n }\n\n private _dispatch(events: ClientEvent[]): void {\n const payload: CollectPayload = { events };\n const body = JSON.stringify(payload);\n\n if (this.debug) {\n console.log('[litemetrics] sending', events.length, 'events', events);\n }\n\n // Try fetch first, fall back to sendBeacon\n if (typeof fetch !== 'undefined') {\n fetch(this.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n keepalive: true,\n }).catch(() => {\n // Retry once with sendBeacon\n this._beacon(body);\n });\n } else {\n this._beacon(body);\n }\n }\n\n private _beacon(body: string): void {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob([body], { type: 'application/json' });\n navigator.sendBeacon(this.endpoint, blob);\n }\n }\n\n private _startTimer(): void {\n if (typeof setInterval !== 'undefined') {\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n }\n }\n\n private _setupUnload(): void {\n if (typeof document === 'undefined') return;\n\n const onUnload = () => {\n if (this.queue.length === 0) return;\n const payload: CollectPayload = { events: this.queue.splice(0) };\n const body = JSON.stringify(payload);\n // sendBeacon is more reliable during page unload\n this._beacon(body);\n };\n\n // visibilitychange + pagehide is the most reliable combo\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n onUnload();\n }\n });\n\n if (typeof addEventListener !== 'undefined') {\n addEventListener('pagehide', onUnload);\n }\n }\n}\n","export type PageCallback = (url: string, referrer?: string) => void;\n\nexport class AutoTracker {\n private onPage: PageCallback;\n private lastUrl: string = '';\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n\n constructor(onPage: PageCallback) {\n this.onPage = onPage;\n }\n\n startSPA(): void {\n if (typeof history === 'undefined' || typeof addEventListener === 'undefined') return;\n\n this.lastUrl = location.href;\n\n // Monkey-patch pushState and replaceState\n this.originalPushState = history.pushState.bind(history);\n this.originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args);\n this._onNavigation();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args);\n this._onNavigation();\n };\n\n addEventListener('popstate', () => this._onNavigation());\n }\n\n stop(): void {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n }\n }\n\n private _onNavigation(): void {\n // Small delay to let the URL update\n setTimeout(() => {\n const current = location.href;\n if (current !== this.lastUrl) {\n const referrer = this.lastUrl;\n this.lastUrl = current;\n this.onPage(current, referrer);\n }\n }, 0);\n }\n}\n","import type { LitemetricsInstance } from './tracker';\n\nconst ATTR_EVENT = 'data-litemetrics-event';\nconst ATTR_PREFIX = 'data-litemetrics-event-';\n\n/**\n * Initialize data-attribute event tracking.\n * Clicks on elements with `data-litemetrics-event=\"EventName\"` will be auto-tracked.\n * Additional properties via `data-litemetrics-event-*` attributes.\n *\n * Example:\n * <button data-litemetrics-event=\"Signup\" data-litemetrics-event-plan=\"pro\">\n * → tracks event \"Signup\" with { plan: \"pro\" }\n */\nexport function initAttributeTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: Event) {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n\n // Walk up the DOM to find an element with data-litemetrics-event\n let el: HTMLElement | null = target;\n while (el) {\n const eventName = el.getAttribute(ATTR_EVENT);\n if (eventName) {\n // Collect data-litemetrics-event-* properties\n const properties: Record<string, string> = {};\n const attrs = el.attributes;\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n if (attr.name.startsWith(ATTR_PREFIX)) {\n const key = attr.name.slice(ATTR_PREFIX.length);\n properties[key] = attr.value;\n }\n }\n\n instance.track(\n eventName,\n Object.keys(properties).length > 0 ? properties : undefined\n );\n return;\n }\n el = el.parentElement;\n }\n }\n\n document.addEventListener('click', handleClick, true);\n\n return () => {\n document.removeEventListener('click', handleClick, true);\n };\n}\n"],"mappings":"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","names":["id"]}
1
+ {"version":3,"sources":["../src/tracker.ts","../src/session.ts","../src/utils.ts","../src/transport.ts","../src/auto.ts","../src/attributes.ts"],"sourcesContent":["import type {\n TrackerConfig,\n ClientEvent,\n PageviewEvent,\n CustomEvent,\n IdentifyEvent,\n ClientContext,\n} from '@litemetrics/core';\nimport { STORAGE_KEY_OPTOUT } from '@litemetrics/core';\nimport { SessionManager } from './session';\nimport { Transport } from './transport';\nimport { AutoTracker, initOutboundTracking, initFileDownloadTracking, initScrollDepthTracking, initRageClickTracking } from './auto';\nimport { parseUTM, now } from './utils';\nimport { initAttributeTracking } from './attributes';\n\nexport interface LitemetricsInstance {\n track(name: string, properties?: Record<string, unknown>): void;\n identify(userId: string, traits?: Record<string, unknown>): void;\n page(url?: string, title?: string): void;\n reset(): void;\n opt_out(): void;\n opt_in(): void;\n destroy(): void;\n}\n\nexport function createTracker(config: TrackerConfig): LitemetricsInstance {\n const {\n siteId,\n endpoint,\n autoTrack = true,\n autoSpa = true,\n debug = false,\n respectDnt = true,\n } = config;\n\n // Check Do Not Track\n if (respectDnt && typeof navigator !== 'undefined' && navigator.doNotTrack === '1') {\n return createNoopTracker();\n }\n\n // Check opt-out\n try {\n if (localStorage.getItem(STORAGE_KEY_OPTOUT) === '1') {\n return createNoopTracker();\n }\n } catch {\n // ignore\n }\n\n const {\n autoOutbound = true,\n autoFileDownloads = true,\n autoScrollDepth = true,\n autoRageClicks = true,\n } = config;\n\n const session = new SessionManager();\n const transport = new Transport({\n endpoint,\n batchSize: config.batchSize,\n flushInterval: config.flushInterval,\n debug,\n });\n let autoTracker: AutoTracker | null = null;\n let cleanupAttributes: (() => void) | null = null;\n const autoCleanups: (() => void)[] = [];\n let optedOut = false;\n\n function getContext(): ClientContext {\n const ctx: ClientContext = {};\n if (typeof screen !== 'undefined') {\n ctx.screen = { width: screen.width, height: screen.height };\n }\n if (typeof navigator !== 'undefined') {\n ctx.language = navigator.language;\n // Network Information API\n const conn = (navigator as any).connection;\n if (conn) {\n ctx.connection = {\n type: conn.type,\n downlink: conn.downlink,\n effectiveType: conn.effectiveType,\n rtt: conn.rtt,\n };\n }\n }\n if (typeof Intl !== 'undefined') {\n ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n const utm = parseUTM();\n if (utm) ctx.utm = utm;\n return ctx;\n }\n\n async function sendEvent(event: ClientEvent): Promise<void> {\n if (optedOut) return;\n session.touch();\n transport.send(event);\n }\n\n async function trackPage(url?: string, title?: string, referrer?: string): Promise<void> {\n const visitorId = await session.getVisitorId();\n const event: PageviewEvent & ClientContext = {\n type: 'pageview',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n url: url || (typeof location !== 'undefined' ? location.href : ''),\n referrer: referrer || (typeof document !== 'undefined' ? document.referrer : undefined),\n title: title || (typeof document !== 'undefined' ? document.title : undefined),\n ...getContext(),\n };\n sendEvent(event);\n }\n\n // Auto-track initial page view\n if (autoTrack) {\n trackPage();\n }\n\n // Auto-track SPA navigation\n if (autoSpa) {\n autoTracker = new AutoTracker((url, ref) => trackPage(url, undefined, ref));\n autoTracker.startSPA();\n }\n\n // We need a reference to the instance for attribute tracking\n // so we create it first then init attributes\n const instance: LitemetricsInstance = {\n track(name: string, properties?: Record<string, unknown>): void {\n session.getVisitorId().then((visitorId) => {\n const event: CustomEvent & ClientContext = {\n type: 'event',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n name,\n properties,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n session.identify(userId);\n session.getVisitorId().then((visitorId) => {\n const event: IdentifyEvent & ClientContext = {\n type: 'identify',\n siteId,\n timestamp: now(),\n sessionId: session.sessionId,\n visitorId,\n userId,\n traits,\n ...getContext(),\n };\n sendEvent(event);\n });\n },\n\n page(url?: string, title?: string): void {\n trackPage(url, title);\n },\n\n reset(): void {\n session.reset();\n },\n\n opt_out(): void {\n optedOut = true;\n try {\n localStorage.setItem(STORAGE_KEY_OPTOUT, '1');\n } catch {\n // ignore\n }\n },\n\n opt_in(): void {\n optedOut = false;\n try {\n localStorage.removeItem(STORAGE_KEY_OPTOUT);\n } catch {\n // ignore\n }\n },\n\n destroy(): void {\n cleanupAttributes?.();\n autoCleanups.forEach((fn) => fn());\n autoTracker?.stop();\n transport.destroy();\n },\n };\n\n // Initialize data-attribute event tracking\n if (autoTrack && typeof document !== 'undefined') {\n cleanupAttributes = initAttributeTracking(instance);\n\n // Enhanced auto-tracking\n if (autoOutbound) autoCleanups.push(initOutboundTracking(instance));\n if (autoFileDownloads) autoCleanups.push(initFileDownloadTracking(instance));\n if (autoScrollDepth) autoCleanups.push(initScrollDepthTracking(instance));\n if (autoRageClicks) autoCleanups.push(initRageClickTracking(instance));\n }\n\n return instance;\n}\n\nfunction createNoopTracker(): LitemetricsInstance {\n return {\n track() {},\n identify() {},\n page() {},\n reset() {},\n opt_out() {},\n opt_in() {},\n destroy() {},\n };\n}\n","import {\n SESSION_TIMEOUT,\n STORAGE_KEY_SESSION,\n STORAGE_KEY_VISITOR,\n STORAGE_KEY_LAST_ACTIVE,\n STORAGE_KEY_USER,\n} from '@litemetrics/core';\nimport { generateId, hashString, getDayString, now } from './utils';\n\nfunction storageGet(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction storageSet(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n // localStorage unavailable (private browsing, etc.)\n }\n}\n\nfunction storageRemove(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n // noop\n }\n}\n\nexport class SessionManager {\n private _sessionId: string;\n private _visitorId: string | null = null;\n private _userId: string | null = null;\n private _hostname: string;\n\n constructor(hostname?: string) {\n this._hostname = hostname || (typeof location !== 'undefined' ? location.hostname : 'unknown');\n this._sessionId = this._getOrCreateSession();\n this._userId = storageGet(STORAGE_KEY_USER);\n }\n\n get sessionId(): string {\n return this._sessionId;\n }\n\n get userId(): string | null {\n return this._userId;\n }\n\n async getVisitorId(): Promise<string> {\n if (this._visitorId) return this._visitorId;\n\n const cached = storageGet(STORAGE_KEY_VISITOR);\n const today = getDayString();\n\n // Visitor ID rotates daily for privacy\n if (cached) {\n const [id, date] = cached.split('|');\n if (date === today) {\n this._visitorId = id;\n return id;\n }\n }\n\n const id = await this._generateVisitorId();\n this._visitorId = id;\n storageSet(STORAGE_KEY_VISITOR, `${id}|${today}`);\n return id;\n }\n\n touch(): void {\n storageSet(STORAGE_KEY_LAST_ACTIVE, now().toString());\n }\n\n identify(userId: string): void {\n this._userId = userId;\n storageSet(STORAGE_KEY_USER, userId);\n }\n\n reset(): void {\n this._sessionId = generateId();\n this._visitorId = null;\n this._userId = null;\n storageRemove(STORAGE_KEY_SESSION);\n storageRemove(STORAGE_KEY_VISITOR);\n storageRemove(STORAGE_KEY_USER);\n storageRemove(STORAGE_KEY_LAST_ACTIVE);\n }\n\n private _getOrCreateSession(): string {\n const stored = storageGet(STORAGE_KEY_SESSION);\n const lastActive = storageGet(STORAGE_KEY_LAST_ACTIVE);\n\n if (stored && lastActive) {\n const elapsed = now() - parseInt(lastActive, 10);\n if (elapsed < SESSION_TIMEOUT) {\n this.touch();\n return stored;\n }\n }\n\n const id = generateId();\n storageSet(STORAGE_KEY_SESSION, id);\n this.touch();\n return id;\n }\n\n private async _generateVisitorId(): Promise<string> {\n const components = [\n this._hostname,\n getDayString(),\n typeof navigator !== 'undefined' ? navigator.userAgent : '',\n typeof navigator !== 'undefined' ? navigator.language : '',\n typeof Intl !== 'undefined'\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : '',\n typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : '',\n ];\n\n const raw = components.join('|');\n const hash = await hashString(raw);\n return hash.slice(0, 16);\n }\n}\n","import type { UTMParams } from '@litemetrics/core';\n\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport async function hashString(input: string): Promise<string> {\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const array = Array.from(new Uint8Array(hash));\n return array.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n // Simple fallback hash\n let h = 0;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) - h + input.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(16).padStart(8, '0');\n}\n\nexport function parseUTM(): UTMParams | undefined {\n if (typeof location === 'undefined') return undefined;\n const params = new URLSearchParams(location.search);\n const utm: UTMParams = {};\n let hasUtm = false;\n\n for (const [key, field] of [\n ['utm_source', 'source'],\n ['utm_medium', 'medium'],\n ['utm_campaign', 'campaign'],\n ['utm_term', 'term'],\n ['utm_content', 'content'],\n ] as const) {\n const val = params.get(key);\n if (val) {\n (utm as Record<string, string>)[field] = val;\n hasUtm = true;\n }\n }\n\n return hasUtm ? utm : undefined;\n}\n\nexport function getDayString(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function now(): number {\n return Date.now();\n}\n","import type { ClientEvent, CollectPayload } from '@litemetrics/core';\nimport { DEFAULT_BATCH_SIZE, DEFAULT_FLUSH_INTERVAL } from '@litemetrics/core';\n\nexport interface TransportOptions {\n endpoint: string;\n batchSize?: number;\n flushInterval?: number;\n debug?: boolean;\n}\n\nexport class Transport {\n private queue: ClientEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private endpoint: string;\n private batchSize: number;\n private flushInterval: number;\n private debug: boolean;\n\n constructor(options: TransportOptions) {\n this.endpoint = options.endpoint;\n this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushInterval = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n this.debug = options.debug ?? false;\n\n this._startTimer();\n this._setupUnload();\n }\n\n send(event: ClientEvent): void {\n this.queue.push(event);\n if (this.queue.length >= this.batchSize) {\n this.flush();\n }\n }\n\n flush(): void {\n if (this.queue.length === 0) return;\n const events = this.queue.splice(0);\n this._dispatch(events);\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n }\n\n private _dispatch(events: ClientEvent[]): void {\n const payload: CollectPayload = { events };\n const body = JSON.stringify(payload);\n\n if (this.debug) {\n console.log('[litemetrics] sending', events.length, 'events', events);\n }\n\n // Try fetch first, fall back to sendBeacon\n if (typeof fetch !== 'undefined') {\n fetch(this.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n keepalive: true,\n }).catch(() => {\n // Retry once with sendBeacon\n this._beacon(body);\n });\n } else {\n this._beacon(body);\n }\n }\n\n private _beacon(body: string): void {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob([body], { type: 'application/json' });\n navigator.sendBeacon(this.endpoint, blob);\n }\n }\n\n private _startTimer(): void {\n if (typeof setInterval !== 'undefined') {\n this.timer = setInterval(() => this.flush(), this.flushInterval);\n }\n }\n\n private _setupUnload(): void {\n if (typeof document === 'undefined') return;\n\n const onUnload = () => {\n if (this.queue.length === 0) return;\n const payload: CollectPayload = { events: this.queue.splice(0) };\n const body = JSON.stringify(payload);\n // sendBeacon is more reliable during page unload\n this._beacon(body);\n };\n\n // visibilitychange + pagehide is the most reliable combo\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n onUnload();\n }\n });\n\n if (typeof addEventListener !== 'undefined') {\n addEventListener('pagehide', onUnload);\n }\n }\n}\n","import type { LitemetricsInstance } from './tracker';\n\nexport type PageCallback = (url: string, referrer?: string) => void;\n\nexport class AutoTracker {\n private onPage: PageCallback;\n private lastUrl: string = '';\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n\n constructor(onPage: PageCallback) {\n this.onPage = onPage;\n }\n\n startSPA(): void {\n if (typeof history === 'undefined' || typeof addEventListener === 'undefined') return;\n\n this.lastUrl = location.href;\n\n // Monkey-patch pushState and replaceState\n this.originalPushState = history.pushState.bind(history);\n this.originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args);\n this._onNavigation();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args);\n this._onNavigation();\n };\n\n addEventListener('popstate', () => this._onNavigation());\n }\n\n stop(): void {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n }\n }\n\n private _onNavigation(): void {\n // Small delay to let the URL update\n setTimeout(() => {\n const current = location.href;\n if (current !== this.lastUrl) {\n const referrer = this.lastUrl;\n this.lastUrl = current;\n this.onPage(current, referrer);\n }\n }, 0);\n }\n}\n\n// ─── Outbound Link Tracking ─────────────────────────────────\n\nconst FILE_EXTENSIONS = /\\.(pdf|zip|doc|docx|xls|xlsx|csv|mp3|mp4|dmg|exe|rar|7z|gz|tar)$/i;\n\nexport function initOutboundTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: MouseEvent) {\n const link = (e.target as HTMLElement)?.closest?.('a');\n if (!link) return;\n\n const href = link.href;\n if (!href || href.startsWith('javascript:') || href.startsWith('#')) return;\n\n try {\n const url = new URL(href, location.href);\n if (url.hostname && url.hostname !== location.hostname) {\n instance.track('Outbound Link', { url: href });\n }\n } catch {\n // ignore malformed URLs\n }\n }\n\n document.addEventListener('click', handleClick, true);\n return () => document.removeEventListener('click', handleClick, true);\n}\n\n// ─── File Download Tracking ─────────────────────────────────\n\nexport function initFileDownloadTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: MouseEvent) {\n const link = (e.target as HTMLElement)?.closest?.('a');\n if (!link) return;\n\n const href = link.href;\n if (!href) return;\n\n try {\n const url = new URL(href, location.href);\n const match = url.pathname.match(FILE_EXTENSIONS);\n if (match) {\n instance.track('File Download', { url: href, extension: match[1].toLowerCase() });\n }\n } catch {\n // ignore malformed URLs\n }\n }\n\n document.addEventListener('click', handleClick, true);\n return () => document.removeEventListener('click', handleClick, true);\n}\n\n// ─── Scroll Depth Tracking ─────────────────────────────────\n\nexport function initScrollDepthTracking(instance: LitemetricsInstance): () => void {\n const milestones = [25, 50, 75, 90];\n const reached = new Set<number>();\n let lastPath = location.pathname;\n let ticking = false;\n\n function check() {\n ticking = false;\n\n // Reset on navigation\n if (location.pathname !== lastPath) {\n lastPath = location.pathname;\n reached.clear();\n }\n\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const docHeight = Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n );\n const viewHeight = window.innerHeight;\n const scrollable = docHeight - viewHeight;\n if (scrollable <= 0) return;\n\n const pct = (scrollTop / scrollable) * 100;\n for (const m of milestones) {\n if (pct >= m && !reached.has(m)) {\n reached.add(m);\n instance.track('Scroll Depth', { depth: `${m}%` });\n }\n }\n }\n\n function onScroll() {\n if (!ticking) {\n ticking = true;\n requestAnimationFrame(check);\n }\n }\n\n window.addEventListener('scroll', onScroll, { passive: true });\n return () => window.removeEventListener('scroll', onScroll);\n}\n\n// ─── Rage Click Detection ──────────────────────────────────\n\nexport function initRageClickTracking(instance: LitemetricsInstance): () => void {\n const clicks: { time: number; x: number; y: number }[] = [];\n const THRESHOLD = 3;\n const TIME_WINDOW = 800;\n const DISTANCE = 30; // pixels\n\n function handleClick(e: MouseEvent) {\n const now = Date.now();\n clicks.push({ time: now, x: e.clientX, y: e.clientY });\n\n // Remove old clicks outside window\n while (clicks.length > 0 && now - clicks[0].time > TIME_WINDOW) {\n clicks.shift();\n }\n\n if (clicks.length >= THRESHOLD) {\n // Check if all clicks are close together\n const first = clicks[0];\n const allClose = clicks.every(\n (c) => Math.abs(c.x - first.x) < DISTANCE && Math.abs(c.y - first.y) < DISTANCE\n );\n\n if (allClose) {\n const target = e.target as HTMLElement | null;\n const tagName = target?.tagName?.toLowerCase() || 'unknown';\n const text = (target?.innerText || '').slice(0, 50).trim();\n instance.track('Rage Click', { element: tagName, text: text || undefined });\n clicks.length = 0; // Reset after detection\n }\n }\n }\n\n document.addEventListener('click', handleClick, true);\n return () => document.removeEventListener('click', handleClick, true);\n}\n","import type { LitemetricsInstance } from './tracker';\n\nconst ATTR_EVENT = 'data-litemetrics-event';\nconst ATTR_PREFIX = 'data-litemetrics-event-';\n\n/**\n * Initialize data-attribute event tracking.\n * Clicks on elements with `data-litemetrics-event=\"EventName\"` will be auto-tracked.\n * Additional properties via `data-litemetrics-event-*` attributes.\n *\n * Example:\n * <button data-litemetrics-event=\"Signup\" data-litemetrics-event-plan=\"pro\">\n * → tracks event \"Signup\" with { plan: \"pro\" }\n */\nexport function initAttributeTracking(instance: LitemetricsInstance): () => void {\n function handleClick(e: Event) {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n\n // Walk up the DOM to find an element with data-litemetrics-event\n let el: HTMLElement | null = target;\n while (el) {\n const eventName = el.getAttribute(ATTR_EVENT);\n if (eventName) {\n // Collect data-litemetrics-event-* properties\n const properties: Record<string, string> = {};\n const attrs = el.attributes;\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i];\n if (attr.name.startsWith(ATTR_PREFIX)) {\n const key = attr.name.slice(ATTR_PREFIX.length);\n properties[key] = attr.value;\n }\n }\n\n instance.track(\n eventName,\n Object.keys(properties).length > 0 ? properties : undefined\n );\n return;\n }\n el = el.parentElement;\n }\n }\n\n document.addEventListener('click', handleClick, true);\n\n return () => {\n document.removeEventListener('click', handleClick, true);\n };\n}\n"],"mappings":";AAQA,SAAS,0BAA0B;;;ACRnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACJA,SAAS,aAAqB;AACnC,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAEA,eAAsB,WAAW,OAAgC;AAC/D,MAAI,OAAO,WAAW,eAAe,OAAO,QAAQ;AAClD,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,OAAO,QAAQ,OAAO,KAAK;AACjC,UAAM,OAAO,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI;AACvD,UAAM,QAAQ,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC;AAC7C,WAAO,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAAA,EAClE;AAEA,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,SAAM,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC,IAAK;AAAA,EAC7C;AACA,SAAO,KAAK,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACjD;AAEO,SAAS,WAAkC;AAChD,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,SAAS,IAAI,gBAAgB,SAAS,MAAM;AAClD,QAAM,MAAiB,CAAC;AACxB,MAAI,SAAS;AAEb,aAAW,CAAC,KAAK,KAAK,KAAK;AAAA,IACzB,CAAC,cAAc,QAAQ;AAAA,IACvB,CAAC,cAAc,QAAQ;AAAA,IACvB,CAAC,gBAAgB,UAAU;AAAA,IAC3B,CAAC,YAAY,MAAM;AAAA,IACnB,CAAC,eAAe,SAAS;AAAA,EAC3B,GAAY;AACV,UAAM,MAAM,OAAO,IAAI,GAAG;AAC1B,QAAI,KAAK;AACP,MAAC,IAA+B,KAAK,IAAI;AACzC,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO,SAAS,MAAM;AACxB;AAEO,SAAS,eAAuB;AACrC,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEO,SAAS,MAAc;AAC5B,SAAO,KAAK,IAAI;AAClB;;;ADlDA,SAAS,WAAW,KAA4B;AAC9C,MAAI;AACF,WAAO,aAAa,QAAQ,GAAG;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,KAAa,OAAqB;AACpD,MAAI;AACF,iBAAa,QAAQ,KAAK,KAAK;AAAA,EACjC,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,cAAc,KAAmB;AACxC,MAAI;AACF,iBAAa,WAAW,GAAG;AAAA,EAC7B,QAAQ;AAAA,EAER;AACF;AAEO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EACA,aAA4B;AAAA,EAC5B,UAAyB;AAAA,EACzB;AAAA,EAER,YAAY,UAAmB;AAC7B,SAAK,YAAY,aAAa,OAAO,aAAa,cAAc,SAAS,WAAW;AACpF,SAAK,aAAa,KAAK,oBAAoB;AAC3C,SAAK,UAAU,WAAW,gBAAgB;AAAA,EAC5C;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAwB;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,eAAgC;AACpC,QAAI,KAAK,WAAY,QAAO,KAAK;AAEjC,UAAM,SAAS,WAAW,mBAAmB;AAC7C,UAAM,QAAQ,aAAa;AAG3B,QAAI,QAAQ;AACV,YAAM,CAACA,KAAI,IAAI,IAAI,OAAO,MAAM,GAAG;AACnC,UAAI,SAAS,OAAO;AAClB,aAAK,aAAaA;AAClB,eAAOA;AAAA,MACT;AAAA,IACF;AAEA,UAAM,KAAK,MAAM,KAAK,mBAAmB;AACzC,SAAK,aAAa;AAClB,eAAW,qBAAqB,GAAG,EAAE,IAAI,KAAK,EAAE;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,eAAW,yBAAyB,IAAI,EAAE,SAAS,CAAC;AAAA,EACtD;AAAA,EAEA,SAAS,QAAsB;AAC7B,SAAK,UAAU;AACf,eAAW,kBAAkB,MAAM;AAAA,EACrC;AAAA,EAEA,QAAc;AACZ,SAAK,aAAa,WAAW;AAC7B,SAAK,aAAa;AAClB,SAAK,UAAU;AACf,kBAAc,mBAAmB;AACjC,kBAAc,mBAAmB;AACjC,kBAAc,gBAAgB;AAC9B,kBAAc,uBAAuB;AAAA,EACvC;AAAA,EAEQ,sBAA8B;AACpC,UAAM,SAAS,WAAW,mBAAmB;AAC7C,UAAM,aAAa,WAAW,uBAAuB;AAErD,QAAI,UAAU,YAAY;AACxB,YAAM,UAAU,IAAI,IAAI,SAAS,YAAY,EAAE;AAC/C,UAAI,UAAU,iBAAiB;AAC7B,aAAK,MAAM;AACX,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,KAAK,WAAW;AACtB,eAAW,qBAAqB,EAAE;AAClC,SAAK,MAAM;AACX,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,qBAAsC;AAClD,UAAM,aAAa;AAAA,MACjB,KAAK;AAAA,MACL,aAAa;AAAA,MACb,OAAO,cAAc,cAAc,UAAU,YAAY;AAAA,MACzD,OAAO,cAAc,cAAc,UAAU,WAAW;AAAA,MACxD,OAAO,SAAS,cACZ,KAAK,eAAe,EAAE,gBAAgB,EAAE,WACxC;AAAA,MACJ,OAAO,WAAW,cAAc,GAAG,OAAO,KAAK,IAAI,OAAO,MAAM,KAAK;AAAA,IACvE;AAEA,UAAM,MAAM,WAAW,KAAK,GAAG;AAC/B,UAAM,OAAO,MAAM,WAAW,GAAG;AACjC,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AACF;;;AE9HA,SAAS,oBAAoB,8BAA8B;AASpD,IAAM,YAAN,MAAgB;AAAA,EACb,QAAuB,CAAC;AAAA,EACxB,QAA+C;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA2B;AACrC,SAAK,WAAW,QAAQ;AACxB,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,QAAQ,QAAQ,SAAS;AAE9B,SAAK,YAAY;AACjB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,KAAK,OAA0B;AAC7B,SAAK,MAAM,KAAK,KAAK;AACrB,QAAI,KAAK,MAAM,UAAU,KAAK,WAAW;AACvC,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,MAAM,WAAW,EAAG;AAC7B,UAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AAClC,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,OAAO;AACd,oBAAc,KAAK,KAAK;AACxB,WAAK,QAAQ;AAAA,IACf;AACA,SAAK,MAAM;AAAA,EACb;AAAA,EAEQ,UAAU,QAA6B;AAC7C,UAAM,UAA0B,EAAE,OAAO;AACzC,UAAM,OAAO,KAAK,UAAU,OAAO;AAEnC,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,yBAAyB,OAAO,QAAQ,UAAU,MAAM;AAAA,IACtE;AAGA,QAAI,OAAO,UAAU,aAAa;AAChC,YAAM,KAAK,UAAU;AAAA,QACnB,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C;AAAA,QACA,WAAW;AAAA,MACb,CAAC,EAAE,MAAM,MAAM;AAEb,aAAK,QAAQ,IAAI;AAAA,MACnB,CAAC;AAAA,IACH,OAAO;AACL,WAAK,QAAQ,IAAI;AAAA,IACnB;AAAA,EACF;AAAA,EAEQ,QAAQ,MAAoB;AAClC,QAAI,OAAO,cAAc,eAAe,UAAU,YAAY;AAC5D,YAAM,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC1D,gBAAU,WAAW,KAAK,UAAU,IAAI;AAAA,IAC1C;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,QAAI,OAAO,gBAAgB,aAAa;AACtC,WAAK,QAAQ,YAAY,MAAM,KAAK,MAAM,GAAG,KAAK,aAAa;AAAA,IACjE;AAAA,EACF;AAAA,EAEQ,eAAqB;AAC3B,QAAI,OAAO,aAAa,YAAa;AAErC,UAAM,WAAW,MAAM;AACrB,UAAI,KAAK,MAAM,WAAW,EAAG;AAC7B,YAAM,UAA0B,EAAE,QAAQ,KAAK,MAAM,OAAO,CAAC,EAAE;AAC/D,YAAM,OAAO,KAAK,UAAU,OAAO;AAEnC,WAAK,QAAQ,IAAI;AAAA,IACnB;AAGA,aAAS,iBAAiB,oBAAoB,MAAM;AAClD,UAAI,SAAS,oBAAoB,UAAU;AACzC,iBAAS;AAAA,MACX;AAAA,IACF,CAAC;AAED,QAAI,OAAO,qBAAqB,aAAa;AAC3C,uBAAiB,YAAY,QAAQ;AAAA,IACvC;AAAA,EACF;AACF;;;ACxGO,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACA,UAAkB;AAAA,EAClB,oBAAqD;AAAA,EACrD,uBAA2D;AAAA,EAEnE,YAAY,QAAsB;AAChC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,WAAiB;AACf,QAAI,OAAO,YAAY,eAAe,OAAO,qBAAqB,YAAa;AAE/E,SAAK,UAAU,SAAS;AAGxB,SAAK,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AACvD,SAAK,uBAAuB,QAAQ,aAAa,KAAK,OAAO;AAE7D,YAAQ,YAAY,IAAI,SAA+C;AACrE,WAAK,kBAAmB,GAAG,IAAI;AAC/B,WAAK,cAAc;AAAA,IACrB;AAEA,YAAQ,eAAe,IAAI,SAAkD;AAC3E,WAAK,qBAAsB,GAAG,IAAI;AAClC,WAAK,cAAc;AAAA,IACrB;AAEA,qBAAiB,YAAY,MAAM,KAAK,cAAc,CAAC;AAAA,EACzD;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,mBAAmB;AAC1B,cAAQ,YAAY,KAAK;AAAA,IAC3B;AACA,QAAI,KAAK,sBAAsB;AAC7B,cAAQ,eAAe,KAAK;AAAA,IAC9B;AAAA,EACF;AAAA,EAEQ,gBAAsB;AAE5B,eAAW,MAAM;AACf,YAAM,UAAU,SAAS;AACzB,UAAI,YAAY,KAAK,SAAS;AAC5B,cAAM,WAAW,KAAK;AACtB,aAAK,UAAU;AACf,aAAK,OAAO,SAAS,QAAQ;AAAA,MAC/B;AAAA,IACF,GAAG,CAAC;AAAA,EACN;AACF;AAIA,IAAM,kBAAkB;AAEjB,SAAS,qBAAqB,UAA2C;AAC9E,WAAS,YAAY,GAAe;AAClC,UAAM,OAAQ,EAAE,QAAwB,UAAU,GAAG;AACrD,QAAI,CAAC,KAAM;AAEX,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,QAAQ,KAAK,WAAW,aAAa,KAAK,KAAK,WAAW,GAAG,EAAG;AAErE,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI;AACvC,UAAI,IAAI,YAAY,IAAI,aAAa,SAAS,UAAU;AACtD,iBAAS,MAAM,iBAAiB,EAAE,KAAK,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,WAAS,iBAAiB,SAAS,aAAa,IAAI;AACpD,SAAO,MAAM,SAAS,oBAAoB,SAAS,aAAa,IAAI;AACtE;AAIO,SAAS,yBAAyB,UAA2C;AAClF,WAAS,YAAY,GAAe;AAClC,UAAM,OAAQ,EAAE,QAAwB,UAAU,GAAG;AACrD,QAAI,CAAC,KAAM;AAEX,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AAEX,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI;AACvC,YAAM,QAAQ,IAAI,SAAS,MAAM,eAAe;AAChD,UAAI,OAAO;AACT,iBAAS,MAAM,iBAAiB,EAAE,KAAK,MAAM,WAAW,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;AAAA,MAClF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,WAAS,iBAAiB,SAAS,aAAa,IAAI;AACpD,SAAO,MAAM,SAAS,oBAAoB,SAAS,aAAa,IAAI;AACtE;AAIO,SAAS,wBAAwB,UAA2C;AACjF,QAAM,aAAa,CAAC,IAAI,IAAI,IAAI,EAAE;AAClC,QAAM,UAAU,oBAAI,IAAY;AAChC,MAAI,WAAW,SAAS;AACxB,MAAI,UAAU;AAEd,WAAS,QAAQ;AACf,cAAU;AAGV,QAAI,SAAS,aAAa,UAAU;AAClC,iBAAW,SAAS;AACpB,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,YAAY,OAAO,WAAW,SAAS,gBAAgB;AAC7D,UAAM,YAAY,KAAK;AAAA,MACrB,SAAS,KAAK;AAAA,MACd,SAAS,gBAAgB;AAAA,IAC3B;AACA,UAAM,aAAa,OAAO;AAC1B,UAAM,aAAa,YAAY;AAC/B,QAAI,cAAc,EAAG;AAErB,UAAM,MAAO,YAAY,aAAc;AACvC,eAAW,KAAK,YAAY;AAC1B,UAAI,OAAO,KAAK,CAAC,QAAQ,IAAI,CAAC,GAAG;AAC/B,gBAAQ,IAAI,CAAC;AACb,iBAAS,MAAM,gBAAgB,EAAE,OAAO,GAAG,CAAC,IAAI,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAEA,WAAS,WAAW;AAClB,QAAI,CAAC,SAAS;AACZ,gBAAU;AACV,4BAAsB,KAAK;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AAC7D,SAAO,MAAM,OAAO,oBAAoB,UAAU,QAAQ;AAC5D;AAIO,SAAS,sBAAsB,UAA2C;AAC/E,QAAM,SAAmD,CAAC;AAC1D,QAAM,YAAY;AAClB,QAAM,cAAc;AACpB,QAAM,WAAW;AAEjB,WAAS,YAAY,GAAe;AAClC,UAAMC,OAAM,KAAK,IAAI;AACrB,WAAO,KAAK,EAAE,MAAMA,MAAK,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC;AAGrD,WAAO,OAAO,SAAS,KAAKA,OAAM,OAAO,CAAC,EAAE,OAAO,aAAa;AAC9D,aAAO,MAAM;AAAA,IACf;AAEA,QAAI,OAAO,UAAU,WAAW;AAE9B,YAAM,QAAQ,OAAO,CAAC;AACtB,YAAM,WAAW,OAAO;AAAA,QACtB,CAAC,MAAM,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,YAAY,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI;AAAA,MACzE;AAEA,UAAI,UAAU;AACZ,cAAM,SAAS,EAAE;AACjB,cAAM,UAAU,QAAQ,SAAS,YAAY,KAAK;AAClD,cAAM,QAAQ,QAAQ,aAAa,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK;AACzD,iBAAS,MAAM,cAAc,EAAE,SAAS,SAAS,MAAM,QAAQ,OAAU,CAAC;AAC1E,eAAO,SAAS;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAEA,WAAS,iBAAiB,SAAS,aAAa,IAAI;AACpD,SAAO,MAAM,SAAS,oBAAoB,SAAS,aAAa,IAAI;AACtE;;;AC7LA,IAAM,aAAa;AACnB,IAAM,cAAc;AAWb,SAAS,sBAAsB,UAA2C;AAC/E,WAAS,YAAY,GAAU;AAC7B,UAAM,SAAS,EAAE;AACjB,QAAI,CAAC,OAAQ;AAGb,QAAI,KAAyB;AAC7B,WAAO,IAAI;AACT,YAAM,YAAY,GAAG,aAAa,UAAU;AAC5C,UAAI,WAAW;AAEb,cAAM,aAAqC,CAAC;AAC5C,cAAM,QAAQ,GAAG;AACjB,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,OAAO,MAAM,CAAC;AACpB,cAAI,KAAK,KAAK,WAAW,WAAW,GAAG;AACrC,kBAAM,MAAM,KAAK,KAAK,MAAM,YAAY,MAAM;AAC9C,uBAAW,GAAG,IAAI,KAAK;AAAA,UACzB;AAAA,QACF;AAEA,iBAAS;AAAA,UACP;AAAA,UACA,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,aAAa;AAAA,QACpD;AACA;AAAA,MACF;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF;AAEA,WAAS,iBAAiB,SAAS,aAAa,IAAI;AAEpD,SAAO,MAAM;AACX,aAAS,oBAAoB,SAAS,aAAa,IAAI;AAAA,EACzD;AACF;;;ALzBO,SAAS,cAAc,QAA4C;AACxE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,EACf,IAAI;AAGJ,MAAI,cAAc,OAAO,cAAc,eAAe,UAAU,eAAe,KAAK;AAClF,WAAO,kBAAkB;AAAA,EAC3B;AAGA,MAAI;AACF,QAAI,aAAa,QAAQ,kBAAkB,MAAM,KAAK;AACpD,aAAO,kBAAkB;AAAA,IAC3B;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,QAAM;AAAA,IACJ,eAAe;AAAA,IACf,oBAAoB;AAAA,IACpB,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,EACnB,IAAI;AAEJ,QAAM,UAAU,IAAI,eAAe;AACnC,QAAM,YAAY,IAAI,UAAU;AAAA,IAC9B;AAAA,IACA,WAAW,OAAO;AAAA,IAClB,eAAe,OAAO;AAAA,IACtB;AAAA,EACF,CAAC;AACD,MAAI,cAAkC;AACtC,MAAI,oBAAyC;AAC7C,QAAM,eAA+B,CAAC;AACtC,MAAI,WAAW;AAEf,WAAS,aAA4B;AACnC,UAAM,MAAqB,CAAC;AAC5B,QAAI,OAAO,WAAW,aAAa;AACjC,UAAI,SAAS,EAAE,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO;AAAA,IAC5D;AACA,QAAI,OAAO,cAAc,aAAa;AACpC,UAAI,WAAW,UAAU;AAEzB,YAAM,OAAQ,UAAkB;AAChC,UAAI,MAAM;AACR,YAAI,aAAa;AAAA,UACf,MAAM,KAAK;AAAA,UACX,UAAU,KAAK;AAAA,UACf,eAAe,KAAK;AAAA,UACpB,KAAK,KAAK;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO,SAAS,aAAa;AAC/B,UAAI,WAAW,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAAA,IACzD;AACA,UAAM,MAAM,SAAS;AACrB,QAAI,IAAK,KAAI,MAAM;AACnB,WAAO;AAAA,EACT;AAEA,iBAAe,UAAU,OAAmC;AAC1D,QAAI,SAAU;AACd,YAAQ,MAAM;AACd,cAAU,KAAK,KAAK;AAAA,EACtB;AAEA,iBAAe,UAAU,KAAc,OAAgB,UAAkC;AACvF,UAAM,YAAY,MAAM,QAAQ,aAAa;AAC7C,UAAM,QAAuC;AAAA,MAC3C,MAAM;AAAA,MACN;AAAA,MACA,WAAW,IAAI;AAAA,MACf,WAAW,QAAQ;AAAA,MACnB;AAAA,MACA,KAAK,QAAQ,OAAO,aAAa,cAAc,SAAS,OAAO;AAAA,MAC/D,UAAU,aAAa,OAAO,aAAa,cAAc,SAAS,WAAW;AAAA,MAC7E,OAAO,UAAU,OAAO,aAAa,cAAc,SAAS,QAAQ;AAAA,MACpE,GAAG,WAAW;AAAA,IAChB;AACA,cAAU,KAAK;AAAA,EACjB;AAGA,MAAI,WAAW;AACb,cAAU;AAAA,EACZ;AAGA,MAAI,SAAS;AACX,kBAAc,IAAI,YAAY,CAAC,KAAK,QAAQ,UAAU,KAAK,QAAW,GAAG,CAAC;AAC1E,gBAAY,SAAS;AAAA,EACvB;AAIA,QAAM,WAAgC;AAAA,IACpC,MAAM,MAAc,YAA4C;AAC9D,cAAQ,aAAa,EAAE,KAAK,CAAC,cAAc;AACzC,cAAM,QAAqC;AAAA,UACzC,MAAM;AAAA,UACN;AAAA,UACA,WAAW,IAAI;AAAA,UACf,WAAW,QAAQ;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAG,WAAW;AAAA,QAChB;AACA,kBAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,IAEA,SAAS,QAAgB,QAAwC;AAC/D,cAAQ,SAAS,MAAM;AACvB,cAAQ,aAAa,EAAE,KAAK,CAAC,cAAc;AACzC,cAAM,QAAuC;AAAA,UAC3C,MAAM;AAAA,UACN;AAAA,UACA,WAAW,IAAI;AAAA,UACf,WAAW,QAAQ;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAG,WAAW;AAAA,QAChB;AACA,kBAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,IAEA,KAAK,KAAc,OAAsB;AACvC,gBAAU,KAAK,KAAK;AAAA,IACtB;AAAA,IAEA,QAAc;AACZ,cAAQ,MAAM;AAAA,IAChB;AAAA,IAEA,UAAgB;AACd,iBAAW;AACX,UAAI;AACF,qBAAa,QAAQ,oBAAoB,GAAG;AAAA,MAC9C,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IAEA,SAAe;AACb,iBAAW;AACX,UAAI;AACF,qBAAa,WAAW,kBAAkB;AAAA,MAC5C,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IAEA,UAAgB;AACd,0BAAoB;AACpB,mBAAa,QAAQ,CAAC,OAAO,GAAG,CAAC;AACjC,mBAAa,KAAK;AAClB,gBAAU,QAAQ;AAAA,IACpB;AAAA,EACF;AAGA,MAAI,aAAa,OAAO,aAAa,aAAa;AAChD,wBAAoB,sBAAsB,QAAQ;AAGlD,QAAI,aAAc,cAAa,KAAK,qBAAqB,QAAQ,CAAC;AAClE,QAAI,kBAAmB,cAAa,KAAK,yBAAyB,QAAQ,CAAC;AAC3E,QAAI,gBAAiB,cAAa,KAAK,wBAAwB,QAAQ,CAAC;AACxE,QAAI,eAAgB,cAAa,KAAK,sBAAsB,QAAQ,CAAC;AAAA,EACvE;AAEA,SAAO;AACT;AAEA,SAAS,oBAAyC;AAChD,SAAO;AAAA,IACL,QAAQ;AAAA,IAAC;AAAA,IACT,WAAW;AAAA,IAAC;AAAA,IACZ,OAAO;AAAA,IAAC;AAAA,IACR,QAAQ;AAAA,IAAC;AAAA,IACT,UAAU;AAAA,IAAC;AAAA,IACX,SAAS;AAAA,IAAC;AAAA,IACV,UAAU;AAAA,IAAC;AAAA,EACb;AACF;","names":["id","now"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litemetrics/tracker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Lightweight browser analytics tracker (<3KB gzipped)",
5
5
  "license": "MIT",
6
6
  "author": "Metehan Kurucu",
@@ -9,7 +9,13 @@
9
9
  "url": "https://github.com/metehankurucu/litemetrics",
10
10
  "directory": "packages/tracker"
11
11
  },
12
- "keywords": ["analytics", "tracking", "litemetrics", "browser", "pageview"],
12
+ "keywords": [
13
+ "analytics",
14
+ "tracking",
15
+ "litemetrics",
16
+ "browser",
17
+ "pageview"
18
+ ],
13
19
  "type": "module",
14
20
  "main": "./dist/litemetrics.cjs",
15
21
  "module": "./dist/litemetrics.js",
@@ -22,7 +28,9 @@
22
28
  },
23
29
  "./script": "./dist/litemetrics.global.js"
24
30
  },
25
- "files": ["dist"],
31
+ "files": [
32
+ "dist"
33
+ ],
26
34
  "publishConfig": {
27
35
  "access": "public"
28
36
  },