UNPKG

@asunalabs/tracker-sdk

Version:

React SDK for EngageTrack analytics and user tracking

3 lines (2 loc) 14.9 kB
"use strict";var e=require("react");const t={serverUrl:"http://localhost:5000/api/track",wsUrl:"ws://localhost:5000/ws",idleTimeout:3e4,checkInterval:1e3,heartbeatInterval:3e4,maxReconnectAttempts:5,enableWebSocket:!0,enableAutoTracking:!0,enableReferralTracking:!0,cookieExpiry:365,debug:!1},n="engage_referral",i="session_id",s="user_id";function o(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}function r(e,t,n=365,i){let s=`${e}=${t}; expires=${new Date(Date.now()+24*n*60*60*1e3).toUTCString()}; path=/`;if(i&&""!==i.trim()){const e="undefined"!=typeof window?window.location.hostname:"";if("localhost"===e||"127.0.0.1"===e||/^\d+\.\d+\.\d+\.\d+$/.test(e));else{s+=`; domain=${i.startsWith(".")?i:`.${i}`}`}}return s+="; SameSite=Lax","undefined"!=typeof document&&(document.cookie=s),a(e)||t}function a(e){var t;if("undefined"==typeof document)return;const n=`; ${document.cookie}`.split(`; ${e}=`);return 2===n.length?null===(t=n.pop())||void 0===t?void 0:t.split(";").shift():void 0}function c(){if("undefined"==typeof window)return{};const e=new URLSearchParams(window.location.search),t=new URLSearchParams(window.location.hash.substring(1)),n={ref:e.get("ref")||t.get("ref"),referral:e.get("referral")||t.get("referral"),referrer:e.get("referrer")||t.get("referrer"),utm_source:e.get("utm_source"),utm_medium:e.get("utm_medium"),utm_campaign:e.get("utm_campaign"),utm_term:e.get("utm_term"),utm_content:e.get("utm_content"),fbclid:e.get("fbclid"),gclid:e.get("gclid"),msclkid:e.get("msclkid"),source:e.get("source"),medium:e.get("medium"),campaign:e.get("campaign"),affiliate:e.get("affiliate"),partner:e.get("partner")};return Object.fromEntries(Object.entries(n).filter(([,e])=>null!=e))}function l(){if("undefined"==typeof document)return null;const e=document.referrer;if(!e)return null;try{const t=new URL(e).hostname.toLowerCase(),n={"google.com":"google","google.co.uk":"google","google.ca":"google","bing.com":"bing","yahoo.com":"yahoo","duckduckgo.com":"duckduckgo","yandex.com":"yandex","baidu.com":"baidu","facebook.com":"facebook","twitter.com":"twitter","x.com":"twitter","linkedin.com":"linkedin","instagram.com":"instagram","tiktok.com":"tiktok","reddit.com":"reddit","pinterest.com":"pinterest","youtube.com":"youtube","github.com":"github","stackoverflow.com":"stackoverflow","medium.com":"medium"};if(n[t])return{source:n[t],domain:t,url:e};for(const[i,s]of Object.entries(n))if(t.includes(i))return{source:s,domain:t,url:e};return{source:"None / Direct",domain:t,url:e}}catch(t){return{source:"unknown",domain:"unknown",url:e}}}function d(){const e=a(n),t=c(),i=l();if(Object.keys(t).length>0){const e={timestamp:(new Date).toISOString(),urlParams:t,source:i,landingPage:"undefined"!=typeof window?window.location.href:"",userAgent:"undefined"!=typeof navigator?navigator.userAgent:""},s=encodeURIComponent(JSON.stringify(e));return r(n,s,30),e}if(e)try{return JSON.parse(decodeURIComponent(e))}catch(e){}if(i){const e={timestamp:(new Date).toISOString(),urlParams:{},source:i,landingPage:"undefined"!=typeof window?window.location.href:"",userAgent:"undefined"!=typeof navigator?navigator.userAgent:""},t=encodeURIComponent(JSON.stringify(e));return r(n,t,30),e}return null}function u(e,t){let n;return function(...i){n||(e(...i),n=!0,setTimeout(()=>n=!1,t))}}class h{constructor(e,n={}){if(this.sessionData=null,this.ws=null,this.heartbeatInterval=null,this.activityInterval=null,this.reconnectAttempts=0,this.onlineUsers=null,this.isInitialized=!1,this.config={...t,...e},this.hooks=n,!this.config.siteId)throw new Error("Site ID is required");if(!this.config.domain)throw new Error("Domain is required");this.init()}init(){if("undefined"!=typeof window)try{this.initializeSession(),this.setupEventListeners(),this.config.enableWebSocket&&this.connectWebSocket(),this.config.enableAutoTracking&&this.startAutoTracking(),this.isInitialized=!0,this.track("session_start"),this.config.enableReferralTracking&&this.trackReferral()}catch(e){this.handleError(e)}else console.warn("EngageTracker: Window is not available, tracking disabled")}initializeSession(){var e,t;const n=a(i)||r(i,o(),1/48,this.config.cookieDomain),c=a(s)||r(s,o(),this.config.cookieExpiry,this.config.cookieDomain);this.sessionData={sessionId:n||o(),userId:c||o(),startTime:Date.now(),lastActivity:Date.now(),isActive:!0,timeSpent:0},null===(t=(e=this.hooks).onSessionStart)||void 0===t||t.call(e,this.sessionData)}setupEventListeners(){if("undefined"==typeof window)return;const e=u(()=>{this.sessionData&&(this.sessionData.lastActivity=Date.now(),this.sessionData.timeSpent=Date.now()-this.sessionData.startTime)},1e3);["mousemove","keydown","scroll","click","touchstart"].forEach(t=>{document.addEventListener(t,e,{passive:!0})}),document.addEventListener("visibilitychange",()=>{document.hidden?(this.track("page_hidden",{timeSpent:this.getTimeSpent()}),this.stopActivityTracking()):this.startActivityTracking()}),document.addEventListener("click",e=>{var t;const n=null===(t=e.target)||void 0===t?void 0:t.closest("a");n&&n.href&&this.trackLinkClick(n,e)}),window.addEventListener("beforeunload",()=>{this.track("page_unload",{timeSpent:this.getTimeSpent()}),this.endSession()}),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{this.track("page_load",{timeSpent:0}),this.track("page_view",{url:window.location.href,title:document.title,timeSpent:0})}):(this.track("page_load",{timeSpent:0}),this.track("page_view",{url:window.location.href,title:document.title,timeSpent:0}))}startActivityTracking(){this.stopActivityTracking(),this.activityInterval=setInterval(()=>{this.checkActivity()},this.config.checkInterval)}stopActivityTracking(){this.activityInterval&&(clearInterval(this.activityInterval),this.activityInterval=null)}checkActivity(){if(!this.sessionData||"undefined"==typeof window)return;const e=Date.now();e-this.sessionData.lastActivity>=this.config.idleTimeout&&(this.track("idle_timeout",{timeSpent:this.getTimeSpent()}),this.sessionData.isActive=!1,this.stopActivityTracking()),this.sessionData.timeSpent=e-this.sessionData.startTime}startAutoTracking(){this.startActivityTracking()}connectWebSocket(){if("undefined"!=typeof window&&this.config.enableWebSocket)try{const e=this.config.wsUrl||this.config.serverUrl.replace("/api/track","").replace("http","ws")+"/ws";this.ws=new WebSocket(e),this.ws.onopen=()=>{var e,t;this.log("WebSocket connected"),this.reconnectAttempts=0,this.startHeartbeat(),null===(t=(e=this.hooks).onWebSocketConnect)||void 0===t||t.call(e)},this.ws.onmessage=e=>{try{const t=JSON.parse(e.data);this.handleWebSocketMessage(t)}catch(e){this.handleError(new Error("Failed to parse WebSocket message"))}},this.ws.onclose=()=>{var e,t;this.log("WebSocket connection closed"),this.stopHeartbeat(),null===(t=(e=this.hooks).onWebSocketDisconnect)||void 0===t||t.call(e),this.scheduleReconnect()},this.ws.onerror=e=>{this.handleError(new Error("WebSocket error"))}}catch(e){this.handleError(e),this.scheduleReconnect()}}handleWebSocketMessage(e){var t,n,i,s;switch(null===(n=(t=this.hooks).onWebSocketMessage)||void 0===n||n.call(t,e),e.type){case"ping":this.sendWebSocketMessage({type:"pong"});break;case"heartbeat_ack":this.log("Heartbeat acknowledged");break;case"session_end_ack":this.log("Session end acknowledged");break;case"online_users_update":this.onlineUsers=e.data,null===(s=(i=this.hooks).onOnlineUsersUpdate)||void 0===s||s.call(i,e.data)}}sendWebSocketMessage(e){this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval(()=>{this.sendHeartbeat()},this.config.heartbeatInterval)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null)}sendHeartbeat(){this.sessionData&&this.sendWebSocketMessage({type:"heartbeat",siteId:this.config.siteId,sessionId:this.sessionData.sessionId,userId:this.sessionData.userId,timestamp:(new Date).toISOString()})}scheduleReconnect(){if(this.reconnectAttempts<this.config.maxReconnectAttempts){this.reconnectAttempts++;const e=Math.min(1e3*Math.pow(2,this.reconnectAttempts),3e4);this.log(`Scheduling reconnect attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts} in ${e}ms`),setTimeout(()=>{this.connectWebSocket()},e)}else this.log("Max reconnection attempts reached")}trackLinkClick(e,t){var n;const i="_blank"===e.target||e.href.startsWith("mailto:")||e.href.startsWith("tel:")||e.hostname&&e.hostname!==window.location.hostname,s={url:e.href,element:(null===(n=e.textContent)||void 0===n?void 0:n.trim())||"Link",isExternal:i,timeSpent:this.getTimeSpent()};i?this.track("user_click",s):(t.preventDefault(),this.trackWithNavigation("user_click",s,()=>{window.location.href=e.href}))}trackWithNavigation(e,t,n){const i=this.buildTrackingPayload(e,t);if(navigator.sendBeacon){if(navigator.sendBeacon(this.config.serverUrl,JSON.stringify(i)))return void n()}fetch(this.config.serverUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i),keepalive:!0}).then(()=>n()).catch(()=>n()),setTimeout(n,1e3)}buildTrackingPayload(e,t={}){if(!this.sessionData)return null;const n=d();return{siteId:this.config.siteId,eventType:e.toUpperCase(),domain:this.config.domain,data:{...t,sessionId:this.sessionData.sessionId,userId:this.sessionData.userId,userAgent:"undefined"!=typeof navigator?navigator.userAgent:"",path:"undefined"!=typeof window?window.location.pathname:"",referer:"undefined"!=typeof document?document.referrer:null,title:"undefined"!=typeof document?document.title:null,referral:n,timeSpent:this.getTimeSpent()},timestamp:(new Date).toISOString()}}trackReferral(){var e,t,n,i,s,o,r,a,c,l;const u=d();u&&(this.track("referral_conversion",{referralSource:(null===(e=u.source)||void 0===e?void 0:e.source)||"direct",referralDomain:(null===(t=u.source)||void 0===t?void 0:t.domain)||null,referralUrl:(null===(n=u.source)||void 0===n?void 0:n.url)||null,utmSource:(null===(i=u.urlParams)||void 0===i?void 0:i.utm_source)||null,utmMedium:(null===(s=u.urlParams)||void 0===s?void 0:s.utm_medium)||null,utmCampaign:(null===(o=u.urlParams)||void 0===o?void 0:o.utm_campaign)||null,referralCode:(null===(r=u.urlParams)||void 0===r?void 0:r.ref)||(null===(a=u.urlParams)||void 0===a?void 0:a.referral)||null,landingPage:u.landingPage,referralTimestamp:u.timestamp}),null===(l=(c=this.hooks).onReferralConversion)||void 0===l||l.call(c,u))}endSession(){var e,t;this.sessionData&&(this.sessionData.isActive=!1,this.sessionData.timeSpent=Date.now()-this.sessionData.startTime,this.sendWebSocketMessage({type:"session_end",siteId:this.config.siteId,sessionId:this.sessionData.sessionId,userId:this.sessionData.userId,timeSpent:Math.floor(this.sessionData.timeSpent/1e3),timestamp:(new Date).toISOString()}),this.track("session_end",{timeSpent:this.sessionData.timeSpent,sessionDuration:Math.floor(this.sessionData.timeSpent/1e3)}),null===(t=(e=this.hooks).onSessionEnd)||void 0===t||t.call(e,this.sessionData),this.cleanup())}cleanup(){this.stopActivityTracking(),this.stopHeartbeat(),this.ws&&(this.ws.close(),this.ws=null)}getTimeSpent(){return this.sessionData?Date.now()-this.sessionData.startTime:0}log(e){this.config.debug&&console.log(`[EngageTracker] ${e}`)}handleError(e){var t,n;this.config.debug&&console.error("[EngageTracker] Error:",e),null===(n=(t=this.hooks).onError)||void 0===n||n.call(t,e)}track(e,t={}){var n,i;if(!this.isInitialized)return this.log("Tracker not initialized, queuing event"),void setTimeout(()=>this.track(e,t),100);const s=this.buildTrackingPayload(e,t);s?(fetch(this.config.serverUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}).catch(e=>{this.handleError(new Error(`Failed to send tracking event: ${e.message}`))}),null===(i=(n=this.hooks).onTrackingEvent)||void 0===i||i.call(n,e,s.data)):this.handleError(new Error("Failed to build tracking payload"))}trackReferralConversion(e={}){const t=d();this.track("referral_conversion_manual",{...e,referralData:t})}getSessionData(){return this.sessionData}getReferralData(){return d()}getOnlineUsers(){return this.onlineUsers}isConnected(){var e;return(null===(e=this.ws)||void 0===e?void 0:e.readyState)===WebSocket.OPEN}reconnect(){this.reconnectAttempts=0,this.connectWebSocket()}destroy(){this.cleanup(),this.isInitialized=!1}}exports.DEFAULT_CONFIG=t,exports.EngageTracker=h,exports.REFERRAL_COOKIE_NAME=n,exports.SESSION_COOKIE_NAME=i,exports.USER_COOKIE_NAME=s,exports.debounce=function(e,t){let n=null;return function(...i){n&&clearTimeout(n),n=setTimeout(()=>{n=null,e(...i)},t)}},exports.deleteCookie=function(e,t){r(e,"",-1,t)},exports.detectReferralSource=l,exports.generateId=o,exports.getCookie=a,exports.getReferralData=d,exports.isLocalStorageAvailable=function(){try{const e="__engagetrack_test__";return localStorage.setItem(e,"test"),localStorage.removeItem(e),!0}catch{return!1}},exports.parseReferralParams=c,exports.setCookie=r,exports.throttle=u,exports.useEngageTrack=function(t,n={}){const[i,s]=e.useState(null),[o,r]=e.useState(!1),[a,c]=e.useState(null),[l,d]=e.useState(null),[u,g]=e.useState(!1);e.useEffect(()=>{var e;if("undefined"==typeof window)return;const o={...n,onSessionStart:e=>{var t;c(e),null===(t=n.onSessionStart)||void 0===t||t.call(n,e)},onSessionEnd:e=>{var t;c(null),null===(t=n.onSessionEnd)||void 0===t||t.call(n,e)},onWebSocketConnect:()=>{var e;g(!0),null===(e=n.onWebSocketConnect)||void 0===e||e.call(n)},onWebSocketDisconnect:()=>{var e;g(!1),null===(e=n.onWebSocketDisconnect)||void 0===e||e.call(n)},onOnlineUsersUpdate:e=>{var t;d(e),null===(t=n.onOnlineUsersUpdate)||void 0===t||t.call(n,e)}};try{const e=new h(t,o);s(e),r(!0)}catch(t){console.error("Failed to initialize EngageTracker:",t),null===(e=n.onError)||void 0===e||e.call(n,t)}return()=>{i&&i.destroy()}},[t.siteId,t.domain]);const m=e.useCallback((e,t)=>{i&&i.track(e,t)},[i]),f=e.useCallback(e=>{i&&i.trackReferralConversion(e)},[i]),p=e.useCallback(()=>(null==i?void 0:i.getSessionData())||null,[i]),k=e.useCallback(()=>(null==i?void 0:i.getReferralData())||null,[i]);return{track:m,trackReferralConversion:f,sessionData:a,onlineUsers:l,isConnected:u,isInitialized:o,reconnect:e.useCallback(()=>{i&&i.reconnect()},[i]),getSessionData:p,getReferralData:k}},exports.useOnlineUsers=function(){const[t,n]=e.useState(null);return e.useEffect(()=>{},[]),t},exports.usePageView=function(t){const[n,i]=e.useState("");e.useEffect(()=>{if("undefined"==typeof window)return;const e=t||window.location.pathname;e!==n&&i(e)},[t,n])},exports.useSessionData=function(){const[t,n]=e.useState(null);return e.useEffect(()=>{},[]),t},exports.useTrackEvent=function(t,n,i=[]){e.useEffect(()=>{},i)}; //# sourceMappingURL=index.js.map