@trlogic/tracker-web
Version:
Formica Tracker Web SDK
2 lines • 55.2 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fingerprintjs/fingerprintjs"),require("bowser")):"function"==typeof define&&define.amd?define(["exports","@fingerprintjs/fingerprintjs","bowser"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TrackerManager={},e.FingerprintJS,e.Bowser)}(this,function(e,t,s){"use strict";var i,r;e.TrackerVariableType=void 0,(i=e.TrackerVariableType||(e.TrackerVariableType={})).URL="URL",i.ELEMENT="ELEMENT",i.COOKIE="COOKIE",i.JAVASCRIPT="JAVASCRIPT",i.EVENT="EVENT",i.IP_ADDRESS="IP_ADDRESS",i.DEVICE_FINGERPRINT="DEVICE_FINGERPRINT",i.SCREEN_RESOLUTION="SCREEN_RESOLUTION",i.USER_AGENT="USER_AGENT",i.BROWSER="BROWSER",i.OS="OS",i.LANGUAGE="LANGUAGE",i.TIMEZONE="TIMEZONE",i.PLATFORM="PLATFORM",i.SDK_VERSION="SDK_VERSION",i.TIMESTAMP="TIMESTAMP",i.ELAPSED_TIME="ELAPSED_TIME",i.CURRENT_TRIGGER_NAME="CURRENT_TRIGGER_NAME",i.THEME_MODE="THEME_MODE",i.SUSPICIOUS_ACTIVITY="SUSPICIOUS_ACTIVITY",i.SUSPICIOUS_BROWSER="SUSPICIOUS_BROWSER",i.REMOTE_ACCESS_SCORE="REMOTE_ACCESS_SCORE",i.REMOTE_ACCESS_FLAGS="REMOTE_ACCESS_FLAGS",i.ENVIRONMENT_CHANGE_FLAGS="ENVIRONMENT_CHANGE_FLAGS",i.ENVIRONMENT_CHANGE_SCORE="ENVIRONMENT_CHANGE_SCORE",i.NO_MOUSE_MOVE_DURATION_MS="NO_MOUSE_MOVE_DURATION_MS",i.CLICK_ONLY_PATTERN="CLICK_ONLY_PATTERN",i.FAST_PAGE_TRANSITION="FAST_PAGE_TRANSITION",i.FORM_FILL_ANOMALY="FORM_FILL_ANOMALY",i.FORM_FILL_DURATION_MS="FORM_FILL_DURATION_MS",i.NAVIGATION_VELOCITY="NAVIGATION_VELOCITY",i.INPUT_PASTE_RATIO="INPUT_PASTE_RATIO",i.HEADLESS_INDICATOR_COUNT="HEADLESS_INDICATOR_COUNT",i.REDIRECT_DETECTED="REDIRECT_DETECTED",i.MOUSE_DYNAMICS="MOUSE_DYNAMICS",i.KEYSTROKE_DYNAMICS="KEYSTROKE_DYNAMICS",i.SCROLL_BEHAVIOR="SCROLL_BEHAVIOR",i.SESSION_BEHAVIOR="SESSION_BEHAVIOR",i.TOUCH_DYNAMICS="TOUCH_DYNAMICS",i.BEHAVIORAL_BIOMETRIC_SCORE="BEHAVIORAL_BIOMETRIC_SCORE",i.BEHAVIORAL_BIOMETRIC_FLAGS="BEHAVIORAL_BIOMETRIC_FLAGS",i.VIEW_DURATION="VIEW_DURATION",i.CUSTOM_TRIGGER_ARGS="CUSTOM_TRIGGER_ARGS",i.REACHABLE="REACHABLE";class n{collectIfNeeded(e,t,s,i){if(!i||n.isVariableMapped(t,i))try{e.set(t,s())}catch{}}static mapVariablesToBackend(t,s){const i={},r=t.event.variableMappings;for(const[t,n]of Object.entries(r)){const r=n,a=s.get(r);r===e.TrackerVariableType.CUSTOM_TRIGGER_ARGS?i[t]=a&&"object"==typeof a?a[t]??null:null:i[t]=a??null}return i}static isVariableMapped(e,t){return t.some(t=>{const s=t.event.variableMappings;return Object.values(s).includes(e)})}}class a extends n{constructor(){super(...arguments),this.cache=new Map}collectWithCache(e,t,s,i,r){this.collectIfNeeded(e,t,()=>{const e=this.cache.get(t),r=Date.now();if(e&&r-e.timestamp<e.ttl)return e.value;const n=i();return this.cache.set(t,{value:n,timestamp:r,ttl:s}),n},r)}invalidateCache(e){this.cache.delete(e)}clearCache(){this.cache.clear()}}e.CacheStrategy=void 0,(r=e.CacheStrategy||(e.CacheStrategy={}))[r.FAST=1e4]="FAST",r[r.MEDIUM=6e4]="MEDIUM",r[r.SLOW=3e5]="SLOW",r[r.HEAVY=9007199254740991]="HEAVY";class o{constructor(){this.n=0,this.m=0,this.m2=0,this.lo=1/0,this.hi=-1/0}push(e){this.n++;const t=e-this.m;this.m+=t/this.n;const s=e-this.m;this.m2+=t*s,e<this.lo&&(this.lo=e),e>this.hi&&(this.hi=e)}getMean(){return 0===this.n?0:this.m}getStddev(){return this.n<2?0:Math.sqrt(this.m2/(this.n-1))}getMin(){return 0===this.n?0:this.lo}getMax(){return 0===this.n?0:this.hi}getCount(){return this.n}reset(){this.n=0,this.m=0,this.m2=0,this.lo=1/0,this.hi=-1/0}toJSON(){return{mean:this.getMean(),stddev:this.getStddev(),min:this.getMin(),max:this.getMax(),count:this.getCount()}}}function l(e,t){let s=0;return(...i)=>{const r=performance.now();r-s>=t&&(s=r,e(...i))}}const c=6e4;class h extends a{constructor(){super(...arguments),this.mouseVelocityStats=new o,this.mouseAccelStats=new o,this.lastMouseSample=null,this.pathSegments=[],this.currentPathLength=0,this.currentPathStartX=0,this.currentPathStartY=0,this.angularVarianceStats=new o,this.lastAngle=null,this.lastMouseSampleTs=0,this.lastKeydownTs=0,this.lastKeyupTs=0,this.interKeyStats=new o,this.dwellTimeStats=new o,this.flightTimeStats=new o,this.pendingKeydowns=new Map,this.lastScrollY=0,this.lastScrollTs=0,this.maxScrollDepthPx=0,this.scrollSpeedStats=new o,this.scrollDirectionChanges=0,this.lastScrollDirection=0,this.scrollTotalDistance=0,this.smoothScrollCount=0,this.totalScrollSamples=0,this.lastAnyInteractionTs=Date.now(),this.idleCount=0,this.totalIdleMs=0,this.maxIdleMs=0,this.currentIdleStart=null,this.tabSwitchCount=0,this.totalHiddenMs=0,this.lastHiddenTs=null,this.interactionTimestamps=[],this.burstCount=0,this.touchSupported=!1,this.touchRadiusStats=new o,this.touchPressureStats=new o,this.swipeVelocityStats=new o,this.tapCount=0,this.swipeCount=0,this.activeTouchStart=null,this.listenersAttached=!1,this.idleCheckIntervalId=null,this.initTs=0,this.boundMouseHandler=null,this.boundKeydownHandler=null,this.boundKeyupHandler=null,this.boundScrollHandler=null,this.boundVisibilityHandler=null,this.boundTouchStartHandler=null,this.boundTouchMoveHandler=null,this.boundTouchEndHandler=null}collect(){const t=new Map,s=Date.now()-this.initTs;return this.collectWithCache(t,e.TrackerVariableType.MOUSE_DYNAMICS,e.CacheStrategy.FAST,()=>this.computeMouseDynamics()),this.collectWithCache(t,e.TrackerVariableType.KEYSTROKE_DYNAMICS,e.CacheStrategy.FAST,()=>this.computeKeystrokeDynamics()),this.collectWithCache(t,e.TrackerVariableType.SCROLL_BEHAVIOR,e.CacheStrategy.FAST,()=>this.computeScrollBehavior()),this.collectWithCache(t,e.TrackerVariableType.SESSION_BEHAVIOR,e.CacheStrategy.FAST,()=>this.computeSessionBehavior()),this.touchSupported?this.collectWithCache(t,e.TrackerVariableType.TOUCH_DYNAMICS,e.CacheStrategy.FAST,()=>this.computeTouchDynamics()):this.collectIfNeeded(t,e.TrackerVariableType.TOUCH_DYNAMICS,()=>null),this.collectIfNeeded(t,e.TrackerVariableType.BEHAVIORAL_BIOMETRIC_SCORE,()=>this.computeBiometricScore(s)),this.collectIfNeeded(t,e.TrackerVariableType.BEHAVIORAL_BIOMETRIC_FLAGS,()=>this.computeBiometricFlags(s)),t}init(){this.initTs=Date.now(),this.lastAnyInteractionTs=Date.now(),this.touchSupported="ontouchstart"in window,this.attachListeners(),this.startIdleCheck()}destroy(){this.detachListeners(),this.stopIdleCheck()}resetRollingWindow(){const e=Date.now()-c;this.interactionTimestamps=this.interactionTimestamps.filter(t=>t>=e)}computeMouseDynamics(){const e=this.mouseVelocityStats,t=this.mouseAccelStats;let s=0;if(this.pathSegments.length>0){let e=0;for(const t of this.pathSegments)e+=t.pathLength>0?t.displacement/t.pathLength:0;s=e/this.pathSegments.length}const i=this.angularVarianceStats,r=i.getCount()>1?Math.min(1,i.getStddev()/Math.PI):0;return{velocity:{mean:e.getMean(),max:e.getMax(),stddev:e.getStddev()},acceleration:{mean:t.getMean(),max:t.getMax()},straightness:s,jitter:r,sampleCount:e.getCount()}}computeKeystrokeDynamics(){const e=this.interKeyStats,t=e.getMean(),s=e.getStddev(),i=t>0&&e.getCount()>2?Math.max(0,Math.min(1,1-s/t)):0;return{interKeyDelay:{mean:t,stddev:s,min:e.getMin(),max:e.getMax()},dwellTime:{mean:this.dwellTimeStats.getMean(),stddev:this.dwellTimeStats.getStddev()},flightTime:{mean:this.flightTimeStats.getMean(),stddev:this.flightTimeStats.getStddev()},rhythmConsistency:i,sampleCount:e.getCount()}}computeScrollBehavior(){const e=document.documentElement.scrollHeight-window.innerHeight;return{maxDepthPercent:e>0?Math.min(100,this.maxScrollDepthPx/e*100):0,avgSpeed:this.scrollSpeedStats.getMean(),directionChanges:this.scrollDirectionChanges,totalDistance:this.scrollTotalDistance,smoothness:this.totalScrollSamples>0?this.smoothScrollCount/this.totalScrollSamples:0,sampleCount:this.totalScrollSamples}}computeSessionBehavior(){const e=Date.now()-c,t=this.interactionTimestamps.filter(t=>t>=e).length;return{idle:{count:this.idleCount,totalMs:this.totalIdleMs,maxMs:this.maxIdleMs},focus:{tabSwitchCount:this.tabSwitchCount,totalHiddenMs:this.totalHiddenMs},tempo:{interactionsPerMinute:t,burstCount:this.burstCount}}}computeTouchDynamics(){return{avgTouchRadius:this.touchRadiusStats.getMean(),avgPressure:this.touchPressureStats.getMean(),swipeVelocity:{mean:this.swipeVelocityStats.getMean(),max:this.swipeVelocityStats.getMax()},tapCount:this.tapCount,swipeCount:this.swipeCount,sampleCount:this.touchRadiusStats.getCount()}}computeBiometricScore(e){let t=0;if(e<5e3)return 0;const s=this.computeMouseDynamics();let i=0;s.sampleCount>10&&(s.straightness>.95&&(i+=.4),s.jitter<.05&&(i+=.3),s.velocity.stddev<.01&&s.velocity.mean>0&&(i+=.3)),t+=.25*Math.min(1,i);const r=this.computeKeystrokeDynamics();let n=0;r.sampleCount>10&&(r.rhythmConsistency>.9&&(n+=.5),r.dwellTime.mean<10&&r.dwellTime.mean>0&&(n+=.5)),t+=.2*Math.min(1,n);const a=this.computeScrollBehavior();let o=0;a.sampleCount>3&&(a.smoothness<.2&&(o+=.5),0===a.directionChanges&&a.totalDistance>500&&(o+=.5)),t+=.15*Math.min(1,o);const l=this.computeSessionBehavior();let c=0;if(e>6e4&&(0===l.idle.count&&(c+=.3),0===l.focus.tabSwitchCount&&(c+=.2)),l.tempo.interactionsPerMinute>200?c+=.5:l.tempo.interactionsPerMinute<1&&e>3e4&&(c+=.3),t+=.15*Math.min(1,c),this.touchSupported){const e=this.computeTouchDynamics();let s=0;e.sampleCount>5&&(0===e.avgPressure&&(s+=.5),0===e.avgTouchRadius&&(s+=.5)),t+=.1*Math.min(1,s)}let h=0;return e>3e4&&(0!==s.sampleCount||this.touchSupported||(h+=.33),0===r.sampleCount&&(h+=.33),0===a.sampleCount&&(h+=.34)),t+=.15*Math.min(1,h),Math.min(1,t)}computeBiometricFlags(e){const t=this.computeMouseDynamics(),s=this.computeKeystrokeDynamics(),i=this.computeScrollBehavior(),r=this.computeSessionBehavior();return{perfectMouseLinearity:t.sampleCount>10&&t.straightness>.95,zeroMouseJitter:t.sampleCount>10&&t.jitter<.05,uniformKeystrokeTiming:s.sampleCount>10&&s.rhythmConsistency>.9,noKeystrokeData:0===s.sampleCount&&e>3e4,programmaticScrolling:i.sampleCount>3&&i.smoothness<.2,noIdlePeriods:0===r.idle.count&&e>6e4,noTouchPressure:this.touchSupported&&this.touchPressureStats.getCount()>5&&0===this.touchPressureStats.getMean(),abnormalTempo:r.tempo.interactionsPerMinute>200||r.tempo.interactionsPerMinute<1&&e>3e4}}attachListeners(){this.listenersAttached||(this.listenersAttached=!0,this.boundMouseHandler=l(e=>{this.onMouseSample(e.clientX,e.clientY,performance.now()),this.recordInteraction()},50),window.addEventListener("mousemove",this.boundMouseHandler,{passive:!0}),this.boundKeydownHandler=e=>{if(e.repeat)return;const t=performance.now();if(this.lastKeydownTs>0){const e=t-this.lastKeydownTs;e<2e3&&this.interKeyStats.push(e)}if(this.lastKeydownTs=t,this.lastKeyupTs>0){const e=t-this.lastKeyupTs;e<2e3&&this.flightTimeStats.push(e)}this.pendingKeydowns.set(e.code,t),this.recordInteraction()},document.addEventListener("keydown",this.boundKeydownHandler,{passive:!0}),this.boundKeyupHandler=e=>{const t=performance.now();this.lastKeyupTs=t;const s=this.pendingKeydowns.get(e.code);if(void 0!==s){const i=t-s;this.dwellTimeStats.push(i),this.pendingKeydowns.delete(e.code)}},document.addEventListener("keyup",this.boundKeyupHandler,{passive:!0}),this.boundScrollHandler=l(()=>{this.onScrollSample(),this.recordInteraction()},200),window.addEventListener("scroll",this.boundScrollHandler,{passive:!0}),this.boundVisibilityHandler=()=>{const e=Date.now();document.hidden?this.lastHiddenTs=e:(null!==this.lastHiddenTs&&(this.totalHiddenMs+=e-this.lastHiddenTs,this.lastHiddenTs=null),this.tabSwitchCount++)},document.addEventListener("visibilitychange",this.boundVisibilityHandler),this.touchSupported&&(this.boundTouchStartHandler=e=>{const t=e.touches[0];t&&(this.activeTouchStart={x:t.clientX,y:t.clientY,t:performance.now()},this.touchRadiusStats.push((t.radiusX+t.radiusY)/2),t.force>0&&this.touchPressureStats.push(t.force),this.recordInteraction())},document.addEventListener("touchstart",this.boundTouchStartHandler,{passive:!0}),this.boundTouchMoveHandler=l(e=>{const t=e.touches[0];t&&(this.touchRadiusStats.push((t.radiusX+t.radiusY)/2),t.force>0&&this.touchPressureStats.push(t.force))},100),document.addEventListener("touchmove",this.boundTouchMoveHandler,{passive:!0}),this.boundTouchEndHandler=e=>{if(!this.activeTouchStart)return;const t=e.changedTouches[0];if(!t)return;const s=performance.now()-this.activeTouchStart.t,i=t.clientX-this.activeTouchStart.x,r=t.clientY-this.activeTouchStart.y,n=Math.sqrt(i*i+r*r);if(s<300&&n<50)this.tapCount++;else if(n>=50){this.swipeCount++;const e=n/s;this.swipeVelocityStats.push(e)}this.activeTouchStart=null},document.addEventListener("touchend",this.boundTouchEndHandler,{passive:!0})))}detachListeners(){this.boundMouseHandler&&window.removeEventListener("mousemove",this.boundMouseHandler),this.boundKeydownHandler&&document.removeEventListener("keydown",this.boundKeydownHandler),this.boundKeyupHandler&&document.removeEventListener("keyup",this.boundKeyupHandler),this.boundScrollHandler&&window.removeEventListener("scroll",this.boundScrollHandler),this.boundVisibilityHandler&&document.removeEventListener("visibilitychange",this.boundVisibilityHandler),this.boundTouchStartHandler&&document.removeEventListener("touchstart",this.boundTouchStartHandler),this.boundTouchMoveHandler&&document.removeEventListener("touchmove",this.boundTouchMoveHandler),this.boundTouchEndHandler&&document.removeEventListener("touchend",this.boundTouchEndHandler),this.listenersAttached=!1}onMouseSample(e,t,s){if(this.lastMouseSample){const i=s-this.lastMouseSample.t;if(i<=0)return;const r=e-this.lastMouseSample.x,n=t-this.lastMouseSample.y,a=Math.sqrt(r*r+n*n),o=a/i;this.mouseVelocityStats.push(o);const l=Math.abs(o-this.lastMouseSample.velocity)/i;this.mouseAccelStats.push(l);if(s-this.lastMouseSampleTs>200&&this.currentPathLength>0){const s=this.lastMouseSample.x-this.currentPathStartX,i=this.lastMouseSample.y-this.currentPathStartY,r=Math.sqrt(s*s+i*i);this.pushPathSegment({displacement:r,pathLength:this.currentPathLength}),this.currentPathLength=0,this.currentPathStartX=e,this.currentPathStartY=t}else 0===this.currentPathLength&&(this.currentPathStartX=this.lastMouseSample.x,this.currentPathStartY=this.lastMouseSample.y);if(this.currentPathLength+=a,a>1){const e=Math.atan2(n,r);if(null!==this.lastAngle){let t=Math.abs(e-this.lastAngle);t>Math.PI&&(t=2*Math.PI-t),this.angularVarianceStats.push(t)}this.lastAngle=e}this.lastMouseSample={x:e,y:t,t:s,velocity:o}}else this.lastMouseSample={x:e,y:t,t:s,velocity:0},this.currentPathStartX=e,this.currentPathStartY=t;this.lastMouseSampleTs=s}pushPathSegment(e){this.pathSegments.length>=20&&this.pathSegments.shift(),this.pathSegments.push(e)}onScrollSample(){const e=window.scrollY,t=performance.now();if(e>this.maxScrollDepthPx&&(this.maxScrollDepthPx=e),this.lastScrollTs>0){const s=t-this.lastScrollTs,i=e-this.lastScrollY,r=Math.abs(i);if(s>0&&r>0){const e=r/s;this.scrollSpeedStats.push(e),this.scrollTotalDistance+=r,this.totalScrollSamples++,r<100&&this.smoothScrollCount++;const t=i>0?1:-1;0!==this.lastScrollDirection&&t!==this.lastScrollDirection&&this.scrollDirectionChanges++,this.lastScrollDirection=t}}this.lastScrollY=e,this.lastScrollTs=t}recordInteraction(){const e=Date.now();if(this.lastAnyInteractionTs=e,this.interactionTimestamps.push(e),null!==this.currentIdleStart){const t=e-this.currentIdleStart;this.totalIdleMs+=t,t>this.maxIdleMs&&(this.maxIdleMs=t),this.idleCount++,this.currentIdleStart=null}const t=e-3e3;if(5===this.interactionTimestamps.filter(e=>e>=t).length&&this.burstCount++,this.interactionTimestamps.length>500){const t=e-c;this.interactionTimestamps=this.interactionTimestamps.filter(e=>e>=t)}}startIdleCheck(){this.idleCheckIntervalId=setInterval(()=>{Date.now()-this.lastAnyInteractionTs>5e3&&null===this.currentIdleStart&&(this.currentIdleStart=this.lastAnyInteractionTs)},5e3)}stopIdleCheck(){null!==this.idleCheckIntervalId&&(clearInterval(this.idleCheckIntervalId),this.idleCheckIntervalId=null)}}class u{static getParser(){return u.parser||(u.parser=s.getParser(navigator.userAgent)),u.parser}static getBrowser(){const e=u.getParser().getBrowser();return{name:e.name??"Unknown",version:e.version??"Unknown"}}static getOS(){const e=u.getParser().getOS();return{name:e.name??"Unknown",version:e.version??"Unknown"}}static getPlatform(){const e=u.getParser().getPlatformType();return"mobile"===e?"mobile":"tablet"===e?"tablet":"desktop"}static getLanguage(){return navigator.language||"unknown"}static getTimezone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return"unknown"}}static isSuspiciousBrowser(){const e=navigator.userAgent.toLowerCase();return["bot","crawler","spider","headless","phantom","selenium","puppeteer","playwright","webdriver","electron"].some(t=>e.includes(t))}static getHeadlessIndicatorCount(){let e=0;navigator.webdriver&&e++,navigator.languages&&0!==navigator.languages.length||e++,navigator.plugins&&0!==navigator.plugins.length||e++,u.isSuspiciousBrowser()&&e++;try{const t=document.createElement("canvas");t.getContext("webgl")||t.getContext("experimental-webgl")||e++}catch{e++}return 0!==screen.width&&0!==screen.height||e++,e}static reset(){u.parser=null}}u.parser=null;class d extends a{constructor(){super(...arguments),this.fingerprintPromise=null,this.resolvedFingerprint=null,this.initTimestamp=Date.now()}collect(){const t=new Map;return this.collectWithCache(t,e.TrackerVariableType.DEVICE_FINGERPRINT,e.CacheStrategy.HEAVY,()=>this.getFingerprint()),this.collectWithCache(t,e.TrackerVariableType.SCREEN_RESOLUTION,e.CacheStrategy.HEAVY,()=>`${screen.width}x${screen.height}`),this.collectWithCache(t,e.TrackerVariableType.USER_AGENT,e.CacheStrategy.HEAVY,()=>navigator.userAgent),this.collectWithCache(t,e.TrackerVariableType.BROWSER,e.CacheStrategy.HEAVY,()=>{const e=u.getBrowser();return`${e.name} ${e.version}`}),this.collectWithCache(t,e.TrackerVariableType.OS,e.CacheStrategy.HEAVY,()=>{const e=u.getOS();return`${e.name} ${e.version}`}),this.collectWithCache(t,e.TrackerVariableType.LANGUAGE,e.CacheStrategy.MEDIUM,()=>u.getLanguage()),this.collectWithCache(t,e.TrackerVariableType.TIMEZONE,e.CacheStrategy.MEDIUM,()=>u.getTimezone()),this.collectWithCache(t,e.TrackerVariableType.PLATFORM,e.CacheStrategy.HEAVY,()=>u.getPlatform()),this.collectWithCache(t,e.TrackerVariableType.SUSPICIOUS_BROWSER,e.CacheStrategy.SLOW,()=>u.isSuspiciousBrowser()),this.collectWithCache(t,e.TrackerVariableType.HEADLESS_INDICATOR_COUNT,e.CacheStrategy.SLOW,()=>u.getHeadlessIndicatorCount()),this.collectWithCache(t,e.TrackerVariableType.THEME_MODE,e.CacheStrategy.MEDIUM,()=>window.matchMedia?.("(prefers-color-scheme: dark)").matches?"DARK":"LIGHT"),this.collectIfNeeded(t,e.TrackerVariableType.SDK_VERSION,()=>"2.0.0"),this.collectIfNeeded(t,e.TrackerVariableType.TIMESTAMP,()=>Date.now()),this.collectIfNeeded(t,e.TrackerVariableType.ELAPSED_TIME,()=>Date.now()-this.initTimestamp),t}getFingerprint(){return this.resolvedFingerprint?this.resolvedFingerprint:(this.fingerprintPromise||(this.fingerprintPromise=t.load().then(e=>e.get()).then(e=>(this.resolvedFingerprint=e.visitorId,e.visitorId)).catch(()=>(this.resolvedFingerprint="unavailable","unavailable"))),this.fingerprintPromise)}async initFingerprint(){const t=this.getFingerprint();if(t instanceof Promise){const s=await t;return this.resolvedFingerprint=s,this.invalidateCache(e.TrackerVariableType.DEVICE_FINGERPRINT),s}return t}}class g{static setEnabled(e){g.enabled=e}static debug(...e){g.enabled&&console.debug(g.PREFIX,...e)}static info(...e){g.enabled&&console.info(g.PREFIX,...e)}static warn(...e){g.enabled&&console.warn(g.PREFIX,...e)}static error(...e){g.enabled&&console.error(g.PREFIX,...e)}}g.PREFIX="[tracker]",g.enabled=!1;class T extends Error{constructor(e,t){super(e),this.status=t,this.name="HttpError"}}class p{static async get(e,t){return p.request(e,{method:"GET",...t})}static async post(e,t,s){return p.request(e,{method:"POST",body:t,...s})}static async request(e,t){const s=new AbortController,i=t.timeout??3e4,r=setTimeout(()=>s.abort(),i);try{const i={...t.headers};void 0!==t.body&&(i["Content-Type"]="application/json");const r={method:t.method,headers:i,signal:s.signal};void 0!==t.body&&(r.body=JSON.stringify(t.body));const n=await fetch(e,r);if(!n.ok)throw new T(`HTTP Error ${n.status}: ${n.statusText}`,n.status);return(n.headers.get("content-type")??"").includes("application/json")?await n.json():await n.text()}catch(e){if(e instanceof T)throw e;if(e instanceof DOMException&&"AbortError"===e.name)throw new T(`Request timeout after ${i}ms`,408);throw g.error("HTTP request failed:",e),e}finally{clearTimeout(r)}}}class S extends a{constructor(){super(...arguments),this.resolvedIp=null}collect(){const t=new Map;return this.collectWithCache(t,e.TrackerVariableType.IP_ADDRESS,e.CacheStrategy.SLOW,()=>this.resolvedIp??"unknown"),this.collectIfNeeded(t,e.TrackerVariableType.REACHABLE,()=>navigator.onLine),t}async fetchIp(){try{const e=await p.get("https://api.ipify.org?format=json",{timeout:5e3});return this.resolvedIp=e.ip,this.resolvedIp}catch(e){return g.warn("Failed to fetch IP address:",e),this.resolvedIp="unknown",this.resolvedIp}}}class E extends n{constructor(){super(...arguments),this.viewDurationMs=0,this.timerIntervalId=null}collect(){const t=new Map;return this.collectIfNeeded(t,e.TrackerVariableType.VIEW_DURATION,()=>this.viewDurationMs),t}resolveVariable(t,s){try{switch(t.type){case e.TrackerVariableType.URL:return this.resolveUrl(t.option);case e.TrackerVariableType.ELEMENT:return this.resolveElement(t.option);case e.TrackerVariableType.COOKIE:return this.resolveCookie(t.option);case e.TrackerVariableType.JAVASCRIPT:return this.resolveJavascript(t.option);case e.TrackerVariableType.EVENT:return this.resolveEvent(t,s);default:return null}}catch(e){return g.warn(`Failed to resolve variable ${t.name}:`,e),null}}startTimer(){null===this.timerIntervalId&&(this.viewDurationMs=0,this.timerIntervalId=setInterval(()=>{this.viewDurationMs+=100},100))}stopTimer(){null!==this.timerIntervalId&&(clearInterval(this.timerIntervalId),this.timerIntervalId=null)}resetTimer(){this.viewDurationMs=0}resolveUrl(e){const t=e?.selection??"full",s=new URL(window.location.href);switch(t){case"host":return s.hostname;case"port":return s.port;case"path":return s.pathname;case"query":return s.search;case"fragment":return s.hash;case"protocol":return s.protocol;default:return s.href}}resolveElement(e){if(!e?.cssSelector)return null;const t=document.querySelector(e.cssSelector);return t?e.attribute?t.getAttribute(e.attribute):t.textContent:null}resolveCookie(e){if(!e?.cookieName)return null;const t=document.cookie.split(";");for(const s of t){const[t,...i]=s.split("=");if(t.trim()===e.cookieName){const t=i.join("=").trim();return e.decodeUrlCookie?decodeURIComponent(t):t}}return null}resolveJavascript(e){if(!e?.code)return null;try{return new Function(`return (${e.code})`)()}catch(e){return g.warn("JavaScript variable evaluation failed:",e),null}}resolveEvent(e,t){return t?t[e.name]??null:null}resolveConfigVariables(t,s){const i=new Map;for(const r of t.variables){const t=r.type;if(t===e.TrackerVariableType.URL||t===e.TrackerVariableType.ELEMENT||t===e.TrackerVariableType.COOKIE||t===e.TrackerVariableType.JAVASCRIPT||t===e.TrackerVariableType.EVENT){const e=this.resolveVariable(r,s);null!=e&&i.set(t,e)}}return i}destroy(){this.stopTimer()}}class m extends a{constructor(){super(...arguments),this.suspiciousFlags={unnaturalMouseMoves:!1,bigClipboardPaste:!1,lowFPSDetected:!1,delayedClickDetected:!1},this.lastMouseMoveTs=Date.now(),this.mouseMoveCounterWindow=0,this.clickCounterWindow=0,this.pageViewTimestamps=[],this.pasteCharCount=0,this.typedCharCount=0,this.lastPageViewTs=null,this.lastFormInteractionStart=null,this.lastFormFieldCount=0,this.lastFormDurationMs=0,this.formFillAnomaly=!1,this.fastPageTransition=!1,this.redirectSource=null,this.lastUrl=null,this.lastMouseX=null,this.lastMouseY=null,this.unnaturalMoveCount=0,this.lowFPSCount=0,this.lastFrameTs=0,this.fpsRafId=null,this.listenersAttached=!1,this.baseline=null,this.envChangeFlags=null,this.envChangeScore=0,this.tenant="",this.deviceId=""}collect(){const t=new Map,s=this.getAggregates();this.collectWithCache(t,e.TrackerVariableType.SUSPICIOUS_ACTIVITY,e.CacheStrategy.FAST,()=>({...this.suspiciousFlags})),this.collectIfNeeded(t,e.TrackerVariableType.NO_MOUSE_MOVE_DURATION_MS,()=>s.noMouseMoveDurationMs),this.collectIfNeeded(t,e.TrackerVariableType.CLICK_ONLY_PATTERN,()=>s.clickOnlyPattern),this.collectIfNeeded(t,e.TrackerVariableType.FAST_PAGE_TRANSITION,()=>s.fastPageTransition),this.collectIfNeeded(t,e.TrackerVariableType.NAVIGATION_VELOCITY,()=>s.navigationVelocity),this.collectIfNeeded(t,e.TrackerVariableType.FORM_FILL_ANOMALY,()=>s.formFillAnomaly),this.collectIfNeeded(t,e.TrackerVariableType.FORM_FILL_DURATION_MS,()=>s.formFillDurationMs),this.collectIfNeeded(t,e.TrackerVariableType.INPUT_PASTE_RATIO,()=>s.inputPasteRatio),this.collectIfNeeded(t,e.TrackerVariableType.REDIRECT_DETECTED,()=>s.redirectSource),this.collectIfNeeded(t,e.TrackerVariableType.ENVIRONMENT_CHANGE_FLAGS,()=>this.envChangeFlags?{...this.envChangeFlags}:null),this.collectIfNeeded(t,e.TrackerVariableType.ENVIRONMENT_CHANGE_SCORE,()=>this.envChangeScore);const i=u.getHeadlessIndicatorCount(),r=this.calcRemoteAccessScore(i);return this.collectIfNeeded(t,e.TrackerVariableType.REMOTE_ACCESS_SCORE,()=>r),this.collectIfNeeded(t,e.TrackerVariableType.REMOTE_ACCESS_FLAGS,()=>({...this.suspiciousFlags,clickOnlyPattern:s.clickOnlyPattern,headlessIndicatorCount:i,environmentMismatch:this.envChangeScore>0})),t}init(e,t){this.tenant=e,this.deviceId=t,this.attachListeners(),this.ensureBaseline()}registerPageView(){const e=Date.now();this.fastPageTransition=null!==this.lastPageViewTs&&e-this.lastPageViewTs<1500,this.lastPageViewTs=e,this.pageViewTimestamps.push(e);const t=e-3e5;this.pageViewTimestamps=this.pageViewTimestamps.filter(e=>e>=t),this.detectRedirect()}getRemoteAccessScore(){return this.calcRemoteAccessScore(u.getHeadlessIndicatorCount())}resetRollingWindow(){this.mouseMoveCounterWindow=0,this.clickCounterWindow=0}updateEnvironmentDiff(){if(!this.baseline)return;const e=this.getCurrentEnvironment();this.envChangeFlags=this.diffEnvironment(this.baseline,e),this.envChangeScore=this.calcEnvironmentChangeScore(this.envChangeFlags)}getAggregates(){const e=Date.now()-this.lastMouseMoveTs,t=e>1e4&&this.clickCounterWindow>=3&&this.mouseMoveCounterWindow<2,s=this.pageViewTimestamps.length/5,i=this.computePasteRatio();return{noMouseMoveDurationMs:e,clickOnlyPattern:t,fastPageTransition:this.fastPageTransition,navigationVelocity:s,formFillAnomaly:this.formFillAnomaly,formFillDurationMs:this.lastFormDurationMs,inputPasteRatio:i,redirectSource:this.redirectSource}}computePasteRatio(){const e=this.pasteCharCount+this.typedCharCount;return 0===e?0:this.pasteCharCount/e}calcRemoteAccessScore(e){const t=this.getAggregates();let s=0;return this.suspiciousFlags.unnaturalMouseMoves&&(s+=.2),this.suspiciousFlags.bigClipboardPaste&&(s+=.15),this.suspiciousFlags.lowFPSDetected&&(s+=.15),this.suspiciousFlags.delayedClickDetected&&(s+=.1),t.clickOnlyPattern&&(s+=.15),e>0&&(s+=Math.min(.05*e,.25)),this.envChangeScore>0&&(s+=.2),Math.min(1,s)}attachListeners(){if(this.listenersAttached)return;this.listenersAttached=!0,window.addEventListener("mousemove",e=>{if(this.lastMouseMoveTs=Date.now(),this.mouseMoveCounterWindow++,null!==this.lastMouseX&&null!==this.lastMouseY){const t=Math.abs(e.clientX-this.lastMouseX),s=Math.abs(e.clientY-this.lastMouseY);(t>100||s>100||0===t&&0===s)&&this.unnaturalMoveCount++,this.unnaturalMoveCount>5&&(this.suspiciousFlags.unnaturalMouseMoves=!0)}this.lastMouseX=e.clientX,this.lastMouseY=e.clientY}),document.addEventListener("click",()=>{this.clickCounterWindow++;const e=performance.now();setTimeout(()=>{performance.now()-e>150&&(this.suspiciousFlags.delayedClickDetected=!0)},0)}),document.addEventListener("paste",e=>{const t=e,s=t.clipboardData?.getData("text")??"";this.pasteCharCount+=s.length,s.length>5e3&&(this.suspiciousFlags.bigClipboardPaste=!0)}),document.addEventListener("keydown",e=>{1===e.key.length&&this.typedCharCount++}),document.addEventListener("focusin",e=>{const t=e.target?.closest("form");t&&null===this.lastFormInteractionStart&&(this.lastFormInteractionStart=Date.now(),this.lastFormFieldCount=t.querySelectorAll("input,textarea,select").length)}),document.addEventListener("submit",e=>{e.target&&(this.lastFormInteractionStart&&(this.lastFormDurationMs=Date.now()-this.lastFormInteractionStart,this.formFillAnomaly=this.detectFormAnomaly(this.lastFormDurationMs,this.lastFormFieldCount)),this.lastFormInteractionStart=null)}),this.lastFrameTs=performance.now();const e=()=>{const t=performance.now();t-this.lastFrameTs>250&&this.lowFPSCount++,this.lowFPSCount>10&&(this.suspiciousFlags.lowFPSDetected=!0),this.lastFrameTs=t,this.fpsRafId=requestAnimationFrame(e)};this.fpsRafId=requestAnimationFrame(e)}detectFormAnomaly(e,t){if(0===t)return!1;return e<Math.min(120*t,1500)}detectRedirect(){try{const e=window.location.href;if(this.lastUrl&&this.lastUrl!==e)if(document.referrer&&document.referrer!==e)try{const t=new URL(document.referrer),s=new URL(e);this.redirectSource=t.origin!==s.origin?t.origin:"internal"}catch{this.redirectSource=document.referrer}else document.referrer?this.redirectSource=null:this.redirectSource="direct";else this.redirectSource=null;this.lastUrl=e}catch{this.redirectSource=null}}ensureBaseline(){try{const e=`formica_baseline_${this.tenant}_${this.deviceId}`,t=localStorage.getItem(e);if(t)this.baseline=JSON.parse(t);else{const t=this.getCurrentEnvironment();this.baseline={...t,firstSeen:Date.now()},localStorage.setItem(e,JSON.stringify(this.baseline))}this.updateEnvironmentDiff()}catch{g.warn("localStorage unavailable for environment baseline")}}getCurrentEnvironment(){const e=u.getBrowser(),t=u.getOS();return{language:u.getLanguage(),timezone:u.getTimezone(),os:`${t.name} ${t.version}`,browser:`${e.name} ${e.version}`}}diffEnvironment(e,t){return{languageChanged:e.language!==t.language,timezoneChanged:e.timezone!==t.timezone,osChanged:e.os!==t.os,browserChanged:e.browser!==t.browser}}calcEnvironmentChangeScore(e){let t=0;return e.languageChanged&&(t+=.25),e.timezoneChanged&&(t+=.25),e.osChanged&&(t+=.2),e.browserChanged&&(t+=.3),Math.min(1,t)}destroy(){null!==this.fpsRafId&&(cancelAnimationFrame(this.fpsRafId),this.fpsRafId=null)}}var v;e.TrackerEventType=void 0,(v=e.TrackerEventType||(e.TrackerEventType={})).PAGE_VIEW="PAGE_VIEW",v.CLICK="CLICK",v.SCROLL="SCROLL",v.CUSTOM="CUSTOM",v.RISK_ALERT="RISK_ALERT",v.VISIBILITY_CHANGE="VISIBILITY_CHANGE",v.IDLE="IDLE",v.FORM_INTERACTION="FORM_INTERACTION",v.COPY_PASTE="COPY_PASTE";class C{constructor(e,t=1e3){this.started=!1,this.boundHandler=null,this.lastTriggerTs=0,this.trigger=e,this.cooldownMs=t}start(){this.started||(this.started=!0,this.boundHandler=()=>{const t=Date.now();t-this.lastTriggerTs>=this.cooldownMs&&(this.lastTriggerTs=t,this.trigger(e.TrackerEventType.CLICK))},document.addEventListener("click",this.boundHandler))}stop(){this.started&&(this.started=!1,this.boundHandler&&(document.removeEventListener("click",this.boundHandler),this.boundHandler=null))}getTypes(){return[e.TrackerEventType.CLICK]}getVariableTypes(){return[e.TrackerVariableType.ELEMENT]}}class I{constructor(e,t=2e3){this.started=!1,this.boundHandler=null,this.lastTriggerTs=0,this.trigger=e,this.cooldownMs=t}start(){this.started||(this.started=!0,this.boundHandler=()=>{const t=Date.now();t-this.lastTriggerTs>=this.cooldownMs&&(this.lastTriggerTs=t,this.trigger(e.TrackerEventType.COPY_PASTE))},document.addEventListener("paste",this.boundHandler))}stop(){this.started&&(this.started=!1,this.boundHandler&&(document.removeEventListener("paste",this.boundHandler),this.boundHandler=null))}getTypes(){return[e.TrackerEventType.COPY_PASTE]}getVariableTypes(){return[e.TrackerVariableType.INPUT_PASTE_RATIO]}}class y{constructor(e,t=3e3){this.started=!1,this.boundFocusHandler=null,this.boundSubmitHandler=null,this.lastTriggerTs=0,this.trigger=e,this.cooldownMs=t}start(){this.started||(this.started=!0,this.boundFocusHandler=e=>{const t=e.target;if(!t)return;(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&this.triggerWithCooldown()},this.boundSubmitHandler=()=>{this.triggerWithCooldown()},document.addEventListener("focusin",this.boundFocusHandler,{passive:!0}),document.addEventListener("submit",this.boundSubmitHandler,{passive:!0}))}stop(){this.started&&(this.started=!1,this.boundFocusHandler&&(document.removeEventListener("focusin",this.boundFocusHandler),this.boundFocusHandler=null),this.boundSubmitHandler&&(document.removeEventListener("submit",this.boundSubmitHandler),this.boundSubmitHandler=null))}getTypes(){return[e.TrackerEventType.FORM_INTERACTION]}getVariableTypes(){return[e.TrackerVariableType.FORM_FILL_ANOMALY,e.TrackerVariableType.FORM_FILL_DURATION_MS]}triggerWithCooldown(){const t=Date.now();t-this.lastTriggerTs>=this.cooldownMs&&(this.lastTriggerTs=t,this.trigger(e.TrackerEventType.FORM_INTERACTION))}}const f=["mousemove","keydown","scroll","touchstart","click"];class b{constructor(e,t=3e4,s=6e4){this.started=!1,this.idleTimer=null,this.boundActivityHandler=null,this.lastTriggerTs=0,this.trigger=e,this.idleThresholdMs=t,this.cooldownMs=s}start(){if(!this.started){this.started=!0,this.boundActivityHandler=()=>this.resetTimer();for(const e of f)document.addEventListener(e,this.boundActivityHandler,{passive:!0});this.resetTimer()}}stop(){if(this.started&&(this.started=!1,this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.boundActivityHandler)){for(const e of f)document.removeEventListener(e,this.boundActivityHandler);this.boundActivityHandler=null}}getTypes(){return[e.TrackerEventType.IDLE]}getVariableTypes(){return[]}resetTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{const t=Date.now();t-this.lastTriggerTs>=this.cooldownMs&&(this.lastTriggerTs=t,this.trigger(e.TrackerEventType.IDLE))},this.idleThresholdMs)}}class A{constructor(e){this.started=!1,this.boundPopstate=null,this.origPushState=null,this.origReplaceState=null,this.trigger=e}start(){if(this.started)return;this.started=!0,this.boundPopstate=()=>this.trigger(e.TrackerEventType.PAGE_VIEW),window.addEventListener("popstate",this.boundPopstate),this.origPushState=history.pushState.bind(history),this.origReplaceState=history.replaceState.bind(history);const t=this.origPushState,s=this.origReplaceState,i=()=>this.trigger(e.TrackerEventType.PAGE_VIEW);history.pushState=function(...e){t&&t(...e),i()},history.replaceState=function(...e){s&&s(...e),i()}}stop(){this.started&&(this.started=!1,this.boundPopstate&&(window.removeEventListener("popstate",this.boundPopstate),this.boundPopstate=null),this.origPushState&&(history.pushState=this.origPushState,this.origPushState=null),this.origReplaceState&&(history.replaceState=this.origReplaceState,this.origReplaceState=null))}getTypes(){return[e.TrackerEventType.PAGE_VIEW]}getVariableTypes(){return[e.TrackerVariableType.URL,e.TrackerVariableType.VIEW_DURATION]}}class _{constructor(e,t=800,s=2e3){this.started=!1,this.boundHandler=null,this.debounceTimer=null,this.lastTriggerTs=0,this.trigger=e,this.debounceMs=t,this.cooldownMs=s}start(){this.started||(this.started=!0,this.boundHandler=()=>{this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{const t=Date.now();t-this.lastTriggerTs>=this.cooldownMs&&(this.lastTriggerTs=t,this.trigger(e.TrackerEventType.SCROLL))},this.debounceMs)},window.addEventListener("scroll",this.boundHandler,{passive:!0}))}stop(){this.started&&(this.started=!1,this.boundHandler&&(window.removeEventListener("scroll",this.boundHandler),this.boundHandler=null),this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null))}getTypes(){return[e.TrackerEventType.SCROLL]}getVariableTypes(){return[]}}class w{constructor(e){this.started=!1,this.boundHandler=null,this.trigger=e}start(){this.started||(this.started=!0,this.boundHandler=()=>this.trigger(e.TrackerEventType.VISIBILITY_CHANGE),document.addEventListener("visibilitychange",this.boundHandler))}stop(){this.started&&(this.started=!1,this.boundHandler&&(document.removeEventListener("visibilitychange",this.boundHandler),this.boundHandler=null))}getTypes(){return[e.TrackerEventType.VISIBILITY_CHANGE]}getVariableTypes(){return[e.TrackerVariableType.VIEW_DURATION]}}var O;e.FilterOperator=void 0,(O=e.FilterOperator||(e.FilterOperator={})).IS_EQUALS="isEquals",O.IS_EQUALS_IGNORE_CASE="isEqualsIgnoreCase",O.NOT_EQUALS="notEquals",O.NOT_EQUALS_IGNORE_CASE="notEqualsIgnoreCase",O.IS_CONTAINS="isContains",O.IS_CONTAINS_IGNORE_CASE="isContainsIgnoreCase",O.NOT_CONTAINS="notContains",O.NOT_CONTAINS_IGNORE_CASE="notContainsIgnoreCase",O.IS_STARTS_WITH="isStartsWith",O.IS_STARTS_WITH_IGNORE_CASE="isStartsWithIgnoreCase",O.NOT_STARTS_WITH="notStartsWith",O.NOT_STARTS_WITH_IGNORE_CASE="notStartsWithIgnoreCase",O.IS_ENDS_WITH="isEndsWith",O.IS_ENDS_WITH_IGNORE_CASE="isEndsWithIgnoreCase",O.NOT_ENDS_WITH="notEndsWith",O.NOT_ENDS_WITH_IGNORE_CASE="notEndsWithIgnoreCase",O.IS_REGEX_MATCH="isRegexMatch",O.NOT_REGEX_MATCH="notRegexMatch",O.LESS_THAN="lessThan",O.LESS_THAN_OR_EQUALS="lessThanOrEquals",O.GREATER_THAN="greaterThan",O.GREATER_THAN_OR_EQUALS="greaterThanOrEquals";class M{static passesTriggerFilters(e,t){return!e||0===e.length||e.every(e=>{const s=t.get(e.field);return M.applyFilter(s,e.operator,e.value)})}static applyFilter(e,t,s){if(null==e)return M.isNegationOperator(t);if(Array.isArray(e)){const i=e.some(e=>M.applySingleFilter(String(e),t,s));return M.isNegationOperator(t)?!i:i}return M.applySingleFilter(String(e),t,s)}static applySingleFilter(t,s,i){switch(s){case e.FilterOperator.IS_EQUALS:return t===i;case e.FilterOperator.IS_EQUALS_IGNORE_CASE:return t.toLowerCase()===i.toLowerCase();case e.FilterOperator.NOT_EQUALS:return t!==i;case e.FilterOperator.NOT_EQUALS_IGNORE_CASE:return t.toLowerCase()!==i.toLowerCase();case e.FilterOperator.IS_CONTAINS:return t.includes(i);case e.FilterOperator.IS_CONTAINS_IGNORE_CASE:return t.toLowerCase().includes(i.toLowerCase());case e.FilterOperator.NOT_CONTAINS:return!t.includes(i);case e.FilterOperator.NOT_CONTAINS_IGNORE_CASE:return!t.toLowerCase().includes(i.toLowerCase());case e.FilterOperator.IS_STARTS_WITH:return t.startsWith(i);case e.FilterOperator.IS_STARTS_WITH_IGNORE_CASE:return t.toLowerCase().startsWith(i.toLowerCase());case e.FilterOperator.NOT_STARTS_WITH:return!t.startsWith(i);case e.FilterOperator.NOT_STARTS_WITH_IGNORE_CASE:return!t.toLowerCase().startsWith(i.toLowerCase());case e.FilterOperator.IS_ENDS_WITH:return t.endsWith(i);case e.FilterOperator.IS_ENDS_WITH_IGNORE_CASE:return t.toLowerCase().endsWith(i.toLowerCase());case e.FilterOperator.NOT_ENDS_WITH:return!t.endsWith(i);case e.FilterOperator.NOT_ENDS_WITH_IGNORE_CASE:return!t.toLowerCase().endsWith(i.toLowerCase());case e.FilterOperator.IS_REGEX_MATCH:return new RegExp(i).test(t);case e.FilterOperator.NOT_REGEX_MATCH:return!new RegExp(i).test(t);case e.FilterOperator.LESS_THAN:return Number(t)<Number(i);case e.FilterOperator.LESS_THAN_OR_EQUALS:return Number(t)<=Number(i);case e.FilterOperator.GREATER_THAN:return Number(t)>Number(i);case e.FilterOperator.GREATER_THAN_OR_EQUALS:return Number(t)>=Number(i);default:return!1}}static isNegationOperator(t){return t===e.FilterOperator.NOT_EQUALS||t===e.FilterOperator.NOT_EQUALS_IGNORE_CASE||t===e.FilterOperator.NOT_CONTAINS||t===e.FilterOperator.NOT_CONTAINS_IGNORE_CASE||t===e.FilterOperator.NOT_STARTS_WITH||t===e.FilterOperator.NOT_STARTS_WITH_IGNORE_CASE||t===e.FilterOperator.NOT_ENDS_WITH||t===e.FilterOperator.NOT_ENDS_WITH_IGNORE_CASE||t===e.FilterOperator.NOT_REGEX_MATCH}}class R{constructor(e,t,s,i,r){this.listeners=[],this.userDefinedValuesProvider=null,this.stopped=!0,this.nativeEventListener=null,this.riskAlertIntervalId=null,this.lastRiskAlertTs=0,this.eventService=e,this.collectors=t,this.payload=s,this.screenActivityCollector=i,this.securityCollector=r}setUserDefinedValuesProvider(e){this.userDefinedValuesProvider=e}start(){g.debug("Starting EventCoordinator"),this.stopped=!1,this.initializeListeners(),this.startRiskAlertEvaluator(),this.triggerNativeEvent(e.TrackerEventType.PAGE_VIEW),g.debug("EventCoordinator started with",this.listeners.length,"listeners")}stop(){g.debug("Stopping EventCoordinator"),this.stopped=!0,null!==this.riskAlertIntervalId&&(clearInterval(this.riskAlertIntervalId),this.riskAlertIntervalId=null);for(const e of this.listeners)try{e.stop()}catch(e){g.error("Error stopping listener:",e)}this.listeners=[],this.nativeEventListener=null,g.debug("EventCoordinator stopped")}setNativeEventListener(e){this.nativeEventListener=e}collectAllVariablesNow(){const e=new Map,t=this.payload.getCollectedVariables();for(const[s,i]of t)e.set(s,i);return this.collectFromAllCollectors(e),e}triggerNativeEvent(e){if(this.stopped)g.warn("Ignoring native event (coordinator stopped):",e);else try{this.triggerEventInternal(e,null,null)}catch(e){g.error("Error in triggerNativeEvent:",e)}}triggerCustomEvent(t,s){try{this.triggerEventInternal(e.TrackerEventType.CUSTOM,t,s)}catch(e){g.error("Error in triggerCustomEvent:",e)}}triggerEventInternal(t,s,i){const r=this.payload.getTrackerConfig(),a=t===e.TrackerEventType.CUSTOM;if(!r)return void g.error("Config not initialized, cannot trigger event");g.debug(a?`Custom event triggered: ${s}`:`Native event triggered: ${t}`),t===e.TrackerEventType.PAGE_VIEW&&(this.securityCollector.registerPageView(),this.screenActivityCollector.resetTimer(),this.screenActivityCollector.startTimer());const o=this.collectAllConfigVariables(),l=this.userDefinedValuesProvider?.()??{};if(a&&s){const t={...l,...i};Object.keys(t).length>0&&o.set(e.TrackerVariableType.CUSTOM_TRIGGER_ARGS,t)}else Object.keys(l).length>0&&o.set(e.TrackerVariableType.CUSTOM_TRIGGER_ARGS,l);for(const i of r.configs)try{const r=this.findMatchingTrigger(i,t,s);if(!r)continue;if(o.set(e.TrackerVariableType.CURRENT_TRIGGER_NAME,r.name),!this.passesTriggerFilters(i,r,o)){g.debug("Trigger",r.name,"blocked by filters");continue}const a=this.screenActivityCollector.resolveConfigVariables(i);for(const[e,t]of a)o.set(e,t);const l=n.mapVariablesToBackend(i,o),c=this.payload.getDeviceId(),h={name:i.event.name,key:c,variables:l};this.eventService.enqueueEvent(h),g.debug("Event enqueued:",i.event.name)}catch(e){g.error("Error processing config for event:",e)}if(this.nativeEventListener)try{const e={type:t,args:Object.fromEntries(o),timestamp:Date.now()};this.nativeEventListener.onNativeEvent(e)}catch(e){g.error("Error notifying native event listener:",e)}}collectAllConfigVariables(){const e=new Map,t=this.payload.getCollectedVariables();for(const[s,i]of t)e.set(s,i);return this.collectFromAllCollectors(e),e}collectFromAllCollectors(e){for(const t of this.collectors)try{const s=t.collect();for(const[t,i]of s)e.set(t,i)}catch(e){g.error("Error collecting from collector:",e)}}findMatchingTrigger(t,s,i){return s===e.TrackerEventType.CUSTOM&&i?t.triggers.find(t=>t.type===e.TrackerEventType.CUSTOM&&t.name===i)??null:t.triggers.find(e=>e.type===s)??null}passesTriggerFilters(e,t,s){if(!t.filters||0===t.filters.length)return!0;const i=new Map;for(const t of e.variables){const e=t.type,r=s.get(e);void 0!==r&&i.set(t.name,r)}return M.passesTriggerFilters(t.filters,i)}initializeListeners(){const e=this.payload.getTrackerConfig();if(!e)return void g.error("Config not initialized, cannot initialize listeners");const t=e=>this.triggerNativeEvent(e);this.listeners.push(new A(t)),this.listeners.push(new C(t)),this.listeners.push(new _(t)),this.listeners.push(new w(t)),this.listeners.push(new b(t)),this.listeners.push(new y(t)),this.listeners.push(new I(t));for(const t of this.listeners){const s=t.getTypes().some(t=>e.configs.some(e=>e.triggers.some(e=>e.type===t))),i=t.getVariableTypes().some(t=>e.configs.some(e=>Object.values(e.event.variableMappings).includes(t)));if(s||i)try{t.start(),g.debug("Started listener:",t.constructor.name)}catch(e){g.error("Failed to start listener:",t.constructor.name,e)}else g.debug("Skipping listener (not configured):",t.constructor.name)}}startRiskAlertEvaluator(){const t=this.payload.getTrackerConfig();if(!t)return;t.configs.some(t=>t.triggers.some(t=>t.type===e.TrackerEventType.RISK_ALERT))&&(this.riskAlertIntervalId=setInterval(()=>{this.evaluateRiskAlert()},3e4))}evaluateRiskAlert(){if(this.stopped)return;if(Date.now()-this.lastRiskAlertTs<6e4)return;const t=this.collectAllConfigVariables(),s=.6*(t.get(e.TrackerVariableType.BEHAVIORAL_BIOMETRIC_SCORE)??0)+.4*(t.get(e.TrackerVariableType.REMOTE_ACCESS_SCORE)??0);s>.6&&(this.lastRiskAlertTs=Date.now(),g.debug("RISK_ALERT triggered (combined score:",s,")"),this.triggerNativeEvent(e.TrackerEventType.RISK_ALERT))}}class N{constructor(e,t,s,i,r,n,a,o,l,c){this.userDefinedValues={},this.isConfigLoaded=!1,this.eventService=e,this.eventCoordinator=t,this.networkService=s,this.payload=i,this.scheduler=r,this.deviceCollector=n,this.networkCollector=a,this.securityCollector=o,this.behavioralCollector=l,this.screenActivityCollector=c,this.eventCoordinator.setUserDefinedValuesProvider(()=>({...this.userDefinedValues}))}async initialize(e){g.debug("Initializing TrackerEngine");try{const t=await this.networkService.fetchConfiguration();if(!t)return void e.onComplete(!1,"Config fetch returned null");this.payload.setTrackerConfig(t),this.isConfigLoaded=!0,await Promise.all([this.deviceCollector.initFingerprint(),this.networkCollector.fetchIp()]);const s=this.payload.getConfiguration(),i=this.payload.getDeviceId();this.securityCollector.init(s?.tenant??"",i),this.behavioralCollector.init(),this.eventCoordinator.start(),this.eventService.start(),this.scheduler.start(),this.screenActivityCollector.startTimer(),g.debug("TrackerEngine initialized successfully"),e.onComplete(!0,"Tracker initialized successfully")}catch(t){g.error("Failed to initialize:",t);const s=t instanceof Error?t.message:String(t);e.onComplete(!1,`Failed to initialize: ${s}`)}}triggerCustomEvent(e,t){this.isConfigLoaded?this.eventCoordinator.triggerCustomEvent(e,t):g.error("Config not loaded, cannot trigger custom event")}async triggerCustomEventSync(t,s,i){if(!this.isConfigLoaded)return void i.onError(new Error("Config not loaded"));const r={...this.userDefinedValues,...s},a=this.payload.getTrackerConfig();if(!a)return void i.onError(new Error("Config not available"));const o=this.eventCoordinator.collectAllVariablesNow();o.set(e.TrackerVariableType.CUSTOM_TRIGGER_ARGS,r);for(const s of a.configs)try{const r=s.triggers.find(s=>s.type===e.TrackerEventType.CUSTOM&&s.name===t);if(!r)continue;o.set(e.TrackerVariableType.CURRENT_TRIGGER_NAME,r.name);const a=this.screenActivityCollector.resolveConfigVariables(s);for(const[e,t]of a)o.set(e,t);const l=n.mapVariablesToBackend(s,o),c=this.payload.getDeviceId(),h={name:s.event.name,key:c,variables:l};await this.eventService.sendEventSync(h,i)}catch(e){g.error("Error creating event for config:",e),i.onError(e instanceof Error?e:new Error(String(e)))}}setUserDefinedValue(e,t){this.userDefinedValues[e]=null==t?void 0:t}getUserDefinedValues(){return{...this.userDefinedValues}}getAllVariables(){return this.eventCoordinator.collectAllVariablesNow()}getConfiguration(){return this.payload.getTrackerConfig()}triggerNativeEvent(e){this.eventCoordinator.triggerNativeEvent(e)}setNativeEventListener(e){this.eventCoordinator.setNativeEventListener(e)}terminate(){g.debug("Terminating TrackerEngine"),this.isConfigLoaded=!1;try{this.eventCoordinator.stop()}catch(e){g.error("Error stopping coordinator:",e)}try{this.eventService.stop()}catch(e){g.error("Error stopping event service:",e)}try{this.scheduler.stop()}catch(e){g.error("Error stopping scheduler:",e)}try{this.securityCollector.destroy()}catch(e){g.error("Error destroying security:",e)}try{this.behavioralCollector.destroy()}catch(e){g.error("Error destroying behavioral:",e)}try{this.screenActivityCollector.destroy()}catch(e){g.error("Error destroying screen:",e)}Object.keys(this.userDefinedValues).forEach(e=>{this.userDefinedValues[e]=void 0}),g.debug("TrackerEngine terminated")}}class V{constructor(e,t=5e3){this.task=e,this.intervalMs=t,this.intervalId=null}start(){null===this.intervalId&&(this.intervalId=setInterval(()=>{try{this.task()}catch{}},this.intervalMs))}stop(){null!==this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}isRunning(){return null!==this.intervalId}}class L{constructor(e){this.configuration=e}async fetchConfiguration(){const e=`${this.configuration.apiUrl}/sentinel/v1`;g.debug("Fetching configuration from:",e);const t=await p.get(e,{headers:{"api-key":this.configuration.apiKey,tenant:this.configuration.tenant,platform:"web"},timeout:this.configuration.timeout});g.debug("Received configuration:",JSON.stringify(t,null,2));const s=t.configs.find(e=>"WEB"===e.platform);return{...t,configs:s?[s]:t.configs}}async sendEvent(e){const t=`${this.configuration.apiUrl}/sentinel/v1`;g.debug("Sending event:",e.name);const{retryCount:s,...i}=e;return p.post(t,i,{headers:{"api-key":this.configuration.apiKey,tenant:this.configuration.tenant,platform:"web"},timeout:this.configuration.timeout})}isNetworkAvailable(){return navigator.onLine}}class k{constructor(e,t,s){this.scheduler=null,this.consecutiveFailures=0,this.eventStorage=e,this.networkService=t,this.configuration=s}start(){g.debug("Starting EventService"),this.scheduler=new V(()=>this.processQueue(),this.configuration.queueProcessInterval??5e3),this.scheduler.start()}stop(){g.debug("Stopping EventService"),this.scheduler?.stop(),this.scheduler=null}enqueueEvent(e){g.debug("Enqueuing event:",e.name),this.eventStorage.saveEvent(e)}async sendEventSync(e,t){try{g.debug("Sending event sync:",e.name);const s=await this.networkService.sendEvent(e);s&&t.onSuccess(s)}catch(s){g.error("Failed to send event:",e.name,s),t.onError(s instanceof Error?s:new Error(String(s)))}}async processQueue(){try{if(!this.networkService.isNetworkAvailable())return void g.debug("Network not available, skipping queue processing");if(this.consecutiveFailures>=k.MAX_CONSECUTIVE_FAILURES)return void g.debug("Too many consecutive failures, pausing queue processing");const e=this.eventStorage.getPendingEvents();if(0===e.length)return;g.debug("Processing queue:",e.length,"events");for(const t of e)try{await this.networkService.sendEvent(t),g.debug("Event sent from queue:",t.name),this.eventStorage.removeEvent(t),this.consecutiveFailures=0}catch{if(this.consecutiveFailures++,t.retryCount=(t.retryCount??0)+1,t.retryCount>=k.MAX_EVENT_RETRIES?(g.warn("Dropping event after max retries:",t.name),this.eventStorage.removeEvent(t)):1===this.consecutiveFailures&&g.warn("Failed to send event, will retry:",t.name),this.consecutiveFailures>=k.MAX_CONSECUTIVE_FAILURES)return void g.warn("Queue paused after",k.MAX_CONSECUTIVE_FAILURES,"consecutive failures")}}catch(e){g.error("Critical error in processQueue:",e)}}resetFailureCount(){this.consecutiveFailures=0}}k.MAX_CONSECUTIVE_FAILURES=5,k.MAX_EVENT_RETRIES=3;class D{constructor(){this.events=[]}saveEvent(e){this.events.push(e)}getPendingEvents(){return[...this.events]}removeEvent(e){this.events=this.events.filter(t=>t.key!==e.key)}clear(){this.events=[]}}class F{constructor(){this.trackerConfig=null,this.configuration=null,this.collectedVariables=new Map,this.userDefinedValues=new Map}setTrackerConfig(e){this.trackerConfig=e}getTrackerConfig(){return this.trackerConfig}setConfiguration(e){this.configuration=e}getConfiguration(){return this.configuration}setVariable(e,t){this.collectedVariables.set(e,t)}getVariable(e){return this.collectedVariables.get(e)}getAllVariables(){return new Map(this.collectedVariables)}getCollectedVariables(){return new Map(this.collectedVariables)}updateVariables(e){for(const[t,s]of e)this.collectedVariables.set(t,s)}setUserDefinedValue(e,t){this.userDefinedValues.set(e,t)}getUserDefinedValue(e){return this.userDefinedValues.get(e)}getUserDefinedValues(){return Object.fromEntries(this.userDefinedValues)}getDeviceId(){try{let e=localStorage.getItem("formica_device_id");return e||(e=crypto.randomUUID(),localStorage.setItem("formica_device_id",e)),e}catch{return`anon_${Date.now()}`}}clear(){this.trackerConfig=null,this.collectedVariables.clear(),this.userDefinedValues.clear()}}class H{constructor(e){this.configuration=e,this.build()}build(){this.payload=new F,this.payload.setConfiguration(this.configuration),this.deviceCollector=new d,this.networkCollector=new S,this.securityCollector=new m,this.behavioralCollector=new h,this.screenActivityCollector=new E;const e=[this.deviceCollector,this.networkCollector,this.securityCollector,this.behavioralCollector,this.screenActivityCollector];this.networkService=new L(this.configuration),this.eventStorage=new D,this.eventService=new k(this.eventStorage,this.networkService,this.configuration),this.eventCoordinator=new R(this.eventService,e,this.payload,this.screenActivityCollector,this.securityCollector),this.scheduler=new V(()=>{this.securityCollector.resetRollingWindow(),this.behavioralCollector.resetRollingWindow()},6e4),this.engine=new N(this.eventService,this.eventCoordinator,this.networkService,this.payload,this.scheduler,this.deviceCollector,this.networkCollector,this.securityCollector,this.behavioralCollector,this.screenActivityCollector)}getTrackerEngine(){return this.engine}}class P{constructor(e){this.isInitialized=!1,g.setEnabled(e.debug??!1);const t=new H(e);this.engine=t.getTrackerEngine()}async initialize(e){try{if(this.isInitialized)return void e.onComplete(!0,"Tracker SDK already initialized");await this.engine.initialize({onComplete:(t,s)=>{t&&(this.isInitialized=!0),e.onComplete(t,s)}})}catch(t){try{const s=t instanceof Error?t.message:String(t);e.onComplete(!1,`Initialization failed: ${s}`)}catch{}}}getIsInitialized(){return this.isInitialized}triggerCustomEvent(e,t={}){if(this.isInitialized)try{this.engine.triggerCustomEvent(e,t)}catch{}}async triggerCustomEventSync(e,t,s){if(this.isInitialized)try{await this.engine.triggerCustomEventSync(e,t,s)}catch(e){try{s.onError(e instanceof Error?e:new Error(String(e)))}catch{}}else s.onError(new Error("Tracker SDK not initialized"))}setUserDefinedValue(e,t){this.isInitialized&&this.engine.setUserDefinedValue(e,t)}getUserDefinedValues(){return this.isInitialized?this.engine.getUserDefinedValues():null}getAllVariables(){return this.isInitialized?this.engine.getAllVariables():null}getConfiguration(){return this.isInitialized?this.engine.getConfiguration():null}triggerNativeEvent(e){this.isInitialized&&this.engine.triggerNativeEvent(e)}setNativeEventListener(e){this.isInitialized&&this.engine.setNativeEventListener(e)}terminate(){if(this.isInitialized)try{this.engine.terminate()}catch{}finally{this.isInitialized=!1}}}P.Builder=class{constructor(){this.configuration=null}setConfiguration(e){return this.configuration=e,this}build(){if(!this.configuration)throw new Error("Configuration must be set before building TrackerSDK");return new P(this.configuration)}},e.TrackerSDK=P});