@linkzly/web-sdk 1.0.0 → 1.0.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/cdn/cdn.js +2 -0
- package/cdn/cdn.js.map +1 -0
- package/cdn/index.js +2 -0
- package/cdn/index.js.map +1 -0
- package/package.json +4 -1
package/cdn/cdn.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";(()=>{var v=(n=>(n[n.PRODUCTION=0]="PRODUCTION",n[n.STAGING=1]="STAGING",n[n.DEVELOPMENT=2]="DEVELOPMENT",n))(v||{});var X={0:"https://ske.linkzly.com",1:"https://ske-staging.linkzly.com",2:"https://ske-dev.linkzly.com"},d={autoTrackPageViews:!1,autoTrackSessions:!0,autoExtractUTM:!0,autoCaptureReferrer:!0,respectDNT:!0,batchSize:20,flushIntervalMs:3e4,sessionTimeoutMs:3e4,debug:!1};function N(r,e,t){return{sdkKey:r,environment:e,baseURL:X[e],autoTrackPageViews:t?.autoTrackPageViews??d.autoTrackPageViews,autoTrackSessions:t?.autoTrackSessions??d.autoTrackSessions,autoExtractUTM:t?.autoExtractUTM??d.autoExtractUTM,autoCaptureReferrer:t?.autoCaptureReferrer??d.autoCaptureReferrer,respectDNT:t?.respectDNT??d.respectDNT,batchSize:t?.batchSize??d.batchSize,flushIntervalMs:t?.flushIntervalMs??d.flushIntervalMs,sessionTimeoutMs:t?.sessionTimeoutMs??d.sessionTimeoutMs,debug:t?.debug??e===2}}var y=class{constructor(e){this.enabled=e}info(...e){this.enabled&&console.log("[LinkzlySDK]",...e)}debug(...e){this.enabled&&console.debug("[LinkzlySDK]",...e)}warn(...e){this.enabled&&console.warn("[LinkzlySDK]",...e)}error(...e){console.error("[LinkzlySDK]",...e)}};function z(){let r=typeof navigator<"u"?navigator:null,e=typeof window<"u"?window:null,t=typeof document<"u"?document:null,n=typeof window<"u"?window.screen:null;return{userAgent:r?.userAgent??"unknown",language:r?.language??"unknown",languages:r?.languages?.join(",")??"",screenSize:n?`${n.width}x${n.height}`:"unknown",viewportSize:e?`${e.innerWidth}x${e.innerHeight}`:"unknown",timezone:Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone??"unknown",timezoneOffset:new Date().getTimezoneOffset(),colorDepth:n?.colorDepth??0,pixelRatio:e?.devicePixelRatio??1,platform:r?.platform??"unknown",cookiesEnabled:r?.cookieEnabled??!1,online:r?.onLine??!0,referrer:t?.referrer??"",pageUrl:e?.location?.href??""}}function O(r){return{userAgent:r.userAgent,language:r.language,languages:r.languages,screenSize:r.screenSize,viewportSize:r.viewportSize,timezone:r.timezone,timezoneOffset:r.timezoneOffset,colorDepth:r.colorDepth,pixelRatio:r.pixelRatio,platform:r.platform,cookiesEnabled:r.cookiesEnabled,online:r.online,referrer:r.referrer,pageUrl:r.pageUrl}}function l(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{let e=Math.random()*16|0;return(r==="x"?e:e&3|8).toString(16)})}function f(r){if(!r)return!1;try{let e="__lz_test__";return r.setItem(e,"1"),r.removeItem(e),!0}catch{return!1}}function h(r){if(typeof localStorage>"u"||!f(localStorage))return null;try{return localStorage.getItem("lz_"+r)}catch{return null}}function c(r,e){if(!(typeof localStorage>"u"||!f(localStorage)))try{localStorage.setItem("lz_"+r,e)}catch{}}function q(r){if(!(typeof localStorage>"u"||!f(localStorage)))try{localStorage.removeItem("lz_"+r)}catch{}}function p(r){if(typeof sessionStorage>"u"||!f(sessionStorage))return null;try{return sessionStorage.getItem("lz_"+r)}catch{return null}}function u(r,e){if(!(typeof sessionStorage>"u"||!f(sessionStorage)))try{sessionStorage.setItem("lz_"+r,e)}catch{}}function x(r){if(!(typeof sessionStorage>"u"||!f(sessionStorage)))try{sessionStorage.removeItem("lz_"+r)}catch{}}function B(r){let e=h(r);if(!e)return null;try{return JSON.parse(e)}catch{return null}}function H(r,e){try{c(r,JSON.stringify(e))}catch{}}var w=3,F=1e3,Y="Bearer linkzly-production-shared-secret-key-2024-secure-min-64",S=class{constructor(e,t){this.config=e,this.logger=t}async sendEvent(e){let t=`${this.config.baseURL}/v1/sdk/events`;return this.logger.debug("Sending event:",e.eventType,e.eventName??""),this.postJSON(t,e)}async sendBatch(e){if(e.length===0)return{success:!0};let t=`${this.config.baseURL}/v1/sdk/events/batch`,n={type:"sdk_event",events:e,batchId:l(),clientTimestamp:Date.now()};return this.logger.debug(`Sending batch of ${e.length} events`),this.postJSON(t,n)}sendBeacon(e){if(e.length===0)return!0;if(typeof navigator>"u"||!navigator.sendBeacon)return!1;let t=`${this.config.baseURL}/v1/sdk/events/batch`,n={type:"sdk_event",events:e,batchId:l(),clientTimestamp:Date.now()};try{let i=new Blob([JSON.stringify(n)],{type:"application/json"}),o=navigator.sendBeacon(t,i);return this.logger.debug(`Beacon ${o?"accepted":"rejected"}: ${e.length} events`),o}catch(i){return this.logger.error("sendBeacon failed:",i),!1}}async postJSON(e,t,n=0){try{let i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","X-Linkzly-SDK":"web/1.0.0",Authorization:Y},body:JSON.stringify(t),keepalive:!0});if(!i.ok){if((i.status>=500||i.status===429)&&n<w){let a=F*Math.pow(2,n);return this.logger.warn(`HTTP ${i.status}, retrying in ${a}ms (attempt ${n+1}/${w})`),await this.sleep(a),this.postJSON(e,t,n+1)}return this.logger.error(`HTTP ${i.status}: ${i.statusText}`),{success:!1,message:`HTTP ${i.status}`}}let o=await i.json().catch(()=>({}));return this.logger.debug("Response:",i.status),{success:!0,...o}}catch(i){if(n<w){let o=F*Math.pow(2,n);return this.logger.warn(`Network error, retrying in ${o}ms (attempt ${n+1}/${w}):`,i),await this.sleep(o),this.postJSON(e,t,n+1)}return this.logger.error("Network request failed after retries:",i),{success:!1,message:String(i)}}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var V="event_queue",R=500,$=100,T=class{constructor(e,t,n){this.queue=[];this.flushTimer=null;this.isFlushing=!1;this.config=e,this.logger=t,this.network=n,this.restoreQueue(),this.startFlushTimer(),typeof window<"u"&&(window.addEventListener("beforeunload",()=>this.flushBeacon()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flushBeacon()}))}enqueue(e){let t={id:l(),payload:e,createdAt:Date.now(),retryCount:0};this.queue.push(t),this.queue.length>R&&(this.queue=this.queue.slice(-R)),this.persistQueue(),this.logger.debug(`Event queued (${this.queue.length} pending): ${e.eventType}/${e.eventName??""}`),this.queue.length>=this.config.batchSize&&this.flush().catch(n=>this.logger.error("Auto-flush failed:",n))}async flush(){if(this.isFlushing||this.queue.length===0)return!0;this.isFlushing=!0;try{let e=this.queue.slice(0,$),t=e.map(i=>i.payload);if((await this.network.sendBatch(t)).success){let i=new Set(e.map(o=>o.id));return this.queue=this.queue.filter(o=>!i.has(o.id)),this.persistQueue(),this.logger.debug(`Flushed ${e.length} events, ${this.queue.length} remaining`),!0}return this.logger.warn(`Batch send failed, ${this.queue.length} events remain in queue`),!1}finally{this.isFlushing=!1}}getPendingCount(){return this.queue.length}destroy(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null)}startFlushTimer(){this.flushTimer||(this.flushTimer=setInterval(()=>{this.flush().catch(e=>this.logger.error("Periodic flush failed:",e))},this.config.flushIntervalMs))}flushBeacon(){if(this.queue.length===0)return;let e=this.queue.slice(0,$),t=e.map(i=>i.payload);if(this.network.sendBeacon(t)){let i=new Set(e.map(o=>o.id));this.queue=this.queue.filter(o=>!i.has(o.id)),this.persistQueue()}}persistQueue(){let e=this.queue.slice(-R);H(V,e)}restoreQueue(){let e=B(V);if(e&&Array.isArray(e)){let t=Date.now()-864e5;this.queue=e.filter(n=>n.createdAt>t),this.queue.length!==e.length&&(this.logger.debug(`Pruned ${e.length-this.queue.length} expired events from queue`),this.persistQueue()),this.queue.length>0&&this.logger.debug(`Restored ${this.queue.length} queued events from storage`)}}};var C="session_id",L="session_last_active",K="first_visit_done",I=class{constructor(e,t){this.callback=null;this.visibilityHandler=null;this.config=e,this.logger=t,this.lastActiveTime=Date.now();let n=p(C),i=p(L);n&&i?Date.now()-parseInt(i,10)<this.config.sessionTimeoutMs?(this.sessionId=n,this.lastActiveTime=parseInt(i,10),this.logger.debug("Resumed session:",this.sessionId)):this.sessionId=this.createNewSession():this.sessionId=this.createNewSession(),this.config.autoTrackSessions&&this.setupVisibilityTracking()}getSessionId(){return this.sessionId}isFirstVisit(){return h(K)===null}markFirstVisitDone(){c(K,String(Date.now()))}onSessionEvent(e){this.callback=e}touch(){this.lastActiveTime=Date.now(),u(L,String(this.lastActiveTime))}startSession(){return this.callback&&this.callback("end",this.sessionId),this.sessionId=this.createNewSession(),this.sessionId}endSession(){this.callback&&this.callback("end",this.sessionId),x(C),x(L)}destroy(){this.visibilityHandler&&typeof document<"u"&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null)}createNewSession(){let e=l();return u(C,e),this.lastActiveTime=Date.now(),u(L,String(this.lastActiveTime)),this.logger.debug("New session started:",e),this.callback&&this.callback("start",e),e}setupVisibilityTracking(){typeof document>"u"||(this.visibilityHandler=()=>{if(document.visibilityState==="visible"){let e=Date.now()-this.lastActiveTime;e>=this.config.sessionTimeoutMs?(this.logger.debug(`Session timeout (${Math.round(e/1e3)}s idle), starting new session`),this.sessionId=this.createNewSession()):this.touch()}else this.touch()},document.addEventListener("visibilitychange",this.visibilityHandler))}};var Q="utm_",E="referrer",J="landing_page",G=[{key:"utmSource",urlParam:"utm_source"},{key:"utmMedium",urlParam:"utm_medium"},{key:"utmCampaign",urlParam:"utm_campaign"},{key:"utmTerm",urlParam:"utm_term"},{key:"utmContent",urlParam:"utm_content"}],D=class{constructor(e,t){this.smartLinkId=null;this.clickId=null;this.deepLinkListeners=new Set;this.popStateHandler=null;this.config=e,this.logger=t,this.utm=this.initUTM(),this.referrer=this.initReferrer(),this.landingPage=this.initLandingPage(),this.initSmartLinkParams(),this.config.autoTrackPageViews&&this.setupSPATracking()}getUTM(){return{...this.utm}}getReferrer(){return this.referrer}getLandingPage(){return this.landingPage}getSmartLinkId(){return this.smartLinkId}getClickId(){return this.clickId}getAttributionData(){return{utm:this.getUTM(),referrer:this.referrer,smartLinkId:this.smartLinkId,clickId:this.clickId,landingPage:this.landingPage}}parseDeepLink(e){let t=e??(typeof window<"u"?window.location.href:""),n={},i="/";try{let g=new URL(t,"https://placeholder.com");i=g.pathname,g.searchParams.forEach((b,m)=>{n[m]=b})}catch{let g=t.split("?");g[0]&&(i=g[0].match(/^[^:]+:\/\/[^/]+(\/.*)?$/)?.[1]??"/"),g[1]&&g[1].split("&").forEach(b=>{let[m,P]=b.split("=");if(m&&P)try{n[decodeURIComponent(m)]=decodeURIComponent(P)}catch{n[m]=P}})}let o=n.slid??n.smartLinkId??null,a=n.cid??n.clickId??null;return delete n.slid,delete n.smartLinkId,delete n.cid,delete n.clickId,{url:t,path:i,parameters:n,smartLinkId:o,clickId:a}}addDeepLinkListener(e){return this.deepLinkListeners.add(e),()=>{this.deepLinkListeners.delete(e)}}removeAllListeners(){this.deepLinkListeners.clear()}destroy(){this.deepLinkListeners.clear(),typeof window<"u"&&this.popStateHandler&&window.removeEventListener("popstate",this.popStateHandler)}initUTM(){let e={utmSource:null,utmMedium:null,utmCampaign:null,utmTerm:null,utmContent:null};if(!this.config.autoExtractUTM)return e;let t=typeof window<"u"?new URLSearchParams(window.location.search):null;for(let{key:n,urlParam:i}of G){let o=t?.get(i)??null;o?(e[n]=o,u(Q+i,o)):e[n]=p(Q+i)}return e.utmSource&&this.logger.debug("UTM parameters captured:",e),e}initReferrer(){if(!this.config.autoCaptureReferrer)return null;let e=p(E);if(e!==null)return e||null;let t=typeof document<"u"?document.referrer:"";if(t){try{let n=new URL(t).hostname,i=typeof window<"u"?window.location.hostname:"";if(n===i)return u(E,""),null}catch{}return u(E,t),this.logger.debug("Referrer captured:",t),t}return u(E,""),null}initLandingPage(){let e=p(J);if(e)return e;let t=typeof window<"u"?window.location.href:null;return t&&u(J,t),t}initSmartLinkParams(){if(typeof window>"u")return;let e=new URLSearchParams(window.location.search);this.smartLinkId=e.get("slid")??e.get("smartLinkId")??null,this.clickId=e.get("cid")??e.get("clickId")??null,this.smartLinkId&&this.logger.debug("Smart link ID captured:",this.smartLinkId)}setupSPATracking(){if(typeof window>"u")return;let e=history.pushState.bind(history),t=history.replaceState.bind(history),n=()=>{let i=this.parseDeepLink();this.deepLinkListeners.forEach(o=>{try{o(i)}catch(a){this.logger.error("Deep link listener error:",a)}})};history.pushState=(...i)=>{e(...i),n()},history.replaceState=(...i)=>{t(...i),n()},this.popStateHandler=n,window.addEventListener("popstate",this.popStateHandler)}};var U="visitor_id",_="user_id",M="tracking_enabled",k=class{constructor(){this.config=null;this.isConfigured=!1;this.scrollDepthHandler=null;this.maxScrollDepth=0;this.pageEntryTime=0;this.outboundClickHandler=null}configure(e,t=0,n){if(this.isConfigured){this.logger?.warn("SDK already configured, ignoring reconfiguration");return}this.config=N(e,t,n),this.logger=new y(this.config.debug),this.config.respectDNT&&this.isDNTEnabled()&&(this.logger.info("DNT/GPC detected \u2014 tracking disabled by default"),c(M,"false")),this.network=new S(this.config,this.logger),this.eventQueue=new T(this.config,this.logger,this.network),this.session=new I(this.config,this.logger),this.attribution=new D(this.config,this.logger),this.session.onSessionEvent((i,o)=>{i==="start"&&(this.logger.debug("Session started, auto-tracking open"),this.trackOpen().catch(a=>this.logger.error("Auto trackOpen failed:",a)))}),this.getVisitorID(),this.isConfigured=!0,this.logger.info(`SDK configured (env: ${v[t]}, key: ${e.substring(0,8)}...)`),this.session.isFirstVisit()&&(this.trackFirstVisit().catch(i=>this.logger.error("trackFirstVisit failed:",i)),this.session.markFirstVisitDone()),this.config.autoTrackPageViews&&this.trackPageView().catch(i=>this.logger.error("Auto trackPageView failed:",i)),this.setupScrollDepthTracking(),this.setupOutboundClickTracking(),this.pageEntryTime=Date.now()}async trackFirstVisit(){this.ensureConfigured();let e=this.buildPayload("install",null);this.eventQueue.enqueue(e),this.logger.info("First visit tracked")}async trackOpen(){this.ensureConfigured();let e=this.buildPayload("open",null);this.eventQueue.enqueue(e),this.logger.debug("App open tracked")}async trackPageView(e,t){this.ensureConfigured();let n=e??(typeof window<"u"?window.location.href:""),i=this.buildPayload("page_view","page_view",{page_url:n,page_title:typeof document<"u"?document.title:"",...t});this.eventQueue.enqueue(i),this.session.touch(),this.logger.debug("Page view tracked:",n)}async trackEvent(e,t){this.ensureConfigured();let n=this.buildPayload("custom",e,t);this.eventQueue.enqueue(n),this.session.touch(),this.logger.debug("Event tracked:",e)}async trackPurchase(e){this.ensureConfigured();let t=this.buildPayload("purchase","purchase",e);this.eventQueue.enqueue(t),this.session.touch(),this.logger.debug("Purchase tracked")}async trackEventBatch(e){this.ensureConfigured();for(let t of e){let n=this.buildPayload("custom",t.eventName,t.parameters);this.eventQueue.enqueue(n)}return this.session.touch(),this.logger.debug(`Batch of ${e.length} events queued`),!0}async flushEvents(){return this.ensureConfigured(),this.eventQueue.flush()}getPendingEventCount(){return this.ensureConfigured(),this.eventQueue.getPendingCount()}handleSmartLink(e){return this.ensureConfigured(),this.attribution.parseDeepLink(e)}addDeepLinkListener(e){return this.ensureConfigured(),this.attribution.addDeepLinkListener(e)}removeAllListeners(){this.attribution&&this.attribution.removeAllListeners()}setUserID(e){c(_,e),this.logger?.debug("User ID set:",e)}getUserID(){return h(_)}clearUserID(){q(_),this.logger?.debug("User ID cleared")}getVisitorID(){let e=h(U);return e||(e=l(),c(U,e),this.logger?.debug("New visitor ID generated:",e)),e}resetVisitorID(){let e=l();return c(U,e),this.logger?.debug("Visitor ID reset:",e),e}startSession(){return this.ensureConfigured(),this.session.startSession()}endSession(){this.ensureConfigured(),this.trackTimeOnPage(),this.session.endSession()}getSessionId(){return this.ensureConfigured(),this.session.getSessionId()}setTrackingEnabled(e){c(M,String(e)),this.logger?.info("Tracking",e?"enabled":"disabled")}isTrackingEnabled(){return h(M)!=="false"}setupScrollDepthTracking(){typeof window>"u"||typeof document>"u"||(this.maxScrollDepth=0,this.scrollDepthHandler=()=>{let e=window.scrollY||document.documentElement.scrollTop,t=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)-window.innerHeight;if(t<=0)return;let n=Math.round(e/t*100);n>this.maxScrollDepth&&(this.maxScrollDepth=n)},window.addEventListener("scroll",this.scrollDepthHandler,{passive:!0}),window.addEventListener("beforeunload",()=>{if(this.maxScrollDepth>0&&this.isTrackingEnabled()){let e=this.buildPayload("custom","scroll_depth",{max_depth_percent:this.maxScrollDepth,page_url:window.location.href});this.network?.sendBeacon([e])}}))}trackTimeOnPage(){if(!this.pageEntryTime||!this.isTrackingEnabled())return;let e=Math.round((Date.now()-this.pageEntryTime)/1e3);if(e>0){let t=this.buildPayload("custom","time_on_page",{duration_seconds:e,page_url:typeof window<"u"?window.location.href:""});this.eventQueue?.enqueue(t)}}setupOutboundClickTracking(){typeof document>"u"||typeof window>"u"||(this.outboundClickHandler=e=>{let t=e.target?.closest?.("a");if(t?.href)try{let n=new URL(t.href).hostname,i=window.location.hostname;if(n!==i&&this.isTrackingEnabled()){let o=this.buildPayload("custom","outbound_click",{destination_url:t.href,link_text:t.textContent?.trim()?.substring(0,100)??"",page_url:window.location.href});this.eventQueue?.enqueue(o)}}catch{}},document.addEventListener("click",this.outboundClickHandler))}destroy(){this.trackTimeOnPage(),this.scrollDepthHandler&&typeof window<"u"&&(window.removeEventListener("scroll",this.scrollDepthHandler),this.scrollDepthHandler=null),this.outboundClickHandler&&typeof document<"u"&&(document.removeEventListener("click",this.outboundClickHandler),this.outboundClickHandler=null),this.eventQueue?.destroy(),this.session?.destroy(),this.attribution?.destroy(),this.isConfigured=!1,this.logger?.info("SDK destroyed")}ensureConfigured(){if(!this.isConfigured||!this.config)throw new Error("LinkzlySDK is not configured. Call LinkzlySDK.configure() first.")}buildPayload(e,t,n){if(!this.config)throw new Error("SDK not configured");if(!this.isTrackingEnabled())return{type:"sdk_event",sdkKey:this.config.sdkKey,eventType:e,eventName:t,appUserId:null,sessionId:"",visitorId:"",platform:"web",deviceFingerprint:{},deepLinkMatched:!1,deepLinkData:null,customData:null,utmSource:null,utmMedium:null,utmCampaign:null,utmTerm:null,utmContent:null,smartLinkId:null,clickId:null,timestamp:Date.now()};let i=z(),o=this.attribution.getUTM(),a=this.attribution.getAttributionData();return{type:"sdk_event",sdkKey:this.config.sdkKey,eventType:e,eventName:t,appUserId:this.getUserID(),sessionId:this.session.getSessionId(),visitorId:this.getVisitorID(),platform:"web",deviceFingerprint:O(i),deepLinkMatched:!!(a.smartLinkId||a.clickId),deepLinkData:a.smartLinkId||a.clickId?{...a.smartLinkId?{slid:a.smartLinkId}:{},...a.clickId?{cid:a.clickId}:{}}:null,customData:n??null,utmSource:o.utmSource,utmMedium:o.utmMedium,utmCampaign:o.utmCampaign,utmTerm:o.utmTerm,utmContent:o.utmContent,smartLinkId:a.smartLinkId,clickId:a.clickId,timestamp:Date.now()}}isDNTEnabled(){return typeof navigator>"u"?!1:navigator.doNotTrack==="1"||navigator.globalPrivacyControl===!0}};var s=new k,Le=s.configure.bind(s),Ie=s.trackFirstVisit.bind(s),Ee=s.trackOpen.bind(s),De=s.trackPageView.bind(s),Pe=s.trackEvent.bind(s),xe=s.trackPurchase.bind(s),Re=s.trackEventBatch.bind(s),Ce=s.flushEvents.bind(s),Ue=s.getPendingEventCount.bind(s),_e=s.handleSmartLink.bind(s),Me=s.addDeepLinkListener.bind(s),Ae=s.removeAllListeners.bind(s),Ne=s.setUserID.bind(s),ze=s.getUserID.bind(s),Oe=s.clearUserID.bind(s),qe=s.getVisitorID.bind(s),Be=s.resetVisitorID.bind(s),He=s.startSession.bind(s),Fe=s.endSession.bind(s),Ve=s.getSessionId.bind(s),$e=s.setTrackingEnabled.bind(s),Ke=s.isTrackingEnabled.bind(s),Qe=s.destroy.bind(s),A=s;typeof window<"u"&&(window.LinkzlySDK=A);var We=A;})();
|
|
2
|
+
//# sourceMappingURL=cdn.js.map
|
package/cdn/cdn.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/types.ts","../src/core/config.ts","../src/utils/logger.ts","../src/utils/BrowserInfo.ts","../src/utils/storage.ts","../src/services/NetworkService.ts","../src/services/EventQueueService.ts","../src/services/SessionService.ts","../src/services/AttributionService.ts","../src/core/LinkzlyWebSDK.ts","../src/index.ts","../src/cdn.ts"],"sourcesContent":["// ─── Environment ────────────────────────────────────────────────────────────\n\nexport enum Environment {\n PRODUCTION = 0,\n STAGING = 1,\n DEVELOPMENT = 2,\n}\n\n// ─── Configuration ──────────────────────────────────────────────────────────\n\nexport interface LinkzlyWebConfig {\n /** Auto-track page views on SPA navigation (default: false — industry standard opt-in) */\n autoTrackPageViews?: boolean;\n /** Auto-track session start/end via visibility API (default: true) */\n autoTrackSessions?: boolean;\n /** Auto-extract UTM parameters from URL (default: true) */\n autoExtractUTM?: boolean;\n /** Auto-capture referrer (default: true) */\n autoCaptureReferrer?: boolean;\n /** Respect navigator.doNotTrack / globalPrivacyControl (default: true) */\n respectDNT?: boolean;\n /** Event batch size before auto-flush (default: 20) */\n batchSize?: number;\n /** Flush interval in milliseconds (default: 30000) */\n flushIntervalMs?: number;\n /** Session timeout in milliseconds (default: 30000 — 30s, matching mobile SDKs) */\n sessionTimeoutMs?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport interface ResolvedConfig {\n sdkKey: string;\n environment: Environment;\n autoTrackPageViews: boolean;\n autoTrackSessions: boolean;\n autoExtractUTM: boolean;\n autoCaptureReferrer: boolean;\n respectDNT: boolean;\n batchSize: number;\n flushIntervalMs: number;\n sessionTimeoutMs: number;\n debug: boolean;\n baseURL: string;\n}\n\n// ─── Events ─────────────────────────────────────────────────────────────────\n\nexport type EventType = 'install' | 'open' | 'page_view' | 'purchase' | 'custom';\n\nexport interface EventParameters {\n [key: string]: string | number | boolean | null | undefined | EventParameters | Array<string | number | boolean>;\n}\n\nexport interface BatchEvent {\n eventName: string;\n parameters?: EventParameters;\n}\n\nexport interface TrackingPayload {\n type: 'sdk_event';\n sdkKey: string;\n eventType: EventType;\n eventName: string | null;\n appUserId: string | null;\n sessionId: string;\n visitorId: string;\n platform: 'web';\n deviceFingerprint: Record<string, string | number | boolean | null>;\n deepLinkMatched: boolean;\n deepLinkData: Record<string, string> | null;\n customData: EventParameters | null;\n utmSource: string | null;\n utmMedium: string | null;\n utmCampaign: string | null;\n utmTerm: string | null;\n utmContent: string | null;\n smartLinkId: string | null;\n clickId: string | null;\n timestamp: number;\n}\n\nexport interface EventBatch {\n type: 'sdk_event';\n events: TrackingPayload[];\n batchId: string;\n clientTimestamp: number;\n}\n\nexport interface QueuedEvent {\n id: string;\n payload: TrackingPayload;\n createdAt: number;\n retryCount: number;\n}\n\n// ─── Deep Link ──────────────────────────────────────────────────────────────\n\nexport interface DeepLinkData {\n url: string;\n path: string;\n parameters: Record<string, string>;\n smartLinkId: string | null;\n clickId: string | null;\n}\n\nexport type DeepLinkListener = (data: DeepLinkData) => void;\n\n// ─── Attribution ────────────────────────────────────────────────────────────\n\nexport interface UTMParameters {\n utmSource: string | null;\n utmMedium: string | null;\n utmCampaign: string | null;\n utmTerm: string | null;\n utmContent: string | null;\n}\n\nexport interface AttributionData {\n utm: UTMParameters;\n referrer: string | null;\n smartLinkId: string | null;\n clickId: string | null;\n landingPage: string | null;\n}\n\n// ─── Browser Info ───────────────────────────────────────────────────────────\n\nexport interface BrowserFingerprint {\n userAgent: string;\n language: string;\n languages: string;\n screenSize: string;\n viewportSize: string;\n timezone: string;\n timezoneOffset: number;\n colorDepth: number;\n pixelRatio: number;\n platform: string;\n cookiesEnabled: boolean;\n online: boolean;\n referrer: string;\n pageUrl: string;\n}\n\n// ─── Network ────────────────────────────────────────────────────────────────\n\nexport interface TrackingResponse {\n success: boolean;\n deepLinkData?: Record<string, unknown>;\n message?: string;\n}\n","import { Environment, type LinkzlyWebConfig, type ResolvedConfig } from './types';\n\nconst BASE_URLS: Record<Environment, string> = {\n [Environment.PRODUCTION]: 'https://ske.linkzly.com',\n [Environment.STAGING]: 'https://ske-staging.linkzly.com',\n [Environment.DEVELOPMENT]: 'https://ske-dev.linkzly.com',\n};\n\nconst DEFAULTS: Omit<ResolvedConfig, 'sdkKey' | 'environment' | 'baseURL'> = {\n autoTrackPageViews: false,\n autoTrackSessions: true,\n autoExtractUTM: true,\n autoCaptureReferrer: true,\n respectDNT: true,\n batchSize: 20,\n flushIntervalMs: 30_000,\n sessionTimeoutMs: 30_000,\n debug: false,\n};\n\nexport function resolveConfig(\n sdkKey: string,\n environment: Environment,\n options?: LinkzlyWebConfig\n): ResolvedConfig {\n return {\n sdkKey,\n environment,\n baseURL: BASE_URLS[environment],\n autoTrackPageViews: options?.autoTrackPageViews ?? DEFAULTS.autoTrackPageViews,\n autoTrackSessions: options?.autoTrackSessions ?? DEFAULTS.autoTrackSessions,\n autoExtractUTM: options?.autoExtractUTM ?? DEFAULTS.autoExtractUTM,\n autoCaptureReferrer: options?.autoCaptureReferrer ?? DEFAULTS.autoCaptureReferrer,\n respectDNT: options?.respectDNT ?? DEFAULTS.respectDNT,\n batchSize: options?.batchSize ?? DEFAULTS.batchSize,\n flushIntervalMs: options?.flushIntervalMs ?? DEFAULTS.flushIntervalMs,\n sessionTimeoutMs: options?.sessionTimeoutMs ?? DEFAULTS.sessionTimeoutMs,\n debug: options?.debug ?? (environment === Environment.DEVELOPMENT),\n };\n}\n","export class Logger {\n private enabled: boolean;\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n }\n\n info(...args: unknown[]): void {\n if (this.enabled) console.log('[LinkzlySDK]', ...args);\n }\n\n debug(...args: unknown[]): void {\n if (this.enabled) console.debug('[LinkzlySDK]', ...args);\n }\n\n warn(...args: unknown[]): void {\n if (this.enabled) console.warn('[LinkzlySDK]', ...args);\n }\n\n error(...args: unknown[]): void {\n // Errors always log regardless of debug setting\n console.error('[LinkzlySDK]', ...args);\n }\n}\n","import type { BrowserFingerprint } from '../core/types';\n\nexport function collectBrowserInfo(): BrowserFingerprint {\n const nav = typeof navigator !== 'undefined' ? navigator : null;\n const win = typeof window !== 'undefined' ? window : null;\n const doc = typeof document !== 'undefined' ? document : null;\n const screen = typeof window !== 'undefined' ? window.screen : null;\n\n return {\n userAgent: nav?.userAgent ?? 'unknown',\n language: nav?.language ?? 'unknown',\n languages: nav?.languages?.join(',') ?? '',\n screenSize: screen ? `${screen.width}x${screen.height}` : 'unknown',\n viewportSize: win ? `${win.innerWidth}x${win.innerHeight}` : 'unknown',\n timezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone ?? 'unknown',\n timezoneOffset: new Date().getTimezoneOffset(),\n colorDepth: screen?.colorDepth ?? 0,\n pixelRatio: win?.devicePixelRatio ?? 1,\n platform: nav?.platform ?? 'unknown',\n cookiesEnabled: nav?.cookieEnabled ?? false,\n online: nav?.onLine ?? true,\n referrer: doc?.referrer ?? '',\n pageUrl: win?.location?.href ?? '',\n };\n}\n\nexport function toBrowserFingerprintRecord(\n info: BrowserFingerprint\n): Record<string, string | number | boolean | null> {\n return {\n userAgent: info.userAgent,\n language: info.language,\n languages: info.languages,\n screenSize: info.screenSize,\n viewportSize: info.viewportSize,\n timezone: info.timezone,\n timezoneOffset: info.timezoneOffset,\n colorDepth: info.colorDepth,\n pixelRatio: info.pixelRatio,\n platform: info.platform,\n cookiesEnabled: info.cookiesEnabled,\n online: info.online,\n referrer: info.referrer,\n pageUrl: info.pageUrl,\n };\n}\n\nexport function generateUUID(): 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","const PREFIX = 'lz_';\n\nfunction isAvailable(storage: Storage | undefined): storage is Storage {\n if (!storage) return false;\n try {\n const key = '__lz_test__';\n storage.setItem(key, '1');\n storage.removeItem(key);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─── localStorage (persistent across sessions) ─────────────────────────────\n\nexport function getLocal(key: string): string | null {\n if (typeof localStorage === 'undefined' || !isAvailable(localStorage)) return null;\n try {\n return localStorage.getItem(PREFIX + key);\n } catch {\n return null;\n }\n}\n\nexport function setLocal(key: string, value: string): void {\n if (typeof localStorage === 'undefined' || !isAvailable(localStorage)) return;\n try {\n localStorage.setItem(PREFIX + key, value);\n } catch {\n // Storage full or private browsing\n }\n}\n\nexport function removeLocal(key: string): void {\n if (typeof localStorage === 'undefined' || !isAvailable(localStorage)) return;\n try {\n localStorage.removeItem(PREFIX + key);\n } catch {\n // Ignore\n }\n}\n\n// ─── sessionStorage (per-tab, survives SPA navigation) ─────────────────────\n\nexport function getSession(key: string): string | null {\n if (typeof sessionStorage === 'undefined' || !isAvailable(sessionStorage)) return null;\n try {\n return sessionStorage.getItem(PREFIX + key);\n } catch {\n return null;\n }\n}\n\nexport function setSession(key: string, value: string): void {\n if (typeof sessionStorage === 'undefined' || !isAvailable(sessionStorage)) return;\n try {\n sessionStorage.setItem(PREFIX + key, value);\n } catch {\n // Storage full or private browsing\n }\n}\n\nexport function removeSession(key: string): void {\n if (typeof sessionStorage === 'undefined' || !isAvailable(sessionStorage)) return;\n try {\n sessionStorage.removeItem(PREFIX + key);\n } catch {\n // Ignore\n }\n}\n\n// ─── JSON helpers ───────────────────────────────────────────────────────────\n\nexport function getLocalJSON<T>(key: string): T | null {\n const raw = getLocal(key);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n\nexport function setLocalJSON(key: string, value: unknown): void {\n try {\n setLocal(key, JSON.stringify(value));\n } catch {\n // Ignore serialization errors\n }\n}\n","import type { ResolvedConfig, TrackingPayload, TrackingResponse, EventBatch } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { generateUUID } from '../utils/BrowserInfo';\n\nconst MAX_RETRIES = 3;\nconst RETRY_BASE_MS = 1000;\nconst AUTH_HEADER = 'Bearer linkzly-production-shared-secret-key-2024-secure-min-64';\n\nexport class NetworkService {\n private config: ResolvedConfig;\n private logger: Logger;\n\n constructor(config: ResolvedConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n }\n\n async sendEvent(payload: TrackingPayload): Promise<TrackingResponse> {\n const url = `${this.config.baseURL}/v1/sdk/events`;\n this.logger.debug('Sending event:', payload.eventType, payload.eventName ?? '');\n return this.postJSON(url, payload);\n }\n\n async sendBatch(payloads: TrackingPayload[]): Promise<TrackingResponse> {\n if (payloads.length === 0) {\n return { success: true };\n }\n\n const url = `${this.config.baseURL}/v1/sdk/events/batch`;\n const batch: EventBatch = {\n type: 'sdk_event',\n events: payloads,\n batchId: generateUUID(),\n clientTimestamp: Date.now(),\n };\n\n this.logger.debug(`Sending batch of ${payloads.length} events`);\n return this.postJSON(url, batch);\n }\n\n /**\n * Send events via navigator.sendBeacon on page unload.\n * Returns true if the browser accepted the beacon.\n */\n sendBeacon(payloads: TrackingPayload[]): boolean {\n if (payloads.length === 0) return true;\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n\n const url = `${this.config.baseURL}/v1/sdk/events/batch`;\n const batch: EventBatch = {\n type: 'sdk_event',\n events: payloads,\n batchId: generateUUID(),\n clientTimestamp: Date.now(),\n };\n\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n const accepted = navigator.sendBeacon(url, blob);\n this.logger.debug(`Beacon ${accepted ? 'accepted' : 'rejected'}: ${payloads.length} events`);\n return accepted;\n } catch (err) {\n this.logger.error('sendBeacon failed:', err);\n return false;\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private async postJSON(url: string, body: unknown, retryCount = 0): Promise<TrackingResponse> {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Linkzly-SDK': 'web/1.0.0',\n 'Authorization': AUTH_HEADER,\n },\n body: JSON.stringify(body),\n keepalive: true,\n });\n\n if (!response.ok) {\n // Retry on 5xx or 429\n if ((response.status >= 500 || response.status === 429) && retryCount < MAX_RETRIES) {\n const delay = RETRY_BASE_MS * Math.pow(2, retryCount);\n this.logger.warn(`HTTP ${response.status}, retrying in ${delay}ms (attempt ${retryCount + 1}/${MAX_RETRIES})`);\n await this.sleep(delay);\n return this.postJSON(url, body, retryCount + 1);\n }\n\n this.logger.error(`HTTP ${response.status}: ${response.statusText}`);\n return { success: false, message: `HTTP ${response.status}` };\n }\n\n const data = await response.json().catch(() => ({}));\n this.logger.debug('Response:', response.status);\n return { success: true, ...data };\n } catch (err) {\n if (retryCount < MAX_RETRIES) {\n const delay = RETRY_BASE_MS * Math.pow(2, retryCount);\n this.logger.warn(`Network error, retrying in ${delay}ms (attempt ${retryCount + 1}/${MAX_RETRIES}):`, err);\n await this.sleep(delay);\n return this.postJSON(url, body, retryCount + 1);\n }\n\n this.logger.error('Network request failed after retries:', err);\n return { success: false, message: String(err) };\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { ResolvedConfig, TrackingPayload, QueuedEvent } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { generateUUID } from '../utils/BrowserInfo';\nimport { getLocalJSON, setLocalJSON } from '../utils/storage';\nimport { NetworkService } from './NetworkService';\n\nconst QUEUE_STORAGE_KEY = 'event_queue';\nconst MAX_QUEUE_SIZE = 500;\nconst MAX_BATCH_SIZE = 100;\n\nexport class EventQueueService {\n private config: ResolvedConfig;\n private logger: Logger;\n private network: NetworkService;\n private queue: QueuedEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n\n constructor(config: ResolvedConfig, logger: Logger, network: NetworkService) {\n this.config = config;\n this.logger = logger;\n this.network = network;\n\n // Restore queued events from localStorage\n this.restoreQueue();\n\n // Start periodic flush\n this.startFlushTimer();\n\n // Flush on page unload via sendBeacon\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => this.flushBeacon());\n // Also try visibilitychange for mobile browsers\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.flushBeacon();\n }\n });\n }\n }\n\n enqueue(payload: TrackingPayload): void {\n const event: QueuedEvent = {\n id: generateUUID(),\n payload,\n createdAt: Date.now(),\n retryCount: 0,\n };\n\n this.queue.push(event);\n\n // Cap queue size\n if (this.queue.length > MAX_QUEUE_SIZE) {\n this.queue = this.queue.slice(-MAX_QUEUE_SIZE);\n }\n\n this.persistQueue();\n this.logger.debug(`Event queued (${this.queue.length} pending): ${payload.eventType}/${payload.eventName ?? ''}`);\n\n // Auto-flush if batch size reached\n if (this.queue.length >= this.config.batchSize) {\n this.flush().catch((err) => this.logger.error('Auto-flush failed:', err));\n }\n }\n\n async flush(): Promise<boolean> {\n if (this.isFlushing || this.queue.length === 0) return true;\n\n this.isFlushing = true;\n try {\n // Take up to MAX_BATCH_SIZE events\n const batch = this.queue.slice(0, MAX_BATCH_SIZE);\n const payloads = batch.map((e) => e.payload);\n\n const result = await this.network.sendBatch(payloads);\n\n if (result.success) {\n // Remove sent events\n const sentIds = new Set(batch.map((e) => e.id));\n this.queue = this.queue.filter((e) => !sentIds.has(e.id));\n this.persistQueue();\n this.logger.debug(`Flushed ${batch.length} events, ${this.queue.length} remaining`);\n return true;\n }\n\n this.logger.warn(`Batch send failed, ${this.queue.length} events remain in queue`);\n return false;\n } finally {\n this.isFlushing = false;\n }\n }\n\n getPendingCount(): number {\n return this.queue.length;\n }\n\n destroy(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(() => {\n this.flush().catch((err) => this.logger.error('Periodic flush failed:', err));\n }, this.config.flushIntervalMs);\n }\n\n private flushBeacon(): void {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.slice(0, MAX_BATCH_SIZE);\n const payloads = batch.map((e) => e.payload);\n const accepted = this.network.sendBeacon(payloads);\n\n if (accepted) {\n const sentIds = new Set(batch.map((e) => e.id));\n this.queue = this.queue.filter((e) => !sentIds.has(e.id));\n this.persistQueue();\n }\n }\n\n private persistQueue(): void {\n // Only persist the most recent events to avoid bloating localStorage\n const toStore = this.queue.slice(-MAX_QUEUE_SIZE);\n setLocalJSON(QUEUE_STORAGE_KEY, toStore);\n }\n\n private restoreQueue(): void {\n const stored = getLocalJSON<QueuedEvent[]>(QUEUE_STORAGE_KEY);\n if (stored && Array.isArray(stored)) {\n // Filter out events older than 24 hours\n const cutoff = Date.now() - 24 * 60 * 60 * 1000;\n this.queue = stored.filter((e) => e.createdAt > cutoff);\n if (this.queue.length !== stored.length) {\n this.logger.debug(`Pruned ${stored.length - this.queue.length} expired events from queue`);\n this.persistQueue();\n }\n if (this.queue.length > 0) {\n this.logger.debug(`Restored ${this.queue.length} queued events from storage`);\n }\n }\n }\n}\n","import type { ResolvedConfig } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { generateUUID } from '../utils/BrowserInfo';\nimport { getLocal, setLocal, getSession, setSession, removeSession } from '../utils/storage';\n\nconst SESSION_ID_KEY = 'session_id';\nconst SESSION_LAST_ACTIVE_KEY = 'session_last_active';\nconst FIRST_VISIT_KEY = 'first_visit_done';\n\nexport type SessionEventCallback = (event: 'start' | 'end', sessionId: string) => void;\n\nexport class SessionService {\n private config: ResolvedConfig;\n private logger: Logger;\n private sessionId: string;\n private lastActiveTime: number;\n private callback: SessionEventCallback | null = null;\n private visibilityHandler: (() => void) | null = null;\n\n constructor(config: ResolvedConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n this.lastActiveTime = Date.now();\n\n // Restore or create session\n const existingId = getSession(SESSION_ID_KEY);\n const lastActive = getSession(SESSION_LAST_ACTIVE_KEY);\n\n if (existingId && lastActive) {\n const elapsed = Date.now() - parseInt(lastActive, 10);\n if (elapsed < this.config.sessionTimeoutMs) {\n // Resume existing session\n this.sessionId = existingId;\n this.lastActiveTime = parseInt(lastActive, 10);\n this.logger.debug('Resumed session:', this.sessionId);\n } else {\n // Session expired, start new one\n this.sessionId = this.createNewSession();\n }\n } else {\n this.sessionId = this.createNewSession();\n }\n\n // Setup visibility-based session management\n if (this.config.autoTrackSessions) {\n this.setupVisibilityTracking();\n }\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n isFirstVisit(): boolean {\n return getLocal(FIRST_VISIT_KEY) === null;\n }\n\n markFirstVisitDone(): void {\n setLocal(FIRST_VISIT_KEY, String(Date.now()));\n }\n\n onSessionEvent(callback: SessionEventCallback): void {\n this.callback = callback;\n }\n\n touch(): void {\n this.lastActiveTime = Date.now();\n setSession(SESSION_LAST_ACTIVE_KEY, String(this.lastActiveTime));\n }\n\n startSession(): string {\n // End previous session\n if (this.callback) {\n this.callback('end', this.sessionId);\n }\n this.sessionId = this.createNewSession();\n return this.sessionId;\n }\n\n endSession(): void {\n if (this.callback) {\n this.callback('end', this.sessionId);\n }\n removeSession(SESSION_ID_KEY);\n removeSession(SESSION_LAST_ACTIVE_KEY);\n }\n\n destroy(): void {\n if (this.visibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private createNewSession(): string {\n const id = generateUUID();\n setSession(SESSION_ID_KEY, id);\n this.lastActiveTime = Date.now();\n setSession(SESSION_LAST_ACTIVE_KEY, String(this.lastActiveTime));\n this.logger.debug('New session started:', id);\n\n if (this.callback) {\n this.callback('start', id);\n }\n\n return id;\n }\n\n private setupVisibilityTracking(): void {\n if (typeof document === 'undefined') return;\n\n this.visibilityHandler = () => {\n if (document.visibilityState === 'visible') {\n const elapsed = Date.now() - this.lastActiveTime;\n if (elapsed >= this.config.sessionTimeoutMs) {\n this.logger.debug(`Session timeout (${Math.round(elapsed / 1000)}s idle), starting new session`);\n this.sessionId = this.createNewSession();\n } else {\n this.touch();\n }\n } else {\n // Going hidden — save last active time\n this.touch();\n }\n };\n\n document.addEventListener('visibilitychange', this.visibilityHandler);\n }\n}\n","import type { ResolvedConfig, UTMParameters, AttributionData, DeepLinkData, DeepLinkListener } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { getSession, setSession } from '../utils/storage';\n\nconst UTM_STORAGE_PREFIX = 'utm_';\nconst REFERRER_KEY = 'referrer';\nconst LANDING_PAGE_KEY = 'landing_page';\n\nconst UTM_PARAMS: Array<{ key: keyof UTMParameters; urlParam: string }> = [\n { key: 'utmSource', urlParam: 'utm_source' },\n { key: 'utmMedium', urlParam: 'utm_medium' },\n { key: 'utmCampaign', urlParam: 'utm_campaign' },\n { key: 'utmTerm', urlParam: 'utm_term' },\n { key: 'utmContent', urlParam: 'utm_content' },\n];\n\nexport class AttributionService {\n private config: ResolvedConfig;\n private logger: Logger;\n private utm: UTMParameters;\n private referrer: string | null;\n private landingPage: string | null;\n private smartLinkId: string | null = null;\n private clickId: string | null = null;\n private deepLinkListeners: Set<DeepLinkListener> = new Set();\n private popStateHandler: (() => void) | null = null;\n\n constructor(config: ResolvedConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n this.utm = this.initUTM();\n this.referrer = this.initReferrer();\n this.landingPage = this.initLandingPage();\n this.initSmartLinkParams();\n\n // Setup SPA page view tracking if enabled\n if (this.config.autoTrackPageViews) {\n this.setupSPATracking();\n }\n }\n\n getUTM(): UTMParameters {\n return { ...this.utm };\n }\n\n getReferrer(): string | null {\n return this.referrer;\n }\n\n getLandingPage(): string | null {\n return this.landingPage;\n }\n\n getSmartLinkId(): string | null {\n return this.smartLinkId;\n }\n\n getClickId(): string | null {\n return this.clickId;\n }\n\n getAttributionData(): AttributionData {\n return {\n utm: this.getUTM(),\n referrer: this.referrer,\n smartLinkId: this.smartLinkId,\n clickId: this.clickId,\n landingPage: this.landingPage,\n };\n }\n\n /**\n * Parse a URL into DeepLinkData format (matching mobile SDK pattern)\n */\n parseDeepLink(url?: string): DeepLinkData {\n const targetUrl = url ?? (typeof window !== 'undefined' ? window.location.href : '');\n const parameters: Record<string, string> = {};\n let path = '/';\n\n try {\n const parsed = new URL(targetUrl, 'https://placeholder.com');\n path = parsed.pathname;\n parsed.searchParams.forEach((value, key) => {\n parameters[key] = value;\n });\n } catch {\n // Fallback: try manual parsing\n const parts = targetUrl.split('?');\n if (parts[0]) {\n const match = parts[0].match(/^[^:]+:\\/\\/[^/]+(\\/.*)?$/);\n path = match?.[1] ?? '/';\n }\n if (parts[1]) {\n parts[1].split('&').forEach((pair) => {\n const [k, v] = pair.split('=');\n if (k && v) {\n try {\n parameters[decodeURIComponent(k)] = decodeURIComponent(v);\n } catch {\n parameters[k] = v;\n }\n }\n });\n }\n }\n\n const slid = parameters['slid'] ?? parameters['smartLinkId'] ?? null;\n const cid = parameters['cid'] ?? parameters['clickId'] ?? null;\n\n // Remove attribution keys from parameters\n delete parameters['slid'];\n delete parameters['smartLinkId'];\n delete parameters['cid'];\n delete parameters['clickId'];\n\n return {\n url: targetUrl,\n path,\n parameters,\n smartLinkId: slid,\n clickId: cid,\n };\n }\n\n addDeepLinkListener(listener: DeepLinkListener): () => void {\n this.deepLinkListeners.add(listener);\n return () => {\n this.deepLinkListeners.delete(listener);\n };\n }\n\n removeAllListeners(): void {\n this.deepLinkListeners.clear();\n }\n\n destroy(): void {\n this.deepLinkListeners.clear();\n if (typeof window !== 'undefined') {\n if (this.popStateHandler) {\n window.removeEventListener('popstate', this.popStateHandler);\n }\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private initUTM(): UTMParameters {\n const utm: UTMParameters = {\n utmSource: null,\n utmMedium: null,\n utmCampaign: null,\n utmTerm: null,\n utmContent: null,\n };\n\n if (!this.config.autoExtractUTM) return utm;\n\n // Try URL first, then fall back to sessionStorage\n const urlParams = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null;\n\n for (const { key, urlParam } of UTM_PARAMS) {\n const fromUrl = urlParams?.get(urlParam) ?? null;\n if (fromUrl) {\n utm[key] = fromUrl;\n setSession(UTM_STORAGE_PREFIX + urlParam, fromUrl);\n } else {\n utm[key] = getSession(UTM_STORAGE_PREFIX + urlParam);\n }\n }\n\n if (utm.utmSource) {\n this.logger.debug('UTM parameters captured:', utm);\n }\n\n return utm;\n }\n\n private initReferrer(): string | null {\n if (!this.config.autoCaptureReferrer) return null;\n\n // Only capture referrer on first page of session (not SPA navigations)\n const stored = getSession(REFERRER_KEY);\n if (stored !== null) return stored || null;\n\n const ref = typeof document !== 'undefined' ? document.referrer : '';\n if (ref) {\n // Don't count same-origin referrers\n try {\n const refHost = new URL(ref).hostname;\n const currentHost = typeof window !== 'undefined' ? window.location.hostname : '';\n if (refHost === currentHost) {\n setSession(REFERRER_KEY, '');\n return null;\n }\n } catch {\n // Invalid referrer URL\n }\n setSession(REFERRER_KEY, ref);\n this.logger.debug('Referrer captured:', ref);\n return ref;\n }\n\n setSession(REFERRER_KEY, '');\n return null;\n }\n\n private initLandingPage(): string | null {\n const stored = getSession(LANDING_PAGE_KEY);\n if (stored) return stored;\n\n const page = typeof window !== 'undefined' ? window.location.href : null;\n if (page) {\n setSession(LANDING_PAGE_KEY, page);\n }\n return page;\n }\n\n private initSmartLinkParams(): void {\n if (typeof window === 'undefined') return;\n\n const params = new URLSearchParams(window.location.search);\n this.smartLinkId = params.get('slid') ?? params.get('smartLinkId') ?? null;\n this.clickId = params.get('cid') ?? params.get('clickId') ?? null;\n\n if (this.smartLinkId) {\n this.logger.debug('Smart link ID captured:', this.smartLinkId);\n }\n }\n\n private setupSPATracking(): void {\n if (typeof window === 'undefined') return;\n\n // Monkey-patch pushState and replaceState for SPA navigation detection\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n const notifyListeners = () => {\n const data = this.parseDeepLink();\n this.deepLinkListeners.forEach((listener) => {\n try {\n listener(data);\n } catch (err) {\n this.logger.error('Deep link listener error:', err);\n }\n });\n };\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n originalPushState(...args);\n notifyListeners();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n originalReplaceState(...args);\n notifyListeners();\n };\n\n this.popStateHandler = notifyListeners;\n window.addEventListener('popstate', this.popStateHandler);\n }\n}\n","import {\n Environment,\n type LinkzlyWebConfig,\n type ResolvedConfig,\n type EventParameters,\n type BatchEvent,\n type TrackingPayload,\n type DeepLinkData,\n type DeepLinkListener,\n type EventType,\n} from './types';\nimport { resolveConfig } from './config';\nimport { Logger } from '../utils/logger';\nimport { collectBrowserInfo, toBrowserFingerprintRecord, generateUUID } from '../utils/BrowserInfo';\nimport { getLocal, setLocal, removeLocal } from '../utils/storage';\nimport { NetworkService } from '../services/NetworkService';\nimport { EventQueueService } from '../services/EventQueueService';\nimport { SessionService } from '../services/SessionService';\nimport { AttributionService } from '../services/AttributionService';\n\nconst VISITOR_ID_KEY = 'visitor_id';\nconst USER_ID_KEY = 'user_id';\nconst TRACKING_ENABLED_KEY = 'tracking_enabled';\n\nexport class LinkzlyWebSDK {\n private config: ResolvedConfig | null = null;\n private logger!: Logger;\n private network!: NetworkService;\n private eventQueue!: EventQueueService;\n private session!: SessionService;\n private attribution!: AttributionService;\n private isConfigured = false;\n\n // Web-specific auto-tracking\n private scrollDepthHandler: (() => void) | null = null;\n private maxScrollDepth = 0;\n private pageEntryTime = 0;\n private outboundClickHandler: ((e: MouseEvent) => void) | null = null;\n\n // ─── Configuration ────────────────────────────────────────────────────────\n\n configure(\n sdkKey: string,\n environment: Environment = Environment.PRODUCTION,\n options?: LinkzlyWebConfig\n ): void {\n if (this.isConfigured) {\n this.logger?.warn('SDK already configured, ignoring reconfiguration');\n return;\n }\n\n this.config = resolveConfig(sdkKey, environment, options);\n this.logger = new Logger(this.config.debug);\n\n // Check DNT / GPC\n if (this.config.respectDNT && this.isDNTEnabled()) {\n this.logger.info('DNT/GPC detected — tracking disabled by default');\n setLocal(TRACKING_ENABLED_KEY, 'false');\n }\n\n // Initialize services\n this.network = new NetworkService(this.config, this.logger);\n this.eventQueue = new EventQueueService(this.config, this.logger, this.network);\n this.session = new SessionService(this.config, this.logger);\n this.attribution = new AttributionService(this.config, this.logger);\n\n // Session event callback — auto-track opens\n this.session.onSessionEvent((event, sessionId) => {\n if (event === 'start') {\n this.logger.debug('Session started, auto-tracking open');\n this.trackOpen().catch((err) => this.logger.error('Auto trackOpen failed:', err));\n }\n });\n\n // Ensure visitor ID exists\n this.getVisitorID();\n\n this.isConfigured = true;\n this.logger.info(`SDK configured (env: ${Environment[environment]}, key: ${sdkKey.substring(0, 8)}...)`);\n\n // Auto-track first visit\n if (this.session.isFirstVisit()) {\n this.trackFirstVisit().catch((err) => this.logger.error('trackFirstVisit failed:', err));\n this.session.markFirstVisitDone();\n }\n\n // Auto-track initial page view if SPA tracking enabled\n if (this.config.autoTrackPageViews) {\n this.trackPageView().catch((err) => this.logger.error('Auto trackPageView failed:', err));\n }\n\n // Setup web-specific auto-tracking features\n this.setupScrollDepthTracking();\n this.setupOutboundClickTracking();\n this.pageEntryTime = Date.now();\n }\n\n // ─── Event Tracking ───────────────────────────────────────────────────────\n\n async trackFirstVisit(): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('install', null);\n this.eventQueue.enqueue(payload);\n this.logger.info('First visit tracked');\n }\n\n async trackOpen(): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('open', null);\n this.eventQueue.enqueue(payload);\n this.logger.debug('App open tracked');\n }\n\n async trackPageView(pageUrl?: string, params?: EventParameters): Promise<void> {\n this.ensureConfigured();\n const url = pageUrl ?? (typeof window !== 'undefined' ? window.location.href : '');\n const payload = this.buildPayload('page_view', 'page_view', {\n page_url: url,\n page_title: typeof document !== 'undefined' ? document.title : '',\n ...params,\n });\n this.eventQueue.enqueue(payload);\n this.session.touch();\n this.logger.debug('Page view tracked:', url);\n }\n\n async trackEvent(eventName: string, parameters?: EventParameters): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('custom', eventName, parameters);\n this.eventQueue.enqueue(payload);\n this.session.touch();\n this.logger.debug('Event tracked:', eventName);\n }\n\n async trackPurchase(parameters?: EventParameters): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('purchase', 'purchase', parameters);\n this.eventQueue.enqueue(payload);\n this.session.touch();\n this.logger.debug('Purchase tracked');\n }\n\n async trackEventBatch(events: BatchEvent[]): Promise<boolean> {\n this.ensureConfigured();\n for (const event of events) {\n const payload = this.buildPayload('custom', event.eventName, event.parameters);\n this.eventQueue.enqueue(payload);\n }\n this.session.touch();\n this.logger.debug(`Batch of ${events.length} events queued`);\n return true;\n }\n\n // ─── Flush / Queue ────────────────────────────────────────────────────────\n\n async flushEvents(): Promise<boolean> {\n this.ensureConfigured();\n return this.eventQueue.flush();\n }\n\n getPendingEventCount(): number {\n this.ensureConfigured();\n return this.eventQueue.getPendingCount();\n }\n\n // ─── Deep Link / Smart Link ───────────────────────────────────────────────\n\n handleSmartLink(url?: string): DeepLinkData {\n this.ensureConfigured();\n return this.attribution.parseDeepLink(url);\n }\n\n addDeepLinkListener(listener: DeepLinkListener): () => void {\n this.ensureConfigured();\n return this.attribution.addDeepLinkListener(listener);\n }\n\n removeAllListeners(): void {\n if (this.attribution) {\n this.attribution.removeAllListeners();\n }\n }\n\n // ─── User Management ──────────────────────────────────────────────────────\n\n setUserID(userID: string): void {\n setLocal(USER_ID_KEY, userID);\n this.logger?.debug('User ID set:', userID);\n }\n\n getUserID(): string | null {\n return getLocal(USER_ID_KEY);\n }\n\n clearUserID(): void {\n removeLocal(USER_ID_KEY);\n this.logger?.debug('User ID cleared');\n }\n\n getVisitorID(): string {\n let id = getLocal(VISITOR_ID_KEY);\n if (!id) {\n id = generateUUID();\n setLocal(VISITOR_ID_KEY, id);\n this.logger?.debug('New visitor ID generated:', id);\n }\n return id;\n }\n\n resetVisitorID(): string {\n const id = generateUUID();\n setLocal(VISITOR_ID_KEY, id);\n this.logger?.debug('Visitor ID reset:', id);\n return id;\n }\n\n // ─── Session ──────────────────────────────────────────────────────────────\n\n startSession(): string {\n this.ensureConfigured();\n return this.session.startSession();\n }\n\n endSession(): void {\n this.ensureConfigured();\n\n // Track time on page before ending session\n this.trackTimeOnPage();\n\n this.session.endSession();\n }\n\n getSessionId(): string {\n this.ensureConfigured();\n return this.session.getSessionId();\n }\n\n // ─── Privacy ──────────────────────────────────────────────────────────────\n\n setTrackingEnabled(enabled: boolean): void {\n setLocal(TRACKING_ENABLED_KEY, String(enabled));\n this.logger?.info('Tracking', enabled ? 'enabled' : 'disabled');\n }\n\n isTrackingEnabled(): boolean {\n const stored = getLocal(TRACKING_ENABLED_KEY);\n return stored !== 'false';\n }\n\n // ─── Web-Specific: Scroll Depth ──────────────────────────────────────────\n\n private setupScrollDepthTracking(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n\n this.maxScrollDepth = 0;\n\n this.scrollDepthHandler = () => {\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const docHeight = Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n ) - window.innerHeight;\n\n if (docHeight <= 0) return;\n\n const depth = Math.round((scrollTop / docHeight) * 100);\n if (depth > this.maxScrollDepth) {\n this.maxScrollDepth = depth;\n }\n };\n\n window.addEventListener('scroll', this.scrollDepthHandler, { passive: true });\n\n // Send scroll depth on page unload\n window.addEventListener('beforeunload', () => {\n if (this.maxScrollDepth > 0 && this.isTrackingEnabled()) {\n const payload = this.buildPayload('custom', 'scroll_depth', {\n max_depth_percent: this.maxScrollDepth,\n page_url: window.location.href,\n });\n this.network?.sendBeacon([payload]);\n }\n });\n }\n\n // ─── Web-Specific: Time on Page ──────────────────────────────────────────\n\n private trackTimeOnPage(): void {\n if (!this.pageEntryTime || !this.isTrackingEnabled()) return;\n\n const duration = Math.round((Date.now() - this.pageEntryTime) / 1000);\n if (duration > 0) {\n const payload = this.buildPayload('custom', 'time_on_page', {\n duration_seconds: duration,\n page_url: typeof window !== 'undefined' ? window.location.href : '',\n });\n this.eventQueue?.enqueue(payload);\n }\n }\n\n // ─── Web-Specific: Outbound Click Tracking ───────────────────────────────\n\n private setupOutboundClickTracking(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n\n this.outboundClickHandler = (e: MouseEvent) => {\n const target = (e.target as HTMLElement)?.closest?.('a') as HTMLAnchorElement | null;\n if (!target?.href) return;\n\n try {\n const linkHost = new URL(target.href).hostname;\n const currentHost = window.location.hostname;\n\n if (linkHost !== currentHost && this.isTrackingEnabled()) {\n const payload = this.buildPayload('custom', 'outbound_click', {\n destination_url: target.href,\n link_text: target.textContent?.trim()?.substring(0, 100) ?? '',\n page_url: window.location.href,\n });\n this.eventQueue?.enqueue(payload);\n }\n } catch {\n // Invalid URL, ignore\n }\n };\n\n document.addEventListener('click', this.outboundClickHandler);\n }\n\n // ─── Cleanup ──────────────────────────────────────────────────────────────\n\n destroy(): void {\n this.trackTimeOnPage();\n\n if (this.scrollDepthHandler && typeof window !== 'undefined') {\n window.removeEventListener('scroll', this.scrollDepthHandler);\n this.scrollDepthHandler = null;\n }\n if (this.outboundClickHandler && typeof document !== 'undefined') {\n document.removeEventListener('click', this.outboundClickHandler);\n this.outboundClickHandler = null;\n }\n\n this.eventQueue?.destroy();\n this.session?.destroy();\n this.attribution?.destroy();\n this.isConfigured = false;\n this.logger?.info('SDK destroyed');\n }\n\n // ─── Private Helpers ──────────────────────────────────────────────────────\n\n private ensureConfigured(): void {\n if (!this.isConfigured || !this.config) {\n throw new Error('LinkzlySDK is not configured. Call LinkzlySDK.configure() first.');\n }\n }\n\n private buildPayload(\n eventType: EventType,\n eventName: string | null,\n customData?: EventParameters\n ): TrackingPayload {\n if (!this.config) throw new Error('SDK not configured');\n\n // Don't collect data if tracking is disabled\n if (!this.isTrackingEnabled()) {\n // Return minimal payload\n return {\n type: 'sdk_event',\n sdkKey: this.config.sdkKey,\n eventType,\n eventName,\n appUserId: null,\n sessionId: '',\n visitorId: '',\n platform: 'web',\n deviceFingerprint: {},\n deepLinkMatched: false,\n deepLinkData: null,\n customData: null,\n utmSource: null,\n utmMedium: null,\n utmCampaign: null,\n utmTerm: null,\n utmContent: null,\n smartLinkId: null,\n clickId: null,\n timestamp: Date.now(),\n };\n }\n\n const browserInfo = collectBrowserInfo();\n const utm = this.attribution.getUTM();\n const attrData = this.attribution.getAttributionData();\n\n return {\n type: 'sdk_event',\n sdkKey: this.config.sdkKey,\n eventType,\n eventName,\n appUserId: this.getUserID(),\n sessionId: this.session.getSessionId(),\n visitorId: this.getVisitorID(),\n platform: 'web',\n deviceFingerprint: toBrowserFingerprintRecord(browserInfo),\n deepLinkMatched: !!(attrData.smartLinkId || attrData.clickId),\n deepLinkData: attrData.smartLinkId || attrData.clickId\n ? {\n ...(attrData.smartLinkId ? { slid: attrData.smartLinkId } : {}),\n ...(attrData.clickId ? { cid: attrData.clickId } : {}),\n }\n : null,\n customData: customData ?? null,\n utmSource: utm.utmSource,\n utmMedium: utm.utmMedium,\n utmCampaign: utm.utmCampaign,\n utmTerm: utm.utmTerm,\n utmContent: utm.utmContent,\n smartLinkId: attrData.smartLinkId,\n clickId: attrData.clickId,\n timestamp: Date.now(),\n };\n }\n\n private isDNTEnabled(): boolean {\n if (typeof navigator === 'undefined') return false;\n // navigator.doNotTrack\n if (navigator.doNotTrack === '1') return true;\n // Global Privacy Control\n if ((navigator as any).globalPrivacyControl === true) return true;\n return false;\n }\n}\n","import { LinkzlyWebSDK } from './core/LinkzlyWebSDK';\n\n// ─── Re-export types ────────────────────────────────────────────────────────\n\nexport {\n Environment,\n type LinkzlyWebConfig,\n type EventParameters,\n type BatchEvent,\n type DeepLinkData,\n type DeepLinkListener,\n type UTMParameters,\n type AttributionData,\n type BrowserFingerprint,\n type TrackingPayload,\n type EventType,\n} from './core/types';\n\nexport { LinkzlyWebSDK } from './core/LinkzlyWebSDK';\n\n// ─── Singleton instance ─────────────────────────────────────────────────────\n\nconst instance = new LinkzlyWebSDK();\n\n// ─── Convenience named exports (use the singleton) ─────────────────────────\n\nexport const configure = instance.configure.bind(instance);\nexport const trackFirstVisit = instance.trackFirstVisit.bind(instance);\nexport const trackOpen = instance.trackOpen.bind(instance);\nexport const trackPageView = instance.trackPageView.bind(instance);\nexport const trackEvent = instance.trackEvent.bind(instance);\nexport const trackPurchase = instance.trackPurchase.bind(instance);\nexport const trackEventBatch = instance.trackEventBatch.bind(instance);\nexport const flushEvents = instance.flushEvents.bind(instance);\nexport const getPendingEventCount = instance.getPendingEventCount.bind(instance);\nexport const handleSmartLink = instance.handleSmartLink.bind(instance);\nexport const addDeepLinkListener = instance.addDeepLinkListener.bind(instance);\nexport const removeAllListeners = instance.removeAllListeners.bind(instance);\nexport const setUserID = instance.setUserID.bind(instance);\nexport const getUserID = instance.getUserID.bind(instance);\nexport const clearUserID = instance.clearUserID.bind(instance);\nexport const getVisitorID = instance.getVisitorID.bind(instance);\nexport const resetVisitorID = instance.resetVisitorID.bind(instance);\nexport const startSession = instance.startSession.bind(instance);\nexport const endSession = instance.endSession.bind(instance);\nexport const getSessionId = instance.getSessionId.bind(instance);\nexport const setTrackingEnabled = instance.setTrackingEnabled.bind(instance);\nexport const isTrackingEnabled = instance.isTrackingEnabled.bind(instance);\nexport const destroy = instance.destroy.bind(instance);\n\n// ─── Default export (singleton) ─────────────────────────────────────────────\n\nexport default instance;\n","import instance from './index';\n\n// Expose the singleton directly on window for <script> tag usage\nif (typeof window !== 'undefined') {\n (window as any).LinkzlySDK = instance;\n}\n\nexport default instance;\n"],"mappings":"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","names":["Environment","BASE_URLS","DEFAULTS","resolveConfig","sdkKey","environment","options","Logger","enabled","args","collectBrowserInfo","nav","win","doc","screen","toBrowserFingerprintRecord","info","generateUUID","c","r","isAvailable","storage","key","getLocal","setLocal","value","removeLocal","getSession","setSession","removeSession","getLocalJSON","raw","setLocalJSON","MAX_RETRIES","RETRY_BASE_MS","AUTH_HEADER","NetworkService","config","logger","payload","url","payloads","batch","generateUUID","blob","accepted","err","body","retryCount","response","delay","data","ms","resolve","QUEUE_STORAGE_KEY","MAX_QUEUE_SIZE","MAX_BATCH_SIZE","EventQueueService","config","logger","network","payload","event","generateUUID","err","batch","payloads","e","sentIds","toStore","setLocalJSON","stored","getLocalJSON","cutoff","SESSION_ID_KEY","SESSION_LAST_ACTIVE_KEY","FIRST_VISIT_KEY","SessionService","config","logger","existingId","getSession","lastActive","getLocal","setLocal","callback","setSession","removeSession","id","generateUUID","elapsed","UTM_STORAGE_PREFIX","REFERRER_KEY","LANDING_PAGE_KEY","UTM_PARAMS","AttributionService","config","logger","url","targetUrl","parameters","path","parsed","value","key","parts","pair","k","v","slid","cid","listener","utm","urlParams","urlParam","fromUrl","setSession","getSession","stored","ref","refHost","currentHost","page","params","originalPushState","originalReplaceState","notifyListeners","data","err","args","VISITOR_ID_KEY","USER_ID_KEY","TRACKING_ENABLED_KEY","LinkzlyWebSDK","sdkKey","environment","options","resolveConfig","Logger","setLocal","NetworkService","EventQueueService","SessionService","AttributionService","event","sessionId","err","Environment","payload","pageUrl","params","url","eventName","parameters","events","listener","userID","getLocal","removeLocal","id","generateUUID","enabled","scrollTop","docHeight","depth","duration","target","linkHost","currentHost","eventType","customData","browserInfo","collectBrowserInfo","utm","attrData","toBrowserFingerprintRecord","instance","LinkzlyWebSDK","configure","trackFirstVisit","trackOpen","trackPageView","trackEvent","trackPurchase","trackEventBatch","flushEvents","getPendingEventCount","handleSmartLink","addDeepLinkListener","removeAllListeners","setUserID","getUserID","clearUserID","getVisitorID","resetVisitorID","startSession","endSession","getSessionId","setTrackingEnabled","isTrackingEnabled","destroy","index_default","index_default","cdn_default"]}
|
package/cdn/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var LinkzlySDK=(()=>{var x=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var G=Object.prototype.hasOwnProperty;var W=(r,e)=>{for(var t in e)x(r,t,{get:e[t],enumerable:!0})},j=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Y(e))!G.call(r,i)&&i!==t&&x(r,i,{get:()=>e[i],enumerable:!(n=X(e,i))||n.enumerable});return r};var Z=r=>j(x({},"__esModule",{value:!0}),r);var De={};W(De,{Environment:()=>f,LinkzlyWebSDK:()=>v,addDeepLinkListener:()=>he,clearUserID:()=>ve,configure:()=>ie,default:()=>Ee,destroy:()=>Ie,endSession:()=>we,flushEvents:()=>ce,getPendingEventCount:()=>de,getSessionId:()=>Se,getUserID:()=>me,getVisitorID:()=>ke,handleSmartLink:()=>ge,isTrackingEnabled:()=>Le,removeAllListeners:()=>pe,resetVisitorID:()=>be,setTrackingEnabled:()=>Te,setUserID:()=>fe,startSession:()=>ye,trackEvent:()=>ae,trackEventBatch:()=>ue,trackFirstVisit:()=>re,trackOpen:()=>se,trackPageView:()=>oe,trackPurchase:()=>le});var f=(n=>(n[n.PRODUCTION=0]="PRODUCTION",n[n.STAGING=1]="STAGING",n[n.DEVELOPMENT=2]="DEVELOPMENT",n))(f||{});var ee={0:"https://ske.linkzly.com",1:"https://ske-staging.linkzly.com",2:"https://ske-dev.linkzly.com"},d={autoTrackPageViews:!1,autoTrackSessions:!0,autoExtractUTM:!0,autoCaptureReferrer:!0,respectDNT:!0,batchSize:20,flushIntervalMs:3e4,sessionTimeoutMs:3e4,debug:!1};function N(r,e,t){return{sdkKey:r,environment:e,baseURL:ee[e],autoTrackPageViews:t?.autoTrackPageViews??d.autoTrackPageViews,autoTrackSessions:t?.autoTrackSessions??d.autoTrackSessions,autoExtractUTM:t?.autoExtractUTM??d.autoExtractUTM,autoCaptureReferrer:t?.autoCaptureReferrer??d.autoCaptureReferrer,respectDNT:t?.respectDNT??d.respectDNT,batchSize:t?.batchSize??d.batchSize,flushIntervalMs:t?.flushIntervalMs??d.flushIntervalMs,sessionTimeoutMs:t?.sessionTimeoutMs??d.sessionTimeoutMs,debug:t?.debug??e===2}}var y=class{constructor(e){this.enabled=e}info(...e){this.enabled&&console.log("[LinkzlySDK]",...e)}debug(...e){this.enabled&&console.debug("[LinkzlySDK]",...e)}warn(...e){this.enabled&&console.warn("[LinkzlySDK]",...e)}error(...e){console.error("[LinkzlySDK]",...e)}};function z(){let r=typeof navigator<"u"?navigator:null,e=typeof window<"u"?window:null,t=typeof document<"u"?document:null,n=typeof window<"u"?window.screen:null;return{userAgent:r?.userAgent??"unknown",language:r?.language??"unknown",languages:r?.languages?.join(",")??"",screenSize:n?`${n.width}x${n.height}`:"unknown",viewportSize:e?`${e.innerWidth}x${e.innerHeight}`:"unknown",timezone:Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone??"unknown",timezoneOffset:new Date().getTimezoneOffset(),colorDepth:n?.colorDepth??0,pixelRatio:e?.devicePixelRatio??1,platform:r?.platform??"unknown",cookiesEnabled:r?.cookieEnabled??!1,online:r?.onLine??!0,referrer:t?.referrer??"",pageUrl:e?.location?.href??""}}function O(r){return{userAgent:r.userAgent,language:r.language,languages:r.languages,screenSize:r.screenSize,viewportSize:r.viewportSize,timezone:r.timezone,timezoneOffset:r.timezoneOffset,colorDepth:r.colorDepth,pixelRatio:r.pixelRatio,platform:r.platform,cookiesEnabled:r.cookiesEnabled,online:r.online,referrer:r.referrer,pageUrl:r.pageUrl}}function l(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{let e=Math.random()*16|0;return(r==="x"?e:e&3|8).toString(16)})}function m(r){if(!r)return!1;try{let e="__lz_test__";return r.setItem(e,"1"),r.removeItem(e),!0}catch{return!1}}function h(r){if(typeof localStorage>"u"||!m(localStorage))return null;try{return localStorage.getItem("lz_"+r)}catch{return null}}function c(r,e){if(!(typeof localStorage>"u"||!m(localStorage)))try{localStorage.setItem("lz_"+r,e)}catch{}}function q(r){if(!(typeof localStorage>"u"||!m(localStorage)))try{localStorage.removeItem("lz_"+r)}catch{}}function p(r){if(typeof sessionStorage>"u"||!m(sessionStorage))return null;try{return sessionStorage.getItem("lz_"+r)}catch{return null}}function u(r,e){if(!(typeof sessionStorage>"u"||!m(sessionStorage)))try{sessionStorage.setItem("lz_"+r,e)}catch{}}function R(r){if(!(typeof sessionStorage>"u"||!m(sessionStorage)))try{sessionStorage.removeItem("lz_"+r)}catch{}}function B(r){let e=h(r);if(!e)return null;try{return JSON.parse(e)}catch{return null}}function H(r,e){try{c(r,JSON.stringify(e))}catch{}}var w=3,F=1e3,te="Bearer linkzly-production-shared-secret-key-2024-secure-min-64",S=class{constructor(e,t){this.config=e,this.logger=t}async sendEvent(e){let t=`${this.config.baseURL}/v1/sdk/events`;return this.logger.debug("Sending event:",e.eventType,e.eventName??""),this.postJSON(t,e)}async sendBatch(e){if(e.length===0)return{success:!0};let t=`${this.config.baseURL}/v1/sdk/events/batch`,n={type:"sdk_event",events:e,batchId:l(),clientTimestamp:Date.now()};return this.logger.debug(`Sending batch of ${e.length} events`),this.postJSON(t,n)}sendBeacon(e){if(e.length===0)return!0;if(typeof navigator>"u"||!navigator.sendBeacon)return!1;let t=`${this.config.baseURL}/v1/sdk/events/batch`,n={type:"sdk_event",events:e,batchId:l(),clientTimestamp:Date.now()};try{let i=new Blob([JSON.stringify(n)],{type:"application/json"}),o=navigator.sendBeacon(t,i);return this.logger.debug(`Beacon ${o?"accepted":"rejected"}: ${e.length} events`),o}catch(i){return this.logger.error("sendBeacon failed:",i),!1}}async postJSON(e,t,n=0){try{let i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","X-Linkzly-SDK":"web/1.0.0",Authorization:te},body:JSON.stringify(t),keepalive:!0});if(!i.ok){if((i.status>=500||i.status===429)&&n<w){let a=F*Math.pow(2,n);return this.logger.warn(`HTTP ${i.status}, retrying in ${a}ms (attempt ${n+1}/${w})`),await this.sleep(a),this.postJSON(e,t,n+1)}return this.logger.error(`HTTP ${i.status}: ${i.statusText}`),{success:!1,message:`HTTP ${i.status}`}}let o=await i.json().catch(()=>({}));return this.logger.debug("Response:",i.status),{success:!0,...o}}catch(i){if(n<w){let o=F*Math.pow(2,n);return this.logger.warn(`Network error, retrying in ${o}ms (attempt ${n+1}/${w}):`,i),await this.sleep(o),this.postJSON(e,t,n+1)}return this.logger.error("Network request failed after retries:",i),{success:!1,message:String(i)}}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var V="event_queue",C=500,$=100,T=class{constructor(e,t,n){this.queue=[];this.flushTimer=null;this.isFlushing=!1;this.config=e,this.logger=t,this.network=n,this.restoreQueue(),this.startFlushTimer(),typeof window<"u"&&(window.addEventListener("beforeunload",()=>this.flushBeacon()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flushBeacon()}))}enqueue(e){let t={id:l(),payload:e,createdAt:Date.now(),retryCount:0};this.queue.push(t),this.queue.length>C&&(this.queue=this.queue.slice(-C)),this.persistQueue(),this.logger.debug(`Event queued (${this.queue.length} pending): ${e.eventType}/${e.eventName??""}`),this.queue.length>=this.config.batchSize&&this.flush().catch(n=>this.logger.error("Auto-flush failed:",n))}async flush(){if(this.isFlushing||this.queue.length===0)return!0;this.isFlushing=!0;try{let e=this.queue.slice(0,$),t=e.map(i=>i.payload);if((await this.network.sendBatch(t)).success){let i=new Set(e.map(o=>o.id));return this.queue=this.queue.filter(o=>!i.has(o.id)),this.persistQueue(),this.logger.debug(`Flushed ${e.length} events, ${this.queue.length} remaining`),!0}return this.logger.warn(`Batch send failed, ${this.queue.length} events remain in queue`),!1}finally{this.isFlushing=!1}}getPendingCount(){return this.queue.length}destroy(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null)}startFlushTimer(){this.flushTimer||(this.flushTimer=setInterval(()=>{this.flush().catch(e=>this.logger.error("Periodic flush failed:",e))},this.config.flushIntervalMs))}flushBeacon(){if(this.queue.length===0)return;let e=this.queue.slice(0,$),t=e.map(i=>i.payload);if(this.network.sendBeacon(t)){let i=new Set(e.map(o=>o.id));this.queue=this.queue.filter(o=>!i.has(o.id)),this.persistQueue()}}persistQueue(){let e=this.queue.slice(-C);H(V,e)}restoreQueue(){let e=B(V);if(e&&Array.isArray(e)){let t=Date.now()-864e5;this.queue=e.filter(n=>n.createdAt>t),this.queue.length!==e.length&&(this.logger.debug(`Pruned ${e.length-this.queue.length} expired events from queue`),this.persistQueue()),this.queue.length>0&&this.logger.debug(`Restored ${this.queue.length} queued events from storage`)}}};var U="session_id",L="session_last_active",K="first_visit_done",I=class{constructor(e,t){this.callback=null;this.visibilityHandler=null;this.config=e,this.logger=t,this.lastActiveTime=Date.now();let n=p(U),i=p(L);n&&i?Date.now()-parseInt(i,10)<this.config.sessionTimeoutMs?(this.sessionId=n,this.lastActiveTime=parseInt(i,10),this.logger.debug("Resumed session:",this.sessionId)):this.sessionId=this.createNewSession():this.sessionId=this.createNewSession(),this.config.autoTrackSessions&&this.setupVisibilityTracking()}getSessionId(){return this.sessionId}isFirstVisit(){return h(K)===null}markFirstVisitDone(){c(K,String(Date.now()))}onSessionEvent(e){this.callback=e}touch(){this.lastActiveTime=Date.now(),u(L,String(this.lastActiveTime))}startSession(){return this.callback&&this.callback("end",this.sessionId),this.sessionId=this.createNewSession(),this.sessionId}endSession(){this.callback&&this.callback("end",this.sessionId),R(U),R(L)}destroy(){this.visibilityHandler&&typeof document<"u"&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null)}createNewSession(){let e=l();return u(U,e),this.lastActiveTime=Date.now(),u(L,String(this.lastActiveTime)),this.logger.debug("New session started:",e),this.callback&&this.callback("start",e),e}setupVisibilityTracking(){typeof document>"u"||(this.visibilityHandler=()=>{if(document.visibilityState==="visible"){let e=Date.now()-this.lastActiveTime;e>=this.config.sessionTimeoutMs?(this.logger.debug(`Session timeout (${Math.round(e/1e3)}s idle), starting new session`),this.sessionId=this.createNewSession()):this.touch()}else this.touch()},document.addEventListener("visibilitychange",this.visibilityHandler))}};var Q="utm_",E="referrer",J="landing_page",ne=[{key:"utmSource",urlParam:"utm_source"},{key:"utmMedium",urlParam:"utm_medium"},{key:"utmCampaign",urlParam:"utm_campaign"},{key:"utmTerm",urlParam:"utm_term"},{key:"utmContent",urlParam:"utm_content"}],D=class{constructor(e,t){this.smartLinkId=null;this.clickId=null;this.deepLinkListeners=new Set;this.popStateHandler=null;this.config=e,this.logger=t,this.utm=this.initUTM(),this.referrer=this.initReferrer(),this.landingPage=this.initLandingPage(),this.initSmartLinkParams(),this.config.autoTrackPageViews&&this.setupSPATracking()}getUTM(){return{...this.utm}}getReferrer(){return this.referrer}getLandingPage(){return this.landingPage}getSmartLinkId(){return this.smartLinkId}getClickId(){return this.clickId}getAttributionData(){return{utm:this.getUTM(),referrer:this.referrer,smartLinkId:this.smartLinkId,clickId:this.clickId,landingPage:this.landingPage}}parseDeepLink(e){let t=e??(typeof window<"u"?window.location.href:""),n={},i="/";try{let g=new URL(t,"https://placeholder.com");i=g.pathname,g.searchParams.forEach((b,k)=>{n[k]=b})}catch{let g=t.split("?");g[0]&&(i=g[0].match(/^[^:]+:\/\/[^/]+(\/.*)?$/)?.[1]??"/"),g[1]&&g[1].split("&").forEach(b=>{let[k,P]=b.split("=");if(k&&P)try{n[decodeURIComponent(k)]=decodeURIComponent(P)}catch{n[k]=P}})}let o=n.slid??n.smartLinkId??null,a=n.cid??n.clickId??null;return delete n.slid,delete n.smartLinkId,delete n.cid,delete n.clickId,{url:t,path:i,parameters:n,smartLinkId:o,clickId:a}}addDeepLinkListener(e){return this.deepLinkListeners.add(e),()=>{this.deepLinkListeners.delete(e)}}removeAllListeners(){this.deepLinkListeners.clear()}destroy(){this.deepLinkListeners.clear(),typeof window<"u"&&this.popStateHandler&&window.removeEventListener("popstate",this.popStateHandler)}initUTM(){let e={utmSource:null,utmMedium:null,utmCampaign:null,utmTerm:null,utmContent:null};if(!this.config.autoExtractUTM)return e;let t=typeof window<"u"?new URLSearchParams(window.location.search):null;for(let{key:n,urlParam:i}of ne){let o=t?.get(i)??null;o?(e[n]=o,u(Q+i,o)):e[n]=p(Q+i)}return e.utmSource&&this.logger.debug("UTM parameters captured:",e),e}initReferrer(){if(!this.config.autoCaptureReferrer)return null;let e=p(E);if(e!==null)return e||null;let t=typeof document<"u"?document.referrer:"";if(t){try{let n=new URL(t).hostname,i=typeof window<"u"?window.location.hostname:"";if(n===i)return u(E,""),null}catch{}return u(E,t),this.logger.debug("Referrer captured:",t),t}return u(E,""),null}initLandingPage(){let e=p(J);if(e)return e;let t=typeof window<"u"?window.location.href:null;return t&&u(J,t),t}initSmartLinkParams(){if(typeof window>"u")return;let e=new URLSearchParams(window.location.search);this.smartLinkId=e.get("slid")??e.get("smartLinkId")??null,this.clickId=e.get("cid")??e.get("clickId")??null,this.smartLinkId&&this.logger.debug("Smart link ID captured:",this.smartLinkId)}setupSPATracking(){if(typeof window>"u")return;let e=history.pushState.bind(history),t=history.replaceState.bind(history),n=()=>{let i=this.parseDeepLink();this.deepLinkListeners.forEach(o=>{try{o(i)}catch(a){this.logger.error("Deep link listener error:",a)}})};history.pushState=(...i)=>{e(...i),n()},history.replaceState=(...i)=>{t(...i),n()},this.popStateHandler=n,window.addEventListener("popstate",this.popStateHandler)}};var _="visitor_id",M="user_id",A="tracking_enabled",v=class{constructor(){this.config=null;this.isConfigured=!1;this.scrollDepthHandler=null;this.maxScrollDepth=0;this.pageEntryTime=0;this.outboundClickHandler=null}configure(e,t=0,n){if(this.isConfigured){this.logger?.warn("SDK already configured, ignoring reconfiguration");return}this.config=N(e,t,n),this.logger=new y(this.config.debug),this.config.respectDNT&&this.isDNTEnabled()&&(this.logger.info("DNT/GPC detected \u2014 tracking disabled by default"),c(A,"false")),this.network=new S(this.config,this.logger),this.eventQueue=new T(this.config,this.logger,this.network),this.session=new I(this.config,this.logger),this.attribution=new D(this.config,this.logger),this.session.onSessionEvent((i,o)=>{i==="start"&&(this.logger.debug("Session started, auto-tracking open"),this.trackOpen().catch(a=>this.logger.error("Auto trackOpen failed:",a)))}),this.getVisitorID(),this.isConfigured=!0,this.logger.info(`SDK configured (env: ${f[t]}, key: ${e.substring(0,8)}...)`),this.session.isFirstVisit()&&(this.trackFirstVisit().catch(i=>this.logger.error("trackFirstVisit failed:",i)),this.session.markFirstVisitDone()),this.config.autoTrackPageViews&&this.trackPageView().catch(i=>this.logger.error("Auto trackPageView failed:",i)),this.setupScrollDepthTracking(),this.setupOutboundClickTracking(),this.pageEntryTime=Date.now()}async trackFirstVisit(){this.ensureConfigured();let e=this.buildPayload("install",null);this.eventQueue.enqueue(e),this.logger.info("First visit tracked")}async trackOpen(){this.ensureConfigured();let e=this.buildPayload("open",null);this.eventQueue.enqueue(e),this.logger.debug("App open tracked")}async trackPageView(e,t){this.ensureConfigured();let n=e??(typeof window<"u"?window.location.href:""),i=this.buildPayload("page_view","page_view",{page_url:n,page_title:typeof document<"u"?document.title:"",...t});this.eventQueue.enqueue(i),this.session.touch(),this.logger.debug("Page view tracked:",n)}async trackEvent(e,t){this.ensureConfigured();let n=this.buildPayload("custom",e,t);this.eventQueue.enqueue(n),this.session.touch(),this.logger.debug("Event tracked:",e)}async trackPurchase(e){this.ensureConfigured();let t=this.buildPayload("purchase","purchase",e);this.eventQueue.enqueue(t),this.session.touch(),this.logger.debug("Purchase tracked")}async trackEventBatch(e){this.ensureConfigured();for(let t of e){let n=this.buildPayload("custom",t.eventName,t.parameters);this.eventQueue.enqueue(n)}return this.session.touch(),this.logger.debug(`Batch of ${e.length} events queued`),!0}async flushEvents(){return this.ensureConfigured(),this.eventQueue.flush()}getPendingEventCount(){return this.ensureConfigured(),this.eventQueue.getPendingCount()}handleSmartLink(e){return this.ensureConfigured(),this.attribution.parseDeepLink(e)}addDeepLinkListener(e){return this.ensureConfigured(),this.attribution.addDeepLinkListener(e)}removeAllListeners(){this.attribution&&this.attribution.removeAllListeners()}setUserID(e){c(M,e),this.logger?.debug("User ID set:",e)}getUserID(){return h(M)}clearUserID(){q(M),this.logger?.debug("User ID cleared")}getVisitorID(){let e=h(_);return e||(e=l(),c(_,e),this.logger?.debug("New visitor ID generated:",e)),e}resetVisitorID(){let e=l();return c(_,e),this.logger?.debug("Visitor ID reset:",e),e}startSession(){return this.ensureConfigured(),this.session.startSession()}endSession(){this.ensureConfigured(),this.trackTimeOnPage(),this.session.endSession()}getSessionId(){return this.ensureConfigured(),this.session.getSessionId()}setTrackingEnabled(e){c(A,String(e)),this.logger?.info("Tracking",e?"enabled":"disabled")}isTrackingEnabled(){return h(A)!=="false"}setupScrollDepthTracking(){typeof window>"u"||typeof document>"u"||(this.maxScrollDepth=0,this.scrollDepthHandler=()=>{let e=window.scrollY||document.documentElement.scrollTop,t=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)-window.innerHeight;if(t<=0)return;let n=Math.round(e/t*100);n>this.maxScrollDepth&&(this.maxScrollDepth=n)},window.addEventListener("scroll",this.scrollDepthHandler,{passive:!0}),window.addEventListener("beforeunload",()=>{if(this.maxScrollDepth>0&&this.isTrackingEnabled()){let e=this.buildPayload("custom","scroll_depth",{max_depth_percent:this.maxScrollDepth,page_url:window.location.href});this.network?.sendBeacon([e])}}))}trackTimeOnPage(){if(!this.pageEntryTime||!this.isTrackingEnabled())return;let e=Math.round((Date.now()-this.pageEntryTime)/1e3);if(e>0){let t=this.buildPayload("custom","time_on_page",{duration_seconds:e,page_url:typeof window<"u"?window.location.href:""});this.eventQueue?.enqueue(t)}}setupOutboundClickTracking(){typeof document>"u"||typeof window>"u"||(this.outboundClickHandler=e=>{let t=e.target?.closest?.("a");if(t?.href)try{let n=new URL(t.href).hostname,i=window.location.hostname;if(n!==i&&this.isTrackingEnabled()){let o=this.buildPayload("custom","outbound_click",{destination_url:t.href,link_text:t.textContent?.trim()?.substring(0,100)??"",page_url:window.location.href});this.eventQueue?.enqueue(o)}}catch{}},document.addEventListener("click",this.outboundClickHandler))}destroy(){this.trackTimeOnPage(),this.scrollDepthHandler&&typeof window<"u"&&(window.removeEventListener("scroll",this.scrollDepthHandler),this.scrollDepthHandler=null),this.outboundClickHandler&&typeof document<"u"&&(document.removeEventListener("click",this.outboundClickHandler),this.outboundClickHandler=null),this.eventQueue?.destroy(),this.session?.destroy(),this.attribution?.destroy(),this.isConfigured=!1,this.logger?.info("SDK destroyed")}ensureConfigured(){if(!this.isConfigured||!this.config)throw new Error("LinkzlySDK is not configured. Call LinkzlySDK.configure() first.")}buildPayload(e,t,n){if(!this.config)throw new Error("SDK not configured");if(!this.isTrackingEnabled())return{type:"sdk_event",sdkKey:this.config.sdkKey,eventType:e,eventName:t,appUserId:null,sessionId:"",visitorId:"",platform:"web",deviceFingerprint:{},deepLinkMatched:!1,deepLinkData:null,customData:null,utmSource:null,utmMedium:null,utmCampaign:null,utmTerm:null,utmContent:null,smartLinkId:null,clickId:null,timestamp:Date.now()};let i=z(),o=this.attribution.getUTM(),a=this.attribution.getAttributionData();return{type:"sdk_event",sdkKey:this.config.sdkKey,eventType:e,eventName:t,appUserId:this.getUserID(),sessionId:this.session.getSessionId(),visitorId:this.getVisitorID(),platform:"web",deviceFingerprint:O(i),deepLinkMatched:!!(a.smartLinkId||a.clickId),deepLinkData:a.smartLinkId||a.clickId?{...a.smartLinkId?{slid:a.smartLinkId}:{},...a.clickId?{cid:a.clickId}:{}}:null,customData:n??null,utmSource:o.utmSource,utmMedium:o.utmMedium,utmCampaign:o.utmCampaign,utmTerm:o.utmTerm,utmContent:o.utmContent,smartLinkId:a.smartLinkId,clickId:a.clickId,timestamp:Date.now()}}isDNTEnabled(){return typeof navigator>"u"?!1:navigator.doNotTrack==="1"||navigator.globalPrivacyControl===!0}};var s=new v,ie=s.configure.bind(s),re=s.trackFirstVisit.bind(s),se=s.trackOpen.bind(s),oe=s.trackPageView.bind(s),ae=s.trackEvent.bind(s),le=s.trackPurchase.bind(s),ue=s.trackEventBatch.bind(s),ce=s.flushEvents.bind(s),de=s.getPendingEventCount.bind(s),ge=s.handleSmartLink.bind(s),he=s.addDeepLinkListener.bind(s),pe=s.removeAllListeners.bind(s),fe=s.setUserID.bind(s),me=s.getUserID.bind(s),ve=s.clearUserID.bind(s),ke=s.getVisitorID.bind(s),be=s.resetVisitorID.bind(s),ye=s.startSession.bind(s),we=s.endSession.bind(s),Se=s.getSessionId.bind(s),Te=s.setTrackingEnabled.bind(s),Le=s.isTrackingEnabled.bind(s),Ie=s.destroy.bind(s),Ee=s;return Z(De);})();
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
package/cdn/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/types.ts","../src/core/config.ts","../src/utils/logger.ts","../src/utils/BrowserInfo.ts","../src/utils/storage.ts","../src/services/NetworkService.ts","../src/services/EventQueueService.ts","../src/services/SessionService.ts","../src/services/AttributionService.ts","../src/core/LinkzlyWebSDK.ts"],"sourcesContent":["import { LinkzlyWebSDK } from './core/LinkzlyWebSDK';\n\n// ─── Re-export types ────────────────────────────────────────────────────────\n\nexport {\n Environment,\n type LinkzlyWebConfig,\n type EventParameters,\n type BatchEvent,\n type DeepLinkData,\n type DeepLinkListener,\n type UTMParameters,\n type AttributionData,\n type BrowserFingerprint,\n type TrackingPayload,\n type EventType,\n} from './core/types';\n\nexport { LinkzlyWebSDK } from './core/LinkzlyWebSDK';\n\n// ─── Singleton instance ─────────────────────────────────────────────────────\n\nconst instance = new LinkzlyWebSDK();\n\n// ─── Convenience named exports (use the singleton) ─────────────────────────\n\nexport const configure = instance.configure.bind(instance);\nexport const trackFirstVisit = instance.trackFirstVisit.bind(instance);\nexport const trackOpen = instance.trackOpen.bind(instance);\nexport const trackPageView = instance.trackPageView.bind(instance);\nexport const trackEvent = instance.trackEvent.bind(instance);\nexport const trackPurchase = instance.trackPurchase.bind(instance);\nexport const trackEventBatch = instance.trackEventBatch.bind(instance);\nexport const flushEvents = instance.flushEvents.bind(instance);\nexport const getPendingEventCount = instance.getPendingEventCount.bind(instance);\nexport const handleSmartLink = instance.handleSmartLink.bind(instance);\nexport const addDeepLinkListener = instance.addDeepLinkListener.bind(instance);\nexport const removeAllListeners = instance.removeAllListeners.bind(instance);\nexport const setUserID = instance.setUserID.bind(instance);\nexport const getUserID = instance.getUserID.bind(instance);\nexport const clearUserID = instance.clearUserID.bind(instance);\nexport const getVisitorID = instance.getVisitorID.bind(instance);\nexport const resetVisitorID = instance.resetVisitorID.bind(instance);\nexport const startSession = instance.startSession.bind(instance);\nexport const endSession = instance.endSession.bind(instance);\nexport const getSessionId = instance.getSessionId.bind(instance);\nexport const setTrackingEnabled = instance.setTrackingEnabled.bind(instance);\nexport const isTrackingEnabled = instance.isTrackingEnabled.bind(instance);\nexport const destroy = instance.destroy.bind(instance);\n\n// ─── Default export (singleton) ─────────────────────────────────────────────\n\nexport default instance;\n","// ─── Environment ────────────────────────────────────────────────────────────\n\nexport enum Environment {\n PRODUCTION = 0,\n STAGING = 1,\n DEVELOPMENT = 2,\n}\n\n// ─── Configuration ──────────────────────────────────────────────────────────\n\nexport interface LinkzlyWebConfig {\n /** Auto-track page views on SPA navigation (default: false — industry standard opt-in) */\n autoTrackPageViews?: boolean;\n /** Auto-track session start/end via visibility API (default: true) */\n autoTrackSessions?: boolean;\n /** Auto-extract UTM parameters from URL (default: true) */\n autoExtractUTM?: boolean;\n /** Auto-capture referrer (default: true) */\n autoCaptureReferrer?: boolean;\n /** Respect navigator.doNotTrack / globalPrivacyControl (default: true) */\n respectDNT?: boolean;\n /** Event batch size before auto-flush (default: 20) */\n batchSize?: number;\n /** Flush interval in milliseconds (default: 30000) */\n flushIntervalMs?: number;\n /** Session timeout in milliseconds (default: 30000 — 30s, matching mobile SDKs) */\n sessionTimeoutMs?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport interface ResolvedConfig {\n sdkKey: string;\n environment: Environment;\n autoTrackPageViews: boolean;\n autoTrackSessions: boolean;\n autoExtractUTM: boolean;\n autoCaptureReferrer: boolean;\n respectDNT: boolean;\n batchSize: number;\n flushIntervalMs: number;\n sessionTimeoutMs: number;\n debug: boolean;\n baseURL: string;\n}\n\n// ─── Events ─────────────────────────────────────────────────────────────────\n\nexport type EventType = 'install' | 'open' | 'page_view' | 'purchase' | 'custom';\n\nexport interface EventParameters {\n [key: string]: string | number | boolean | null | undefined | EventParameters | Array<string | number | boolean>;\n}\n\nexport interface BatchEvent {\n eventName: string;\n parameters?: EventParameters;\n}\n\nexport interface TrackingPayload {\n type: 'sdk_event';\n sdkKey: string;\n eventType: EventType;\n eventName: string | null;\n appUserId: string | null;\n sessionId: string;\n visitorId: string;\n platform: 'web';\n deviceFingerprint: Record<string, string | number | boolean | null>;\n deepLinkMatched: boolean;\n deepLinkData: Record<string, string> | null;\n customData: EventParameters | null;\n utmSource: string | null;\n utmMedium: string | null;\n utmCampaign: string | null;\n utmTerm: string | null;\n utmContent: string | null;\n smartLinkId: string | null;\n clickId: string | null;\n timestamp: number;\n}\n\nexport interface EventBatch {\n type: 'sdk_event';\n events: TrackingPayload[];\n batchId: string;\n clientTimestamp: number;\n}\n\nexport interface QueuedEvent {\n id: string;\n payload: TrackingPayload;\n createdAt: number;\n retryCount: number;\n}\n\n// ─── Deep Link ──────────────────────────────────────────────────────────────\n\nexport interface DeepLinkData {\n url: string;\n path: string;\n parameters: Record<string, string>;\n smartLinkId: string | null;\n clickId: string | null;\n}\n\nexport type DeepLinkListener = (data: DeepLinkData) => void;\n\n// ─── Attribution ────────────────────────────────────────────────────────────\n\nexport interface UTMParameters {\n utmSource: string | null;\n utmMedium: string | null;\n utmCampaign: string | null;\n utmTerm: string | null;\n utmContent: string | null;\n}\n\nexport interface AttributionData {\n utm: UTMParameters;\n referrer: string | null;\n smartLinkId: string | null;\n clickId: string | null;\n landingPage: string | null;\n}\n\n// ─── Browser Info ───────────────────────────────────────────────────────────\n\nexport interface BrowserFingerprint {\n userAgent: string;\n language: string;\n languages: string;\n screenSize: string;\n viewportSize: string;\n timezone: string;\n timezoneOffset: number;\n colorDepth: number;\n pixelRatio: number;\n platform: string;\n cookiesEnabled: boolean;\n online: boolean;\n referrer: string;\n pageUrl: string;\n}\n\n// ─── Network ────────────────────────────────────────────────────────────────\n\nexport interface TrackingResponse {\n success: boolean;\n deepLinkData?: Record<string, unknown>;\n message?: string;\n}\n","import { Environment, type LinkzlyWebConfig, type ResolvedConfig } from './types';\n\nconst BASE_URLS: Record<Environment, string> = {\n [Environment.PRODUCTION]: 'https://ske.linkzly.com',\n [Environment.STAGING]: 'https://ske-staging.linkzly.com',\n [Environment.DEVELOPMENT]: 'https://ske-dev.linkzly.com',\n};\n\nconst DEFAULTS: Omit<ResolvedConfig, 'sdkKey' | 'environment' | 'baseURL'> = {\n autoTrackPageViews: false,\n autoTrackSessions: true,\n autoExtractUTM: true,\n autoCaptureReferrer: true,\n respectDNT: true,\n batchSize: 20,\n flushIntervalMs: 30_000,\n sessionTimeoutMs: 30_000,\n debug: false,\n};\n\nexport function resolveConfig(\n sdkKey: string,\n environment: Environment,\n options?: LinkzlyWebConfig\n): ResolvedConfig {\n return {\n sdkKey,\n environment,\n baseURL: BASE_URLS[environment],\n autoTrackPageViews: options?.autoTrackPageViews ?? DEFAULTS.autoTrackPageViews,\n autoTrackSessions: options?.autoTrackSessions ?? DEFAULTS.autoTrackSessions,\n autoExtractUTM: options?.autoExtractUTM ?? DEFAULTS.autoExtractUTM,\n autoCaptureReferrer: options?.autoCaptureReferrer ?? DEFAULTS.autoCaptureReferrer,\n respectDNT: options?.respectDNT ?? DEFAULTS.respectDNT,\n batchSize: options?.batchSize ?? DEFAULTS.batchSize,\n flushIntervalMs: options?.flushIntervalMs ?? DEFAULTS.flushIntervalMs,\n sessionTimeoutMs: options?.sessionTimeoutMs ?? DEFAULTS.sessionTimeoutMs,\n debug: options?.debug ?? (environment === Environment.DEVELOPMENT),\n };\n}\n","export class Logger {\n private enabled: boolean;\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n }\n\n info(...args: unknown[]): void {\n if (this.enabled) console.log('[LinkzlySDK]', ...args);\n }\n\n debug(...args: unknown[]): void {\n if (this.enabled) console.debug('[LinkzlySDK]', ...args);\n }\n\n warn(...args: unknown[]): void {\n if (this.enabled) console.warn('[LinkzlySDK]', ...args);\n }\n\n error(...args: unknown[]): void {\n // Errors always log regardless of debug setting\n console.error('[LinkzlySDK]', ...args);\n }\n}\n","import type { BrowserFingerprint } from '../core/types';\n\nexport function collectBrowserInfo(): BrowserFingerprint {\n const nav = typeof navigator !== 'undefined' ? navigator : null;\n const win = typeof window !== 'undefined' ? window : null;\n const doc = typeof document !== 'undefined' ? document : null;\n const screen = typeof window !== 'undefined' ? window.screen : null;\n\n return {\n userAgent: nav?.userAgent ?? 'unknown',\n language: nav?.language ?? 'unknown',\n languages: nav?.languages?.join(',') ?? '',\n screenSize: screen ? `${screen.width}x${screen.height}` : 'unknown',\n viewportSize: win ? `${win.innerWidth}x${win.innerHeight}` : 'unknown',\n timezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone ?? 'unknown',\n timezoneOffset: new Date().getTimezoneOffset(),\n colorDepth: screen?.colorDepth ?? 0,\n pixelRatio: win?.devicePixelRatio ?? 1,\n platform: nav?.platform ?? 'unknown',\n cookiesEnabled: nav?.cookieEnabled ?? false,\n online: nav?.onLine ?? true,\n referrer: doc?.referrer ?? '',\n pageUrl: win?.location?.href ?? '',\n };\n}\n\nexport function toBrowserFingerprintRecord(\n info: BrowserFingerprint\n): Record<string, string | number | boolean | null> {\n return {\n userAgent: info.userAgent,\n language: info.language,\n languages: info.languages,\n screenSize: info.screenSize,\n viewportSize: info.viewportSize,\n timezone: info.timezone,\n timezoneOffset: info.timezoneOffset,\n colorDepth: info.colorDepth,\n pixelRatio: info.pixelRatio,\n platform: info.platform,\n cookiesEnabled: info.cookiesEnabled,\n online: info.online,\n referrer: info.referrer,\n pageUrl: info.pageUrl,\n };\n}\n\nexport function generateUUID(): 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","const PREFIX = 'lz_';\n\nfunction isAvailable(storage: Storage | undefined): storage is Storage {\n if (!storage) return false;\n try {\n const key = '__lz_test__';\n storage.setItem(key, '1');\n storage.removeItem(key);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─── localStorage (persistent across sessions) ─────────────────────────────\n\nexport function getLocal(key: string): string | null {\n if (typeof localStorage === 'undefined' || !isAvailable(localStorage)) return null;\n try {\n return localStorage.getItem(PREFIX + key);\n } catch {\n return null;\n }\n}\n\nexport function setLocal(key: string, value: string): void {\n if (typeof localStorage === 'undefined' || !isAvailable(localStorage)) return;\n try {\n localStorage.setItem(PREFIX + key, value);\n } catch {\n // Storage full or private browsing\n }\n}\n\nexport function removeLocal(key: string): void {\n if (typeof localStorage === 'undefined' || !isAvailable(localStorage)) return;\n try {\n localStorage.removeItem(PREFIX + key);\n } catch {\n // Ignore\n }\n}\n\n// ─── sessionStorage (per-tab, survives SPA navigation) ─────────────────────\n\nexport function getSession(key: string): string | null {\n if (typeof sessionStorage === 'undefined' || !isAvailable(sessionStorage)) return null;\n try {\n return sessionStorage.getItem(PREFIX + key);\n } catch {\n return null;\n }\n}\n\nexport function setSession(key: string, value: string): void {\n if (typeof sessionStorage === 'undefined' || !isAvailable(sessionStorage)) return;\n try {\n sessionStorage.setItem(PREFIX + key, value);\n } catch {\n // Storage full or private browsing\n }\n}\n\nexport function removeSession(key: string): void {\n if (typeof sessionStorage === 'undefined' || !isAvailable(sessionStorage)) return;\n try {\n sessionStorage.removeItem(PREFIX + key);\n } catch {\n // Ignore\n }\n}\n\n// ─── JSON helpers ───────────────────────────────────────────────────────────\n\nexport function getLocalJSON<T>(key: string): T | null {\n const raw = getLocal(key);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n\nexport function setLocalJSON(key: string, value: unknown): void {\n try {\n setLocal(key, JSON.stringify(value));\n } catch {\n // Ignore serialization errors\n }\n}\n","import type { ResolvedConfig, TrackingPayload, TrackingResponse, EventBatch } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { generateUUID } from '../utils/BrowserInfo';\n\nconst MAX_RETRIES = 3;\nconst RETRY_BASE_MS = 1000;\nconst AUTH_HEADER = 'Bearer linkzly-production-shared-secret-key-2024-secure-min-64';\n\nexport class NetworkService {\n private config: ResolvedConfig;\n private logger: Logger;\n\n constructor(config: ResolvedConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n }\n\n async sendEvent(payload: TrackingPayload): Promise<TrackingResponse> {\n const url = `${this.config.baseURL}/v1/sdk/events`;\n this.logger.debug('Sending event:', payload.eventType, payload.eventName ?? '');\n return this.postJSON(url, payload);\n }\n\n async sendBatch(payloads: TrackingPayload[]): Promise<TrackingResponse> {\n if (payloads.length === 0) {\n return { success: true };\n }\n\n const url = `${this.config.baseURL}/v1/sdk/events/batch`;\n const batch: EventBatch = {\n type: 'sdk_event',\n events: payloads,\n batchId: generateUUID(),\n clientTimestamp: Date.now(),\n };\n\n this.logger.debug(`Sending batch of ${payloads.length} events`);\n return this.postJSON(url, batch);\n }\n\n /**\n * Send events via navigator.sendBeacon on page unload.\n * Returns true if the browser accepted the beacon.\n */\n sendBeacon(payloads: TrackingPayload[]): boolean {\n if (payloads.length === 0) return true;\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return false;\n\n const url = `${this.config.baseURL}/v1/sdk/events/batch`;\n const batch: EventBatch = {\n type: 'sdk_event',\n events: payloads,\n batchId: generateUUID(),\n clientTimestamp: Date.now(),\n };\n\n try {\n const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });\n const accepted = navigator.sendBeacon(url, blob);\n this.logger.debug(`Beacon ${accepted ? 'accepted' : 'rejected'}: ${payloads.length} events`);\n return accepted;\n } catch (err) {\n this.logger.error('sendBeacon failed:', err);\n return false;\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private async postJSON(url: string, body: unknown, retryCount = 0): Promise<TrackingResponse> {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Linkzly-SDK': 'web/1.0.0',\n 'Authorization': AUTH_HEADER,\n },\n body: JSON.stringify(body),\n keepalive: true,\n });\n\n if (!response.ok) {\n // Retry on 5xx or 429\n if ((response.status >= 500 || response.status === 429) && retryCount < MAX_RETRIES) {\n const delay = RETRY_BASE_MS * Math.pow(2, retryCount);\n this.logger.warn(`HTTP ${response.status}, retrying in ${delay}ms (attempt ${retryCount + 1}/${MAX_RETRIES})`);\n await this.sleep(delay);\n return this.postJSON(url, body, retryCount + 1);\n }\n\n this.logger.error(`HTTP ${response.status}: ${response.statusText}`);\n return { success: false, message: `HTTP ${response.status}` };\n }\n\n const data = await response.json().catch(() => ({}));\n this.logger.debug('Response:', response.status);\n return { success: true, ...data };\n } catch (err) {\n if (retryCount < MAX_RETRIES) {\n const delay = RETRY_BASE_MS * Math.pow(2, retryCount);\n this.logger.warn(`Network error, retrying in ${delay}ms (attempt ${retryCount + 1}/${MAX_RETRIES}):`, err);\n await this.sleep(delay);\n return this.postJSON(url, body, retryCount + 1);\n }\n\n this.logger.error('Network request failed after retries:', err);\n return { success: false, message: String(err) };\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { ResolvedConfig, TrackingPayload, QueuedEvent } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { generateUUID } from '../utils/BrowserInfo';\nimport { getLocalJSON, setLocalJSON } from '../utils/storage';\nimport { NetworkService } from './NetworkService';\n\nconst QUEUE_STORAGE_KEY = 'event_queue';\nconst MAX_QUEUE_SIZE = 500;\nconst MAX_BATCH_SIZE = 100;\n\nexport class EventQueueService {\n private config: ResolvedConfig;\n private logger: Logger;\n private network: NetworkService;\n private queue: QueuedEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n\n constructor(config: ResolvedConfig, logger: Logger, network: NetworkService) {\n this.config = config;\n this.logger = logger;\n this.network = network;\n\n // Restore queued events from localStorage\n this.restoreQueue();\n\n // Start periodic flush\n this.startFlushTimer();\n\n // Flush on page unload via sendBeacon\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => this.flushBeacon());\n // Also try visibilitychange for mobile browsers\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.flushBeacon();\n }\n });\n }\n }\n\n enqueue(payload: TrackingPayload): void {\n const event: QueuedEvent = {\n id: generateUUID(),\n payload,\n createdAt: Date.now(),\n retryCount: 0,\n };\n\n this.queue.push(event);\n\n // Cap queue size\n if (this.queue.length > MAX_QUEUE_SIZE) {\n this.queue = this.queue.slice(-MAX_QUEUE_SIZE);\n }\n\n this.persistQueue();\n this.logger.debug(`Event queued (${this.queue.length} pending): ${payload.eventType}/${payload.eventName ?? ''}`);\n\n // Auto-flush if batch size reached\n if (this.queue.length >= this.config.batchSize) {\n this.flush().catch((err) => this.logger.error('Auto-flush failed:', err));\n }\n }\n\n async flush(): Promise<boolean> {\n if (this.isFlushing || this.queue.length === 0) return true;\n\n this.isFlushing = true;\n try {\n // Take up to MAX_BATCH_SIZE events\n const batch = this.queue.slice(0, MAX_BATCH_SIZE);\n const payloads = batch.map((e) => e.payload);\n\n const result = await this.network.sendBatch(payloads);\n\n if (result.success) {\n // Remove sent events\n const sentIds = new Set(batch.map((e) => e.id));\n this.queue = this.queue.filter((e) => !sentIds.has(e.id));\n this.persistQueue();\n this.logger.debug(`Flushed ${batch.length} events, ${this.queue.length} remaining`);\n return true;\n }\n\n this.logger.warn(`Batch send failed, ${this.queue.length} events remain in queue`);\n return false;\n } finally {\n this.isFlushing = false;\n }\n }\n\n getPendingCount(): number {\n return this.queue.length;\n }\n\n destroy(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private startFlushTimer(): void {\n if (this.flushTimer) return;\n this.flushTimer = setInterval(() => {\n this.flush().catch((err) => this.logger.error('Periodic flush failed:', err));\n }, this.config.flushIntervalMs);\n }\n\n private flushBeacon(): void {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.slice(0, MAX_BATCH_SIZE);\n const payloads = batch.map((e) => e.payload);\n const accepted = this.network.sendBeacon(payloads);\n\n if (accepted) {\n const sentIds = new Set(batch.map((e) => e.id));\n this.queue = this.queue.filter((e) => !sentIds.has(e.id));\n this.persistQueue();\n }\n }\n\n private persistQueue(): void {\n // Only persist the most recent events to avoid bloating localStorage\n const toStore = this.queue.slice(-MAX_QUEUE_SIZE);\n setLocalJSON(QUEUE_STORAGE_KEY, toStore);\n }\n\n private restoreQueue(): void {\n const stored = getLocalJSON<QueuedEvent[]>(QUEUE_STORAGE_KEY);\n if (stored && Array.isArray(stored)) {\n // Filter out events older than 24 hours\n const cutoff = Date.now() - 24 * 60 * 60 * 1000;\n this.queue = stored.filter((e) => e.createdAt > cutoff);\n if (this.queue.length !== stored.length) {\n this.logger.debug(`Pruned ${stored.length - this.queue.length} expired events from queue`);\n this.persistQueue();\n }\n if (this.queue.length > 0) {\n this.logger.debug(`Restored ${this.queue.length} queued events from storage`);\n }\n }\n }\n}\n","import type { ResolvedConfig } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { generateUUID } from '../utils/BrowserInfo';\nimport { getLocal, setLocal, getSession, setSession, removeSession } from '../utils/storage';\n\nconst SESSION_ID_KEY = 'session_id';\nconst SESSION_LAST_ACTIVE_KEY = 'session_last_active';\nconst FIRST_VISIT_KEY = 'first_visit_done';\n\nexport type SessionEventCallback = (event: 'start' | 'end', sessionId: string) => void;\n\nexport class SessionService {\n private config: ResolvedConfig;\n private logger: Logger;\n private sessionId: string;\n private lastActiveTime: number;\n private callback: SessionEventCallback | null = null;\n private visibilityHandler: (() => void) | null = null;\n\n constructor(config: ResolvedConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n this.lastActiveTime = Date.now();\n\n // Restore or create session\n const existingId = getSession(SESSION_ID_KEY);\n const lastActive = getSession(SESSION_LAST_ACTIVE_KEY);\n\n if (existingId && lastActive) {\n const elapsed = Date.now() - parseInt(lastActive, 10);\n if (elapsed < this.config.sessionTimeoutMs) {\n // Resume existing session\n this.sessionId = existingId;\n this.lastActiveTime = parseInt(lastActive, 10);\n this.logger.debug('Resumed session:', this.sessionId);\n } else {\n // Session expired, start new one\n this.sessionId = this.createNewSession();\n }\n } else {\n this.sessionId = this.createNewSession();\n }\n\n // Setup visibility-based session management\n if (this.config.autoTrackSessions) {\n this.setupVisibilityTracking();\n }\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n isFirstVisit(): boolean {\n return getLocal(FIRST_VISIT_KEY) === null;\n }\n\n markFirstVisitDone(): void {\n setLocal(FIRST_VISIT_KEY, String(Date.now()));\n }\n\n onSessionEvent(callback: SessionEventCallback): void {\n this.callback = callback;\n }\n\n touch(): void {\n this.lastActiveTime = Date.now();\n setSession(SESSION_LAST_ACTIVE_KEY, String(this.lastActiveTime));\n }\n\n startSession(): string {\n // End previous session\n if (this.callback) {\n this.callback('end', this.sessionId);\n }\n this.sessionId = this.createNewSession();\n return this.sessionId;\n }\n\n endSession(): void {\n if (this.callback) {\n this.callback('end', this.sessionId);\n }\n removeSession(SESSION_ID_KEY);\n removeSession(SESSION_LAST_ACTIVE_KEY);\n }\n\n destroy(): void {\n if (this.visibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private createNewSession(): string {\n const id = generateUUID();\n setSession(SESSION_ID_KEY, id);\n this.lastActiveTime = Date.now();\n setSession(SESSION_LAST_ACTIVE_KEY, String(this.lastActiveTime));\n this.logger.debug('New session started:', id);\n\n if (this.callback) {\n this.callback('start', id);\n }\n\n return id;\n }\n\n private setupVisibilityTracking(): void {\n if (typeof document === 'undefined') return;\n\n this.visibilityHandler = () => {\n if (document.visibilityState === 'visible') {\n const elapsed = Date.now() - this.lastActiveTime;\n if (elapsed >= this.config.sessionTimeoutMs) {\n this.logger.debug(`Session timeout (${Math.round(elapsed / 1000)}s idle), starting new session`);\n this.sessionId = this.createNewSession();\n } else {\n this.touch();\n }\n } else {\n // Going hidden — save last active time\n this.touch();\n }\n };\n\n document.addEventListener('visibilitychange', this.visibilityHandler);\n }\n}\n","import type { ResolvedConfig, UTMParameters, AttributionData, DeepLinkData, DeepLinkListener } from '../core/types';\nimport { Logger } from '../utils/logger';\nimport { getSession, setSession } from '../utils/storage';\n\nconst UTM_STORAGE_PREFIX = 'utm_';\nconst REFERRER_KEY = 'referrer';\nconst LANDING_PAGE_KEY = 'landing_page';\n\nconst UTM_PARAMS: Array<{ key: keyof UTMParameters; urlParam: string }> = [\n { key: 'utmSource', urlParam: 'utm_source' },\n { key: 'utmMedium', urlParam: 'utm_medium' },\n { key: 'utmCampaign', urlParam: 'utm_campaign' },\n { key: 'utmTerm', urlParam: 'utm_term' },\n { key: 'utmContent', urlParam: 'utm_content' },\n];\n\nexport class AttributionService {\n private config: ResolvedConfig;\n private logger: Logger;\n private utm: UTMParameters;\n private referrer: string | null;\n private landingPage: string | null;\n private smartLinkId: string | null = null;\n private clickId: string | null = null;\n private deepLinkListeners: Set<DeepLinkListener> = new Set();\n private popStateHandler: (() => void) | null = null;\n\n constructor(config: ResolvedConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n this.utm = this.initUTM();\n this.referrer = this.initReferrer();\n this.landingPage = this.initLandingPage();\n this.initSmartLinkParams();\n\n // Setup SPA page view tracking if enabled\n if (this.config.autoTrackPageViews) {\n this.setupSPATracking();\n }\n }\n\n getUTM(): UTMParameters {\n return { ...this.utm };\n }\n\n getReferrer(): string | null {\n return this.referrer;\n }\n\n getLandingPage(): string | null {\n return this.landingPage;\n }\n\n getSmartLinkId(): string | null {\n return this.smartLinkId;\n }\n\n getClickId(): string | null {\n return this.clickId;\n }\n\n getAttributionData(): AttributionData {\n return {\n utm: this.getUTM(),\n referrer: this.referrer,\n smartLinkId: this.smartLinkId,\n clickId: this.clickId,\n landingPage: this.landingPage,\n };\n }\n\n /**\n * Parse a URL into DeepLinkData format (matching mobile SDK pattern)\n */\n parseDeepLink(url?: string): DeepLinkData {\n const targetUrl = url ?? (typeof window !== 'undefined' ? window.location.href : '');\n const parameters: Record<string, string> = {};\n let path = '/';\n\n try {\n const parsed = new URL(targetUrl, 'https://placeholder.com');\n path = parsed.pathname;\n parsed.searchParams.forEach((value, key) => {\n parameters[key] = value;\n });\n } catch {\n // Fallback: try manual parsing\n const parts = targetUrl.split('?');\n if (parts[0]) {\n const match = parts[0].match(/^[^:]+:\\/\\/[^/]+(\\/.*)?$/);\n path = match?.[1] ?? '/';\n }\n if (parts[1]) {\n parts[1].split('&').forEach((pair) => {\n const [k, v] = pair.split('=');\n if (k && v) {\n try {\n parameters[decodeURIComponent(k)] = decodeURIComponent(v);\n } catch {\n parameters[k] = v;\n }\n }\n });\n }\n }\n\n const slid = parameters['slid'] ?? parameters['smartLinkId'] ?? null;\n const cid = parameters['cid'] ?? parameters['clickId'] ?? null;\n\n // Remove attribution keys from parameters\n delete parameters['slid'];\n delete parameters['smartLinkId'];\n delete parameters['cid'];\n delete parameters['clickId'];\n\n return {\n url: targetUrl,\n path,\n parameters,\n smartLinkId: slid,\n clickId: cid,\n };\n }\n\n addDeepLinkListener(listener: DeepLinkListener): () => void {\n this.deepLinkListeners.add(listener);\n return () => {\n this.deepLinkListeners.delete(listener);\n };\n }\n\n removeAllListeners(): void {\n this.deepLinkListeners.clear();\n }\n\n destroy(): void {\n this.deepLinkListeners.clear();\n if (typeof window !== 'undefined') {\n if (this.popStateHandler) {\n window.removeEventListener('popstate', this.popStateHandler);\n }\n }\n }\n\n // ─── Private ──────────────────────────────────────────────────────────────\n\n private initUTM(): UTMParameters {\n const utm: UTMParameters = {\n utmSource: null,\n utmMedium: null,\n utmCampaign: null,\n utmTerm: null,\n utmContent: null,\n };\n\n if (!this.config.autoExtractUTM) return utm;\n\n // Try URL first, then fall back to sessionStorage\n const urlParams = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null;\n\n for (const { key, urlParam } of UTM_PARAMS) {\n const fromUrl = urlParams?.get(urlParam) ?? null;\n if (fromUrl) {\n utm[key] = fromUrl;\n setSession(UTM_STORAGE_PREFIX + urlParam, fromUrl);\n } else {\n utm[key] = getSession(UTM_STORAGE_PREFIX + urlParam);\n }\n }\n\n if (utm.utmSource) {\n this.logger.debug('UTM parameters captured:', utm);\n }\n\n return utm;\n }\n\n private initReferrer(): string | null {\n if (!this.config.autoCaptureReferrer) return null;\n\n // Only capture referrer on first page of session (not SPA navigations)\n const stored = getSession(REFERRER_KEY);\n if (stored !== null) return stored || null;\n\n const ref = typeof document !== 'undefined' ? document.referrer : '';\n if (ref) {\n // Don't count same-origin referrers\n try {\n const refHost = new URL(ref).hostname;\n const currentHost = typeof window !== 'undefined' ? window.location.hostname : '';\n if (refHost === currentHost) {\n setSession(REFERRER_KEY, '');\n return null;\n }\n } catch {\n // Invalid referrer URL\n }\n setSession(REFERRER_KEY, ref);\n this.logger.debug('Referrer captured:', ref);\n return ref;\n }\n\n setSession(REFERRER_KEY, '');\n return null;\n }\n\n private initLandingPage(): string | null {\n const stored = getSession(LANDING_PAGE_KEY);\n if (stored) return stored;\n\n const page = typeof window !== 'undefined' ? window.location.href : null;\n if (page) {\n setSession(LANDING_PAGE_KEY, page);\n }\n return page;\n }\n\n private initSmartLinkParams(): void {\n if (typeof window === 'undefined') return;\n\n const params = new URLSearchParams(window.location.search);\n this.smartLinkId = params.get('slid') ?? params.get('smartLinkId') ?? null;\n this.clickId = params.get('cid') ?? params.get('clickId') ?? null;\n\n if (this.smartLinkId) {\n this.logger.debug('Smart link ID captured:', this.smartLinkId);\n }\n }\n\n private setupSPATracking(): void {\n if (typeof window === 'undefined') return;\n\n // Monkey-patch pushState and replaceState for SPA navigation detection\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n const notifyListeners = () => {\n const data = this.parseDeepLink();\n this.deepLinkListeners.forEach((listener) => {\n try {\n listener(data);\n } catch (err) {\n this.logger.error('Deep link listener error:', err);\n }\n });\n };\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n originalPushState(...args);\n notifyListeners();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n originalReplaceState(...args);\n notifyListeners();\n };\n\n this.popStateHandler = notifyListeners;\n window.addEventListener('popstate', this.popStateHandler);\n }\n}\n","import {\n Environment,\n type LinkzlyWebConfig,\n type ResolvedConfig,\n type EventParameters,\n type BatchEvent,\n type TrackingPayload,\n type DeepLinkData,\n type DeepLinkListener,\n type EventType,\n} from './types';\nimport { resolveConfig } from './config';\nimport { Logger } from '../utils/logger';\nimport { collectBrowserInfo, toBrowserFingerprintRecord, generateUUID } from '../utils/BrowserInfo';\nimport { getLocal, setLocal, removeLocal } from '../utils/storage';\nimport { NetworkService } from '../services/NetworkService';\nimport { EventQueueService } from '../services/EventQueueService';\nimport { SessionService } from '../services/SessionService';\nimport { AttributionService } from '../services/AttributionService';\n\nconst VISITOR_ID_KEY = 'visitor_id';\nconst USER_ID_KEY = 'user_id';\nconst TRACKING_ENABLED_KEY = 'tracking_enabled';\n\nexport class LinkzlyWebSDK {\n private config: ResolvedConfig | null = null;\n private logger!: Logger;\n private network!: NetworkService;\n private eventQueue!: EventQueueService;\n private session!: SessionService;\n private attribution!: AttributionService;\n private isConfigured = false;\n\n // Web-specific auto-tracking\n private scrollDepthHandler: (() => void) | null = null;\n private maxScrollDepth = 0;\n private pageEntryTime = 0;\n private outboundClickHandler: ((e: MouseEvent) => void) | null = null;\n\n // ─── Configuration ────────────────────────────────────────────────────────\n\n configure(\n sdkKey: string,\n environment: Environment = Environment.PRODUCTION,\n options?: LinkzlyWebConfig\n ): void {\n if (this.isConfigured) {\n this.logger?.warn('SDK already configured, ignoring reconfiguration');\n return;\n }\n\n this.config = resolveConfig(sdkKey, environment, options);\n this.logger = new Logger(this.config.debug);\n\n // Check DNT / GPC\n if (this.config.respectDNT && this.isDNTEnabled()) {\n this.logger.info('DNT/GPC detected — tracking disabled by default');\n setLocal(TRACKING_ENABLED_KEY, 'false');\n }\n\n // Initialize services\n this.network = new NetworkService(this.config, this.logger);\n this.eventQueue = new EventQueueService(this.config, this.logger, this.network);\n this.session = new SessionService(this.config, this.logger);\n this.attribution = new AttributionService(this.config, this.logger);\n\n // Session event callback — auto-track opens\n this.session.onSessionEvent((event, sessionId) => {\n if (event === 'start') {\n this.logger.debug('Session started, auto-tracking open');\n this.trackOpen().catch((err) => this.logger.error('Auto trackOpen failed:', err));\n }\n });\n\n // Ensure visitor ID exists\n this.getVisitorID();\n\n this.isConfigured = true;\n this.logger.info(`SDK configured (env: ${Environment[environment]}, key: ${sdkKey.substring(0, 8)}...)`);\n\n // Auto-track first visit\n if (this.session.isFirstVisit()) {\n this.trackFirstVisit().catch((err) => this.logger.error('trackFirstVisit failed:', err));\n this.session.markFirstVisitDone();\n }\n\n // Auto-track initial page view if SPA tracking enabled\n if (this.config.autoTrackPageViews) {\n this.trackPageView().catch((err) => this.logger.error('Auto trackPageView failed:', err));\n }\n\n // Setup web-specific auto-tracking features\n this.setupScrollDepthTracking();\n this.setupOutboundClickTracking();\n this.pageEntryTime = Date.now();\n }\n\n // ─── Event Tracking ───────────────────────────────────────────────────────\n\n async trackFirstVisit(): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('install', null);\n this.eventQueue.enqueue(payload);\n this.logger.info('First visit tracked');\n }\n\n async trackOpen(): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('open', null);\n this.eventQueue.enqueue(payload);\n this.logger.debug('App open tracked');\n }\n\n async trackPageView(pageUrl?: string, params?: EventParameters): Promise<void> {\n this.ensureConfigured();\n const url = pageUrl ?? (typeof window !== 'undefined' ? window.location.href : '');\n const payload = this.buildPayload('page_view', 'page_view', {\n page_url: url,\n page_title: typeof document !== 'undefined' ? document.title : '',\n ...params,\n });\n this.eventQueue.enqueue(payload);\n this.session.touch();\n this.logger.debug('Page view tracked:', url);\n }\n\n async trackEvent(eventName: string, parameters?: EventParameters): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('custom', eventName, parameters);\n this.eventQueue.enqueue(payload);\n this.session.touch();\n this.logger.debug('Event tracked:', eventName);\n }\n\n async trackPurchase(parameters?: EventParameters): Promise<void> {\n this.ensureConfigured();\n const payload = this.buildPayload('purchase', 'purchase', parameters);\n this.eventQueue.enqueue(payload);\n this.session.touch();\n this.logger.debug('Purchase tracked');\n }\n\n async trackEventBatch(events: BatchEvent[]): Promise<boolean> {\n this.ensureConfigured();\n for (const event of events) {\n const payload = this.buildPayload('custom', event.eventName, event.parameters);\n this.eventQueue.enqueue(payload);\n }\n this.session.touch();\n this.logger.debug(`Batch of ${events.length} events queued`);\n return true;\n }\n\n // ─── Flush / Queue ────────────────────────────────────────────────────────\n\n async flushEvents(): Promise<boolean> {\n this.ensureConfigured();\n return this.eventQueue.flush();\n }\n\n getPendingEventCount(): number {\n this.ensureConfigured();\n return this.eventQueue.getPendingCount();\n }\n\n // ─── Deep Link / Smart Link ───────────────────────────────────────────────\n\n handleSmartLink(url?: string): DeepLinkData {\n this.ensureConfigured();\n return this.attribution.parseDeepLink(url);\n }\n\n addDeepLinkListener(listener: DeepLinkListener): () => void {\n this.ensureConfigured();\n return this.attribution.addDeepLinkListener(listener);\n }\n\n removeAllListeners(): void {\n if (this.attribution) {\n this.attribution.removeAllListeners();\n }\n }\n\n // ─── User Management ──────────────────────────────────────────────────────\n\n setUserID(userID: string): void {\n setLocal(USER_ID_KEY, userID);\n this.logger?.debug('User ID set:', userID);\n }\n\n getUserID(): string | null {\n return getLocal(USER_ID_KEY);\n }\n\n clearUserID(): void {\n removeLocal(USER_ID_KEY);\n this.logger?.debug('User ID cleared');\n }\n\n getVisitorID(): string {\n let id = getLocal(VISITOR_ID_KEY);\n if (!id) {\n id = generateUUID();\n setLocal(VISITOR_ID_KEY, id);\n this.logger?.debug('New visitor ID generated:', id);\n }\n return id;\n }\n\n resetVisitorID(): string {\n const id = generateUUID();\n setLocal(VISITOR_ID_KEY, id);\n this.logger?.debug('Visitor ID reset:', id);\n return id;\n }\n\n // ─── Session ──────────────────────────────────────────────────────────────\n\n startSession(): string {\n this.ensureConfigured();\n return this.session.startSession();\n }\n\n endSession(): void {\n this.ensureConfigured();\n\n // Track time on page before ending session\n this.trackTimeOnPage();\n\n this.session.endSession();\n }\n\n getSessionId(): string {\n this.ensureConfigured();\n return this.session.getSessionId();\n }\n\n // ─── Privacy ──────────────────────────────────────────────────────────────\n\n setTrackingEnabled(enabled: boolean): void {\n setLocal(TRACKING_ENABLED_KEY, String(enabled));\n this.logger?.info('Tracking', enabled ? 'enabled' : 'disabled');\n }\n\n isTrackingEnabled(): boolean {\n const stored = getLocal(TRACKING_ENABLED_KEY);\n return stored !== 'false';\n }\n\n // ─── Web-Specific: Scroll Depth ──────────────────────────────────────────\n\n private setupScrollDepthTracking(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n\n this.maxScrollDepth = 0;\n\n this.scrollDepthHandler = () => {\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const docHeight = Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n ) - window.innerHeight;\n\n if (docHeight <= 0) return;\n\n const depth = Math.round((scrollTop / docHeight) * 100);\n if (depth > this.maxScrollDepth) {\n this.maxScrollDepth = depth;\n }\n };\n\n window.addEventListener('scroll', this.scrollDepthHandler, { passive: true });\n\n // Send scroll depth on page unload\n window.addEventListener('beforeunload', () => {\n if (this.maxScrollDepth > 0 && this.isTrackingEnabled()) {\n const payload = this.buildPayload('custom', 'scroll_depth', {\n max_depth_percent: this.maxScrollDepth,\n page_url: window.location.href,\n });\n this.network?.sendBeacon([payload]);\n }\n });\n }\n\n // ─── Web-Specific: Time on Page ──────────────────────────────────────────\n\n private trackTimeOnPage(): void {\n if (!this.pageEntryTime || !this.isTrackingEnabled()) return;\n\n const duration = Math.round((Date.now() - this.pageEntryTime) / 1000);\n if (duration > 0) {\n const payload = this.buildPayload('custom', 'time_on_page', {\n duration_seconds: duration,\n page_url: typeof window !== 'undefined' ? window.location.href : '',\n });\n this.eventQueue?.enqueue(payload);\n }\n }\n\n // ─── Web-Specific: Outbound Click Tracking ───────────────────────────────\n\n private setupOutboundClickTracking(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n\n this.outboundClickHandler = (e: MouseEvent) => {\n const target = (e.target as HTMLElement)?.closest?.('a') as HTMLAnchorElement | null;\n if (!target?.href) return;\n\n try {\n const linkHost = new URL(target.href).hostname;\n const currentHost = window.location.hostname;\n\n if (linkHost !== currentHost && this.isTrackingEnabled()) {\n const payload = this.buildPayload('custom', 'outbound_click', {\n destination_url: target.href,\n link_text: target.textContent?.trim()?.substring(0, 100) ?? '',\n page_url: window.location.href,\n });\n this.eventQueue?.enqueue(payload);\n }\n } catch {\n // Invalid URL, ignore\n }\n };\n\n document.addEventListener('click', this.outboundClickHandler);\n }\n\n // ─── Cleanup ──────────────────────────────────────────────────────────────\n\n destroy(): void {\n this.trackTimeOnPage();\n\n if (this.scrollDepthHandler && typeof window !== 'undefined') {\n window.removeEventListener('scroll', this.scrollDepthHandler);\n this.scrollDepthHandler = null;\n }\n if (this.outboundClickHandler && typeof document !== 'undefined') {\n document.removeEventListener('click', this.outboundClickHandler);\n this.outboundClickHandler = null;\n }\n\n this.eventQueue?.destroy();\n this.session?.destroy();\n this.attribution?.destroy();\n this.isConfigured = false;\n this.logger?.info('SDK destroyed');\n }\n\n // ─── Private Helpers ──────────────────────────────────────────────────────\n\n private ensureConfigured(): void {\n if (!this.isConfigured || !this.config) {\n throw new Error('LinkzlySDK is not configured. Call LinkzlySDK.configure() first.');\n }\n }\n\n private buildPayload(\n eventType: EventType,\n eventName: string | null,\n customData?: EventParameters\n ): TrackingPayload {\n if (!this.config) throw new Error('SDK not configured');\n\n // Don't collect data if tracking is disabled\n if (!this.isTrackingEnabled()) {\n // Return minimal payload\n return {\n type: 'sdk_event',\n sdkKey: this.config.sdkKey,\n eventType,\n eventName,\n appUserId: null,\n sessionId: '',\n visitorId: '',\n platform: 'web',\n deviceFingerprint: {},\n deepLinkMatched: false,\n deepLinkData: null,\n customData: null,\n utmSource: null,\n utmMedium: null,\n utmCampaign: null,\n utmTerm: null,\n utmContent: null,\n smartLinkId: null,\n clickId: null,\n timestamp: Date.now(),\n };\n }\n\n const browserInfo = collectBrowserInfo();\n const utm = this.attribution.getUTM();\n const attrData = this.attribution.getAttributionData();\n\n return {\n type: 'sdk_event',\n sdkKey: this.config.sdkKey,\n eventType,\n eventName,\n appUserId: this.getUserID(),\n sessionId: this.session.getSessionId(),\n visitorId: this.getVisitorID(),\n platform: 'web',\n deviceFingerprint: toBrowserFingerprintRecord(browserInfo),\n deepLinkMatched: !!(attrData.smartLinkId || attrData.clickId),\n deepLinkData: attrData.smartLinkId || attrData.clickId\n ? {\n ...(attrData.smartLinkId ? { slid: attrData.smartLinkId } : {}),\n ...(attrData.clickId ? { cid: attrData.clickId } : {}),\n }\n : null,\n customData: customData ?? null,\n utmSource: utm.utmSource,\n utmMedium: utm.utmMedium,\n utmCampaign: utm.utmCampaign,\n utmTerm: utm.utmTerm,\n utmContent: utm.utmContent,\n smartLinkId: attrData.smartLinkId,\n clickId: attrData.clickId,\n timestamp: Date.now(),\n };\n }\n\n private isDNTEnabled(): boolean {\n if (typeof navigator === 'undefined') return false;\n // navigator.doNotTrack\n if (navigator.doNotTrack === '1') return true;\n // Global Privacy Control\n if ((navigator as any).globalPrivacyControl === true) return true;\n return false;\n }\n}\n"],"mappings":"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","names":["index_exports","__export","Environment","LinkzlyWebSDK","addDeepLinkListener","clearUserID","configure","index_default","destroy","endSession","flushEvents","getPendingEventCount","getSessionId","getUserID","getVisitorID","handleSmartLink","isTrackingEnabled","removeAllListeners","resetVisitorID","setTrackingEnabled","setUserID","startSession","trackEvent","trackEventBatch","trackFirstVisit","trackOpen","trackPageView","trackPurchase","Environment","BASE_URLS","DEFAULTS","resolveConfig","sdkKey","environment","options","Logger","enabled","args","collectBrowserInfo","nav","win","doc","screen","toBrowserFingerprintRecord","info","generateUUID","c","r","isAvailable","storage","key","getLocal","setLocal","value","removeLocal","getSession","setSession","removeSession","getLocalJSON","raw","setLocalJSON","MAX_RETRIES","RETRY_BASE_MS","AUTH_HEADER","NetworkService","config","logger","payload","url","payloads","batch","generateUUID","blob","accepted","err","body","retryCount","response","delay","data","ms","resolve","QUEUE_STORAGE_KEY","MAX_QUEUE_SIZE","MAX_BATCH_SIZE","EventQueueService","config","logger","network","payload","event","generateUUID","err","batch","payloads","e","sentIds","toStore","setLocalJSON","stored","getLocalJSON","cutoff","SESSION_ID_KEY","SESSION_LAST_ACTIVE_KEY","FIRST_VISIT_KEY","SessionService","config","logger","existingId","getSession","lastActive","getLocal","setLocal","callback","setSession","removeSession","id","generateUUID","elapsed","UTM_STORAGE_PREFIX","REFERRER_KEY","LANDING_PAGE_KEY","UTM_PARAMS","AttributionService","config","logger","url","targetUrl","parameters","path","parsed","value","key","parts","pair","v","slid","cid","listener","utm","urlParams","urlParam","fromUrl","setSession","getSession","stored","ref","refHost","currentHost","page","params","originalPushState","originalReplaceState","notifyListeners","data","err","args","VISITOR_ID_KEY","USER_ID_KEY","TRACKING_ENABLED_KEY","LinkzlyWebSDK","sdkKey","environment","options","resolveConfig","Logger","setLocal","NetworkService","EventQueueService","SessionService","AttributionService","event","sessionId","err","Environment","payload","pageUrl","params","url","eventName","parameters","events","listener","userID","getLocal","removeLocal","id","generateUUID","enabled","scrollTop","docHeight","depth","duration","target","linkHost","currentHost","eventType","customData","browserInfo","collectBrowserInfo","utm","attrData","toBrowserFingerprintRecord","instance","LinkzlyWebSDK","configure","trackFirstVisit","trackOpen","trackPageView","trackEvent","trackPurchase","trackEventBatch","flushEvents","getPendingEventCount","handleSmartLink","addDeepLinkListener","removeAllListeners","setUserID","getUserID","clearUserID","getVisitorID","resetVisitorID","startSession","endSession","getSessionId","setTrackingEnabled","isTrackingEnabled","destroy","index_default"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linkzly/web-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Full-featured browser SDK for Linkzly — event tracking, attribution, sessions, deep linking, and affiliate tracking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
+
"cdn",
|
|
18
19
|
"README.md"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
|
21
22
|
"build": "tsup src/index.ts --format esm,cjs --dts --minify",
|
|
23
|
+
"build:cdn": "tsup --config tsup.cdn.ts",
|
|
24
|
+
"build:all": "npm run build && npm run build:cdn",
|
|
22
25
|
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
23
26
|
"typecheck": "tsc --noEmit"
|
|
24
27
|
},
|