@marlinjai/analytics-tracker 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {a,e,f,c as c$1,b as b$1}from'./chunk-YN3C5VXM.js';var m="ap_session_id",c="ap_last_activity";function v(){let r=Date.now(),e$1=sessionStorage.getItem(m),t=Number(sessionStorage.getItem(c)||"0");if(e$1&&r-t<e)return sessionStorage.setItem(c,String(r)),{sessionId:e$1,isNew:false};let n=crypto.randomUUID();return sessionStorage.setItem(m,n),sessionStorage.setItem(c,String(r)),{sessionId:n,isNew:true}}function y(){sessionStorage.setItem(c,String(Date.now()));}var w=3,T=1e3,l=class{constructor(e,t,n,i=false){a(this,"queue",[]);a(this,"timer",null);a(this,"endpoint");a(this,"apiKey");a(this,"debug");this.endpoint=e,this.apiKey=t,this.debug=i,this.timer=setInterval(()=>this.flush(),n??f),typeof window<"u"&&(window.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush(true);}),window.addEventListener("pagehide",()=>this.flush(true)));}add(e){this.queue.push(e),this.debug&&console.log("[analytics] queued:",e.type,e),this.queue.length>=c$1&&this.flush();}async flush(e=false){if(this.queue.length===0)return;let t=this.queue.splice(0,c$1),n=JSON.stringify(t);if(e&&typeof navigator<"u"&&navigator.sendBeacon){let i=new Blob([n],{type:"application/json"});if(navigator.sendBeacon(this.endpoint,i)){this.debug&&console.log("[analytics] beacon sent:",t.length,"events");return}}await this.fetchWithRetry(n,t);}async fetchWithRetry(e,t){for(let n=0;n<=w;n++){try{let i=await fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:e,keepalive:!0});if(i.ok){this.debug&&console.log("[analytics] flushed:",t.length,"events");return}if(i.status>=400&&i.status<500){this.debug&&console.warn("[analytics] flush rejected:",i.status);return}}catch{}if(n<w){let i=T*Math.pow(2,n);await new Promise(a=>setTimeout(a,i));}}this.debug&&console.warn("[analytics] flush failed after retries, dropping",t.length,"events");}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flush(true);}get pending(){return this.queue.length}};function b(r){return r<b$1.mobile?"mobile":r<b$1.tablet?"tablet":"desktop"}function S(){return {screenWidth:window.screen.width,screenHeight:window.screen.height}}function C(r){let e=[],t=r;for(;t&&t!==document.body&&e.length<5;){let n=t.tagName.toLowerCase();if(t.id){n+=`#${t.id}`,e.unshift(n);break}if(t.className&&typeof t.className=="string"){let i=t.className.trim().split(/\s+/).slice(0,2).join(".");i&&(n+=`.${i}`);}e.unshift(n),t=t.parentElement;}return e.join(" > ").slice(0,256)}function E(r){let e=()=>{r({type:"pageview",url:location.href,referrer:document.referrer,title:document.title});},t=history.pushState.bind(history),n=history.replaceState.bind(history);return history.pushState=function(...i){t(...i),e();},history.replaceState=function(...i){n(...i),e();},window.addEventListener("popstate",e),e(),()=>{history.pushState=t,history.replaceState=n,window.removeEventListener("popstate",e);}}function k(r){let e=t=>{let n=t.target;n&&r({type:"click",url:location.href,x:t.pageX,y:t.pageY,selector:C(n)});};return document.addEventListener("click",e,{capture:true}),()=>document.removeEventListener("click",e,{capture:true})}function I(r){let e=0,t=false,n=()=>{t||(t=true,requestAnimationFrame(()=>{let i=window.scrollY,a=document.documentElement.scrollHeight-window.innerHeight,h=a>0?Math.round(i/a*100):0;h>e&&(e=h,r({type:"scroll",url:location.href,scrollDepth:e})),t=false;}));};return window.addEventListener("scroll",n,{passive:true}),()=>{window.removeEventListener("scroll",n);}}var p=class{constructor(e){a(this,"config");a(this,"sessionId");a(this,"batcher");a(this,"cleanups",[]);this.config=e;let{sessionId:t,isNew:n}=v();this.sessionId=t,this.batcher=new l(e.endpoint,e.apiKey,e.flushInterval,e.debug),n&&this.track({type:"session_start",url:location.href}),this.cleanups.push(E(i=>this.track(i))),e.heatmap!==false&&this.cleanups.push(k(i=>this.track(i))),e.scrollDepth!==false&&this.cleanups.push(I(i=>this.track(i)));}track(e){y();let{screenWidth:t,screenHeight:n}=S(),i={...e,projectId:this.config.projectId,sessionId:this.sessionId,timestamp:Date.now(),screenWidth:t,screenHeight:n,deviceType:b(window.innerWidth),userAgent:navigator.userAgent};this.batcher.add(i);}destroy(){for(let e of this.cleanups)e();this.cleanups=[],this.batcher.destroy();}};var o=null;function Y(r){return o?(r.debug&&console.warn("[analytics] already initialized, returning existing instance"),o):(o=new p(r),r.replay&&import('./replay-HK5T636Z.js').then(e=>e.initReplay(o)).catch(()=>{r.debug&&console.warn("[analytics] rrweb not available, replay disabled");}),o)}function W(){return o}function U(){o?.destroy(),o=null;}export{p as AnalyticsTracker,U as destroy,W as getTracker,Y as init};//# sourceMappingURL=index.js.map
1
+ import {a,e,f,c as c$1,b as b$1}from'./chunk-YN3C5VXM.js';var g="ap_session_id",c="ap_last_activity";function v(){let r=Date.now(),e$1=sessionStorage.getItem(g),t=Number(sessionStorage.getItem(c)||"0");if(e$1&&r-t<e)return sessionStorage.setItem(c,String(r)),{sessionId:e$1,isNew:false};let n=crypto.randomUUID();return sessionStorage.setItem(g,n),sessionStorage.setItem(c,String(r)),{sessionId:n,isNew:true}}function y(){sessionStorage.setItem(c,String(Date.now()));}var w=3,T=1e3,l=class{constructor(e,t,n,i=false){a(this,"queue",[]);a(this,"timer",null);a(this,"endpoint");a(this,"apiKey");a(this,"debug");this.endpoint=e,this.apiKey=t,this.debug=i,this.timer=setInterval(()=>this.flush(),n??f),typeof window<"u"&&(window.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush(true);}),window.addEventListener("pagehide",()=>this.flush(true)));}add(e){this.queue.push(e),this.debug&&console.log("[analytics] queued:",e.type,e),this.queue.length>=c$1&&this.flush();}async flush(e=false){if(this.queue.length===0)return;let t=this.queue.splice(0,c$1),n=JSON.stringify(t);if(e&&typeof navigator<"u"&&navigator.sendBeacon){let i=new Blob([n],{type:"application/json"});if(navigator.sendBeacon(this.endpoint,i)){this.debug&&console.log("[analytics] beacon sent:",t.length,"events");return}}await this.fetchWithRetry(n,t);}async fetchWithRetry(e,t){for(let n=0;n<=w;n++){try{let i=await fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:e,keepalive:!0,credentials:"omit"});if(i.ok){this.debug&&console.log("[analytics] flushed:",t.length,"events");return}if(i.status>=400&&i.status<500){this.debug&&console.warn("[analytics] flush rejected:",i.status);return}}catch{}if(n<w){let i=T*Math.pow(2,n);await new Promise(a=>setTimeout(a,i));}}this.debug&&console.warn("[analytics] flush failed after retries, dropping",t.length,"events");}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flush(true);}get pending(){return this.queue.length}};function b(r){return r<b$1.mobile?"mobile":r<b$1.tablet?"tablet":"desktop"}function S(){return {screenWidth:window.screen.width,screenHeight:window.screen.height}}function C(r){let e=[],t=r;for(;t&&t!==document.body&&e.length<5;){let n=t.tagName.toLowerCase();if(t.id){n+=`#${t.id}`,e.unshift(n);break}if(t.className&&typeof t.className=="string"){let i=t.className.trim().split(/\s+/).slice(0,2).join(".");i&&(n+=`.${i}`);}e.unshift(n),t=t.parentElement;}return e.join(" > ").slice(0,256)}function E(r){let e=()=>{r({type:"pageview",url:location.href,referrer:document.referrer,title:document.title});},t=history.pushState.bind(history),n=history.replaceState.bind(history);return history.pushState=function(...i){t(...i),e();},history.replaceState=function(...i){n(...i),e();},window.addEventListener("popstate",e),e(),()=>{history.pushState=t,history.replaceState=n,window.removeEventListener("popstate",e);}}function k(r){let e=t=>{let n=t.target;n&&r({type:"click",url:location.href,x:t.pageX,y:t.pageY,selector:C(n)});};return document.addEventListener("click",e,{capture:true}),()=>document.removeEventListener("click",e,{capture:true})}function I(r){let e=0,t=false,n=()=>{t||(t=true,requestAnimationFrame(()=>{let i=window.scrollY,a=document.documentElement.scrollHeight-window.innerHeight,h=a>0?Math.round(i/a*100):0;h>e&&(e=h,r({type:"scroll",url:location.href,scrollDepth:e})),t=false;}));};return window.addEventListener("scroll",n,{passive:true}),()=>{window.removeEventListener("scroll",n);}}var p=class{constructor(e){a(this,"config");a(this,"sessionId");a(this,"batcher");a(this,"cleanups",[]);this.config=e;let{sessionId:t,isNew:n}=v();this.sessionId=t,this.batcher=new l(e.endpoint,e.apiKey,e.flushInterval,e.debug),n&&this.track({type:"session_start",url:location.href}),this.cleanups.push(E(i=>this.track(i))),e.heatmap!==false&&this.cleanups.push(k(i=>this.track(i))),e.scrollDepth!==false&&this.cleanups.push(I(i=>this.track(i)));}track(e){y();let{screenWidth:t,screenHeight:n}=S(),i={...e,projectId:this.config.projectId,sessionId:this.sessionId,timestamp:Date.now(),screenWidth:t,screenHeight:n,deviceType:b(window.innerWidth),userAgent:navigator.userAgent};this.batcher.add(i);}destroy(){for(let e of this.cleanups)e();this.cleanups=[],this.batcher.destroy();}};var o=null;function Y(r){return o?(r.debug&&console.warn("[analytics] already initialized, returning existing instance"),o):(o=new p(r),r.replay&&import('./replay-HK5T636Z.js').then(e=>e.initReplay(o)).catch(()=>{r.debug&&console.warn("[analytics] rrweb not available, replay disabled");}),o)}function W(){return o}function U(){o?.destroy(),o=null;}export{p as AnalyticsTracker,U as destroy,W as getTracker,Y as init};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/session.ts","../src/batch.ts","../src/device.ts","../src/listeners.ts","../src/tracker.ts","../src/index.ts"],"names":["SESSION_KEY","LAST_ACTIVITY_KEY","getOrCreateSession","now","stored","lastActivity","SESSION_TIMEOUT_MS","sessionId","touchSession","MAX_RETRIES","BASE_DELAY_MS","EventBatcher","endpoint","apiKey","flushInterval","debug","__publicField","FLUSH_INTERVAL_MS","event","MAX_BATCH_SIZE","useBeacon","batch","body","blob","attempt","res","delay","r","getDeviceType","width","DEVICE_BREAKPOINTS","getScreenDimensions","getCssSelector","el","parts","current","selector","cls","attachPageviewListener","cb","trackPageview","origPushState","origReplaceState","args","attachClickListener","handler","e","target","attachScrollListener","maxDepth","ticking","scrollTop","docHeight","depth","AnalyticsTracker","config","isNew","partial","screenWidth","screenHeight","cleanup","instance","init","mod","getTracker","destroy"],"mappings":"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","file":"index.js","sourcesContent":["import { SESSION_TIMEOUT_MS } from './constants';\n\nconst SESSION_KEY = 'ap_session_id';\nconst LAST_ACTIVITY_KEY = 'ap_last_activity';\n\nexport function getOrCreateSession(): { sessionId: string; isNew: boolean } {\n const now = Date.now();\n const stored = sessionStorage.getItem(SESSION_KEY);\n const lastActivity = Number(sessionStorage.getItem(LAST_ACTIVITY_KEY) || '0');\n\n // Existing session that hasn't timed out\n if (stored && now - lastActivity < SESSION_TIMEOUT_MS) {\n sessionStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n return { sessionId: stored, isNew: false };\n }\n\n // New session\n const sessionId = crypto.randomUUID();\n sessionStorage.setItem(SESSION_KEY, sessionId);\n sessionStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n return { sessionId, isNew: true };\n}\n\nexport function touchSession(): void {\n sessionStorage.setItem(LAST_ACTIVITY_KEY, String(Date.now()));\n}\n\nexport function getSessionId(): string | null {\n return sessionStorage.getItem(SESSION_KEY);\n}\n","import type { TrackerEvent } from './constants';\nimport { FLUSH_INTERVAL_MS, MAX_BATCH_SIZE } from './constants';\n\nconst MAX_RETRIES = 3;\nconst BASE_DELAY_MS = 1000;\n\nexport class EventBatcher {\n private queue: TrackerEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private endpoint: string;\n private apiKey: string;\n private debug: boolean;\n\n constructor(endpoint: string, apiKey: string, flushInterval?: number, debug = false) {\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.debug = debug;\n this.timer = setInterval(() => this.flush(), flushInterval ?? FLUSH_INTERVAL_MS);\n\n if (typeof window !== 'undefined') {\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.flush(true);\n });\n window.addEventListener('pagehide', () => this.flush(true));\n }\n }\n\n add(event: TrackerEvent): void {\n this.queue.push(event);\n if (this.debug) console.log('[analytics] queued:', event.type, event);\n if (this.queue.length >= MAX_BATCH_SIZE) this.flush();\n }\n\n async flush(useBeacon = false): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, MAX_BATCH_SIZE);\n const body = JSON.stringify(batch);\n\n if (useBeacon && typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob([body], { type: 'application/json' });\n const sent = navigator.sendBeacon(this.endpoint, blob);\n if (sent) {\n if (this.debug) console.log('[analytics] beacon sent:', batch.length, 'events');\n return;\n }\n // Beacon failed, fall through to fetch\n }\n\n await this.fetchWithRetry(body, batch);\n }\n\n private async fetchWithRetry(body: string, batch: TrackerEvent[]): Promise<void> {\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const res = await fetch(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body,\n keepalive: true,\n });\n\n if (res.ok) {\n if (this.debug) console.log('[analytics] flushed:', batch.length, 'events');\n return;\n }\n\n // Don't retry 4xx errors\n if (res.status >= 400 && res.status < 500) {\n if (this.debug) console.warn('[analytics] flush rejected:', res.status);\n return;\n }\n } catch {\n // Network error, retry\n }\n\n if (attempt < MAX_RETRIES) {\n const delay = BASE_DELAY_MS * Math.pow(2, attempt);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n if (this.debug) console.warn('[analytics] flush failed after retries, dropping', batch.length, 'events');\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush(true);\n }\n\n get pending(): number {\n return this.queue.length;\n }\n}\n","import type { DeviceType } from './constants';\nimport { DEVICE_BREAKPOINTS } from './constants';\n\nexport function getDeviceType(width: number): DeviceType {\n if (width < DEVICE_BREAKPOINTS.mobile) return 'mobile';\n if (width < DEVICE_BREAKPOINTS.tablet) return 'tablet';\n return 'desktop';\n}\n\nexport function getScreenDimensions() {\n return {\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n };\n}\n","import type { TrackerEvent } from './constants';\n\ntype EventCallback = (event: Omit<TrackerEvent, 'projectId' | 'sessionId' | 'timestamp'>) => void;\n\nfunction getCssSelector(el: Element): string {\n const parts: string[] = [];\n let current: Element | null = el;\n\n while (current && current !== document.body && parts.length < 5) {\n let selector = current.tagName.toLowerCase();\n if (current.id) {\n selector += `#${current.id}`;\n parts.unshift(selector);\n break;\n }\n if (current.className && typeof current.className === 'string') {\n const cls = current.className.trim().split(/\\s+/).slice(0, 2).join('.');\n if (cls) selector += `.${cls}`;\n }\n parts.unshift(selector);\n current = current.parentElement;\n }\n\n return parts.join(' > ').slice(0, 256);\n}\n\nexport function attachPageviewListener(cb: EventCallback): () => void {\n const trackPageview = () => {\n cb({\n type: 'pageview',\n url: location.href,\n referrer: document.referrer,\n title: document.title,\n });\n };\n\n // Monkey-patch history methods\n const origPushState = history.pushState.bind(history);\n const origReplaceState = history.replaceState.bind(history);\n\n history.pushState = function (...args) {\n origPushState(...args);\n trackPageview();\n };\n\n history.replaceState = function (...args) {\n origReplaceState(...args);\n trackPageview();\n };\n\n window.addEventListener('popstate', trackPageview);\n\n // Track initial pageview\n trackPageview();\n\n return () => {\n history.pushState = origPushState;\n history.replaceState = origReplaceState;\n window.removeEventListener('popstate', trackPageview);\n };\n}\n\nexport function attachClickListener(cb: EventCallback): () => void {\n const handler = (e: MouseEvent) => {\n const target = e.target as Element | null;\n if (!target) return;\n\n cb({\n type: 'click',\n url: location.href,\n x: e.pageX,\n y: e.pageY,\n selector: getCssSelector(target),\n });\n };\n\n document.addEventListener('click', handler, { capture: true });\n return () => document.removeEventListener('click', handler, { capture: true });\n}\n\nexport function attachScrollListener(cb: EventCallback): () => void {\n let maxDepth = 0;\n let ticking = false;\n\n const handler = () => {\n if (ticking) return;\n ticking = true;\n\n requestAnimationFrame(() => {\n const scrollTop = window.scrollY;\n const docHeight = document.documentElement.scrollHeight - window.innerHeight;\n const depth = docHeight > 0 ? Math.round((scrollTop / docHeight) * 100) : 0;\n\n if (depth > maxDepth) {\n maxDepth = depth;\n cb({\n type: 'scroll',\n url: location.href,\n scrollDepth: maxDepth,\n });\n }\n ticking = false;\n });\n };\n\n window.addEventListener('scroll', handler, { passive: true });\n\n return () => {\n window.removeEventListener('scroll', handler);\n };\n}\n","import type { TrackerEvent } from './constants';\nimport type { TrackerConfig } from './index.js';\nimport { getOrCreateSession, touchSession } from './session.js';\nimport { EventBatcher } from './batch.js';\nimport { getDeviceType, getScreenDimensions } from './device.js';\nimport { attachPageviewListener, attachClickListener, attachScrollListener } from './listeners.js';\n\nexport class AnalyticsTracker {\n private config: Required<Pick<TrackerConfig, 'projectId' | 'endpoint'>> & TrackerConfig;\n private sessionId: string;\n private batcher: EventBatcher;\n private cleanups: (() => void)[] = [];\n\n constructor(config: TrackerConfig) {\n this.config = config;\n const { sessionId, isNew } = getOrCreateSession();\n this.sessionId = sessionId;\n\n this.batcher = new EventBatcher(\n config.endpoint,\n config.apiKey,\n config.flushInterval,\n config.debug\n );\n\n // Fire session_start if new\n if (isNew) {\n this.track({ type: 'session_start', url: location.href });\n }\n\n // Pageview listener (always on)\n this.cleanups.push(\n attachPageviewListener((e) => this.track(e))\n );\n\n // Click / heatmap listener\n if (config.heatmap !== false) {\n this.cleanups.push(\n attachClickListener((e) => this.track(e))\n );\n }\n\n // Scroll depth listener\n if (config.scrollDepth !== false) {\n this.cleanups.push(\n attachScrollListener((e) => this.track(e))\n );\n }\n }\n\n track(partial: Omit<TrackerEvent, 'projectId' | 'sessionId' | 'timestamp'>): void {\n touchSession();\n const { screenWidth, screenHeight } = getScreenDimensions();\n const event: TrackerEvent = {\n ...partial,\n projectId: this.config.projectId,\n sessionId: this.sessionId,\n timestamp: Date.now(),\n screenWidth,\n screenHeight,\n deviceType: getDeviceType(window.innerWidth),\n userAgent: navigator.userAgent,\n };\n this.batcher.add(event);\n }\n\n destroy(): void {\n for (const cleanup of this.cleanups) cleanup();\n this.cleanups = [];\n this.batcher.destroy();\n }\n}\n","import type { TrackerEvent } from './constants';\nimport { AnalyticsTracker } from './tracker.js';\n\nexport interface TrackerConfig {\n /** Project ID (UUID). */\n projectId: string;\n /** Ingestion endpoint URL. */\n endpoint: string;\n /** API key (ap_live_... or ap_test_...). */\n apiKey: string;\n /** Enable session replay (requires rrweb peer dep). Default: false. */\n replay?: boolean;\n /** Enable click heatmap tracking. Default: true. */\n heatmap?: boolean;\n /** Enable scroll depth tracking. Default: true. */\n scrollDepth?: boolean;\n /** Batch flush interval in ms. Default: 5000. */\n flushInterval?: number;\n /** Debug mode — logs events to console. Default: false. */\n debug?: boolean;\n}\n\nlet instance: AnalyticsTracker | null = null;\n\n/**\n * Initialize the analytics tracker. Creates a singleton.\n */\nexport function init(config: TrackerConfig): AnalyticsTracker {\n if (instance) {\n if (config.debug) console.warn('[analytics] already initialized, returning existing instance');\n return instance;\n }\n\n instance = new AnalyticsTracker(config);\n\n // Lazy-load replay if enabled\n if (config.replay) {\n import('./replay.js')\n .then((mod) => mod.initReplay(instance!))\n .catch(() => {\n if (config.debug) console.warn('[analytics] rrweb not available, replay disabled');\n });\n }\n\n return instance;\n}\n\n/**\n * Get the current tracker instance, or null if not initialized.\n */\nexport function getTracker(): AnalyticsTracker | null {\n return instance;\n}\n\n/**\n * Destroy the tracker and clean up listeners.\n */\nexport function destroy(): void {\n instance?.destroy();\n instance = null;\n}\n\nexport type { TrackerEvent, TrackerConfig as AnalyticsConfig };\nexport { AnalyticsTracker };\n"]}
1
+ {"version":3,"sources":["../src/session.ts","../src/batch.ts","../src/device.ts","../src/listeners.ts","../src/tracker.ts","../src/index.ts"],"names":["SESSION_KEY","LAST_ACTIVITY_KEY","getOrCreateSession","now","stored","lastActivity","SESSION_TIMEOUT_MS","sessionId","touchSession","MAX_RETRIES","BASE_DELAY_MS","EventBatcher","endpoint","apiKey","flushInterval","debug","__publicField","FLUSH_INTERVAL_MS","event","MAX_BATCH_SIZE","useBeacon","batch","body","blob","attempt","res","delay","r","getDeviceType","width","DEVICE_BREAKPOINTS","getScreenDimensions","getCssSelector","el","parts","current","selector","cls","attachPageviewListener","cb","trackPageview","origPushState","origReplaceState","args","attachClickListener","handler","e","target","attachScrollListener","maxDepth","ticking","scrollTop","docHeight","depth","AnalyticsTracker","config","isNew","partial","screenWidth","screenHeight","cleanup","instance","init","mod","getTracker","destroy"],"mappings":"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","file":"index.js","sourcesContent":["import { SESSION_TIMEOUT_MS } from './constants';\n\nconst SESSION_KEY = 'ap_session_id';\nconst LAST_ACTIVITY_KEY = 'ap_last_activity';\n\nexport function getOrCreateSession(): { sessionId: string; isNew: boolean } {\n const now = Date.now();\n const stored = sessionStorage.getItem(SESSION_KEY);\n const lastActivity = Number(sessionStorage.getItem(LAST_ACTIVITY_KEY) || '0');\n\n // Existing session that hasn't timed out\n if (stored && now - lastActivity < SESSION_TIMEOUT_MS) {\n sessionStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n return { sessionId: stored, isNew: false };\n }\n\n // New session\n const sessionId = crypto.randomUUID();\n sessionStorage.setItem(SESSION_KEY, sessionId);\n sessionStorage.setItem(LAST_ACTIVITY_KEY, String(now));\n return { sessionId, isNew: true };\n}\n\nexport function touchSession(): void {\n sessionStorage.setItem(LAST_ACTIVITY_KEY, String(Date.now()));\n}\n\nexport function getSessionId(): string | null {\n return sessionStorage.getItem(SESSION_KEY);\n}\n","import type { TrackerEvent } from './constants';\nimport { FLUSH_INTERVAL_MS, MAX_BATCH_SIZE } from './constants';\n\nconst MAX_RETRIES = 3;\nconst BASE_DELAY_MS = 1000;\n\nexport class EventBatcher {\n private queue: TrackerEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private endpoint: string;\n private apiKey: string;\n private debug: boolean;\n\n constructor(endpoint: string, apiKey: string, flushInterval?: number, debug = false) {\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.debug = debug;\n this.timer = setInterval(() => this.flush(), flushInterval ?? FLUSH_INTERVAL_MS);\n\n if (typeof window !== 'undefined') {\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') this.flush(true);\n });\n window.addEventListener('pagehide', () => this.flush(true));\n }\n }\n\n add(event: TrackerEvent): void {\n this.queue.push(event);\n if (this.debug) console.log('[analytics] queued:', event.type, event);\n if (this.queue.length >= MAX_BATCH_SIZE) this.flush();\n }\n\n async flush(useBeacon = false): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, MAX_BATCH_SIZE);\n const body = JSON.stringify(batch);\n\n if (useBeacon && typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob([body], { type: 'application/json' });\n const sent = navigator.sendBeacon(this.endpoint, blob);\n if (sent) {\n if (this.debug) console.log('[analytics] beacon sent:', batch.length, 'events');\n return;\n }\n // Beacon failed, fall through to fetch\n }\n\n await this.fetchWithRetry(body, batch);\n }\n\n private async fetchWithRetry(body: string, batch: TrackerEvent[]): Promise<void> {\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const res = await fetch(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body,\n keepalive: true,\n credentials: 'omit',\n });\n\n if (res.ok) {\n if (this.debug) console.log('[analytics] flushed:', batch.length, 'events');\n return;\n }\n\n // Don't retry 4xx errors\n if (res.status >= 400 && res.status < 500) {\n if (this.debug) console.warn('[analytics] flush rejected:', res.status);\n return;\n }\n } catch {\n // Network error, retry\n }\n\n if (attempt < MAX_RETRIES) {\n const delay = BASE_DELAY_MS * Math.pow(2, attempt);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n if (this.debug) console.warn('[analytics] flush failed after retries, dropping', batch.length, 'events');\n }\n\n destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush(true);\n }\n\n get pending(): number {\n return this.queue.length;\n }\n}\n","import type { DeviceType } from './constants';\nimport { DEVICE_BREAKPOINTS } from './constants';\n\nexport function getDeviceType(width: number): DeviceType {\n if (width < DEVICE_BREAKPOINTS.mobile) return 'mobile';\n if (width < DEVICE_BREAKPOINTS.tablet) return 'tablet';\n return 'desktop';\n}\n\nexport function getScreenDimensions() {\n return {\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n };\n}\n","import type { TrackerEvent } from './constants';\n\ntype EventCallback = (event: Omit<TrackerEvent, 'projectId' | 'sessionId' | 'timestamp'>) => void;\n\nfunction getCssSelector(el: Element): string {\n const parts: string[] = [];\n let current: Element | null = el;\n\n while (current && current !== document.body && parts.length < 5) {\n let selector = current.tagName.toLowerCase();\n if (current.id) {\n selector += `#${current.id}`;\n parts.unshift(selector);\n break;\n }\n if (current.className && typeof current.className === 'string') {\n const cls = current.className.trim().split(/\\s+/).slice(0, 2).join('.');\n if (cls) selector += `.${cls}`;\n }\n parts.unshift(selector);\n current = current.parentElement;\n }\n\n return parts.join(' > ').slice(0, 256);\n}\n\nexport function attachPageviewListener(cb: EventCallback): () => void {\n const trackPageview = () => {\n cb({\n type: 'pageview',\n url: location.href,\n referrer: document.referrer,\n title: document.title,\n });\n };\n\n // Monkey-patch history methods\n const origPushState = history.pushState.bind(history);\n const origReplaceState = history.replaceState.bind(history);\n\n history.pushState = function (...args) {\n origPushState(...args);\n trackPageview();\n };\n\n history.replaceState = function (...args) {\n origReplaceState(...args);\n trackPageview();\n };\n\n window.addEventListener('popstate', trackPageview);\n\n // Track initial pageview\n trackPageview();\n\n return () => {\n history.pushState = origPushState;\n history.replaceState = origReplaceState;\n window.removeEventListener('popstate', trackPageview);\n };\n}\n\nexport function attachClickListener(cb: EventCallback): () => void {\n const handler = (e: MouseEvent) => {\n const target = e.target as Element | null;\n if (!target) return;\n\n cb({\n type: 'click',\n url: location.href,\n x: e.pageX,\n y: e.pageY,\n selector: getCssSelector(target),\n });\n };\n\n document.addEventListener('click', handler, { capture: true });\n return () => document.removeEventListener('click', handler, { capture: true });\n}\n\nexport function attachScrollListener(cb: EventCallback): () => void {\n let maxDepth = 0;\n let ticking = false;\n\n const handler = () => {\n if (ticking) return;\n ticking = true;\n\n requestAnimationFrame(() => {\n const scrollTop = window.scrollY;\n const docHeight = document.documentElement.scrollHeight - window.innerHeight;\n const depth = docHeight > 0 ? Math.round((scrollTop / docHeight) * 100) : 0;\n\n if (depth > maxDepth) {\n maxDepth = depth;\n cb({\n type: 'scroll',\n url: location.href,\n scrollDepth: maxDepth,\n });\n }\n ticking = false;\n });\n };\n\n window.addEventListener('scroll', handler, { passive: true });\n\n return () => {\n window.removeEventListener('scroll', handler);\n };\n}\n","import type { TrackerEvent } from './constants';\nimport type { TrackerConfig } from './index.js';\nimport { getOrCreateSession, touchSession } from './session.js';\nimport { EventBatcher } from './batch.js';\nimport { getDeviceType, getScreenDimensions } from './device.js';\nimport { attachPageviewListener, attachClickListener, attachScrollListener } from './listeners.js';\n\nexport class AnalyticsTracker {\n private config: Required<Pick<TrackerConfig, 'projectId' | 'endpoint'>> & TrackerConfig;\n private sessionId: string;\n private batcher: EventBatcher;\n private cleanups: (() => void)[] = [];\n\n constructor(config: TrackerConfig) {\n this.config = config;\n const { sessionId, isNew } = getOrCreateSession();\n this.sessionId = sessionId;\n\n this.batcher = new EventBatcher(\n config.endpoint,\n config.apiKey,\n config.flushInterval,\n config.debug\n );\n\n // Fire session_start if new\n if (isNew) {\n this.track({ type: 'session_start', url: location.href });\n }\n\n // Pageview listener (always on)\n this.cleanups.push(\n attachPageviewListener((e) => this.track(e))\n );\n\n // Click / heatmap listener\n if (config.heatmap !== false) {\n this.cleanups.push(\n attachClickListener((e) => this.track(e))\n );\n }\n\n // Scroll depth listener\n if (config.scrollDepth !== false) {\n this.cleanups.push(\n attachScrollListener((e) => this.track(e))\n );\n }\n }\n\n track(partial: Omit<TrackerEvent, 'projectId' | 'sessionId' | 'timestamp'>): void {\n touchSession();\n const { screenWidth, screenHeight } = getScreenDimensions();\n const event: TrackerEvent = {\n ...partial,\n projectId: this.config.projectId,\n sessionId: this.sessionId,\n timestamp: Date.now(),\n screenWidth,\n screenHeight,\n deviceType: getDeviceType(window.innerWidth),\n userAgent: navigator.userAgent,\n };\n this.batcher.add(event);\n }\n\n destroy(): void {\n for (const cleanup of this.cleanups) cleanup();\n this.cleanups = [];\n this.batcher.destroy();\n }\n}\n","import type { TrackerEvent } from './constants';\nimport { AnalyticsTracker } from './tracker.js';\n\nexport interface TrackerConfig {\n /** Project ID (UUID). */\n projectId: string;\n /** Ingestion endpoint URL. */\n endpoint: string;\n /** API key (ap_live_... or ap_test_...). */\n apiKey: string;\n /** Enable session replay (requires rrweb peer dep). Default: false. */\n replay?: boolean;\n /** Enable click heatmap tracking. Default: true. */\n heatmap?: boolean;\n /** Enable scroll depth tracking. Default: true. */\n scrollDepth?: boolean;\n /** Batch flush interval in ms. Default: 5000. */\n flushInterval?: number;\n /** Debug mode — logs events to console. Default: false. */\n debug?: boolean;\n}\n\nlet instance: AnalyticsTracker | null = null;\n\n/**\n * Initialize the analytics tracker. Creates a singleton.\n */\nexport function init(config: TrackerConfig): AnalyticsTracker {\n if (instance) {\n if (config.debug) console.warn('[analytics] already initialized, returning existing instance');\n return instance;\n }\n\n instance = new AnalyticsTracker(config);\n\n // Lazy-load replay if enabled\n if (config.replay) {\n import('./replay.js')\n .then((mod) => mod.initReplay(instance!))\n .catch(() => {\n if (config.debug) console.warn('[analytics] rrweb not available, replay disabled');\n });\n }\n\n return instance;\n}\n\n/**\n * Get the current tracker instance, or null if not initialized.\n */\nexport function getTracker(): AnalyticsTracker | null {\n return instance;\n}\n\n/**\n * Destroy the tracker and clean up listeners.\n */\nexport function destroy(): void {\n instance?.destroy();\n instance = null;\n}\n\nexport type { TrackerEvent, TrackerConfig as AnalyticsConfig };\nexport { AnalyticsTracker };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marlinjai/analytics-tracker",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Lightweight browser analytics tracker — pageviews, clicks, heatmaps, session replay",
5
5
  "type": "module",
6
6
  "exports": {