@serge-ai/js 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -6
- package/dist/index.d.ts +1 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/s.js +1 -1
- package/package.json +3 -7
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';function j(){let e=[],t="unknown";try{navigator.webdriver===!0&&e.push("webdriver");}catch{e.push("webdriver_error");}("__playwright__binding__"in window||"__pwInitScripts"in window)&&(e.push("playwright_globals"),t="browser_use");let r=[{selector:'[id^="claude-agent-"]',signal:"claude_dom",platform:"claude"},{selector:'[id^="comet-agent-"]',signal:"comet_dom",platform:"perplexity"},{selector:'[id^="perplexity-comet-"]',signal:"comet_dom_alt",platform:"perplexity"},{selector:'[id^="chatgpt-atlas-"]',signal:"atlas_dom",platform:"chatgpt"},{selector:'[id^="openai-atlas-"]',signal:"atlas_dom_alt",platform:"chatgpt"}];for(let i of r)document.querySelector(i.selector)&&(e.push(i.signal),t=i.platform);let n=navigator.userAgent;return /PerplexityBot\/|Perplexity-User\//i.test(n)?(e.push("perplexity_ua"),t="perplexity"):/OAI-SearchBot\//i.test(n)?(e.push("oai_searchbot_ua"),t="chatgpt"):/ChatGPT-User\//i.test(n)?(e.push("chatgpt_user_ua"),t="chatgpt"):/Operator\//i.test(n)?(e.push("operator_ua"),t="chatgpt"):/GPTBot\//i.test(n)?(e.push("gptbot_ua"),t="chatgpt"):/ClaudeBot\//i.test(n)?(e.push("claudebot_ua"),t="claude"):/Claude-User\/|claude-web\//i.test(n)?(e.push("claude_user_ua"),t="claude"):/Claude-SearchBot\//i.test(n)?(e.push("claude_searchbot_ua"),t="claude"):/Claude-Code\//i.test(n)?(e.push("claude_code_ua"),t="claude"):/Google-Extended\//i.test(n)?(e.push("google_extended_ua"),t="gemini"):/Applebot-Extended\//i.test(n)?(e.push("applebot_extended_ua"),t="apple"):/Applebot\//i.test(n)?(e.push("applebot_ua"),t="apple"):/Meta-ExternalAgent\/|Meta-ExternalFetcher\//i.test(n)?(e.push("meta_externalagent_ua"),t="meta"):/bingbot\/|BingPreview\//i.test(n)&&(e.push("bingbot_ua"),t="bing"),e.length===0?null:{platform:t,confidence:.99,signals:e}}function W(){let e=[],t=0,r;try{let n=navigator.userAgent,i=/Macintosh|Mac OS X/i.test(n),s=/Windows NT/i.test(n),a=navigator.userAgentData;a&&a.platform==="Linux"&&(i||s)&&(e.push("platform_mismatch"),t+=.5,r="chatgpt");}catch{}try{let n=navigator.userAgent,i=navigator.userAgentData;if(i&&Array.isArray(i.brands)&&/Chrome\//i.test(n)){let s=i.brands.map(u=>u.brand),a=s.some(u=>/Google Chrome/i.test(u)),c=s.some(u=>/Chromium/i.test(u));!a&&c&&(e.push("brand_list_anomaly"),t+=.4,r=r??"chatgpt");}}catch{}try{let n=navigator.userAgent;/Mobile|Android|iPhone/i.test(n)&&navigator.maxTouchPoints===0&&(e.push("touch_mismatch"),t+=.3);}catch{}try{(screen.width===0||screen.height===0)&&(e.push("zero_screen"),t+=.4),screen.colorDepth===0&&(e.push("zero_colordepth"),t+=.3);}catch{}try{window.innerWidth>0&&window.outerWidth>0&&window.outerWidth<window.innerWidth&&(e.push("viewport_mismatch"),t+=.2);}catch{}try{"Notification"in window||(e.push("missing_notification_api"),t+=.2);}catch{}try{let n=document.createElement("canvas"),i=n.getContext("webgl")||n.getContext("experimental-webgl");if(i){let s=i.getExtension("WEBGL_debug_renderer_info"),c=String(s?i.getParameter(s.UNMASKED_RENDERER_WEBGL):i.getParameter(i.RENDERER)).toLowerCase();(c.includes("swiftshader")||c.includes("swangle")||c.includes("llvmpipe")||c.includes("mesa offscreen"))&&(e.push("webgl_software_renderer"),t+=.45);}else i===null&&(e.push("missing_webgl"),t+=.25);}catch{}try{let n=document.createElement("canvas");n.width=32,n.height=16;let i=n.getContext("2d");if(i){i.textBaseline="top",i.font='13px "Arial"',i.fillStyle="#f60",i.fillRect(0,0,32,16),i.fillStyle="#069",i.fillText("serge:ag",1,1);let s=i.getImageData(0,0,32,16).data,a=0,c=0;for(let d=0;d<s.length;d+=4)s[d]===0&&s[d+1]===0&&s[d+2]===0&&a++,s[d]>c&&(c=s[d]);a/(s.length/4)>.95&&c<20&&(e.push("canvas_flat_render"),t+=.2);}}catch{}try{let n=navigator.userAgentData;if(n&&typeof n.getHighEntropyValues=="function"){let i=navigator.userAgent,s=/Chrome\/(\d+)/i.exec(i);if(s&&Array.isArray(n.brands)){let a=Number(s[1]),c=n.brands.find(u=>/Chrome/i.test(u.brand)&&!/Chromium/i.test(u.brand));if(c&&typeof c.version=="string"){let u=Number(c.version.split(".")[0]);Number.isFinite(a)&&Number.isFinite(u)&&Math.abs(a-u)>2&&(e.push("ua_version_major_mismatch"),t+=.35);}}}}catch{}return t=Math.min(t,1),{signals:e,score:t,platform:r}}var o=null,L=false,b=null;function me(){return {mouseMoveCount:0,scrollEvents:0,clickCount:0,clickTimestamps:[],anyHoverDetected:false,firstInteractionMs:null,pageLoadTime:performance.now()}}function G(){o&&o.mouseMoveCount++;}function K(){o&&(o.anyHoverDetected=true);}function q(){o&&o.scrollEvents++;}function z(){if(!o)return;let e=performance.now();o.clickCount++,o.clickTimestamps.push(e),o.firstInteractionMs===null&&(o.firstInteractionMs=e-o.pageLoadTime);}function he(){document.removeEventListener("mousemove",G),document.removeEventListener("mouseover",K),document.removeEventListener("scroll",q),document.removeEventListener("click",z),L=false;}function ve(){if(!o)return {signals:[],score:0};let e=[],t=0;if(o.mouseMoveCount===0&&o.clickCount>0&&(e.push("no_mouse"),t+=.3),o.firstInteractionMs!==null&&o.firstInteractionMs<100&&(e.push("fast_interaction"),t+=.4),o.clickCount>0&&!o.anyHoverDetected&&o.mouseMoveCount===0&&(e.push("no_hover"),t+=.2),o.clickTimestamps.length>=3){let r=[];for(let a=1;a<o.clickTimestamps.length;a++)r.push(o.clickTimestamps[a]-o.clickTimestamps[a-1]);let n=r.reduce((a,c)=>a+c,0)/r.length,i=r.reduce((a,c)=>a+(c-n)**2,0)/r.length;(n>0?Math.sqrt(i)/n:0)<.1&&(e.push("mechanical_timing"),t+=.3);}return o.scrollEvents===0&&document.documentElement.scrollHeight>window.innerHeight*2&&o.clickCount>0&&(e.push("no_scroll"),t+=.15),{signals:e,score:Math.min(t,1)}}function $(e=5e3){return L?new Promise(t=>{b=t;}):(o=me(),L=true,document.addEventListener("mousemove",G,{passive:true}),document.addEventListener("mouseover",K,{passive:true}),document.addEventListener("scroll",q,{passive:true}),document.addEventListener("click",z,{passive:true}),new Promise(t=>{b=t,setTimeout(()=>{he();let r=ve();o=null,b&&(b(r),b=null);},e);}))}function Y(){let e=j();if(e&&e.confidence>=.85)return {immediate:e,refine:Promise.resolve(e)};let t=W(),r=[...e?.signals??[],...t.signals],n=e?.platform??t.platform??"unknown",i=Math.min((e?.confidence??0)*.5+t.score,1),s={platform:n,confidence:i,signals:[...r]};if(i>=.85)return {immediate:s,refine:Promise.resolve(s)};let a=$(5e3).then(c=>{let u=[...r,...c.signals],d=c.score,I;return i>0?I=Math.min(.3*i+.7*d,1):I=d*.85,{platform:n,confidence:I,signals:u,behavioralScore:d}});return {immediate:s,refine:a}}var g="";function D(e){g=e;}function m(e){if(!g)return;let t=JSON.stringify(e);J(t).catch(()=>{setTimeout(()=>J(t).catch(()=>{}),2e3);});}function J(e){return fetch(g,{method:"POST",body:e,headers:{"Content-Type":"text/plain"},keepalive:true,credentials:"omit"}).then(t=>{if(!t.ok&&t.status!==429)throw new Error(`HTTP ${t.status}`)})}function F(e){if(!g)return;let t=JSON.stringify(e);if(typeof fetchLater=="function")try{fetchLater(g,{method:"POST",body:t,headers:{"Content-Type":"text/plain"},activateAfter:0,credentials:"omit"});return}catch{}try{navigator.sendBeacon(g,t);}catch{fetch(g,{method:"POST",body:t,headers:{"Content-Type":"text/plain"},keepalive:true,credentials:"omit"}).catch(()=>{});}}var M="",X=null;function Q(e){if(X=e,M=location.pathname+location.search,"navigation"in window)window.navigation.addEventListener("navigatesuccess",()=>{_();});else {let t=history.pushState,r=history.replaceState;history.pushState=function(...n){t.apply(this,n),_();},history.replaceState=function(...n){r.apply(this,n),_();},window.addEventListener("popstate",_);}window.addEventListener("pageshow",t=>{t.persisted&&_();});}function _(){let e=location.pathname+location.search;e!==M&&(M=e,X?.());}var ye="serge_signals";function P(e){let t=`${e}/api/v1/signals`;fetch(t,{method:"GET",credentials:"omit"}).then(r=>{if(r.ok)return r.json()}).then(r=>{if(r)try{let n={data:r,fetchedAt:Date.now()};sessionStorage.setItem(ye,JSON.stringify(n));}catch{}}).catch(()=>{});}var Z="_serge_consent",h=null;function be(){try{let e=window.localStorage.getItem(Z);if(e==="granted"||e==="denied")return e}catch{}return "unknown"}function _e(e){if(e!=="unknown")try{window.localStorage.setItem(Z,e);}catch{}}function ee(){return h!==null||(h=be()),h}function te(e){return e!=="granted"&&e!=="denied"&&e!=="unknown"?h??"unknown":(h=e,_e(e),e)}var re="serge-traps-root",ie="serge_trap_config";var we=new Set(["div","span","p","a","button","select","option","h1","h2","h3","h4","h5","h6","ul","li","img","form","input","label"]),Se=new Set(["role","aria-label","aria-hidden","aria-describedby","aria-labelledby","tabindex","href","target","rel","type","name","value","placeholder","src","alt","data-trap","data-trap-kind","data-trap-token"]),Ee=new Set(["position","top","left","right","bottom","width","height","overflow","visibility","opacity","clip-path","clip","pointer-events","color","background","background-color","font-size"]),Ce=new Set(["click","change","submit","mouseover","focus"]);function oe(e){let t=String(e.tag??"").toLowerCase();if(!we.has(t))return null;let r=document.createElement(t);if(e.attrs&&typeof e.attrs=="object"){for(let[n,i]of Object.entries(e.attrs))if(Se.has(n)&&typeof i=="string")try{r.setAttribute(n,i);}catch{}}if(e.style&&typeof e.style=="object"){for(let[n,i]of Object.entries(e.style))if(Ee.has(n)&&typeof i=="string")try{r.style.setProperty(n,i);}catch{}}if(typeof e.text=="string"&&r.appendChild(document.createTextNode(e.text)),Array.isArray(e.children))for(let n of e.children){let i=oe(n);i&&r.appendChild(i);}return r}function Te(e){try{if("sendBeacon"in navigator){navigator.sendBeacon(e);return}}catch{}try{fetch(e,{method:"GET",mode:"no-cors",credentials:"omit",keepalive:!0}).catch(()=>{});}catch{}}function Ae(e,t){for(let r of t.triggers){if(!Ce.has(r.event))continue;let n=e.querySelector(r.selector);n&&n.addEventListener(r.event,()=>{Te(t.trapUrl);},{once:true,capture:true});}}function xe(){try{let e=sessionStorage.getItem(ie);if(!e)return null;let t=JSON.parse(e);return Date.now()-t.fetchedAt>36e5?null:t.data}catch{return null}}function ke(e){try{let t={data:e,fetchedAt:Date.now()};sessionStorage.setItem(ie,JSON.stringify(t));}catch{}}function O(){return !!document.getElementById(re)}function ne(e){if(O()||!e||!Array.isArray(e.traps)||e.traps.length===0)return;let t=document.createElement("div");t.id=re,t.setAttribute("aria-hidden","true"),t.setAttribute("data-serge-traps","1"),t.style.setProperty("position","absolute"),t.style.setProperty("width","1px"),t.style.setProperty("height","1px"),t.style.setProperty("overflow","hidden"),t.style.setProperty("clip-path","inset(50%)");for(let r of e.traps){let n=oe(r.root);n&&(n.setAttribute("data-serge-trap-id",String(r.id??"")),n.setAttribute("data-serge-trap-kind",String(r.kind??"")),t.appendChild(n),Ae(n,r));}document.body?document.body.appendChild(t):document.addEventListener("DOMContentLoaded",()=>document.body&&document.body.appendChild(t),{once:true});}function B(e){if(e.enabled===false||O())return;let t=xe();t&&ne(t);let r=`${e.apiBase}/api/v1/trap-config?s=${encodeURIComponent(e.siteId)}&sid=${encodeURIComponent(e.sessionId)}`;fetch(r,{method:"GET",credentials:"omit"}).then(n=>n.ok?n.json():null).then(n=>{!n||!Array.isArray(n.traps)||(ke(n),O()||ne(n));}).catch(()=>{});}var Re=new Set(["access_token","api_key","apikey","auth","authorization","bearer","code","cvc","cvv","email","id_token","jwt","key","oauth_token","one_time_password","otp","password","pin","refresh_token","reset_token","secret","session","sig","signature","ssn","token"]);function se(e){if(!e)return "";if(!e.includes("?"))return e;let t,r=false;try{e.startsWith("/")||!/^[a-z]+:\/\//i.test(e)&&!e.startsWith("//")?(t=new URL(e,"https://placeholder.invalid"),r=!0):t=new URL(e);}catch{return e}if(t.searchParams.size===0)return e;let n=false;for(let i of Array.from(t.searchParams.keys()))Re.has(i.toLowerCase())&&(t.searchParams.delete(i),n=true);return n?r?t.pathname+t.search+t.hash:t.toString():e}var ae="_s_sid",v=null;function Ie(){let e=new Uint8Array(12);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(36).padStart(2,"0")).join("")}function w(){if(v)return v;try{let e=sessionStorage.getItem(ae);if(e)return v=e,e}catch{}v=Ie();try{sessionStorage.setItem(ae,v);}catch{}return v}function N(){let e=Date.now().toString(36),t=new Uint8Array(4);crypto.getRandomValues(t);let r=Array.from(t,n=>n.toString(36).padStart(2,"0")).join("");return `${e}-${r}`}var ce="0.2.0";var A=false,l=null,f=null,E=0,x="",U=0,S=false,p="unknown",y="",V=false;function k(){return l?l.debug?true:!!f&&f.confidence>=.35:false}function R(){return typeof window<"u"&&typeof document<"u"}function ot(e,t={}){if(!R())return;if(!e){t.debug&&console.error("[serge] init(token) requires a token");return}let r=performance.now(),n=(t.apiBase??"https://serge.ai").replace(/\/$/,"");y=n,D(`${n}/api/ingest/snippet`),l={token:e,apiBase:n,debug:t.debug??false,requireConsent:t.requireConsent??false,defaults:t.defaults},A=true,U=performance.now()-r,!(l.requireConsent&&(p=ee(),p==="denied"||p!=="granted"))&&(de(),B({apiBase:n,siteId:e,sessionId:w()}),P(n),document.visibilityState==="hidden"||document.visibilityState==="prerender"?document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&T();},{once:true}):T());}function de(){if(!l)return;let e={s:l.token,sid:w(),eid:N(),ap:"unknown",ac:0,ds:[],et:"init",ts:new Date().toISOString(),u:location.pathname,t:document.title||void 0};m(e);}async function T(){if(!l)return;E=performance.now(),x=location.pathname;let{immediate:e,refine:t}=Y();f=e,(l.debug||e.confidence>=.35)&&(ue(),H(),le());let r=await t;f=r,!l.debug&&e.confidence<.35&&r.confidence>=.35&&(ue(),H(),le());}function le(){V||(V=true,Q(fe),document.addEventListener("visibilitychange",De),window.addEventListener("pagehide",Me));}function C(e,t){if(!l||!f)throw new Error("[serge] tracker not initialized \u2014 call init() first");let r=document.referrer?se(document.referrer):"";return {s:l.token,sid:w(),eid:N(),ap:f.platform,ac:Math.round(f.confidence*100)/100,ds:f.signals,bs:f.behavioralScore!=null?Math.round(f.behavioralScore*100)/100:void 0,et:e,ts:new Date().toISOString(),u:location.pathname,r:r||void 0,t:document.title||void 0,...t}}function ue(){S=false;let e={init_ms:Math.round(U),v:ce,sv:3};l?.defaults&&(e.d=l.defaults),m(C("session_start",{vw:window.innerWidth,vh:window.innerHeight,sw:screen.width,sh:screen.height,ua:navigator.userAgent,p:Pe(),l:navigator.language,_m:e}));}function H(){E=performance.now(),x=location.pathname,m(C("page_view"));}function fe(){if(!k())return;let e=Math.round(performance.now()-E);m(C("route_change",{u:x,tp:e,sd:ge()})),H(),S=false;}function De(){document.visibilityState==="hidden"&&pe();}function Me(){pe();}function pe(){if(S||!k())return;S=true;let e=Math.round(performance.now()-E);F(C("session_end",{tp:e,sd:ge()}));}function ge(){let e=document.documentElement.scrollHeight,t=window.innerHeight;if(e<=t)return 100;let r=window.scrollY||document.documentElement.scrollTop;return Math.min(100,Math.round((r+t)/e*100))}function Pe(){try{return navigator.userAgentData?.platform??navigator.platform??""}catch{return ""}}function st(e,t){!R()||!A||k()&&m(C("semantic_action",{ix:[{t:e,tg:JSON.stringify(t??{}),o:0,ok:true}]}));}function at(e){if(!R())return;let t=p;p=te(e),!(!l||!l.requireConsent)&&t!==p&&p==="granted"&&t!=="granted"&&(y!==""&&P(y),de(),y!==""&&B({apiBase:y,siteId:l.token,sessionId:w()}),document.visibilityState==="hidden"||document.visibilityState==="prerender"?document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&T();},{once:true}):T());}function ct(){!R()||!A||k()&&fe();}function lt(){A=false,l=null,f=null,E=0,x="",U=0,S=false,p="unknown",y="",V=false,D("");}
|
|
1
|
+
'use strict';function j(){let e=[],t="unknown";try{navigator.webdriver===!0&&e.push("webdriver");}catch{e.push("webdriver_error");}("__playwright__binding__"in window||"__pwInitScripts"in window)&&(e.push("playwright_globals"),t="browser_use");let r=[{selector:'[id^="claude-agent-"]',signal:"claude_dom",platform:"claude"},{selector:'[id^="comet-agent-"]',signal:"comet_dom",platform:"perplexity"},{selector:'[id^="perplexity-comet-"]',signal:"comet_dom_alt",platform:"perplexity"},{selector:'[id^="chatgpt-atlas-"]',signal:"atlas_dom",platform:"chatgpt"},{selector:'[id^="openai-atlas-"]',signal:"atlas_dom_alt",platform:"chatgpt"}];for(let i of r)document.querySelector(i.selector)&&(e.push(i.signal),t=i.platform);let n=navigator.userAgent;return /PerplexityBot\/|Perplexity-User\//i.test(n)?(e.push("perplexity_ua"),t="perplexity"):/OAI-SearchBot\//i.test(n)?(e.push("oai_searchbot_ua"),t="chatgpt"):/ChatGPT-User\//i.test(n)?(e.push("chatgpt_user_ua"),t="chatgpt"):/Operator\//i.test(n)?(e.push("operator_ua"),t="chatgpt"):/GPTBot\//i.test(n)?(e.push("gptbot_ua"),t="chatgpt"):/ClaudeBot\//i.test(n)?(e.push("claudebot_ua"),t="claude"):/Claude-User\/|claude-web\//i.test(n)?(e.push("claude_user_ua"),t="claude"):/Claude-SearchBot\//i.test(n)?(e.push("claude_searchbot_ua"),t="claude"):/Claude-Code\//i.test(n)?(e.push("claude_code_ua"),t="claude"):/Google-Extended\//i.test(n)?(e.push("google_extended_ua"),t="gemini"):/Applebot-Extended\//i.test(n)?(e.push("applebot_extended_ua"),t="apple"):/Applebot\//i.test(n)?(e.push("applebot_ua"),t="apple"):/Meta-ExternalAgent\/|Meta-ExternalFetcher\//i.test(n)?(e.push("meta_externalagent_ua"),t="meta"):/bingbot\/|BingPreview\//i.test(n)&&(e.push("bingbot_ua"),t="bing"),e.length===0?null:{platform:t,confidence:.99,signals:e}}function W(){let e=[],t=0,r;try{let n=navigator.userAgent,i=/Macintosh|Mac OS X/i.test(n),s=/Windows NT/i.test(n),a=navigator.userAgentData;a&&a.platform==="Linux"&&(i||s)&&(e.push("platform_mismatch"),t+=.5,r="chatgpt");}catch{}try{let n=navigator.userAgent,i=navigator.userAgentData;if(i&&Array.isArray(i.brands)&&/Chrome\//i.test(n)){let s=i.brands.map(u=>u.brand),a=s.some(u=>/Google Chrome/i.test(u)),c=s.some(u=>/Chromium/i.test(u));!a&&c&&(e.push("brand_list_anomaly"),t+=.4,r=r??"chatgpt");}}catch{}try{let n=navigator.userAgent;/Mobile|Android|iPhone/i.test(n)&&navigator.maxTouchPoints===0&&(e.push("touch_mismatch"),t+=.3);}catch{}try{(screen.width===0||screen.height===0)&&(e.push("zero_screen"),t+=.4),screen.colorDepth===0&&(e.push("zero_colordepth"),t+=.3);}catch{}try{window.innerWidth>0&&window.outerWidth>0&&window.outerWidth<window.innerWidth&&(e.push("viewport_mismatch"),t+=.2);}catch{}try{"Notification"in window||(e.push("missing_notification_api"),t+=.2);}catch{}try{let n=document.createElement("canvas"),i=n.getContext("webgl")||n.getContext("experimental-webgl");if(i){let s=i.getExtension("WEBGL_debug_renderer_info"),c=String(s?i.getParameter(s.UNMASKED_RENDERER_WEBGL):i.getParameter(i.RENDERER)).toLowerCase();(c.includes("swiftshader")||c.includes("swangle")||c.includes("llvmpipe")||c.includes("mesa offscreen"))&&(e.push("webgl_software_renderer"),t+=.45);}else i===null&&(e.push("missing_webgl"),t+=.25);}catch{}try{let n=document.createElement("canvas");n.width=32,n.height=16;let i=n.getContext("2d");if(i){i.textBaseline="top",i.font='13px "Arial"',i.fillStyle="#f60",i.fillRect(0,0,32,16),i.fillStyle="#069",i.fillText("serge:ag",1,1);let s=i.getImageData(0,0,32,16).data,a=0,c=0;for(let d=0;d<s.length;d+=4)s[d]===0&&s[d+1]===0&&s[d+2]===0&&a++,s[d]>c&&(c=s[d]);a/(s.length/4)>.95&&c<20&&(e.push("canvas_flat_render"),t+=.2);}}catch{}try{let n=navigator.userAgentData;if(n&&typeof n.getHighEntropyValues=="function"){let i=navigator.userAgent,s=/Chrome\/(\d+)/i.exec(i);if(s&&Array.isArray(n.brands)){let a=Number(s[1]),c=n.brands.find(u=>/Chrome/i.test(u.brand)&&!/Chromium/i.test(u.brand));if(c&&typeof c.version=="string"){let u=Number(c.version.split(".")[0]);Number.isFinite(a)&&Number.isFinite(u)&&Math.abs(a-u)>2&&(e.push("ua_version_major_mismatch"),t+=.35);}}}}catch{}return t=Math.min(t,1),{signals:e,score:t,platform:r}}var o=null,L=false,b=null;function me(){return {mouseMoveCount:0,scrollEvents:0,clickCount:0,clickTimestamps:[],anyHoverDetected:false,firstInteractionMs:null,pageLoadTime:performance.now()}}function G(){o&&o.mouseMoveCount++;}function K(){o&&(o.anyHoverDetected=true);}function q(){o&&o.scrollEvents++;}function z(){if(!o)return;let e=performance.now();o.clickCount++,o.clickTimestamps.push(e),o.firstInteractionMs===null&&(o.firstInteractionMs=e-o.pageLoadTime);}function he(){document.removeEventListener("mousemove",G),document.removeEventListener("mouseover",K),document.removeEventListener("scroll",q),document.removeEventListener("click",z),L=false;}function ve(){if(!o)return {signals:[],score:0};let e=[],t=0;if(o.mouseMoveCount===0&&o.clickCount>0&&(e.push("no_mouse"),t+=.3),o.firstInteractionMs!==null&&o.firstInteractionMs<100&&(e.push("fast_interaction"),t+=.4),o.clickCount>0&&!o.anyHoverDetected&&o.mouseMoveCount===0&&(e.push("no_hover"),t+=.2),o.clickTimestamps.length>=3){let r=[];for(let a=1;a<o.clickTimestamps.length;a++)r.push(o.clickTimestamps[a]-o.clickTimestamps[a-1]);let n=r.reduce((a,c)=>a+c,0)/r.length,i=r.reduce((a,c)=>a+(c-n)**2,0)/r.length;(n>0?Math.sqrt(i)/n:0)<.1&&(e.push("mechanical_timing"),t+=.3);}return o.scrollEvents===0&&document.documentElement.scrollHeight>window.innerHeight*2&&o.clickCount>0&&(e.push("no_scroll"),t+=.15),{signals:e,score:Math.min(t,1)}}function $(e=5e3){return L?new Promise(t=>{b=t;}):(o=me(),L=true,document.addEventListener("mousemove",G,{passive:true}),document.addEventListener("mouseover",K,{passive:true}),document.addEventListener("scroll",q,{passive:true}),document.addEventListener("click",z,{passive:true}),new Promise(t=>{b=t,setTimeout(()=>{he();let r=ve();o=null,b&&(b(r),b=null);},e);}))}function Y(){let e=j();if(e&&e.confidence>=.85)return {immediate:e,refine:Promise.resolve(e)};let t=W(),r=[...e?.signals??[],...t.signals],n=e?.platform??t.platform??"unknown",i=Math.min((e?.confidence??0)*.5+t.score,1),s={platform:n,confidence:i,signals:[...r]};if(i>=.85)return {immediate:s,refine:Promise.resolve(s)};let a=$(5e3).then(c=>{let u=[...r,...c.signals],d=c.score,I;return i>0?I=Math.min(.3*i+.7*d,1):I=d*.85,{platform:n,confidence:I,signals:u,behavioralScore:d}});return {immediate:s,refine:a}}var g="";function D(e){g=e;}function m(e){if(!g)return;let t=JSON.stringify(e);J(t).catch(()=>{setTimeout(()=>J(t).catch(()=>{}),2e3);});}function J(e){return fetch(g,{method:"POST",body:e,headers:{"Content-Type":"text/plain"},keepalive:true,credentials:"omit"}).then(t=>{if(!t.ok&&t.status!==429)throw new Error(`HTTP ${t.status}`)})}function F(e){if(!g)return;let t=JSON.stringify(e);if(typeof fetchLater=="function")try{fetchLater(g,{method:"POST",body:t,headers:{"Content-Type":"text/plain"},activateAfter:0,credentials:"omit"});return}catch{}try{navigator.sendBeacon(g,t);}catch{fetch(g,{method:"POST",body:t,headers:{"Content-Type":"text/plain"},keepalive:true,credentials:"omit"}).catch(()=>{});}}var M="",X=null;function Q(e){if(X=e,M=location.pathname+location.search,"navigation"in window)window.navigation.addEventListener("navigatesuccess",()=>{_();});else {let t=history.pushState,r=history.replaceState;history.pushState=function(...n){t.apply(this,n),_();},history.replaceState=function(...n){r.apply(this,n),_();},window.addEventListener("popstate",_);}window.addEventListener("pageshow",t=>{t.persisted&&_();});}function _(){let e=location.pathname+location.search;e!==M&&(M=e,X?.());}var ye="serge_signals";function P(e){let t=`${e}/api/v1/signals`;fetch(t,{method:"GET",credentials:"omit"}).then(r=>{if(r.ok)return r.json()}).then(r=>{if(r)try{let n={data:r,fetchedAt:Date.now()};sessionStorage.setItem(ye,JSON.stringify(n));}catch{}}).catch(()=>{});}var Z="_serge_consent",h=null;function be(){try{let e=window.localStorage.getItem(Z);if(e==="granted"||e==="denied")return e}catch{}return "unknown"}function _e(e){if(e!=="unknown")try{window.localStorage.setItem(Z,e);}catch{}}function ee(){return h!==null||(h=be()),h}function te(e){return e!=="granted"&&e!=="denied"&&e!=="unknown"?h??"unknown":(h=e,_e(e),e)}var re="serge-traps-root",ie="serge_trap_config";var we=new Set(["div","span","p","a","button","select","option","h1","h2","h3","h4","h5","h6","ul","li","img","form","input","label"]),Se=new Set(["role","aria-label","aria-hidden","aria-describedby","aria-labelledby","tabindex","href","target","rel","type","name","value","placeholder","src","alt","data-trap","data-trap-kind","data-trap-token"]),Ee=new Set(["position","top","left","right","bottom","width","height","overflow","visibility","opacity","clip-path","clip","pointer-events","color","background","background-color","font-size"]),Ce=new Set(["click","change","submit","mouseover","focus"]);function oe(e){let t=String(e.tag??"").toLowerCase();if(!we.has(t))return null;let r=document.createElement(t);if(e.attrs&&typeof e.attrs=="object"){for(let[n,i]of Object.entries(e.attrs))if(Se.has(n)&&typeof i=="string")try{r.setAttribute(n,i);}catch{}}if(e.style&&typeof e.style=="object"){for(let[n,i]of Object.entries(e.style))if(Ee.has(n)&&typeof i=="string")try{r.style.setProperty(n,i);}catch{}}if(typeof e.text=="string"&&r.appendChild(document.createTextNode(e.text)),Array.isArray(e.children))for(let n of e.children){let i=oe(n);i&&r.appendChild(i);}return r}function Te(e){try{if("sendBeacon"in navigator){navigator.sendBeacon(e);return}}catch{}try{fetch(e,{method:"GET",mode:"no-cors",credentials:"omit",keepalive:!0}).catch(()=>{});}catch{}}function Ae(e,t){for(let r of t.triggers){if(!Ce.has(r.event))continue;let n=e.querySelector(r.selector);n&&n.addEventListener(r.event,()=>{Te(t.trapUrl);},{once:true,capture:true});}}function xe(){try{let e=sessionStorage.getItem(ie);if(!e)return null;let t=JSON.parse(e);return Date.now()-t.fetchedAt>36e5?null:t.data}catch{return null}}function ke(e){try{let t={data:e,fetchedAt:Date.now()};sessionStorage.setItem(ie,JSON.stringify(t));}catch{}}function O(){return !!document.getElementById(re)}function ne(e){if(O()||!e||!Array.isArray(e.traps)||e.traps.length===0)return;let t=document.createElement("div");t.id=re,t.setAttribute("aria-hidden","true"),t.setAttribute("data-serge-traps","1"),t.style.setProperty("position","absolute"),t.style.setProperty("width","1px"),t.style.setProperty("height","1px"),t.style.setProperty("overflow","hidden"),t.style.setProperty("clip-path","inset(50%)");for(let r of e.traps){let n=oe(r.root);n&&(n.setAttribute("data-serge-trap-id",String(r.id??"")),n.setAttribute("data-serge-trap-kind",String(r.kind??"")),t.appendChild(n),Ae(n,r));}document.body?document.body.appendChild(t):document.addEventListener("DOMContentLoaded",()=>document.body&&document.body.appendChild(t),{once:true});}function B(e){if(e.enabled===false||O())return;let t=xe();t&&ne(t);let r=`${e.apiBase}/api/v1/trap-config?s=${encodeURIComponent(e.siteId)}&sid=${encodeURIComponent(e.sessionId)}`;fetch(r,{method:"GET",credentials:"omit"}).then(n=>n.ok?n.json():null).then(n=>{!n||!Array.isArray(n.traps)||(ke(n),O()||ne(n));}).catch(()=>{});}var Re=new Set(["access_token","api_key","apikey","auth","authorization","bearer","code","cvc","cvv","email","id_token","jwt","key","oauth_token","one_time_password","otp","password","pin","refresh_token","reset_token","secret","session","sig","signature","ssn","token"]);function se(e){if(!e)return "";if(!e.includes("?"))return e;let t,r=false;try{e.startsWith("/")||!/^[a-z]+:\/\//i.test(e)&&!e.startsWith("//")?(t=new URL(e,"https://placeholder.invalid"),r=!0):t=new URL(e);}catch{return e}if(t.searchParams.size===0)return e;let n=false;for(let i of Array.from(t.searchParams.keys()))Re.has(i.toLowerCase())&&(t.searchParams.delete(i),n=true);return n?r?t.pathname+t.search+t.hash:t.toString():e}var ae="_s_sid",v=null;function Ie(){let e=new Uint8Array(12);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(36).padStart(2,"0")).join("")}function w(){if(v)return v;try{let e=sessionStorage.getItem(ae);if(e)return v=e,e}catch{}v=Ie();try{sessionStorage.setItem(ae,v);}catch{}return v}function N(){let e=Date.now().toString(36),t=new Uint8Array(4);crypto.getRandomValues(t);let r=Array.from(t,n=>n.toString(36).padStart(2,"0")).join("");return `${e}-${r}`}var ce="0.2.2";var A=false,l=null,f=null,E=0,x="",U=0,S=false,p="unknown",y="",V=false;function k(){return l?l.debug?true:!!f&&f.confidence>=.35:false}function R(){return typeof window<"u"&&typeof document<"u"}function ot(e,t={}){if(!R())return;if(!e){t.debug&&console.error("[serge] init(token) requires a token");return}let r=performance.now(),n=(t.apiBase??"https://serge.ai").replace(/\/$/,"");y=n,D(`${n}/api/ingest/snippet`),l={token:e,apiBase:n,debug:t.debug??false,requireConsent:t.requireConsent??false,defaults:t.defaults},A=true,U=performance.now()-r,!(l.requireConsent&&(p=ee(),p==="denied"||p!=="granted"))&&(de(),B({apiBase:n,siteId:e,sessionId:w()}),P(n),document.visibilityState==="hidden"||document.visibilityState==="prerender"?document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&T();},{once:true}):T());}function de(){if(!l)return;let e={s:l.token,sid:w(),eid:N(),ap:"unknown",ac:0,ds:[],et:"init",ts:new Date().toISOString(),u:location.pathname,t:document.title||void 0};m(e);}async function T(){if(!l)return;E=performance.now(),x=location.pathname;let{immediate:e,refine:t}=Y();f=e,(l.debug||e.confidence>=.35)&&(ue(),H(),le());let r=await t;f=r,!l.debug&&e.confidence<.35&&r.confidence>=.35&&(ue(),H(),le());}function le(){V||(V=true,Q(fe),document.addEventListener("visibilitychange",De),window.addEventListener("pagehide",Me));}function C(e,t){if(!l||!f)throw new Error("[serge] tracker not initialized \u2014 call init() first");let r=document.referrer?se(document.referrer):"";return {s:l.token,sid:w(),eid:N(),ap:f.platform,ac:Math.round(f.confidence*100)/100,ds:f.signals,bs:f.behavioralScore!=null?Math.round(f.behavioralScore*100)/100:void 0,et:e,ts:new Date().toISOString(),u:location.pathname,r:r||void 0,t:document.title||void 0,...t}}function ue(){S=false;let e={init_ms:Math.round(U),v:ce,sv:3};l?.defaults&&(e.d=l.defaults),m(C("session_start",{vw:window.innerWidth,vh:window.innerHeight,sw:screen.width,sh:screen.height,ua:navigator.userAgent,p:Pe(),l:navigator.language,_m:e}));}function H(){E=performance.now(),x=location.pathname,m(C("page_view"));}function fe(){if(!k())return;let e=Math.round(performance.now()-E);m(C("route_change",{u:x,tp:e,sd:ge()})),H(),S=false;}function De(){document.visibilityState==="hidden"&&pe();}function Me(){pe();}function pe(){if(S||!k())return;S=true;let e=Math.round(performance.now()-E);F(C("session_end",{tp:e,sd:ge()}));}function ge(){let e=document.documentElement.scrollHeight,t=window.innerHeight;if(e<=t)return 100;let r=window.scrollY||document.documentElement.scrollTop;return Math.min(100,Math.round((r+t)/e*100))}function Pe(){try{return navigator.userAgentData?.platform??navigator.platform??""}catch{return ""}}function st(e,t){!R()||!A||k()&&m(C("semantic_action",{ix:[{t:e,tg:JSON.stringify(t??{}),o:0,ok:true}]}));}function at(e){if(!R())return;let t=p;p=te(e),!(!l||!l.requireConsent)&&t!==p&&p==="granted"&&t!=="granted"&&(y!==""&&P(y),de(),y!==""&&B({apiBase:y,siteId:l.token,sessionId:w()}),document.visibilityState==="hidden"||document.visibilityState==="prerender"?document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&T();},{once:true}):T());}function ct(){!R()||!A||k()&&fe();}function lt(){A=false,l=null,f=null,E=0,x="",U=0,S=false,p="unknown",y="",V=false,D("");}
|
|
2
2
|
exports.SDK_VERSION=ce;exports._resetForTesting=lt;exports.consent=at;exports.init=ot;exports.pageview=ct;exports.track=st;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/detect/tier1.ts","../src/detect/tier2.ts","../src/detect/tier3.ts","../src/detect/score.ts","../src/transport.ts","../src/spa.ts","../src/signals.ts","../src/consent.ts","../src/honeypot.ts","../src/scrub.ts","../src/session.ts","../src/core/version.ts","../src/core/index.ts"],"names":["detectTier1","signals","platform","EXTENSION_ARTIFACTS","artifact","ua","detectTier2","score","claimsMac","claimsWindows","uaData","brandNames","b","hasGoogleChrome","n","hasChromium","canvas","gl","ext","rendererLc","ctx","data","zeroCount","maxVal","i","match","claimedMajor","chromeBrand","brandMajor","state","observing","resolvePromise","initState","onMouseMove","onMouseOver","onScroll","onClick","now","removeListeners","computeScore","intervals","mean","variance","sum","v","observeBehavior","durationMs","resolve","result","runDetection","tier1","tier2","tier12Confidence","immediate","refine","behavioral","allSignals","behavioralScore","finalConfidence","endpoint","setEndpoint","url","sendEvent","event","body","doSend","res","sendBeaconEvent","lastUrl","onNav","initSpaDetection","callback","handleNavigation","originalPushState","originalReplaceState","args","e","currentUrl","STORAGE_KEY","fetchSignalUpdates","apiBase","cached","readStorage","writeStorage","value","getInitialConsent","setConsent","WRAPPER_ID","ALLOWED_TAGS","ALLOWED_ATTRS","ALLOWED_STYLE","ALLOWED_EVENTS","buildElement","spec","tag","el","name","prop","child","built","fireTrap","trapUrl","attachTriggers","root","variant","trigger","target","readCachedConfig","stored","writeCachedConfig","config","alreadyInjected","injectAll","wrapper","injectHoneypots","options","SENSITIVE_QUERY_KEYS","scrubUrl","input","parsed","isRelative","mutated","key","SESSION_KEY","sessionId","generateId","bytes","getSessionId","generateEventId","ts","rand","SDK_VERSION","injected","detection","pageLoadTime","initMs","sessionEndSent","consentState","apiBaseRef","listenersAttached","shouldTrack","isBrowser","init","token","startTime","sendInitPing","startTracking","sendSessionStart","sendPageviewInternal","setupListeners","refined","onNavigation","onVisibilityChange","onPageHide","createEvent","eventType","extra","meta","getNavigatorPlatform","timeOnPage","getScrollDepth","sendSessionEnd","docHeight","viewHeight","scrollTop","track","properties","consent","previous","pageview","_resetForTesting"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { DetectionResult } from '../types'\n\n/**\n * Tier 1: Deterministic identity signals.\n * If any fire, classify immediately with high confidence.\n * Zero false positives expected.\n */\nexport function detectTier1(): DetectionResult | null {\n const signals: string[] = []\n let platform = 'unknown'\n\n // 1. navigator.webdriver — unmasked automation\n // Stealth plugins patch this to false, so absence means nothing.\n // But if true, it's definitive.\n try {\n if (navigator.webdriver === true) {\n signals.push('webdriver')\n }\n } catch {\n // Property access failed — unusual, treat as signal\n signals.push('webdriver_error')\n }\n\n // 2. Playwright globals\n if ('__playwright__binding__' in window || '__pwInitScripts' in window) {\n signals.push('playwright_globals')\n platform = 'browser_use' // Playwright-based agents (browser-use, etc.)\n }\n\n // 3. In-Chrome agent extension DOM artifacts.\n //\n // The extension-driven agents (Claude-in-Chrome, Perplexity\n // Comet, ChatGPT Atlas extension) all run inside the user's\n // own Chrome, so UA/webdriver/playwright globals are useless —\n // they're real Chrome. What they DO leave is injected DOM:\n // stop-controls, animation styles, status toasts, root\n // containers. Detecting those artifacts is the only reliable\n // way to attribute a Chrome-UA session to a specific extension.\n //\n // Registry pattern so new artifacts (different versions, other\n // extensions) can be added without changing the flow. Selectors\n // must be safe against false positives on legitimate sites —\n // stick to `[id^=\"...\"]` prefixes that match the extension's\n // own internal naming scheme and would be absurd to find on a\n // normal store.\n const EXTENSION_ARTIFACTS: ReadonlyArray<{\n selector: string\n signal: string\n platform: string\n }> = [\n /* Claude Chrome extension — `claude-agent-stop-container`,\n * `claude-agent-animation-styles`, and any future\n * `claude-agent-*` the extension might inject. Validated\n * against CHEQ's 2025 research + observed production\n * events. */\n { selector: '[id^=\"claude-agent-\"]', signal: 'claude_dom', platform: 'claude' },\n /* Perplexity Comet — the browser's extension root uses the\n * `comet-` id prefix for its overlay and toolbar containers.\n * Speculative until field-verified: selectors marked\n * TODO-research can be tightened once we have observed\n * events from Comet sessions on a production site. */\n { selector: '[id^=\"comet-agent-\"]', signal: 'comet_dom', platform: 'perplexity' },\n { selector: '[id^=\"perplexity-comet-\"]', signal: 'comet_dom_alt', platform: 'perplexity' },\n /* ChatGPT Atlas extension — injects a status indicator for\n * the agent's current action. Same TODO-research caveat. */\n { selector: '[id^=\"chatgpt-atlas-\"]', signal: 'atlas_dom', platform: 'chatgpt' },\n { selector: '[id^=\"openai-atlas-\"]', signal: 'atlas_dom_alt', platform: 'chatgpt' },\n ]\n for (const artifact of EXTENSION_ARTIFACTS) {\n if (document.querySelector(artifact.selector)) {\n signals.push(artifact.signal)\n platform = artifact.platform\n /* Keep scanning — multiple extensions CAN coexist in a\n * browser, and if we see both Claude and Atlas artifacts\n * the signals list will record both. Last-wins on\n * platform is fine because detection confidence stays at\n * 0.99 either way. */\n }\n }\n\n // 4. Known agent user-agents (self-identifying crawlers + browse\n // sessions). Order matters — most specific first so we don't\n // catch a generic substring inside a more specific UA.\n //\n // SOURCE OF TRUTH: `shared/lib/agent-ua-patterns.ts`. The\n // server-side pixel route uses the same rules. When you add\n // a new agent here, add it there too (and vice versa).\n // A future refactor will let the snippet build import from\n // shared/ directly; until then, keep both lists in sync.\n //\n // Coverage (May 2026):\n // OpenAI: GPTBot, OAI-SearchBot, ChatGPT-User, Operator\n // Anthropic: ClaudeBot, Claude-User, claude-web, Claude-SearchBot, Claude-Code\n // Perplexity: PerplexityBot, Perplexity-User\n // Google: Google-Extended (Gemini training)\n // Microsoft: bingbot, BingPreview, MicrosoftPreview\n // Apple: Applebot, Applebot-Extended (Apple Intelligence)\n // Meta: Meta-ExternalAgent, Meta-ExternalFetcher, FacebookBot\n //\n // Spoofed-UA agents (ChatGPT Atlas, Claude in Chrome via\n // extension, Perplexity Comet) use a generic Chrome UA — those\n // fall through to Tier 2's platform-mismatch heuristic and to\n // the DOM check above.\n const ua = navigator.userAgent\n if (/PerplexityBot\\/|Perplexity-User\\//i.test(ua)) {\n signals.push('perplexity_ua')\n platform = 'perplexity'\n } else if (/OAI-SearchBot\\//i.test(ua)) {\n signals.push('oai_searchbot_ua')\n platform = 'chatgpt'\n } else if (/ChatGPT-User\\//i.test(ua)) {\n signals.push('chatgpt_user_ua')\n platform = 'chatgpt'\n } else if (/Operator\\//i.test(ua)) {\n signals.push('operator_ua')\n platform = 'chatgpt'\n } else if (/GPTBot\\//i.test(ua)) {\n signals.push('gptbot_ua')\n platform = 'chatgpt'\n } else if (/ClaudeBot\\//i.test(ua)) {\n signals.push('claudebot_ua')\n platform = 'claude'\n } else if (/Claude-User\\/|claude-web\\//i.test(ua)) {\n signals.push('claude_user_ua')\n platform = 'claude'\n } else if (/Claude-SearchBot\\//i.test(ua)) {\n signals.push('claude_searchbot_ua')\n platform = 'claude'\n } else if (/Claude-Code\\//i.test(ua)) {\n signals.push('claude_code_ua')\n platform = 'claude'\n } else if (/Google-Extended\\//i.test(ua)) {\n signals.push('google_extended_ua')\n platform = 'gemini'\n } else if (/Applebot-Extended\\//i.test(ua)) {\n signals.push('applebot_extended_ua')\n platform = 'apple'\n } else if (/Applebot\\//i.test(ua)) {\n signals.push('applebot_ua')\n platform = 'apple'\n } else if (/Meta-ExternalAgent\\/|Meta-ExternalFetcher\\//i.test(ua)) {\n signals.push('meta_externalagent_ua')\n platform = 'meta'\n } else if (/bingbot\\/|BingPreview\\//i.test(ua)) {\n signals.push('bingbot_ua')\n platform = 'bing'\n }\n\n if (signals.length === 0) return null\n\n return {\n platform,\n confidence: 0.99,\n signals,\n }\n}\n","/**\n * Tier 2: Heuristic identity signals.\n * Instant checks, low false positive rate.\n * Returns signal names and a score contribution (0-1).\n */\n\ninterface Tier2Result {\n signals: string[]\n score: number // 0-1\n platform?: string\n}\n\nexport function detectTier2(): Tier2Result {\n const signals: string[] = []\n let score = 0\n let platform: string | undefined\n\n // 1. Sec-Ch-Ua-Platform mismatch (ChatGPT Agent / Operator / Atlas\n // fingerprint per Castle's research — see\n // blog.castle.io/from-puppeteer-stealth-to-nodriver/).\n //\n // The agent runs on Linux infrastructure but spoofs a desktop\n // UA so e-commerce sites don't reject it. The Sec-Ch-Ua-Platform\n // client hint isn't easily forged at the same place the UA is\n // rewritten, so a UA that claims macOS or Windows while\n // userAgentData.platform reports \"Linux\" is a near-certain tell.\n // Score is 0.5 (up from 0.4 in v1) — this signal is high\n // precision in the field and should anchor classification when\n // no Tier 1 UA fired.\n try {\n const ua = navigator.userAgent\n const claimsMac = /Macintosh|Mac OS X/i.test(ua)\n const claimsWindows = /Windows NT/i.test(ua)\n // @ts-expect-error -- userAgentData is not in all TS lib definitions\n const uaData = navigator.userAgentData\n if (uaData && uaData.platform === 'Linux' && (claimsMac || claimsWindows)) {\n signals.push('platform_mismatch')\n score += 0.5\n platform = 'chatgpt'\n }\n } catch {\n // userAgentData not available\n }\n\n // 2. Sec-Ch-Ua brand-list anomaly: ChatGPT Agent's brand list is\n // `\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\"` — no \"Google Chrome\"\n // entry even though the UA claims Chrome. Real Chrome always\n // includes a \"Google Chrome\" brand in the hint. Castle / Simon\n // Willison documented this in mid-2025.\n try {\n const ua = navigator.userAgent\n // @ts-expect-error -- userAgentData typing\n const uaData = navigator.userAgentData\n if (uaData && Array.isArray(uaData.brands) && /Chrome\\//i.test(ua)) {\n const brandNames = uaData.brands.map((b: { brand: string }) => b.brand)\n const hasGoogleChrome = brandNames.some((n: string) => /Google Chrome/i.test(n))\n const hasChromium = brandNames.some((n: string) => /Chromium/i.test(n))\n if (!hasGoogleChrome && hasChromium) {\n signals.push('brand_list_anomaly')\n score += 0.4\n platform = platform ?? 'chatgpt'\n }\n }\n } catch {\n // userAgentData.brands not available\n }\n\n // 2. maxTouchPoints + mobile UA mismatch\n try {\n const ua = navigator.userAgent\n const claimsMobile = /Mobile|Android|iPhone/i.test(ua)\n if (claimsMobile && navigator.maxTouchPoints === 0) {\n signals.push('touch_mismatch')\n score += 0.3\n }\n } catch {\n // Ignore\n }\n\n // 3. Screen resolution anomalies\n try {\n if (screen.width === 0 || screen.height === 0) {\n signals.push('zero_screen')\n score += 0.4\n }\n // Common headless default: 800x600 with 0 colorDepth\n if (screen.colorDepth === 0) {\n signals.push('zero_colordepth')\n score += 0.3\n }\n } catch {\n // Ignore\n }\n\n // 4. Viewport inconsistencies\n try {\n if (\n window.innerWidth > 0 &&\n window.outerWidth > 0 &&\n window.outerWidth < window.innerWidth\n ) {\n signals.push('viewport_mismatch')\n score += 0.2\n }\n } catch {\n // Ignore\n }\n\n // 5. Missing browser APIs that real browsers always have\n try {\n // Notification API — present in all real browsers, often missing in headless\n if (!('Notification' in window)) {\n signals.push('missing_notification_api')\n score += 0.2\n }\n } catch {\n // Ignore\n }\n\n // 6. WebGL renderer string. Real user Chrome on consumer hardware\n // exposes a concrete GPU name — \"ANGLE (Apple, Apple M3 Pro,\n // OpenGL 4.1)\", \"ANGLE (NVIDIA, NVIDIA GeForce RTX 3080...)\",\n // \"ANGLE (Intel, Intel(R) Iris Plus Graphics...)\". Managed\n // headless Chrome on server infra (Browserbase, Steel,\n // Azure-hosted Operator, Cloud Run with Xvfb) falls back to\n // software rasterizers with telltale names: \"Google\n // SwiftShader\", \"SwANGLE\", \"ANGLE (Google, Vulkan 1.3.0\n // (SwiftShader Device…\"), or \"llvmpipe\" on Mesa.\n try {\n const canvas = document.createElement('canvas')\n const gl = (canvas.getContext('webgl') ||\n canvas.getContext('experimental-webgl')) as WebGLRenderingContext | null\n if (gl) {\n const ext = gl.getExtension('WEBGL_debug_renderer_info')\n // getParameter for UNMASKED_RENDERER_WEBGL is the actual GPU\n // string; gating on ext presence keeps the type signature\n // clean across browsers that block the extension.\n const renderer =\n ext\n ? String(gl.getParameter((ext as { UNMASKED_RENDERER_WEBGL: number }).UNMASKED_RENDERER_WEBGL))\n : String(gl.getParameter(gl.RENDERER))\n const rendererLc = renderer.toLowerCase()\n if (\n rendererLc.includes('swiftshader') ||\n rendererLc.includes('swangle') ||\n rendererLc.includes('llvmpipe') ||\n rendererLc.includes('mesa offscreen')\n ) {\n signals.push('webgl_software_renderer')\n score += 0.45\n // No platform pinning — many agent fleets (Browserbase, Steel,\n // Operator infra, user's own headless Chrome) share this\n // signature. Attribution happens at the next layer up via IP\n // or referrer, with this as a boost for \"is it agent infra\".\n }\n } else if (gl === null) {\n // No WebGL at all is a strong headless tell — real Chrome has\n // WebGL on even on low-end hardware via software fallback.\n signals.push('missing_webgl')\n score += 0.25\n }\n } catch {\n // Ignore — private mode or policy can block canvas/WebGL.\n }\n\n // 7. Canvas fingerprint coarseness. Real hardware rasterization\n // produces per-GPU micro-variations in anti-aliasing. Pure\n // software rasterizers in agent infra produce a VERY small set\n // of canvas hashes — specifically, the output is indistinguishable\n // from every other SwiftShader instance running the same draw.\n // We can't ship a hash DB in the snippet, but we can probe for\n // the simpler tell: the \"consistent zero-bit at specific pixels\"\n // that indicates no sub-pixel jitter. Done inline to keep the\n // snippet tiny.\n try {\n const canvas = document.createElement('canvas')\n canvas.width = 32\n canvas.height = 16\n const ctx = canvas.getContext('2d')\n if (ctx) {\n ctx.textBaseline = 'top'\n ctx.font = '13px \"Arial\"'\n ctx.fillStyle = '#f60'\n ctx.fillRect(0, 0, 32, 16)\n ctx.fillStyle = '#069'\n ctx.fillText('serge:ag', 1, 1)\n const data = ctx.getImageData(0, 0, 32, 16).data\n let zeroCount = 0\n let maxVal = 0\n for (let i = 0; i < data.length; i += 4) {\n if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0) zeroCount++\n if (data[i] > maxVal) maxVal = data[i]\n }\n // SwiftShader outputs have a narrow distribution — far more\n // exact-zero pixels from the clear/fill boundary than GPU AA.\n const zeroRatio = zeroCount / (data.length / 4)\n if (zeroRatio > 0.95 && maxVal < 20) {\n signals.push('canvas_flat_render')\n score += 0.2\n }\n }\n } catch {\n // Ignore\n }\n\n // 8. Sec-CH-UA-Full-Version-List + Arch + Bitness + Model.\n // Requires `accept-ch` on the HTML response to be delivered —\n // when present it's a richer surface than brand+version.\n // Two patterns to flag:\n // (a) UA claims Chrome X, but the full-version-list entry for\n // \"Google Chrome\" reports a different major. Real Chrome\n // always agrees. Spoofing agents patch the UA string but\n // not the high-entropy client hint.\n // (b) The arch doesn't match the UA. \"Windows NT 10.0; Win64;\n // x64\" but arch='arm' is a headless emulation tell.\n try {\n // @ts-expect-error — client hints API typing lags in TS lib.dom.\n const uaData = navigator.userAgentData\n if (uaData && typeof uaData.getHighEntropyValues === 'function') {\n // High-entropy hints are a Promise. Fire-and-forget via an\n // unawaited call — we push synthesised signals into the shared\n // `signals` array synchronously only when already-resolved\n // hints contradict the UA. The *async* version of this check\n // folds into a Tier-2.5 observation that score.ts awaits.\n // Sync cheap path: check the brand-major against UA version.\n const ua = navigator.userAgent\n const match = /Chrome\\/(\\d+)/i.exec(ua)\n if (match && Array.isArray(uaData.brands)) {\n const claimedMajor = Number(match[1])\n const chromeBrand = uaData.brands.find((b: { brand: string }) =>\n /Chrome/i.test(b.brand) && !/Chromium/i.test(b.brand),\n )\n if (chromeBrand && typeof chromeBrand.version === 'string') {\n const brandMajor = Number(chromeBrand.version.split('.')[0])\n if (\n Number.isFinite(claimedMajor) &&\n Number.isFinite(brandMajor) &&\n Math.abs(claimedMajor - brandMajor) > 2\n ) {\n signals.push('ua_version_major_mismatch')\n score += 0.35\n }\n }\n }\n }\n } catch {\n // Ignore — client hints blocked or unavailable.\n }\n\n // Cap at 1.0\n score = Math.min(score, 1)\n\n return { signals, score, platform }\n}\n","/**\n * Tier 3: Behavioral signals.\n * Observed over a 5-second window after page load.\n * Classification happens client-side — only the score and signal names\n * are sent to the server. Raw behavioral data never leaves the browser.\n */\n\ninterface BehavioralResult {\n signals: string[]\n score: number // 0-1\n}\n\ninterface BehavioralState {\n mouseMoveCount: number\n scrollEvents: number\n clickCount: number\n clickTimestamps: number[]\n /** True if at least one hover event occurred anywhere in the session */\n anyHoverDetected: boolean\n firstInteractionMs: number | null\n pageLoadTime: number\n}\n\nlet state: BehavioralState | null = null\nlet observing = false\nlet resolvePromise: ((result: BehavioralResult) => void) | null = null\n\nfunction initState(): BehavioralState {\n return {\n mouseMoveCount: 0,\n scrollEvents: 0,\n clickCount: 0,\n clickTimestamps: [],\n anyHoverDetected: false,\n firstInteractionMs: null,\n pageLoadTime: performance.now(),\n }\n}\n\nfunction onMouseMove() {\n if (!state) return\n state.mouseMoveCount++\n}\n\nfunction onMouseOver() {\n if (!state) return\n // Track that hover occurred at any point in the session (not per-click)\n state.anyHoverDetected = true\n}\n\nfunction onScroll() {\n if (!state) return\n state.scrollEvents++\n}\n\nfunction onClick() {\n if (!state) return\n const now = performance.now()\n state.clickCount++\n state.clickTimestamps.push(now)\n if (state.firstInteractionMs === null) {\n state.firstInteractionMs = now - state.pageLoadTime\n }\n}\n\nfunction removeListeners() {\n document.removeEventListener('mousemove', onMouseMove)\n document.removeEventListener('mouseover', onMouseOver)\n document.removeEventListener('scroll', onScroll)\n document.removeEventListener('click', onClick)\n observing = false\n}\n\nfunction computeScore(): BehavioralResult {\n if (!state) return { signals: [], score: 0 }\n\n const signals: string[] = []\n let score = 0\n\n // 1. No mouse events at all (strong signal, but also touch devices)\n if (state.mouseMoveCount === 0 && state.clickCount > 0) {\n signals.push('no_mouse')\n score += 0.3\n }\n\n // 2. Impossible timing — interaction <100ms after page load\n if (state.firstInteractionMs !== null && state.firstInteractionMs < 100) {\n signals.push('fast_interaction')\n score += 0.4\n }\n\n // 3. No hover events at all before/during any clicks\n if (state.clickCount > 0 && !state.anyHoverDetected && state.mouseMoveCount === 0) {\n signals.push('no_hover')\n score += 0.2\n }\n\n // 4. Mechanical click timing — low variance between click intervals\n if (state.clickTimestamps.length >= 3) {\n const intervals: number[] = []\n for (let i = 1; i < state.clickTimestamps.length; i++) {\n intervals.push(state.clickTimestamps[i] - state.clickTimestamps[i - 1])\n }\n const mean = intervals.reduce((a, b) => a + b, 0) / intervals.length\n const variance =\n intervals.reduce((sum, v) => sum + (v - mean) ** 2, 0) / intervals.length\n const cv = mean > 0 ? Math.sqrt(variance) / mean : 0\n // Coefficient of variation < 0.1 = very regular timing (human CV is typically 0.3-0.8)\n if (cv < 0.1) {\n signals.push('mechanical_timing')\n score += 0.3\n }\n }\n\n // 5. No scroll events but page is long (agent jumped to content)\n if (\n state.scrollEvents === 0 &&\n document.documentElement.scrollHeight > window.innerHeight * 2 &&\n state.clickCount > 0\n ) {\n signals.push('no_scroll')\n score += 0.15\n }\n\n return { signals, score: Math.min(score, 1) }\n}\n\n/**\n * Start observing behavioral signals for 5 seconds.\n * Returns a promise that resolves with the classification.\n */\nexport function observeBehavior(durationMs = 5000): Promise<BehavioralResult> {\n if (observing) {\n return new Promise((resolve) => {\n resolvePromise = resolve\n })\n }\n\n state = initState()\n observing = true\n\n document.addEventListener('mousemove', onMouseMove, { passive: true })\n document.addEventListener('mouseover', onMouseOver, { passive: true })\n document.addEventListener('scroll', onScroll, { passive: true })\n document.addEventListener('click', onClick, { passive: true })\n\n return new Promise((resolve) => {\n resolvePromise = resolve\n\n setTimeout(() => {\n removeListeners()\n const result = computeScore()\n state = null\n if (resolvePromise) {\n resolvePromise(result)\n resolvePromise = null\n }\n }, durationMs)\n })\n}\n\n","import type { DetectionResult } from '../types'\nimport { detectTier1 } from './tier1'\nimport { detectTier2 } from './tier2'\nimport { observeBehavior } from './tier3'\n\n/**\n * Two-phase detection:\n * - `immediate`: Tier 1+2 results (sync, <1ms). Use this to decide whether to start tracking.\n * - `refine`: Promise that resolves after 5s with Tier 3 behavioral data folded in.\n *\n * This lets the caller send events immediately for high-confidence detections\n * without waiting 5 seconds for behavioral observation.\n */\nexport function runDetection(): {\n immediate: DetectionResult\n refine: Promise<DetectionResult>\n} {\n // Tier 1 — instant deterministic\n const tier1 = detectTier1()\n\n if (tier1 && tier1.confidence >= 0.85) {\n // High confidence from Tier 1 alone — no need for Tier 2 or 3\n return {\n immediate: tier1,\n refine: Promise.resolve(tier1),\n }\n }\n\n // Tier 2 — instant heuristic\n const tier2 = detectTier2()\n\n // Combine Tier 1 + Tier 2 signals\n const signals = [...(tier1?.signals ?? []), ...tier2.signals]\n const platform = tier1?.platform ?? tier2.platform ?? 'unknown'\n const tier12Confidence = Math.min((tier1?.confidence ?? 0) * 0.5 + tier2.score, 1)\n\n const immediate: DetectionResult = {\n platform,\n confidence: tier12Confidence,\n signals: [...signals],\n }\n\n // If already high confidence, skip behavioral\n if (tier12Confidence >= 0.85) {\n return {\n immediate,\n refine: Promise.resolve(immediate),\n }\n }\n\n // Tier 3 — 5-second behavioral observation (async)\n const refine = observeBehavior(5000).then((behavioral): DetectionResult => {\n const allSignals = [...signals, ...behavioral.signals]\n const behavioralScore = behavioral.score\n\n // Composite: weighted combination of Tier 1+2 and Tier 3\n let finalConfidence: number\n if (tier12Confidence > 0) {\n finalConfidence = Math.min(0.3 * tier12Confidence + 0.7 * behavioralScore, 1)\n } else {\n // Behavioral-only: cap at 0.85 (never high-confidence without identity signals)\n finalConfidence = behavioralScore * 0.85\n }\n\n return {\n platform,\n confidence: finalConfidence,\n signals: allSignals,\n behavioralScore,\n }\n })\n\n return { immediate, refine }\n}\n\n","import type { AgentEvent } from './types'\n\nlet endpoint = ''\n\nexport function setEndpoint(url: string) {\n endpoint = url\n}\n\n/**\n * Send an event via fetch with keepalive.\n * Content-Type: text/plain to avoid CORS preflight.\n * Single retry after 2 seconds on failure.\n */\nexport function sendEvent(event: AgentEvent): void {\n if (!endpoint) return\n\n const body = JSON.stringify(event)\n\n doSend(body).catch(() => {\n // Single retry after 2 seconds\n setTimeout(() => doSend(body).catch(() => { /* drop */ }), 2000)\n })\n}\n\nfunction doSend(body: string): Promise<void> {\n return fetch(endpoint, {\n method: 'POST',\n body,\n headers: { 'Content-Type': 'text/plain' },\n keepalive: true,\n /* Defense-in-depth on the cookieless contract. The default fetch\n * credentials policy is `same-origin` — fine for the common case\n * (cross-origin to serge.ai sends no cookies), but a customer\n * configured with first-party proxy `apiBase: '/serge'` puts the\n * request in same-origin mode where the customer's site cookies\n * WOULD be sent by default. Explicit `omit` makes the contract\n * unconditional: the SDK never carries cookies on the wire,\n * regardless of how the customer routes traffic. */\n credentials: 'omit',\n }).then((res) => {\n if (!res.ok && res.status !== 429) {\n throw new Error(`HTTP ${res.status}`)\n }\n })\n}\n\n/**\n * Send event via sendBeacon (for page unload).\n * Does not support retry — best-effort delivery.\n */\nexport function sendBeaconEvent(event: AgentEvent): void {\n if (!endpoint) return\n\n const body = JSON.stringify(event)\n\n // Try fetchLater first (98% reliability in Chrome M134+)\n // @ts-expect-error -- fetchLater is not in TS lib definitions yet\n if (typeof fetchLater === 'function') {\n try {\n // @ts-expect-error -- fetchLater is not in TS lib definitions yet\n fetchLater(endpoint, {\n method: 'POST',\n body,\n headers: { 'Content-Type': 'text/plain' },\n activateAfter: 0, // send immediately on page close\n credentials: 'omit', // cookieless contract — see doSend above\n })\n return\n } catch {\n // Fall through to sendBeacon\n }\n }\n\n /* Fallback: sendBeacon. Note: navigator.sendBeacon does NOT\n * accept a credentials option — it always uses the browser's\n * default same-origin policy. For the cross-origin case (no\n * proxy, direct to serge.ai) this means no cookies sent (correct).\n * For the first-party-proxy case (apiBase: '/serge') the request\n * is same-origin and customer-site cookies WOULD be sent on the\n * sendBeacon call. This is a browser API limitation, not an SDK\n * bug — documented in the README's first-party-proxy section. */\n try {\n navigator.sendBeacon(endpoint, body)\n } catch {\n // Last resort: fire-and-forget fetch\n fetch(endpoint, {\n method: 'POST',\n body,\n headers: { 'Content-Type': 'text/plain' },\n keepalive: true,\n credentials: 'omit', // cookieless contract — see doSend above\n }).catch(() => { /* drop */ })\n }\n}\n","/**\n * SPA navigation detection.\n * Primary: Navigation API (Baseline January 2026).\n * Fallback: History API wrapping + popstate.\n * Also handles bfcache restoration.\n */\n\nlet lastUrl = ''\nlet onNav: (() => void) | null = null\n\nexport function initSpaDetection(callback: () => void): void {\n onNav = callback\n lastUrl = location.pathname + location.search\n\n // Primary: Navigation API (Chrome, Safari, Firefox — Baseline January 2026)\n if ('navigation' in window) {\n const nav = (window as { navigation: EventTarget }).navigation\n nav.addEventListener('navigatesuccess', () => {\n // navigatesuccess fires AFTER the URL has committed — safe to read location\n handleNavigation()\n })\n } else {\n // Fallback: History API wrapping\n const originalPushState = history.pushState\n const originalReplaceState = history.replaceState\n\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState.apply(this, args)\n handleNavigation()\n }\n\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\n originalReplaceState.apply(this, args)\n handleNavigation()\n }\n\n window.addEventListener('popstate', handleNavigation)\n }\n\n // Handle bfcache restoration (back/forward cache)\n window.addEventListener('pageshow', (e) => {\n if (e.persisted) handleNavigation()\n })\n}\n\nfunction handleNavigation() {\n const currentUrl = location.pathname + location.search\n if (currentUrl === lastUrl) return // Deduplicate\n lastUrl = currentUrl\n onNav?.()\n}\n","/**\n * Signal definitions loader — fetches updated signal definitions from the server.\n *\n * Pattern: bundled fallback + async update (Cloudflare Turnstile / Fingerprint Pro).\n * - Detection runs immediately with bundled signals (zero added latency)\n * - This module async-fetches /api/v1/signals and caches in sessionStorage\n * - On subsequent page loads, sessionStorage signals are used immediately\n * - If fetch fails, bundled signals are sufficient\n *\n * The API response is CDN-cached for 24 hours, so this fetch is nearly free.\n */\n\nconst STORAGE_KEY = 'serge_signals'\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\n\ninterface CachedSignals {\n data: unknown\n fetchedAt: number\n}\n\n/**\n * Try to load cached signal definitions from sessionStorage.\n * Returns null if no cached signals or if they're expired.\n */\nexport function getCachedSignals(): unknown | null {\n try {\n const stored = sessionStorage.getItem(STORAGE_KEY)\n if (!stored) return null\n const cached = JSON.parse(stored) as CachedSignals\n if (Date.now() - cached.fetchedAt > CACHE_TTL_MS) return null\n return cached.data\n } catch {\n return null\n }\n}\n\n/**\n * Async-fetch updated signal definitions from the server.\n * Non-blocking — fire and forget. Stores result in sessionStorage for next page load.\n *\n * @param apiBase The API base URL (derived from the snippet's own origin)\n */\nexport function fetchSignalUpdates(apiBase: string): void {\n const url = `${apiBase}/api/v1/signals`\n\n fetch(url, {\n method: 'GET',\n credentials: 'omit', // No cookies needed for public endpoint\n })\n .then((res) => {\n if (!res.ok) return\n return res.json()\n })\n .then((data) => {\n if (!data) return\n try {\n const cached: CachedSignals = {\n data,\n fetchedAt: Date.now(),\n }\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(cached))\n } catch {\n // sessionStorage full or blocked — ignore\n }\n })\n .catch(() => {\n // Fetch failed — no problem, bundled signals are sufficient\n })\n}\n","/**\n * Snippet consent gating — for customers who require explicit opt-in\n * before any tracking script writes storage on their site.\n *\n * Activated by adding `data-require-consent=\"true\"` on the script tag.\n * In that mode, the snippet still loads + reads the script config but\n * does NOT call `startTracking()` or send the init ping until the\n * customer's consent management platform fires:\n *\n * window.serge('consent', 'granted') // start tracking\n * window.serge('consent', 'denied') // session-only, no cookies\n * window.serge('consent', 'unknown') // pending, snippet stays dormant\n *\n * Choice is persisted in localStorage under `_serge_consent` so\n * subsequent page loads inherit the previous answer and don't wait for\n * the CMP to fire again.\n *\n * The localStorage write itself is technically subject to ePrivacy\n * Art. 5(3), but since it stores the consent answer (the regulated\n * action's own metadata, not user data), it is treated as essential\n * by the EDPB FAQ on the EDPB Guidelines 2/2023 — same status as a\n * \"remember-my-choice\" cookie set by the CMP itself.\n */\n\nconst STORAGE_KEY = '_serge_consent'\n\nexport type ConsentValue = 'granted' | 'denied' | 'unknown'\n\nlet cached: ConsentValue | null = null\n\nfunction readStorage(): ConsentValue {\n try {\n const v = window.localStorage.getItem(STORAGE_KEY)\n if (v === 'granted' || v === 'denied') return v\n } catch {\n /* private mode / blocked storage */\n }\n return 'unknown'\n}\n\nfunction writeStorage(value: ConsentValue): void {\n if (value === 'unknown') return\n try {\n window.localStorage.setItem(STORAGE_KEY, value)\n } catch {\n /* never throw on storage rejection */\n }\n}\n\n/** Initial consent state at snippet boot. Used by init() to decide whether\n * to start tracking immediately or sit in the pending state. */\nexport function getInitialConsent(): ConsentValue {\n if (cached !== null) return cached\n cached = readStorage()\n return cached\n}\n\n/** Record the consumer's consent choice. Persists for next visits. */\nexport function setConsent(value: ConsentValue): ConsentValue {\n if (value !== 'granted' && value !== 'denied' && value !== 'unknown') {\n /* Ignore unknown action values — defensive against bad CMP wiring */\n return cached ?? 'unknown'\n }\n cached = value\n writeStorage(value)\n return value\n}\n","/**\n * Agent-detection honeypot — server-driven trap injector + watcher.\n *\n * The trap *definitions* (what to render, what to watch for) live\n * server-side in `server/services/trap-config.ts`. This module ships\n * with the snippet ONCE and renders whatever the server returns from\n * `/api/v1/trap-config?s=...&sid=...`. Updating traps = changing the\n * server-side config; no snippet redeploy.\n *\n * Safety rails:\n * - Tag list, attribute names, and style props are validated\n * against the closed enums declared by the schema. The renderer\n * uses `document.createElement` + `setAttribute` exclusively —\n * no `innerHTML`, no `insertAdjacentHTML`, no eval. Even a\n * server compromise can't escalate to XSS in the customer's\n * site through this surface.\n * - The trap roots are appended to <body> so the customer's\n * CSS / layout isn't perturbed (visually-hidden via clip-path).\n *\n * Trap fires:\n * - URL-fetch traps (kind=link/sku) fire server-side when the\n * hidden link is followed by an agent — the agent's GET to\n * /api/v1/trap/fire is the signal.\n * - DOM-interaction traps (kind=button/variant/form) fire\n * client-side: the watcher attaches event listeners to the\n * trap root and POSTs to /api/v1/trap/fire on interaction.\n *\n * Idempotency: re-injection on SPA navigations is suppressed by\n * tagging the wrapper element with `data-serge-traps=\"1\"` and\n * checking before each pass.\n */\n\nconst WRAPPER_ID = 'serge-traps-root'\nconst STORAGE_KEY = 'serge_trap_config'\nconst CACHE_TTL_MS = 60 * 60 * 1000 /* 1 hour — matches token TTL */\n\n/* ── Allowed surface — must match shared/lib/trap-config-schema.ts\n * (kept inline to avoid the cross-package import). If the server\n * ships an unknown tag/attribute we silently drop it rather than\n * crashing the snippet. */\nconst ALLOWED_TAGS = new Set([\n 'div', 'span', 'p', 'a', 'button', 'select', 'option',\n 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'ul', 'li', 'img', 'form', 'input', 'label',\n])\n\nconst ALLOWED_ATTRS = new Set([\n 'role', 'aria-label', 'aria-hidden', 'aria-describedby', 'aria-labelledby',\n 'tabindex', 'href', 'target', 'rel', 'type', 'name', 'value', 'placeholder',\n 'src', 'alt', 'data-trap', 'data-trap-kind', 'data-trap-token',\n])\n\nconst ALLOWED_STYLE = new Set([\n 'position', 'top', 'left', 'right', 'bottom',\n 'width', 'height', 'overflow', 'visibility', 'opacity',\n 'clip-path', 'clip', 'pointer-events',\n 'color', 'background', 'background-color', 'font-size',\n])\n\nconst ALLOWED_EVENTS = new Set(['click', 'change', 'submit', 'mouseover', 'focus'])\n\ninterface TrapElement {\n tag: string\n attrs?: Record<string, string>\n style?: Record<string, string>\n text?: string\n children?: TrapElement[]\n}\n\ninterface TrapTrigger {\n selector: string\n event: string\n fireKind: string\n}\n\ninterface TrapVariant {\n id: string\n kind: string\n trapUrl: string\n root: TrapElement\n triggers: TrapTrigger[]\n}\n\ninterface TrapConfig {\n version: string\n traps: TrapVariant[]\n}\n\ninterface CachedConfig {\n data: TrapConfig\n fetchedAt: number\n}\n\n// ── Safe DOM builder ──────────────────────────────────────────\n\nfunction buildElement(spec: TrapElement): HTMLElement | null {\n const tag = String(spec.tag ?? '').toLowerCase()\n if (!ALLOWED_TAGS.has(tag)) return null\n\n const el = document.createElement(tag)\n\n if (spec.attrs && typeof spec.attrs === 'object') {\n for (const [name, value] of Object.entries(spec.attrs)) {\n if (!ALLOWED_ATTRS.has(name)) continue\n if (typeof value !== 'string') continue\n try {\n el.setAttribute(name, value)\n } catch {\n /* setAttribute can throw on illegal values for some attrs;\n * silently skip */\n }\n }\n }\n\n if (spec.style && typeof spec.style === 'object') {\n for (const [prop, value] of Object.entries(spec.style)) {\n if (!ALLOWED_STYLE.has(prop)) continue\n if (typeof value !== 'string') continue\n try {\n el.style.setProperty(prop, value)\n } catch {\n /* invalid CSS value — skip */\n }\n }\n }\n\n if (typeof spec.text === 'string') {\n el.appendChild(document.createTextNode(spec.text))\n }\n\n if (Array.isArray(spec.children)) {\n for (const child of spec.children) {\n const built = buildElement(child)\n if (built) el.appendChild(built)\n }\n }\n\n return el\n}\n\n// ── Trap fire — client-side path ──────────────────────────────\n\nfunction fireTrap(trapUrl: string): void {\n /* Use sendBeacon when available (survives page navigation) and\n * fall back to a fire-and-forget fetch otherwise. The endpoint\n * returns a 1×1 PNG so we never need to read the response. */\n try {\n if ('sendBeacon' in navigator) {\n navigator.sendBeacon(trapUrl)\n return\n }\n } catch {\n /* fall through */\n }\n try {\n fetch(trapUrl, {\n method: 'GET',\n mode: 'no-cors',\n credentials: 'omit',\n keepalive: true,\n }).catch(() => undefined)\n } catch {\n /* The trap is best-effort — never throw into the customer's\n * page from this path. */\n }\n}\n\nfunction attachTriggers(root: HTMLElement, variant: TrapVariant): void {\n for (const trigger of variant.triggers) {\n if (!ALLOWED_EVENTS.has(trigger.event)) continue\n const target = root.querySelector(trigger.selector)\n if (!target) continue\n target.addEventListener(trigger.event, () => {\n fireTrap(variant.trapUrl)\n }, { once: true, capture: true })\n }\n}\n\n// ── Cache ──────────────────────────────────────────────────────\n\nfunction readCachedConfig(): TrapConfig | null {\n try {\n const stored = sessionStorage.getItem(STORAGE_KEY)\n if (!stored) return null\n const cached = JSON.parse(stored) as CachedConfig\n if (Date.now() - cached.fetchedAt > CACHE_TTL_MS) return null\n return cached.data\n } catch {\n return null\n }\n}\n\nfunction writeCachedConfig(config: TrapConfig): void {\n try {\n const cached: CachedConfig = { data: config, fetchedAt: Date.now() }\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(cached))\n } catch {\n /* QuotaExceeded / privacy mode — caching is best effort. */\n }\n}\n\n// ── Render ─────────────────────────────────────────────────────\n\nfunction alreadyInjected(): boolean {\n return Boolean(document.getElementById(WRAPPER_ID))\n}\n\nfunction injectAll(config: TrapConfig): void {\n if (alreadyInjected()) return\n if (!config || !Array.isArray(config.traps) || config.traps.length === 0) return\n\n const wrapper = document.createElement('div')\n wrapper.id = WRAPPER_ID\n wrapper.setAttribute('aria-hidden', 'true')\n wrapper.setAttribute('data-serge-traps', '1')\n /* Safety net — if the server-config style accidentally exposes\n * the wrapper, this inline style still hides it. */\n wrapper.style.setProperty('position', 'absolute')\n wrapper.style.setProperty('width', '1px')\n wrapper.style.setProperty('height', '1px')\n wrapper.style.setProperty('overflow', 'hidden')\n wrapper.style.setProperty('clip-path', 'inset(50%)')\n\n for (const variant of config.traps) {\n const root = buildElement(variant.root)\n if (!root) continue\n /* Stamp the variant id + kind so we can identify the source\n * if a trap fires through an unexpected path. */\n root.setAttribute('data-serge-trap-id', String(variant.id ?? ''))\n root.setAttribute('data-serge-trap-kind', String(variant.kind ?? ''))\n wrapper.appendChild(root)\n attachTriggers(root, variant)\n }\n\n /* Wait for body before appending — the snippet may run very early. */\n if (document.body) {\n document.body.appendChild(wrapper)\n } else {\n document.addEventListener(\n 'DOMContentLoaded',\n () => document.body && document.body.appendChild(wrapper),\n { once: true },\n )\n }\n}\n\n// ── Public entry ──────────────────────────────────────────────\n\nexport interface InjectHoneypotsOptions {\n apiBase: string\n siteId: string\n sessionId: string\n /** Set to false to skip injection entirely (consent gate, debug). */\n enabled?: boolean\n}\n\n/**\n * Fetch the latest trap config from the server, render the traps\n * into the page, and attach client-side interaction watchers.\n *\n * Idempotent — safe to call multiple times. Subsequent calls are\n * no-ops if the wrapper element is already in the DOM.\n *\n * Failures are silent — honeypots are best-effort. The snippet's\n * other detection paths still run.\n */\nexport function injectHoneypots(options: InjectHoneypotsOptions): void {\n if (options.enabled === false) return\n if (alreadyInjected()) return\n\n /* Try the cache first so subsequent page-loads in the same tab\n * inject instantly without a network round-trip. */\n const cached = readCachedConfig()\n if (cached) {\n injectAll(cached)\n }\n\n const url =\n `${options.apiBase}/api/v1/trap-config?s=${encodeURIComponent(options.siteId)}` +\n `&sid=${encodeURIComponent(options.sessionId)}`\n fetch(url, { method: 'GET', credentials: 'omit' })\n .then((res) => (res.ok ? res.json() : null))\n .then((data: TrapConfig | null) => {\n if (!data || !Array.isArray(data.traps)) return\n writeCachedConfig(data)\n /* If we already injected from cache, the new config will\n * apply on the next page in this session — re-injecting now\n * would race with running watchers. The version field lets\n * us invalidate aggressively in a future revision. */\n if (!alreadyInjected()) {\n injectAll(data)\n }\n })\n .catch(() => undefined)\n}\n","/**\n * URL query-string PII scrubber.\n *\n * Strips a denylist of well-known sensitive query keys from a URL\n * before it leaves the browser. The dominant leak vector is\n * `document.referrer` — a user landing on the customer's site from\n * a password-reset email, OAuth callback, or magic-link login\n * carries those tokens in the previous URL's query string.\n *\n * The denylist is intentionally tight. Pruning *all* unknown query\n * params would corrupt legitimate analytics signal (utm_*, ref,\n * source, etc.). We only redact keys that are reliably sensitive\n * across products in the wild.\n *\n * Case-insensitive match. Both `?Token=…` and `?TOKEN=…` are\n * stripped. Non-URL inputs are returned unchanged so the caller\n * doesn't need a try/catch on every transmission site.\n */\n\n/* Keep this list short. The cost of false-positives (dropping a\n * legit utm-style param because its name happens to contain \"key\")\n * is borne on every event in production. Match exact key names,\n * not substrings.\n *\n * Pre-publish security audit (2026-05-10) added the second cluster:\n * bearer / jwt / pin / cvv / cvc / ssn / otp / one_time_password.\n * None of those are utm-style analytics keys, so the false-positive\n * risk is effectively zero, and they cover real-world magic-link /\n * payment / one-time-password flows that occasionally land tokens\n * in URL fragments and become referer leakage. */\nconst SENSITIVE_QUERY_KEYS = new Set([\n 'access_token',\n 'api_key',\n 'apikey',\n 'auth',\n 'authorization',\n 'bearer',\n 'code',\n 'cvc',\n 'cvv',\n 'email',\n 'id_token',\n 'jwt',\n 'key',\n 'oauth_token',\n 'one_time_password',\n 'otp',\n 'password',\n 'pin',\n 'refresh_token',\n 'reset_token',\n 'secret',\n 'session',\n 'sig',\n 'signature',\n 'ssn',\n 'token',\n])\n\n/**\n * Strip sensitive query keys from a URL string. Returns the input\n * unchanged when:\n * - the input is empty / null\n * - the input is not a parseable absolute or root-relative URL\n *\n * Passes through hash fragments, path, and host; only the search\n * portion is rewritten.\n */\nexport function scrubUrl(input: string | null | undefined): string {\n if (!input) return ''\n /* Fast path: no query string, nothing to scrub. Avoids the URL\n * constructor overhead on every page_view event. */\n if (!input.includes('?')) return input\n\n /* `URL` requires an absolute URL. For inputs that come from\n * `document.referrer` this is always absolute. For root-relative\n * inputs (uncommon here) we anchor against a placeholder origin\n * so the parser succeeds, then strip the origin back off. */\n let parsed: URL\n let isRelative = false\n try {\n if (input.startsWith('/') || (!/^[a-z]+:\\/\\//i.test(input) && !input.startsWith('//'))) {\n parsed = new URL(input, 'https://placeholder.invalid')\n isRelative = true\n } else {\n parsed = new URL(input)\n }\n } catch {\n return input\n }\n\n if (parsed.searchParams.size === 0) return input\n\n let mutated = false\n for (const key of Array.from(parsed.searchParams.keys())) {\n if (SENSITIVE_QUERY_KEYS.has(key.toLowerCase())) {\n parsed.searchParams.delete(key)\n mutated = true\n }\n }\n\n if (!mutated) return input\n\n if (isRelative) {\n return parsed.pathname + parsed.search + parsed.hash\n }\n return parsed.toString()\n}\n","/**\n * Session and event identity for the snippet.\n *\n * Visitor-level identity is NOT generated client-side. It is derived\n * server-side at ingest from a daily-rotating salt and the request's\n * truncated IP + User-Agent + workspace + domain. The snippet does\n * not write a cookie, does not write to localStorage, and does not\n * send a `vid` field. See server/services/analytics-salt.ts for the\n * full protocol and CNIL/EDPB privacy posture.\n *\n * What's left here:\n * - sessionId: per-tab transient ID (sessionStorage). Dies on tab\n * close. Used to correlate events within a browsing session.\n * - generateEventId: per-event unique id, ordering-friendly.\n */\n\nconst SESSION_KEY = '_s_sid'\n\nlet sessionId: string | null = null\n\nfunction generateId(): string {\n const bytes = new Uint8Array(12)\n crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(36).padStart(2, '0')).join('')\n}\n\n/**\n * Get or create a session ID (tab-scoped, dies on tab close).\n * Uses sessionStorage with in-memory fallback.\n */\nexport function getSessionId(): string {\n if (sessionId) return sessionId\n\n try {\n const stored = sessionStorage.getItem(SESSION_KEY)\n if (stored) {\n sessionId = stored\n return stored\n }\n } catch {\n // sessionStorage blocked\n }\n\n sessionId = generateId()\n\n try {\n sessionStorage.setItem(SESSION_KEY, sessionId)\n } catch {\n // storage blocked — in-memory only\n }\n\n return sessionId\n}\n\n/** Generate a unique event ID (timestamp-prefixed for ordering) */\nexport function generateEventId(): string {\n const ts = Date.now().toString(36)\n const bytes = new Uint8Array(4)\n crypto.getRandomValues(bytes)\n const rand = Array.from(bytes, (b) => b.toString(36).padStart(2, '0')).join('')\n return `${ts}-${rand}`\n}\n","/**\n * SDK version constants.\n *\n * `SDK_VERSION` is replaced at build time by tsup's `define` (and by\n * rollup's `replace` for the IIFE build) so the published artifact\n * contains the literal string `'0.1.0'` not `__SDK_VERSION__`.\n *\n * `SCHEMA_VERSION` mirrors the snippet event schema version so the\n * server can route SDK-emitted events through the same schema-aware\n * ingest path as the snippet.\n */\n\ndeclare const __SDK_VERSION__: string\n\nexport const SDK_VERSION: string =\n typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : '0.0.0-dev'\n\n/* Mirrors snippet/server contract — bumped when AgentEvent shape changes\n * in a way the ingest API needs to know about. Kept in sync with\n * shared/lib/agent-events.ts SNIPPET_EVENT_SCHEMA_VERSION. The SDK\n * inlines the constant to avoid an external import that would break\n * the npm publish (the shared/ folder is outside this package). */\nexport const SCHEMA_VERSION = 3 as const\n","/**\n * @serge-ai/js — vanilla-JS core for the Serge tracker.\n *\n * This file exposes the public API for npm consumers. The shape mirrors\n * the snippet's `window.serge(...)` actions but with TypeScript types\n * and ESM-friendly factory ergonomics.\n *\n * SSR-safe: every browser-API access is guarded so the module can be\n * imported from a server component, Edge runtime, or Node test without\n * crashing. The actual side effects (event listeners, network requests)\n * only fire after `init()` is called in a browser context.\n *\n * Three packages, one core (this file):\n *\n * 1. Vanilla JS (this package — @serge-ai/js):\n *\n * import { init, track } from '@serge-ai/js'\n * init('srg_pub_xxxxxxxxxxxxxxxxxxxxxxxxxx')\n * track('signup_clicked', { plan: 'pro' })\n *\n * 2. React (separate package — @serge-ai/react):\n *\n * import { Analytics } from '@serge-ai/react'\n * <Analytics token=\"srg_pub_xxx\" />\n *\n * 3. Next.js App Router (separate package — @serge-ai/nextjs):\n *\n * import { Analytics } from '@serge-ai/nextjs'\n * <Analytics token=\"srg_pub_xxx\" />\n *\n * Tokens:\n * - `srg_pub_*` — public, embed-safe, the only token format the SDK\n * accepts. Mirrors PostHog's `phc_*` shape — public-by-design,\n * scoped to ingest. The (future) management API will use a\n * visually distinct `srg_sec_*` prefix so an operator who sees\n * the wrong one in logs / a tweet knows immediately whether it's\n * a leak that matters.\n * - The legacy `site_*` format from the pre-token era is still\n * accepted at ingest server-side for one major version cycle.\n *\n * Cookieless by construction:\n * - sessionStorage only (per-tab, dies on tab close — CNIL Sheet 16\n * classifies this as technically necessary, not consent-bearing).\n * - localStorage written ONLY when `requireConsent: true` is set AND\n * the user calls `consent('granted')`, and even then only the\n * consent answer itself.\n * - No cookies. No `vid` field. No persistent visitor identifier.\n * Visitor identity is derived server-side at ingest from a\n * daily-rotating salt. ePrivacy Art. 5(3) compliant by default —\n * no consent banner required for the tracker.\n *\n * Tree-shakeable: each named export is independent. Consumers that only\n * call `track()` from event handlers don't pay for the auto-init\n * lifecycle code.\n */\n\nimport { runDetection } from '../detect/score'\nimport { sendEvent, sendBeaconEvent, setEndpoint } from '../transport'\nimport { initSpaDetection } from '../spa'\nimport { fetchSignalUpdates } from '../signals'\nimport { getInitialConsent, setConsent as persistConsent, type ConsentValue } from '../consent'\nimport { injectHoneypots } from '../honeypot'\nimport { scrubUrl } from '../scrub'\nimport { getSessionId, generateEventId } from '../session'\nimport type { AgentEvent, DetectionResult } from '../types'\nimport { SDK_VERSION, SCHEMA_VERSION } from './version'\n\nexport type { ConsentValue }\nexport { SDK_VERSION }\n\n/**\n * Optional second argument to `init(token, options?)`. Every field is\n * optional; the production-safe defaults match the snippet's behavior\n * out of the box.\n */\nexport interface InitOptions {\n /** Origin of the Serge ingest API. Defaults to `https://serge.ai`.\n * Override to a first-party proxy (`/serge`) to dodge ad-blockers\n * on the customer's domain — the snippet pattern in RFC-001 §First-\n * party proxy. */\n apiBase?: string\n /** Track every session, not just agents. Development only. Mirrors\n * the snippet's `data-debug` flag. */\n debug?: boolean\n /** Hold tracking until `consent('granted')` is called. Required for\n * EU/Swiss/UK installs that need explicit opt-in for any storage\n * beyond the ePrivacy 5(3) \"technically necessary\" carve-out (we're\n * inside that carve-out by default — `requireConsent: true` is for\n * customers whose own legal team disagrees). */\n requireConsent?: boolean\n /** Pin SDK behavior to a published \"defaults date\" — same shape as\n * PostHog's `defaults` option. Lets us ship breaking-default\n * changes in future SDK versions without breaking installed tags;\n * customers who set `defaults: '2026-05-10'` keep the v0.2 behavior\n * forever. No behavioral effect today; locks the contract for\n * later. The string is sent in the session_start event meta so the\n * server can downgrade behavior per-customer if needed. */\n defaults?: string\n}\n\n/* ── Module state ─────────────────────────────────────────────\n *\n * Single-instance per page (matches the snippet's runtime model).\n * Calling `init()` twice with the same token is a no-op; calling with\n * a different token replaces the previous configuration. Same shape\n * as PostHog's posthog.init().\n *\n * State is intentionally module-level (not factory-closure) so that\n * `track()` / `consent()` / `pageview()` can be imported and called\n * from anywhere in the consumer's app without threading a `tracker`\n * instance through props. The PostHog research (May 2026) confirms\n * this is the convergent industry pattern — Sentry, Mixpanel,\n * Amplitude, Segment all do the same. */\n\ninterface ResolvedConfig {\n token: string\n apiBase: string\n debug: boolean\n requireConsent: boolean\n defaults: string | undefined\n}\n\nlet injected = false\nlet config: ResolvedConfig | null = null\nlet detection: DetectionResult | null = null\nlet pageLoadTime = 0\nlet currentUrl = ''\nlet initMs = 0\nlet sessionEndSent = false\nlet consentState: ConsentValue = 'unknown'\nlet apiBaseRef = ''\nlet listenersAttached = false\n\nfunction shouldTrack(): boolean {\n if (!config) return false\n if (config.debug) return true\n return !!detection && detection.confidence >= 0.35\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined'\n}\n\n/**\n * Initialize the tracker. Idempotent in a browser context (calling\n * `init()` again with the same token is a no-op; with a different\n * token, replaces the previous configuration).\n *\n * SSR-safe: in non-browser environments this function returns without\n * side effects, so it can be called unconditionally from any app\n * entry point that runs in both server and client contexts.\n *\n * @param token Public token from the Serge dashboard (`srg_pub_*`).\n * The legacy `site_*` format is still accepted at ingest\n * server-side for one major version cycle.\n * @param options Optional configuration. See {@link InitOptions}.\n */\nexport function init(token: string, options: InitOptions = {}): void {\n if (!isBrowser()) return\n if (!token) {\n if (options.debug) console.error('[serge] init(token) requires a token')\n return\n }\n\n const startTime = performance.now()\n const apiBase = (options.apiBase ?? 'https://serge.ai').replace(/\\/$/, '')\n apiBaseRef = apiBase\n setEndpoint(`${apiBase}/api/ingest/snippet`)\n\n config = {\n token,\n apiBase,\n debug: options.debug ?? false,\n requireConsent: options.requireConsent ?? false,\n defaults: options.defaults,\n }\n injected = true\n initMs = performance.now() - startTime\n\n if (config.requireConsent) {\n consentState = getInitialConsent()\n if (consentState === 'denied') return\n if (consentState !== 'granted') return\n }\n\n sendInitPing()\n injectHoneypots({ apiBase, siteId: token, sessionId: getSessionId() })\n fetchSignalUpdates(apiBase)\n\n if (\n document.visibilityState === 'hidden' ||\n (document.visibilityState as string) === 'prerender'\n ) {\n document.addEventListener(\n 'visibilitychange',\n () => {\n if (document.visibilityState === 'visible') startTracking()\n },\n { once: true },\n )\n } else {\n startTracking()\n }\n}\n\nfunction sendInitPing(): void {\n if (!config) return\n const event: AgentEvent = {\n s: config.token,\n sid: getSessionId(),\n eid: generateEventId(),\n ap: 'unknown',\n ac: 0,\n ds: [],\n et: 'init',\n ts: new Date().toISOString(),\n u: location.pathname,\n t: document.title || undefined,\n }\n sendEvent(event)\n}\n\nasync function startTracking(): Promise<void> {\n if (!config) return\n\n pageLoadTime = performance.now()\n currentUrl = location.pathname\n\n const { immediate, refine } = runDetection()\n detection = immediate\n\n if (config.debug || immediate.confidence >= 0.35) {\n sendSessionStart()\n sendPageviewInternal()\n setupListeners()\n }\n\n const refined = await refine\n detection = refined\n\n if (!config.debug && immediate.confidence < 0.35 && refined.confidence >= 0.35) {\n sendSessionStart()\n sendPageviewInternal()\n setupListeners()\n }\n}\n\nfunction setupListeners(): void {\n if (listenersAttached) return\n listenersAttached = true\n initSpaDetection(onNavigation)\n document.addEventListener('visibilitychange', onVisibilityChange)\n window.addEventListener('pagehide', onPageHide)\n}\n\nfunction createEvent(eventType: string, extra?: Partial<AgentEvent>): AgentEvent {\n if (!config || !detection) {\n throw new Error('[serge] tracker not initialized — call init() first')\n }\n const r = document.referrer ? scrubUrl(document.referrer) : ''\n return {\n s: config.token,\n sid: getSessionId(),\n eid: generateEventId(),\n ap: detection.platform,\n ac: Math.round(detection.confidence * 100) / 100,\n ds: detection.signals,\n bs:\n detection.behavioralScore != null\n ? Math.round(detection.behavioralScore * 100) / 100\n : undefined,\n et: eventType,\n ts: new Date().toISOString(),\n u: location.pathname,\n r: r || undefined,\n t: document.title || undefined,\n ...extra,\n }\n}\n\nfunction sendSessionStart(): void {\n sessionEndSent = false\n const meta: AgentEvent['_m'] = {\n init_ms: Math.round(initMs),\n v: SDK_VERSION,\n sv: SCHEMA_VERSION,\n }\n /* `defaults` rides along in the meta if the customer set it, so the\n * server can downgrade behavior on a per-customer basis when we\n * later ship a breaking-default change. Cheap to send (it's a date\n * string), free to ignore on the server until we need it. */\n if (config?.defaults) {\n /* `d` is a contracted wire-field — server-side ingest treats\n * unknown _m fields as opaque (RFC-001 §forward-compat) so we\n * widen via `unknown` to add it without polluting the AgentEvent\n * type. */\n ;(meta as unknown as Record<string, unknown>).d = config.defaults\n }\n sendEvent(\n createEvent('session_start', {\n vw: window.innerWidth,\n vh: window.innerHeight,\n sw: screen.width,\n sh: screen.height,\n ua: navigator.userAgent,\n p: getNavigatorPlatform(),\n l: navigator.language,\n _m: meta,\n }),\n )\n}\n\nfunction sendPageviewInternal(): void {\n pageLoadTime = performance.now()\n currentUrl = location.pathname\n sendEvent(createEvent('page_view'))\n}\n\nfunction onNavigation(): void {\n if (!shouldTrack()) return\n const timeOnPage = Math.round(performance.now() - pageLoadTime)\n sendEvent(\n createEvent('route_change', {\n u: currentUrl,\n tp: timeOnPage,\n sd: getScrollDepth(),\n }),\n )\n sendPageviewInternal()\n sessionEndSent = false\n}\n\nfunction onVisibilityChange(): void {\n if (document.visibilityState === 'hidden') sendSessionEnd()\n}\n\nfunction onPageHide(): void {\n sendSessionEnd()\n}\n\nfunction sendSessionEnd(): void {\n if (sessionEndSent) return\n if (!shouldTrack()) return\n sessionEndSent = true\n const timeOnPage = Math.round(performance.now() - pageLoadTime)\n sendBeaconEvent(\n createEvent('session_end', {\n tp: timeOnPage,\n sd: getScrollDepth(),\n }),\n )\n}\n\nfunction getScrollDepth(): number {\n const docHeight = document.documentElement.scrollHeight\n const viewHeight = window.innerHeight\n if (docHeight <= viewHeight) return 100\n const scrollTop = window.scrollY || document.documentElement.scrollTop\n return Math.min(100, Math.round(((scrollTop + viewHeight) / docHeight) * 100))\n}\n\nfunction getNavigatorPlatform(): string {\n try {\n // @ts-expect-error -- userAgentData is not in all TS lib definitions yet\n return navigator.userAgentData?.platform ?? navigator.platform ?? ''\n } catch {\n return ''\n }\n}\n\n/**\n * Track a custom event. Validated server-side at ingest:\n * - event name: `^[a-z][a-z0-9_]{0,63}$` (lowercase snake_case, ≤64 chars)\n * - properties: optional, capped at ~4 KB JSON\n *\n * Silently no-op when:\n * - The tracker hasn't been initialized yet\n * - Detection confidence is below the agent threshold (and `debug=false`)\n * - The page is server-rendered (no `window`)\n */\nexport function track(event: string, properties?: Record<string, unknown>): void {\n if (!isBrowser() || !injected) return\n if (!shouldTrack()) return\n sendEvent(\n createEvent('semantic_action', {\n ix: [{ t: event, tg: JSON.stringify(properties ?? {}), o: 0, ok: true }],\n }),\n )\n}\n\n/**\n * Update the consent state. Required when `init(token, { requireConsent: true })`\n * was used. Persisted to localStorage so the choice survives page reload — and\n * ONLY then; default installs are localStorage-free.\n *\n * - 'granted' — start tracking (idempotent if already granted)\n * - 'denied' — stop tracking; no cookies set, no events sent\n * - 'unknown' — pending; tracker stays dormant\n */\nexport function consent(value: ConsentValue): void {\n if (!isBrowser()) return\n const previous = consentState\n consentState = persistConsent(value)\n if (!config || !config.requireConsent) return\n if (previous === consentState) return\n\n if (consentState === 'granted' && previous !== 'granted') {\n if (apiBaseRef !== '') fetchSignalUpdates(apiBaseRef)\n sendInitPing()\n if (apiBaseRef !== '') {\n injectHoneypots({\n apiBase: apiBaseRef,\n siteId: config.token,\n sessionId: getSessionId(),\n })\n }\n if (\n document.visibilityState === 'hidden' ||\n (document.visibilityState as string) === 'prerender'\n ) {\n document.addEventListener(\n 'visibilitychange',\n () => {\n if (document.visibilityState === 'visible') startTracking()\n },\n { once: true },\n )\n } else {\n startTracking()\n }\n }\n}\n\n/**\n * Manual pageview emit. The tracker auto-tracks browser navigation\n * (history pushState, popstate, hashchange) — call this only when the\n * SDK can't detect a route change automatically (e.g., a custom\n * client-router that doesn't go through the History API, or the\n * Next.js App Router which we already handle in `@serge-ai/tracker/next`).\n */\nexport function pageview(): void {\n if (!isBrowser() || !injected) return\n if (!shouldTrack()) return\n onNavigation()\n}\n\n/**\n * Test-only: reset all module state. Not part of the documented API.\n * Used by Vitest tests that need a clean tracker between cases.\n */\nexport function _resetForTesting(): void {\n injected = false\n config = null\n detection = null\n pageLoadTime = 0\n currentUrl = ''\n initMs = 0\n sessionEndSent = false\n consentState = 'unknown'\n apiBaseRef = ''\n listenersAttached = false\n setEndpoint('')\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/detect/tier1.ts","../src/detect/tier2.ts","../src/detect/tier3.ts","../src/detect/score.ts","../src/transport.ts","../src/spa.ts","../src/signals.ts","../src/consent.ts","../src/honeypot.ts","../src/scrub.ts","../src/session.ts","../src/core/version.ts","../src/core/index.ts"],"names":["detectTier1","signals","platform","EXTENSION_ARTIFACTS","artifact","ua","detectTier2","score","claimsMac","claimsWindows","uaData","brandNames","b","hasGoogleChrome","n","hasChromium","canvas","gl","ext","rendererLc","ctx","data","zeroCount","maxVal","i","match","claimedMajor","chromeBrand","brandMajor","state","observing","resolvePromise","initState","onMouseMove","onMouseOver","onScroll","onClick","now","removeListeners","computeScore","intervals","mean","variance","sum","v","observeBehavior","durationMs","resolve","result","runDetection","tier1","tier2","tier12Confidence","immediate","refine","behavioral","allSignals","behavioralScore","finalConfidence","endpoint","setEndpoint","url","sendEvent","event","body","doSend","res","sendBeaconEvent","lastUrl","onNav","initSpaDetection","callback","handleNavigation","originalPushState","originalReplaceState","args","e","currentUrl","STORAGE_KEY","fetchSignalUpdates","apiBase","cached","readStorage","writeStorage","value","getInitialConsent","setConsent","WRAPPER_ID","ALLOWED_TAGS","ALLOWED_ATTRS","ALLOWED_STYLE","ALLOWED_EVENTS","buildElement","spec","tag","el","name","prop","child","built","fireTrap","trapUrl","attachTriggers","root","variant","trigger","target","readCachedConfig","stored","writeCachedConfig","config","alreadyInjected","injectAll","wrapper","injectHoneypots","options","SENSITIVE_QUERY_KEYS","scrubUrl","input","parsed","isRelative","mutated","key","SESSION_KEY","sessionId","generateId","bytes","getSessionId","generateEventId","ts","rand","SDK_VERSION","injected","detection","pageLoadTime","initMs","sessionEndSent","consentState","apiBaseRef","listenersAttached","shouldTrack","isBrowser","init","token","startTime","sendInitPing","startTracking","sendSessionStart","sendPageviewInternal","setupListeners","refined","onNavigation","onVisibilityChange","onPageHide","createEvent","eventType","extra","meta","getNavigatorPlatform","timeOnPage","getScrollDepth","sendSessionEnd","docHeight","viewHeight","scrollTop","track","properties","consent","previous","pageview","_resetForTesting"],"mappings":"aAOO,SAASA,CAAAA,EAAsC,CACpD,IAAMC,CAAAA,CAAoB,EAAC,CACvBC,CAAAA,CAAW,UAKf,GAAI,CACE,UAAU,SAAA,GAAc,CAAA,CAAA,EAC1BD,EAAQ,IAAA,CAAK,WAAW,EAE5B,CAAA,KAAQ,CAENA,CAAAA,CAAQ,IAAA,CAAK,iBAAiB,EAChC,EAGI,yBAAA,GAA6B,MAAA,EAAU,oBAAqB,MAAA,IAC9DA,CAAAA,CAAQ,KAAK,oBAAoB,CAAA,CACjCC,EAAW,aAAA,CAAA,CAmBb,IAAMC,EAID,CAMH,CAAE,SAAU,uBAAA,CAAyB,MAAA,CAAQ,aAAc,QAAA,CAAU,QAAS,CAAA,CAM9E,CAAE,QAAA,CAAU,sBAAA,CAAwB,OAAQ,WAAA,CAAa,QAAA,CAAU,YAAa,CAAA,CAChF,CAAE,SAAU,2BAAA,CAA6B,MAAA,CAAQ,gBAAiB,QAAA,CAAU,YAAa,EAGzF,CAAE,QAAA,CAAU,yBAA0B,MAAA,CAAQ,WAAA,CAAa,SAAU,SAAU,CAAA,CAC/E,CAAE,QAAA,CAAU,uBAAA,CAAyB,MAAA,CAAQ,gBAAiB,QAAA,CAAU,SAAU,CACpF,CAAA,CACA,IAAA,IAAWC,KAAYD,CAAAA,CACjB,QAAA,CAAS,aAAA,CAAcC,CAAAA,CAAS,QAAQ,CAAA,GAC1CH,EAAQ,IAAA,CAAKG,CAAAA,CAAS,MAAM,CAAA,CAC5BF,CAAAA,CAAWE,EAAS,QAAA,CAAA,CAgCxB,IAAMC,CAAAA,CAAK,SAAA,CAAU,SAAA,CA6CrB,OA5CI,qCAAqC,IAAA,CAAKA,CAAE,GAC9CJ,CAAAA,CAAQ,IAAA,CAAK,eAAe,CAAA,CAC5BC,CAAAA,CAAW,cACF,kBAAA,CAAmB,IAAA,CAAKG,CAAE,CAAA,EACnCJ,CAAAA,CAAQ,KAAK,kBAAkB,CAAA,CAC/BC,EAAW,SAAA,EACF,iBAAA,CAAkB,IAAA,CAAKG,CAAE,CAAA,EAClCJ,CAAAA,CAAQ,KAAK,iBAAiB,CAAA,CAC9BC,EAAW,SAAA,EACF,aAAA,CAAc,KAAKG,CAAE,CAAA,EAC9BJ,EAAQ,IAAA,CAAK,aAAa,EAC1BC,CAAAA,CAAW,SAAA,EACF,YAAY,IAAA,CAAKG,CAAE,GAC5BJ,CAAAA,CAAQ,IAAA,CAAK,WAAW,CAAA,CACxBC,CAAAA,CAAW,SAAA,EACF,eAAe,IAAA,CAAKG,CAAE,GAC/BJ,CAAAA,CAAQ,IAAA,CAAK,cAAc,CAAA,CAC3BC,CAAAA,CAAW,UACF,6BAAA,CAA8B,IAAA,CAAKG,CAAE,CAAA,EAC9CJ,CAAAA,CAAQ,KAAK,gBAAgB,CAAA,CAC7BC,EAAW,QAAA,EACF,qBAAA,CAAsB,IAAA,CAAKG,CAAE,CAAA,EACtCJ,CAAAA,CAAQ,KAAK,qBAAqB,CAAA,CAClCC,EAAW,QAAA,EACF,gBAAA,CAAiB,KAAKG,CAAE,CAAA,EACjCJ,CAAAA,CAAQ,IAAA,CAAK,gBAAgB,CAAA,CAC7BC,EAAW,QAAA,EACF,oBAAA,CAAqB,KAAKG,CAAE,CAAA,EACrCJ,EAAQ,IAAA,CAAK,oBAAoB,CAAA,CACjCC,CAAAA,CAAW,QAAA,EACF,sBAAA,CAAuB,KAAKG,CAAE,CAAA,EACvCJ,EAAQ,IAAA,CAAK,sBAAsB,EACnCC,CAAAA,CAAW,OAAA,EACF,cAAc,IAAA,CAAKG,CAAE,GAC9BJ,CAAAA,CAAQ,IAAA,CAAK,aAAa,CAAA,CAC1BC,CAAAA,CAAW,SACF,8CAAA,CAA+C,IAAA,CAAKG,CAAE,CAAA,EAC/DJ,CAAAA,CAAQ,IAAA,CAAK,uBAAuB,CAAA,CACpCC,CAAAA,CAAW,QACF,0BAAA,CAA2B,IAAA,CAAKG,CAAE,CAAA,GAC3CJ,CAAAA,CAAQ,KAAK,YAAY,CAAA,CACzBC,EAAW,MAAA,CAAA,CAGTD,CAAAA,CAAQ,SAAW,CAAA,CAAU,IAAA,CAE1B,CACL,QAAA,CAAAC,CAAAA,CACA,UAAA,CAAY,GAAA,CACZ,OAAA,CAAAD,CACF,CACF,CC/IO,SAASK,GAA2B,CACzC,IAAML,EAAoB,EAAC,CACvBM,EAAQ,CAAA,CACRL,CAAAA,CAcJ,GAAI,CACF,IAAMG,EAAK,SAAA,CAAU,SAAA,CACfG,EAAY,qBAAA,CAAsB,IAAA,CAAKH,CAAE,CAAA,CACzCI,CAAAA,CAAgB,aAAA,CAAc,KAAKJ,CAAE,CAAA,CAErCK,EAAS,SAAA,CAAU,aAAA,CACrBA,GAAUA,CAAAA,CAAO,QAAA,GAAa,OAAA,GAAYF,CAAAA,EAAaC,CAAAA,CAAAA,GACzDR,CAAAA,CAAQ,KAAK,mBAAmB,CAAA,CAChCM,GAAS,EAAA,CACTL,CAAAA,CAAW,WAEf,CAAA,KAAQ,CAER,CAOA,GAAI,CACF,IAAMG,EAAK,SAAA,CAAU,SAAA,CAEfK,EAAS,SAAA,CAAU,aAAA,CACzB,GAAIA,CAAAA,EAAU,KAAA,CAAM,QAAQA,CAAAA,CAAO,MAAM,GAAK,WAAA,CAAY,IAAA,CAAKL,CAAE,CAAA,CAAG,CAClE,IAAMM,CAAAA,CAAaD,CAAAA,CAAO,MAAA,CAAO,GAAA,CAAKE,CAAAA,EAAyBA,CAAAA,CAAE,KAAK,CAAA,CAChEC,CAAAA,CAAkBF,EAAW,IAAA,CAAMG,CAAAA,EAAc,iBAAiB,IAAA,CAAKA,CAAC,CAAC,CAAA,CACzEC,CAAAA,CAAcJ,EAAW,IAAA,CAAMG,CAAAA,EAAc,YAAY,IAAA,CAAKA,CAAC,CAAC,CAAA,CAClE,CAACD,CAAAA,EAAmBE,CAAAA,GACtBd,CAAAA,CAAQ,IAAA,CAAK,oBAAoB,CAAA,CACjCM,CAAAA,EAAS,GACTL,CAAAA,CAAWA,CAAAA,EAAY,WAE3B,CACF,CAAA,KAAQ,CAER,CAGA,GAAI,CACF,IAAMG,CAAAA,CAAK,UAAU,SAAA,CACA,wBAAA,CAAyB,KAAKA,CAAE,CAAA,EACjC,SAAA,CAAU,cAAA,GAAmB,CAAA,GAC/CJ,CAAAA,CAAQ,KAAK,gBAAgB,CAAA,CAC7BM,GAAS,EAAA,EAEb,CAAA,KAAQ,CAER,CAGA,GAAI,CAAA,CACE,MAAA,CAAO,KAAA,GAAU,CAAA,EAAK,OAAO,MAAA,GAAW,CAAA,IAC1CN,EAAQ,IAAA,CAAK,aAAa,EAC1BM,CAAAA,EAAS,EAAA,CAAA,CAGP,MAAA,CAAO,UAAA,GAAe,CAAA,GACxBN,CAAAA,CAAQ,KAAK,iBAAiB,CAAA,CAC9BM,GAAS,EAAA,EAEb,CAAA,KAAQ,CAER,CAGA,GAAI,CAEA,MAAA,CAAO,UAAA,CAAa,GACpB,MAAA,CAAO,UAAA,CAAa,GACpB,MAAA,CAAO,UAAA,CAAa,OAAO,UAAA,GAE3BN,CAAAA,CAAQ,IAAA,CAAK,mBAAmB,CAAA,CAChCM,CAAAA,EAAS,IAEb,CAAA,KAAQ,CAER,CAGA,GAAI,CAEI,iBAAkB,MAAA,GACtBN,CAAAA,CAAQ,KAAK,0BAA0B,CAAA,CACvCM,GAAS,EAAA,EAEb,CAAA,KAAQ,CAER,CAWA,GAAI,CACF,IAAMS,CAAAA,CAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CACxCC,EAAMD,CAAAA,CAAO,UAAA,CAAW,OAAO,CAAA,EACnCA,CAAAA,CAAO,WAAW,oBAAoB,CAAA,CACxC,GAAIC,CAAAA,CAAI,CACN,IAAMC,CAAAA,CAAMD,CAAAA,CAAG,aAAa,2BAA2B,CAAA,CAQjDE,EAFA,MAAA,CADJD,CAAAA,CACWD,CAAAA,CAAG,YAAA,CAAcC,CAAAA,CAA4C,uBAAuB,EACpFD,CAAAA,CAAG,YAAA,CAAaA,EAAG,QAAQ,CAD0D,EAEtE,WAAA,EAAY,CAAA,CAEtCE,CAAAA,CAAW,QAAA,CAAS,aAAa,CAAA,EACjCA,EAAW,QAAA,CAAS,SAAS,GAC7BA,CAAAA,CAAW,QAAA,CAAS,UAAU,CAAA,EAC9BA,CAAAA,CAAW,QAAA,CAAS,gBAAgB,CAAA,IAEpClB,CAAAA,CAAQ,KAAK,yBAAyB,CAAA,CACtCM,GAAS,GAAA,EAMb,CAAA,KAAWU,IAAO,IAAA,GAGhBhB,CAAAA,CAAQ,KAAK,eAAe,CAAA,CAC5BM,GAAS,GAAA,EAEb,CAAA,KAAQ,CAER,CAWA,GAAI,CACF,IAAMS,CAAAA,CAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC9CA,EAAO,KAAA,CAAQ,EAAA,CACfA,EAAO,MAAA,CAAS,EAAA,CAChB,IAAMI,CAAAA,CAAMJ,CAAAA,CAAO,WAAW,IAAI,CAAA,CAClC,GAAII,CAAAA,CAAK,CACPA,EAAI,YAAA,CAAe,KAAA,CACnBA,EAAI,IAAA,CAAO,cAAA,CACXA,CAAAA,CAAI,SAAA,CAAY,MAAA,CAChBA,CAAAA,CAAI,SAAS,CAAA,CAAG,CAAA,CAAG,GAAI,EAAE,CAAA,CACzBA,EAAI,SAAA,CAAY,MAAA,CAChBA,EAAI,QAAA,CAAS,UAAA,CAAY,EAAG,CAAC,CAAA,CAC7B,IAAMC,CAAAA,CAAOD,CAAAA,CAAI,aAAa,CAAA,CAAG,CAAA,CAAG,EAAA,CAAI,EAAE,CAAA,CAAE,IAAA,CACxCE,EAAY,CAAA,CACZC,CAAAA,CAAS,EACb,IAAA,IAASC,CAAAA,CAAI,EAAGA,CAAAA,CAAIH,CAAAA,CAAK,MAAA,CAAQG,CAAAA,EAAK,CAAA,CAChCH,CAAAA,CAAKG,CAAC,CAAA,GAAM,CAAA,EAAKH,EAAKG,CAAAA,CAAI,CAAC,IAAM,CAAA,EAAKH,CAAAA,CAAKG,CAAAA,CAAI,CAAC,CAAA,GAAM,CAAA,EAAGF,IACzDD,CAAAA,CAAKG,CAAC,EAAID,CAAAA,GAAQA,CAAAA,CAASF,EAAKG,CAAC,CAAA,CAAA,CAIrBF,GAAaD,CAAAA,CAAK,MAAA,CAAS,GAC7B,GAAA,EAAQE,CAAAA,CAAS,KAC/BtB,CAAAA,CAAQ,IAAA,CAAK,oBAAoB,CAAA,CACjCM,CAAAA,EAAS,EAAA,EAEb,CACF,CAAA,KAAQ,CAER,CAYA,GAAI,CAEF,IAAMG,CAAAA,CAAS,SAAA,CAAU,cACzB,GAAIA,CAAAA,EAAU,OAAOA,CAAAA,CAAO,oBAAA,EAAyB,WAAY,CAO/D,IAAML,EAAK,SAAA,CAAU,SAAA,CACfoB,EAAQ,gBAAA,CAAiB,IAAA,CAAKpB,CAAE,CAAA,CACtC,GAAIoB,CAAAA,EAAS,MAAM,OAAA,CAAQf,CAAAA,CAAO,MAAM,CAAA,CAAG,CACzC,IAAMgB,CAAAA,CAAe,MAAA,CAAOD,EAAM,CAAC,CAAC,EAC9BE,CAAAA,CAAcjB,CAAAA,CAAO,OAAO,IAAA,CAAME,CAAAA,EACtC,UAAU,IAAA,CAAKA,CAAAA,CAAE,KAAK,CAAA,EAAK,CAAC,WAAA,CAAY,KAAKA,CAAAA,CAAE,KAAK,CACtD,CAAA,CACA,GAAIe,GAAe,OAAOA,CAAAA,CAAY,OAAA,EAAY,QAAA,CAAU,CAC1D,IAAMC,EAAa,MAAA,CAAOD,CAAAA,CAAY,QAAQ,KAAA,CAAM,GAAG,EAAE,CAAC,CAAC,CAAA,CAEzD,MAAA,CAAO,QAAA,CAASD,CAAY,GAC5B,MAAA,CAAO,QAAA,CAASE,CAAU,CAAA,EAC1B,IAAA,CAAK,IAAIF,CAAAA,CAAeE,CAAU,EAAI,CAAA,GAEtC3B,CAAAA,CAAQ,KAAK,2BAA2B,CAAA,CACxCM,GAAS,GAAA,EAEb,CACF,CACF,CACF,CAAA,KAAQ,CAER,CAGA,OAAAA,CAAAA,CAAQ,KAAK,GAAA,CAAIA,CAAAA,CAAO,CAAC,CAAA,CAElB,CAAE,QAAAN,CAAAA,CAAS,KAAA,CAAAM,CAAAA,CAAO,QAAA,CAAAL,CAAS,CACpC,CCtOA,IAAI2B,CAAAA,CAAgC,KAChCC,CAAAA,CAAY,KAAA,CACZC,EAA8D,IAAA,CAElE,SAASC,EAAAA,EAA6B,CACpC,OAAO,CACL,eAAgB,CAAA,CAChB,YAAA,CAAc,EACd,UAAA,CAAY,CAAA,CACZ,gBAAiB,EAAC,CAClB,iBAAkB,KAAA,CAClB,kBAAA,CAAoB,KACpB,YAAA,CAAc,WAAA,CAAY,KAC5B,CACF,CAEA,SAASC,CAAAA,EAAc,CAChBJ,CAAAA,EACLA,CAAAA,CAAM,cAAA,GACR,CAEA,SAASK,CAAAA,EAAc,CAChBL,CAAAA,GAELA,CAAAA,CAAM,iBAAmB,IAAA,EAC3B,CAEA,SAASM,CAAAA,EAAW,CACbN,CAAAA,EACLA,EAAM,YAAA,GACR,CAEA,SAASO,CAAAA,EAAU,CACjB,GAAI,CAACP,CAAAA,CAAO,OACZ,IAAMQ,CAAAA,CAAM,WAAA,CAAY,KAAI,CAC5BR,CAAAA,CAAM,aACNA,CAAAA,CAAM,eAAA,CAAgB,KAAKQ,CAAG,CAAA,CAC1BR,EAAM,kBAAA,GAAuB,IAAA,GAC/BA,EAAM,kBAAA,CAAqBQ,CAAAA,CAAMR,EAAM,YAAA,EAE3C,CAEA,SAASS,EAAAA,EAAkB,CACzB,QAAA,CAAS,mBAAA,CAAoB,WAAA,CAAaL,CAAW,EACrD,QAAA,CAAS,mBAAA,CAAoB,YAAaC,CAAW,CAAA,CACrD,SAAS,mBAAA,CAAoB,QAAA,CAAUC,CAAQ,CAAA,CAC/C,QAAA,CAAS,oBAAoB,OAAA,CAASC,CAAO,EAC7CN,CAAAA,CAAY,MACd,CAEA,SAASS,EAAAA,EAAiC,CACxC,GAAI,CAACV,CAAAA,CAAO,OAAO,CAAE,OAAA,CAAS,EAAC,CAAG,KAAA,CAAO,CAAE,CAAA,CAE3C,IAAM5B,EAAoB,EAAC,CACvBM,EAAQ,CAAA,CAqBZ,GAlBIsB,EAAM,cAAA,GAAmB,CAAA,EAAKA,EAAM,UAAA,CAAa,CAAA,GACnD5B,CAAAA,CAAQ,IAAA,CAAK,UAAU,CAAA,CACvBM,GAAS,EAAA,CAAA,CAIPsB,CAAAA,CAAM,qBAAuB,IAAA,EAAQA,CAAAA,CAAM,mBAAqB,GAAA,GAClE5B,CAAAA,CAAQ,IAAA,CAAK,kBAAkB,CAAA,CAC/BM,CAAAA,EAAS,IAIPsB,CAAAA,CAAM,UAAA,CAAa,GAAK,CAACA,CAAAA,CAAM,kBAAoBA,CAAAA,CAAM,cAAA,GAAmB,CAAA,GAC9E5B,CAAAA,CAAQ,IAAA,CAAK,UAAU,EACvBM,CAAAA,EAAS,EAAA,CAAA,CAIPsB,EAAM,eAAA,CAAgB,MAAA,EAAU,EAAG,CACrC,IAAMW,EAAsB,EAAC,CAC7B,QAAShB,CAAAA,CAAI,CAAA,CAAGA,EAAIK,CAAAA,CAAM,eAAA,CAAgB,OAAQL,CAAAA,EAAAA,CAChDgB,CAAAA,CAAU,IAAA,CAAKX,CAAAA,CAAM,eAAA,CAAgBL,CAAC,EAAIK,CAAAA,CAAM,eAAA,CAAgBL,EAAI,CAAC,CAAC,EAExE,IAAMiB,CAAAA,CAAOD,EAAU,MAAA,CAAO,CAAC,EAAG5B,CAAAA,GAAM,CAAA,CAAIA,EAAG,CAAC,CAAA,CAAI4B,EAAU,MAAA,CACxDE,CAAAA,CACJF,CAAAA,CAAU,MAAA,CAAO,CAACG,CAAAA,CAAKC,IAAMD,CAAAA,CAAAA,CAAOC,CAAAA,CAAIH,IAAS,CAAA,CAAG,CAAC,EAAID,CAAAA,CAAU,MAAA,CAAA,CAC1DC,EAAO,CAAA,CAAI,IAAA,CAAK,KAAKC,CAAQ,CAAA,CAAID,EAAO,CAAA,EAE1C,EAAA,GACPxC,EAAQ,IAAA,CAAK,mBAAmB,CAAA,CAChCM,CAAAA,EAAS,EAAA,EAEb,CAGA,OACEsB,CAAAA,CAAM,YAAA,GAAiB,GACvB,QAAA,CAAS,eAAA,CAAgB,aAAe,MAAA,CAAO,WAAA,CAAc,CAAA,EAC7DA,CAAAA,CAAM,UAAA,CAAa,CAAA,GAEnB5B,EAAQ,IAAA,CAAK,WAAW,EACxBM,CAAAA,EAAS,GAAA,CAAA,CAGJ,CAAE,OAAA,CAAAN,CAAAA,CAAS,KAAA,CAAO,IAAA,CAAK,GAAA,CAAIM,CAAAA,CAAO,CAAC,CAAE,CAC9C,CAMO,SAASsC,CAAAA,CAAgBC,EAAa,GAAA,CAAiC,CAC5E,OAAIhB,CAAAA,CACK,IAAI,QAASiB,CAAAA,EAAY,CAC9BhB,EAAiBgB,EACnB,CAAC,GAGHlB,CAAAA,CAAQG,EAAAA,EAAU,CAClBF,CAAAA,CAAY,IAAA,CAEZ,QAAA,CAAS,iBAAiB,WAAA,CAAaG,CAAAA,CAAa,CAAE,OAAA,CAAS,IAAK,CAAC,CAAA,CACrE,QAAA,CAAS,iBAAiB,WAAA,CAAaC,CAAAA,CAAa,CAAE,OAAA,CAAS,IAAK,CAAC,CAAA,CACrE,QAAA,CAAS,iBAAiB,QAAA,CAAUC,CAAAA,CAAU,CAAE,OAAA,CAAS,IAAK,CAAC,EAC/D,QAAA,CAAS,gBAAA,CAAiB,QAASC,CAAAA,CAAS,CAAE,QAAS,IAAK,CAAC,EAEtD,IAAI,OAAA,CAASW,GAAY,CAC9BhB,CAAAA,CAAiBgB,EAEjB,UAAA,CAAW,IAAM,CACfT,EAAAA,EAAgB,CAChB,IAAMU,CAAAA,CAAST,EAAAA,EAAa,CAC5BV,EAAQ,IAAA,CACJE,CAAAA,GACFA,EAAeiB,CAAM,CAAA,CACrBjB,EAAiB,IAAA,EAErB,CAAA,CAAGe,CAAU,EACf,CAAC,CAAA,CACH,CClJO,SAASG,CAAAA,EAGd,CAEA,IAAMC,CAAAA,CAAQlD,GAAY,CAE1B,GAAIkD,CAAAA,EAASA,CAAAA,CAAM,UAAA,EAAc,GAAA,CAE/B,OAAO,CACL,SAAA,CAAWA,EACX,MAAA,CAAQ,OAAA,CAAQ,QAAQA,CAAK,CAC/B,EAIF,IAAMC,CAAAA,CAAQ7C,GAAY,CAGpBL,CAAAA,CAAU,CAAC,GAAIiD,CAAAA,EAAO,SAAW,EAAC,CAAI,GAAGC,CAAAA,CAAM,OAAO,CAAA,CACtDjD,EAAWgD,CAAAA,EAAO,QAAA,EAAYC,EAAM,QAAA,EAAY,SAAA,CAChDC,EAAmB,IAAA,CAAK,GAAA,CAAA,CAAKF,GAAO,UAAA,EAAc,CAAA,EAAK,GAAMC,CAAAA,CAAM,KAAA,CAAO,CAAC,CAAA,CAE3EE,CAAAA,CAA6B,CACjC,QAAA,CAAAnD,CAAAA,CACA,UAAA,CAAYkD,CAAAA,CACZ,OAAA,CAAS,CAAC,GAAGnD,CAAO,CACtB,EAGA,GAAImD,CAAAA,EAAoB,IACtB,OAAO,CACL,UAAAC,CAAAA,CACA,MAAA,CAAQ,QAAQ,OAAA,CAAQA,CAAS,CACnC,CAAA,CAIF,IAAMC,EAAST,CAAAA,CAAgB,GAAI,CAAA,CAAE,IAAA,CAAMU,CAAAA,EAAgC,CACzE,IAAMC,CAAAA,CAAa,CAAC,GAAGvD,CAAAA,CAAS,GAAGsD,EAAW,OAAO,CAAA,CAC/CE,CAAAA,CAAkBF,CAAAA,CAAW,KAAA,CAG/BG,CAAAA,CACJ,OAAIN,CAAAA,CAAmB,CAAA,CACrBM,EAAkB,IAAA,CAAK,GAAA,CAAI,GAAMN,CAAAA,CAAmB,EAAA,CAAMK,CAAAA,CAAiB,CAAC,CAAA,CAG5EC,CAAAA,CAAkBD,EAAkB,GAAA,CAG/B,CACL,SAAAvD,CAAAA,CACA,UAAA,CAAYwD,EACZ,OAAA,CAASF,CAAAA,CACT,gBAAAC,CACF,CACF,CAAC,CAAA,CAED,OAAO,CAAE,SAAA,CAAAJ,CAAAA,CAAW,OAAAC,CAAO,CAC7B,CCvEA,IAAIK,CAAAA,CAAW,EAAA,CAER,SAASC,CAAAA,CAAYC,CAAAA,CAAa,CACvCF,CAAAA,CAAWE,EACb,CAOO,SAASC,CAAAA,CAAUC,EAAyB,CACjD,GAAI,CAACJ,CAAAA,CAAU,OAEf,IAAMK,CAAAA,CAAO,IAAA,CAAK,UAAUD,CAAK,CAAA,CAEjCE,CAAAA,CAAOD,CAAI,CAAA,CAAE,KAAA,CAAM,IAAM,CAEvB,UAAA,CAAW,IAAMC,CAAAA,CAAOD,CAAI,EAAE,KAAA,CAAM,IAAM,CAAa,CAAC,CAAA,CAAG,GAAI,EACjE,CAAC,EACH,CAEA,SAASC,EAAOD,CAAAA,CAA6B,CAC3C,OAAO,KAAA,CAAML,CAAAA,CAAU,CACrB,OAAQ,MAAA,CACR,IAAA,CAAAK,EACA,OAAA,CAAS,CAAE,eAAgB,YAAa,CAAA,CACxC,SAAA,CAAW,IAAA,CASX,WAAA,CAAa,MACf,CAAC,CAAA,CAAE,IAAA,CAAME,GAAQ,CACf,GAAI,CAACA,CAAAA,CAAI,EAAA,EAAMA,CAAAA,CAAI,MAAA,GAAW,GAAA,CAC5B,MAAM,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQA,EAAI,MAAM,CAAA,CAAE,CAExC,CAAC,CACH,CAMO,SAASC,CAAAA,CAAgBJ,EAAyB,CACvD,GAAI,CAACJ,CAAAA,CAAU,OAEf,IAAMK,CAAAA,CAAO,IAAA,CAAK,SAAA,CAAUD,CAAK,CAAA,CAIjC,GAAI,OAAO,UAAA,EAAe,UAAA,CACxB,GAAI,CAEF,UAAA,CAAWJ,EAAU,CACnB,MAAA,CAAQ,OACR,IAAA,CAAAK,CAAAA,CACA,QAAS,CAAE,cAAA,CAAgB,YAAa,CAAA,CACxC,aAAA,CAAe,EACf,WAAA,CAAa,MACf,CAAC,CAAA,CACD,MACF,CAAA,KAAQ,CAER,CAWF,GAAI,CACF,SAAA,CAAU,UAAA,CAAWL,EAAUK,CAAI,EACrC,MAAQ,CAEN,KAAA,CAAML,EAAU,CACd,MAAA,CAAQ,OACR,IAAA,CAAAK,CAAAA,CACA,QAAS,CAAE,cAAA,CAAgB,YAAa,CAAA,CACxC,SAAA,CAAW,IAAA,CACX,YAAa,MACf,CAAC,EAAE,KAAA,CAAM,IAAM,CAAa,CAAC,EAC/B,CACF,CCtFA,IAAII,CAAAA,CAAU,GACVC,CAAAA,CAA6B,IAAA,CAE1B,SAASC,CAAAA,CAAiBC,CAAAA,CAA4B,CAK3D,GAJAF,CAAAA,CAAQE,CAAAA,CACRH,CAAAA,CAAU,QAAA,CAAS,QAAA,CAAW,SAAS,MAAA,CAGnC,YAAA,GAAgB,OACL,MAAA,CAAuC,UAAA,CAChD,iBAAiB,iBAAA,CAAmB,IAAM,CAE5CI,CAAAA,GACF,CAAC,CAAA,CAAA,KACI,CAEL,IAAMC,CAAAA,CAAoB,OAAA,CAAQ,UAC5BC,CAAAA,CAAuB,OAAA,CAAQ,YAAA,CAErC,OAAA,CAAQ,SAAA,CAAY,SAAA,GAAaC,EAA4C,CAC3EF,CAAAA,CAAkB,MAAM,IAAA,CAAME,CAAI,EAClCH,CAAAA,GACF,EAEA,OAAA,CAAQ,YAAA,CAAe,YAAaG,CAAAA,CAA+C,CACjFD,EAAqB,KAAA,CAAM,IAAA,CAAMC,CAAI,CAAA,CACrCH,CAAAA,GACF,CAAA,CAEA,MAAA,CAAO,gBAAA,CAAiB,WAAYA,CAAgB,EACtD,CAGA,MAAA,CAAO,gBAAA,CAAiB,WAAaI,CAAAA,EAAM,CACrCA,EAAE,SAAA,EAAWJ,CAAAA,GACnB,CAAC,EACH,CAEA,SAASA,CAAAA,EAAmB,CAC1B,IAAMK,CAAAA,CAAa,QAAA,CAAS,QAAA,CAAW,QAAA,CAAS,MAAA,CAC5CA,IAAeT,CAAAA,GACnBA,CAAAA,CAAUS,EACVR,CAAAA,IAAQ,EACV,CCtCA,IAAMS,EAAAA,CAAc,eAAA,CA8Bb,SAASC,CAAAA,CAAmBC,CAAAA,CAAuB,CACxD,IAAMnB,CAAAA,CAAM,GAAGmB,CAAO,CAAA,eAAA,CAAA,CAEtB,MAAMnB,CAAAA,CAAK,CACT,MAAA,CAAQ,KAAA,CACR,WAAA,CAAa,MACf,CAAC,CAAA,CACE,IAAA,CAAMK,GAAQ,CACb,GAAKA,EAAI,EAAA,CACT,OAAOA,EAAI,IAAA,EACb,CAAC,CAAA,CACA,IAAA,CAAM7C,GAAS,CACd,GAAKA,EACL,GAAI,CACF,IAAM4D,CAAAA,CAAwB,CAC5B,IAAA,CAAA5D,EACA,SAAA,CAAW,IAAA,CAAK,KAClB,CAAA,CACA,eAAe,OAAA,CAAQyD,EAAAA,CAAa,IAAA,CAAK,SAAA,CAAUG,CAAM,CAAC,EAC5D,CAAA,KAAQ,CAER,CACF,CAAC,CAAA,CACA,MAAM,IAAM,CAEb,CAAC,EACL,CC5CA,IAAMH,EAAc,gBAAA,CAIhBG,CAAAA,CAA8B,KAElC,SAASC,EAAAA,EAA4B,CACnC,GAAI,CACF,IAAMtC,CAAAA,CAAI,MAAA,CAAO,aAAa,OAAA,CAAQkC,CAAW,EACjD,GAAIlC,CAAAA,GAAM,WAAaA,CAAAA,GAAM,QAAA,CAAU,OAAOA,CAChD,CAAA,KAAQ,CAER,CACA,OAAO,SACT,CAEA,SAASuC,EAAAA,CAAaC,EAA2B,CAC/C,GAAIA,CAAAA,GAAU,SAAA,CACd,GAAI,CACF,OAAO,YAAA,CAAa,OAAA,CAAQN,EAAaM,CAAK,EAChD,MAAQ,CAER,CACF,CAIO,SAASC,EAAAA,EAAkC,CAChD,OAAIJ,CAAAA,GAAW,IAAA,GACfA,EAASC,EAAAA,EAAY,CAAA,CACdD,CACT,CAGO,SAASK,GAAWF,CAAAA,CAAmC,CAC5D,OAAIA,CAAAA,GAAU,SAAA,EAAaA,IAAU,QAAA,EAAYA,CAAAA,GAAU,UAElDH,CAAAA,EAAU,SAAA,EAEnBA,CAAAA,CAASG,CAAAA,CACTD,EAAAA,CAAaC,CAAK,EACXA,CAAAA,CACT,CClCA,IAAMG,EAAAA,CAAa,kBAAA,CACbT,GAAc,mBAAA,CAOpB,IAAMU,GAAe,IAAI,GAAA,CAAI,CAC3B,KAAA,CAAO,MAAA,CAAQ,IAAK,GAAA,CAAK,QAAA,CAAU,SAAU,QAAA,CAC7C,IAAA,CAAM,IAAA,CAAM,IAAA,CAAM,IAAA,CAAM,IAAA,CAAM,KAC9B,IAAA,CAAM,IAAA,CAAM,MAAO,MAAA,CAAQ,OAAA,CAAS,OACtC,CAAC,CAAA,CAEKC,GAAgB,IAAI,GAAA,CAAI,CAC5B,MAAA,CAAQ,YAAA,CAAc,cAAe,kBAAA,CAAoB,iBAAA,CACzD,WAAY,MAAA,CAAQ,QAAA,CAAU,KAAA,CAAO,MAAA,CAAQ,MAAA,CAAQ,OAAA,CAAS,cAC9D,KAAA,CAAO,KAAA,CAAO,YAAa,gBAAA,CAAkB,iBAC/C,CAAC,CAAA,CAEKC,EAAAA,CAAgB,IAAI,GAAA,CAAI,CAC5B,UAAA,CAAY,MAAO,MAAA,CAAQ,OAAA,CAAS,SACpC,OAAA,CAAS,QAAA,CAAU,WAAY,YAAA,CAAc,SAAA,CAC7C,WAAA,CAAa,MAAA,CAAQ,gBAAA,CACrB,OAAA,CAAS,aAAc,kBAAA,CAAoB,WAC7C,CAAC,CAAA,CAEKC,EAAAA,CAAiB,IAAI,GAAA,CAAI,CAAC,QAAS,QAAA,CAAU,QAAA,CAAU,YAAa,OAAO,CAAC,EAoClF,SAASC,EAAAA,CAAaC,EAAuC,CAC3D,IAAMC,CAAAA,CAAM,MAAA,CAAOD,CAAAA,CAAK,GAAA,EAAO,EAAE,CAAA,CAAE,WAAA,GACnC,GAAI,CAACL,GAAa,GAAA,CAAIM,CAAG,EAAG,OAAO,IAAA,CAEnC,IAAMC,CAAAA,CAAK,QAAA,CAAS,cAAcD,CAAG,CAAA,CAErC,GAAID,CAAAA,CAAK,KAAA,EAAS,OAAOA,CAAAA,CAAK,KAAA,EAAU,QAAA,CAAA,CACtC,OAAW,CAACG,CAAAA,CAAMZ,CAAK,CAAA,GAAK,MAAA,CAAO,QAAQS,CAAAA,CAAK,KAAK,EACnD,GAAKJ,EAAAA,CAAc,IAAIO,CAAI,CAAA,EACvB,OAAOZ,CAAAA,EAAU,QAAA,CACrB,GAAI,CACFW,CAAAA,CAAG,YAAA,CAAaC,CAAAA,CAAMZ,CAAK,EAC7B,MAAQ,CAGR,CAAA,CAIJ,GAAIS,CAAAA,CAAK,KAAA,EAAS,OAAOA,CAAAA,CAAK,KAAA,EAAU,QAAA,CAAA,CACtC,IAAA,GAAW,CAACI,CAAAA,CAAMb,CAAK,CAAA,GAAK,MAAA,CAAO,QAAQS,CAAAA,CAAK,KAAK,EACnD,GAAKH,EAAAA,CAAc,GAAA,CAAIO,CAAI,CAAA,EACvB,OAAOb,GAAU,QAAA,CACrB,GAAI,CACFW,CAAAA,CAAG,KAAA,CAAM,YAAYE,CAAAA,CAAMb,CAAK,EAClC,CAAA,KAAQ,CAER,EAQJ,GAJI,OAAOS,EAAK,IAAA,EAAS,QAAA,EACvBE,EAAG,WAAA,CAAY,QAAA,CAAS,cAAA,CAAeF,CAAAA,CAAK,IAAI,CAAC,EAG/C,KAAA,CAAM,OAAA,CAAQA,EAAK,QAAQ,CAAA,CAC7B,QAAWK,CAAAA,IAASL,CAAAA,CAAK,SAAU,CACjC,IAAMM,EAAQP,EAAAA,CAAaM,CAAK,EAC5BC,CAAAA,EAAOJ,CAAAA,CAAG,YAAYI,CAAK,EACjC,CAGF,OAAOJ,CACT,CAIA,SAASK,EAAAA,CAASC,CAAAA,CAAuB,CAIvC,GAAI,CACF,GAAI,YAAA,GAAgB,SAAA,CAAW,CAC7B,SAAA,CAAU,UAAA,CAAWA,CAAO,CAAA,CAC5B,MACF,CACF,CAAA,KAAQ,CAER,CACA,GAAI,CACF,KAAA,CAAMA,CAAAA,CAAS,CACb,MAAA,CAAQ,MACR,IAAA,CAAM,SAAA,CACN,YAAa,MAAA,CACb,SAAA,CAAW,EACb,CAAC,CAAA,CAAE,KAAA,CAAM,IAAG,CAAA,CAAY,EAC1B,MAAQ,CAGR,CACF,CAEA,SAASC,EAAAA,CAAeC,EAAmBC,CAAAA,CAA4B,CACrE,IAAA,IAAWC,CAAAA,IAAWD,CAAAA,CAAQ,QAAA,CAAU,CACtC,GAAI,CAACb,GAAe,GAAA,CAAIc,CAAAA,CAAQ,KAAK,CAAA,CAAG,SACxC,IAAMC,CAAAA,CAASH,CAAAA,CAAK,cAAcE,CAAAA,CAAQ,QAAQ,EAC7CC,CAAAA,EACLA,CAAAA,CAAO,iBAAiBD,CAAAA,CAAQ,KAAA,CAAO,IAAM,CAC3CL,EAAAA,CAASI,CAAAA,CAAQ,OAAO,EAC1B,CAAA,CAAG,CAAE,IAAA,CAAM,IAAA,CAAM,QAAS,IAAK,CAAC,EAClC,CACF,CAIA,SAASG,EAAAA,EAAsC,CAC7C,GAAI,CACF,IAAMC,EAAS,cAAA,CAAe,OAAA,CAAQ9B,EAAW,CAAA,CACjD,GAAI,CAAC8B,EAAQ,OAAO,IAAA,CACpB,IAAM3B,CAAAA,CAAS,IAAA,CAAK,MAAM2B,CAAM,CAAA,CAChC,OAAI,IAAA,CAAK,GAAA,GAAQ3B,CAAAA,CAAO,SAAA,CAAY,KAAqB,IAAA,CAClDA,CAAAA,CAAO,IAChB,CAAA,KAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS4B,EAAAA,CAAkBC,CAAAA,CAA0B,CACnD,GAAI,CACF,IAAM7B,CAAAA,CAAuB,CAAE,IAAA,CAAM6B,CAAAA,CAAQ,SAAA,CAAW,IAAA,CAAK,KAAM,CAAA,CACnE,eAAe,OAAA,CAAQhC,EAAAA,CAAa,KAAK,SAAA,CAAUG,CAAM,CAAC,EAC5D,CAAA,KAAQ,CAER,CACF,CAIA,SAAS8B,GAA2B,CAClC,OAAO,EAAQ,QAAA,CAAS,cAAA,CAAexB,EAAU,CACnD,CAEA,SAASyB,EAAAA,CAAUF,CAAAA,CAA0B,CAE3C,GADIC,CAAAA,IACA,CAACD,CAAAA,EAAU,CAAC,KAAA,CAAM,OAAA,CAAQA,CAAAA,CAAO,KAAK,CAAA,EAAKA,CAAAA,CAAO,MAAM,MAAA,GAAW,CAAA,CAAG,OAE1E,IAAMG,CAAAA,CAAU,SAAS,aAAA,CAAc,KAAK,EAC5CA,CAAAA,CAAQ,EAAA,CAAK1B,GACb0B,CAAAA,CAAQ,YAAA,CAAa,cAAe,MAAM,CAAA,CAC1CA,CAAAA,CAAQ,YAAA,CAAa,kBAAA,CAAoB,GAAG,EAG5CA,CAAAA,CAAQ,KAAA,CAAM,YAAY,UAAA,CAAY,UAAU,EAChDA,CAAAA,CAAQ,KAAA,CAAM,YAAY,OAAA,CAAS,KAAK,EACxCA,CAAAA,CAAQ,KAAA,CAAM,YAAY,QAAA,CAAU,KAAK,EACzCA,CAAAA,CAAQ,KAAA,CAAM,WAAA,CAAY,UAAA,CAAY,QAAQ,CAAA,CAC9CA,EAAQ,KAAA,CAAM,WAAA,CAAY,YAAa,YAAY,CAAA,CAEnD,QAAWT,CAAAA,IAAWM,CAAAA,CAAO,KAAA,CAAO,CAClC,IAAMP,CAAAA,CAAOX,GAAaY,CAAAA,CAAQ,IAAI,EACjCD,CAAAA,GAGLA,CAAAA,CAAK,aAAa,oBAAA,CAAsB,MAAA,CAAOC,CAAAA,CAAQ,EAAA,EAAM,EAAE,CAAC,EAChED,CAAAA,CAAK,YAAA,CAAa,uBAAwB,MAAA,CAAOC,CAAAA,CAAQ,MAAQ,EAAE,CAAC,EACpES,CAAAA,CAAQ,WAAA,CAAYV,CAAI,CAAA,CACxBD,EAAAA,CAAeC,EAAMC,CAAO,CAAA,EAC9B,CAGI,QAAA,CAAS,IAAA,CACX,QAAA,CAAS,IAAA,CAAK,WAAA,CAAYS,CAAO,EAEjC,QAAA,CAAS,gBAAA,CACP,mBACA,IAAM,QAAA,CAAS,MAAQ,QAAA,CAAS,IAAA,CAAK,YAAYA,CAAO,CAAA,CACxD,CAAE,IAAA,CAAM,IAAK,CACf,EAEJ,CAsBO,SAASC,CAAAA,CAAgBC,CAAAA,CAAuC,CAErE,GADIA,CAAAA,CAAQ,OAAA,GAAY,OACpBJ,CAAAA,EAAgB,CAAG,OAIvB,IAAM9B,CAAAA,CAAS0B,IAAiB,CAC5B1B,CAAAA,EACF+B,GAAU/B,CAAM,CAAA,CAGlB,IAAMpB,CAAAA,CACJ,CAAA,EAAGsD,EAAQ,OAAO,CAAA,sBAAA,EAAyB,mBAAmBA,CAAAA,CAAQ,MAAM,CAAC,CAAA,KAAA,EACrE,kBAAA,CAAmBA,CAAAA,CAAQ,SAAS,CAAC,CAAA,CAAA,CAC/C,MAAMtD,CAAAA,CAAK,CAAE,OAAQ,KAAA,CAAO,WAAA,CAAa,MAAO,CAAC,CAAA,CAC9C,IAAA,CAAMK,GAASA,CAAAA,CAAI,EAAA,CAAKA,EAAI,IAAA,EAAK,CAAI,IAAK,CAAA,CAC1C,IAAA,CAAM7C,CAAAA,EAA4B,CAC7B,CAACA,CAAAA,EAAQ,CAAC,KAAA,CAAM,OAAA,CAAQA,EAAK,KAAK,CAAA,GACtCwF,GAAkBxF,CAAI,CAAA,CAKjB0F,GAAgB,EACnBC,EAAAA,CAAU3F,CAAI,CAAA,EAElB,CAAC,EACA,KAAA,CAAM,IAAG,EAAY,EAC1B,CCxQA,IAAM+F,EAAAA,CAAuB,IAAI,GAAA,CAAI,CACnC,cAAA,CACA,SAAA,CACA,SACA,MAAA,CACA,eAAA,CACA,SACA,MAAA,CACA,KAAA,CACA,MACA,OAAA,CACA,UAAA,CACA,MACA,KAAA,CACA,aAAA,CACA,oBACA,KAAA,CACA,UAAA,CACA,MACA,eAAA,CACA,aAAA,CACA,QAAA,CACA,SAAA,CACA,KAAA,CACA,WAAA,CACA,MACA,OACF,CAAC,EAWM,SAASC,EAAAA,CAASC,EAA0C,CACjE,GAAI,CAACA,CAAAA,CAAO,OAAO,GAGnB,GAAI,CAACA,EAAM,QAAA,CAAS,GAAG,EAAG,OAAOA,CAAAA,CAMjC,IAAIC,CAAAA,CACAC,CAAAA,CAAa,KAAA,CACjB,GAAI,CACEF,CAAAA,CAAM,WAAW,GAAG,CAAA,EAAM,CAAC,eAAA,CAAgB,IAAA,CAAKA,CAAK,CAAA,EAAK,CAACA,CAAAA,CAAM,WAAW,IAAI,CAAA,EAClFC,EAAS,IAAI,GAAA,CAAID,EAAO,6BAA6B,CAAA,CACrDE,CAAAA,CAAa,CAAA,CAAA,EAEbD,CAAAA,CAAS,IAAI,IAAID,CAAK,EAE1B,MAAQ,CACN,OAAOA,CACT,CAEA,GAAIC,EAAO,YAAA,CAAa,IAAA,GAAS,EAAG,OAAOD,CAAAA,CAE3C,IAAIG,CAAAA,CAAU,KAAA,CACd,QAAWC,CAAAA,IAAO,KAAA,CAAM,IAAA,CAAKH,CAAAA,CAAO,YAAA,CAAa,IAAA,EAAM,CAAA,CACjDH,EAAAA,CAAqB,IAAIM,CAAAA,CAAI,WAAA,EAAa,CAAA,GAC5CH,CAAAA,CAAO,YAAA,CAAa,MAAA,CAAOG,CAAG,CAAA,CAC9BD,EAAU,IAAA,CAAA,CAId,OAAKA,EAEDD,CAAAA,CACKD,CAAAA,CAAO,SAAWA,CAAAA,CAAO,MAAA,CAASA,CAAAA,CAAO,IAAA,CAE3CA,CAAAA,CAAO,QAAA,GALOD,CAMvB,CC3FA,IAAMK,EAAAA,CAAc,QAAA,CAEhBC,EAA2B,IAAA,CAE/B,SAASC,IAAqB,CAC5B,IAAMC,EAAQ,IAAI,UAAA,CAAW,EAAE,CAAA,CAC/B,OAAA,MAAA,CAAO,gBAAgBA,CAAK,CAAA,CACrB,KAAA,CAAM,IAAA,CAAKA,CAAAA,CAAQlH,CAAAA,EAAMA,EAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,CAAG,GAAG,CAAC,CAAA,CAAE,IAAA,CAAK,EAAE,CAC1E,CAMO,SAASmH,CAAAA,EAAuB,CACrC,GAAIH,CAAAA,CAAW,OAAOA,EAEtB,GAAI,CACF,IAAMhB,CAAAA,CAAS,cAAA,CAAe,OAAA,CAAQe,EAAW,CAAA,CACjD,GAAIf,EACF,OAAAgB,CAAAA,CAAYhB,EACLA,CAEX,CAAA,KAAQ,CAER,CAEAgB,CAAAA,CAAYC,IAAW,CAEvB,GAAI,CACF,cAAA,CAAe,OAAA,CAAQF,GAAaC,CAAS,EAC/C,CAAA,KAAQ,CAER,CAEA,OAAOA,CACT,CAGO,SAASI,GAA0B,CACxC,IAAMC,EAAK,IAAA,CAAK,GAAA,GAAM,QAAA,CAAS,EAAE,EAC3BH,CAAAA,CAAQ,IAAI,WAAW,CAAC,CAAA,CAC9B,OAAO,eAAA,CAAgBA,CAAK,CAAA,CAC5B,IAAMI,CAAAA,CAAO,KAAA,CAAM,KAAKJ,CAAAA,CAAQlH,CAAAA,EAAMA,EAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,CAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA,CAC9E,OAAO,CAAA,EAAGqH,CAAE,IAAIC,CAAI,CAAA,CACtB,CC/CO,IAAMC,EAAAA,CAC8B,QC2G3C,IAAIC,CAAAA,CAAW,KAAA,CACXtB,EAAgC,IAAA,CAChCuB,CAAAA,CAAoC,KACpCC,CAAAA,CAAe,CAAA,CACfzD,CAAAA,CAAa,EAAA,CACb0D,CAAAA,CAAS,CAAA,CACTC,EAAiB,KAAA,CACjBC,CAAAA,CAA6B,UAC7BC,CAAAA,CAAa,EAAA,CACbC,EAAoB,KAAA,CAExB,SAASC,CAAAA,EAAuB,CAC9B,OAAK9B,CAAAA,CACDA,EAAO,KAAA,CAAc,IAAA,CAClB,CAAC,CAACuB,CAAAA,EAAaA,EAAU,UAAA,EAAc,GAAA,CAF1B,KAGtB,CAEA,SAASQ,GAAqB,CAC5B,OAAO,OAAO,MAAA,CAAW,GAAA,EAAe,OAAO,QAAA,CAAa,GAC9D,CAgBO,SAASC,EAAAA,CAAKC,CAAAA,CAAe5B,EAAuB,EAAC,CAAS,CACnE,GAAI,CAAC0B,GAAU,CAAG,OAClB,GAAI,CAACE,CAAAA,CAAO,CACN5B,CAAAA,CAAQ,KAAA,EAAO,QAAQ,KAAA,CAAM,sCAAsC,EACvE,MACF,CAEA,IAAM6B,CAAAA,CAAY,WAAA,CAAY,GAAA,GACxBhE,CAAAA,CAAAA,CAAWmC,CAAAA,CAAQ,SAAW,kBAAA,EAAoB,OAAA,CAAQ,MAAO,EAAE,CAAA,CACzEuB,EAAa1D,CAAAA,CACbpB,CAAAA,CAAY,GAAGoB,CAAO,CAAA,mBAAA,CAAqB,EAE3C8B,CAAAA,CAAS,CACP,MAAAiC,CAAAA,CACA,OAAA,CAAA/D,CAAAA,CACA,KAAA,CAAOmC,CAAAA,CAAQ,KAAA,EAAS,MACxB,cAAA,CAAgBA,CAAAA,CAAQ,gBAAkB,KAAA,CAC1C,QAAA,CAAUA,EAAQ,QACpB,CAAA,CACAiB,CAAAA,CAAW,IAAA,CACXG,CAAAA,CAAS,WAAA,CAAY,KAAI,CAAIS,CAAAA,CAEzB,EAAAlC,CAAAA,CAAO,cAAA,GACT2B,EAAepD,EAAAA,EAAkB,CAC7BoD,CAAAA,GAAiB,QAAA,EACjBA,CAAAA,GAAiB,SAAA,CAAA,CAAA,GAGvBQ,IAAa,CACb/B,CAAAA,CAAgB,CAAE,OAAA,CAAAlC,CAAAA,CAAS,OAAQ+D,CAAAA,CAAO,SAAA,CAAWhB,GAAe,CAAC,EACrEhD,CAAAA,CAAmBC,CAAO,EAGxB,QAAA,CAAS,eAAA,GAAoB,UAC5B,QAAA,CAAS,eAAA,GAA+B,WAAA,CAEzC,QAAA,CAAS,gBAAA,CACP,kBAAA,CACA,IAAM,CACA,QAAA,CAAS,kBAAoB,SAAA,EAAWkE,CAAAA,GAC9C,CAAA,CACA,CAAE,KAAM,IAAK,CACf,EAEAA,CAAAA,EAAc,EAElB,CAEA,SAASD,EAAAA,EAAqB,CAC5B,GAAI,CAACnC,CAAAA,CAAQ,OACb,IAAM/C,CAAAA,CAAoB,CACxB,CAAA,CAAG+C,CAAAA,CAAO,MACV,GAAA,CAAKiB,CAAAA,GACL,GAAA,CAAKC,CAAAA,GACL,EAAA,CAAI,SAAA,CACJ,GAAI,CAAA,CACJ,EAAA,CAAI,EAAC,CACL,EAAA,CAAI,OACJ,EAAA,CAAI,IAAI,IAAA,EAAK,CAAE,WAAA,EAAY,CAC3B,EAAG,QAAA,CAAS,QAAA,CACZ,EAAG,QAAA,CAAS,KAAA,EAAS,MACvB,CAAA,CACAlE,CAAAA,CAAUC,CAAK,EACjB,CAEA,eAAemF,GAA+B,CAC5C,GAAI,CAACpC,CAAAA,CAAQ,OAEbwB,EAAe,WAAA,CAAY,GAAA,EAAI,CAC/BzD,CAAAA,CAAa,QAAA,CAAS,QAAA,CAEtB,GAAM,CAAE,SAAA,CAAAxB,EAAW,MAAA,CAAAC,CAAO,EAAIL,CAAAA,EAAa,CAC3CoF,EAAYhF,CAAAA,CAAAA,CAERyD,CAAAA,CAAO,OAASzD,CAAAA,CAAU,UAAA,EAAc,OAC1C8F,EAAAA,EAAiB,CACjBC,GAAqB,CACrBC,EAAAA,EAAe,CAAA,CAGjB,IAAMC,CAAAA,CAAU,MAAMhG,EACtB+E,CAAAA,CAAYiB,CAAAA,CAER,CAACxC,CAAAA,CAAO,KAAA,EAASzD,EAAU,UAAA,CAAa,GAAA,EAAQiG,CAAAA,CAAQ,UAAA,EAAc,GAAA,GACxEH,EAAAA,GACAC,CAAAA,EAAqB,CACrBC,IAAe,EAEnB,CAEA,SAASA,EAAAA,EAAuB,CAC1BV,CAAAA,GACJA,CAAAA,CAAoB,IAAA,CACpBrE,CAAAA,CAAiBiF,EAAY,CAAA,CAC7B,QAAA,CAAS,iBAAiB,kBAAA,CAAoBC,EAAkB,EAChE,MAAA,CAAO,gBAAA,CAAiB,WAAYC,EAAU,CAAA,EAChD,CAEA,SAASC,CAAAA,CAAYC,EAAmBC,CAAAA,CAAyC,CAC/E,GAAI,CAAC9C,CAAAA,EAAU,CAACuB,CAAAA,CACd,MAAM,IAAI,MAAM,0DAAqD,CAAA,CAEvE,IAAM,CAAA,CAAI,QAAA,CAAS,SAAWhB,EAAAA,CAAS,QAAA,CAAS,QAAQ,CAAA,CAAI,EAAA,CAC5D,OAAO,CACL,CAAA,CAAGP,CAAAA,CAAO,MACV,GAAA,CAAKiB,CAAAA,GACL,GAAA,CAAKC,CAAAA,EAAgB,CACrB,EAAA,CAAIK,CAAAA,CAAU,QAAA,CACd,GAAI,IAAA,CAAK,KAAA,CAAMA,EAAU,UAAA,CAAa,GAAG,EAAI,GAAA,CAC7C,EAAA,CAAIA,EAAU,OAAA,CACd,EAAA,CACEA,EAAU,eAAA,EAAmB,IAAA,CACzB,KAAK,KAAA,CAAMA,CAAAA,CAAU,gBAAkB,GAAG,CAAA,CAAI,GAAA,CAC9C,MAAA,CACN,EAAA,CAAIsB,CAAAA,CACJ,GAAI,IAAI,IAAA,GAAO,WAAA,EAAY,CAC3B,EAAG,QAAA,CAAS,QAAA,CACZ,EAAG,CAAA,EAAK,MAAA,CACR,EAAG,QAAA,CAAS,KAAA,EAAS,OACrB,GAAGC,CACL,CACF,CAEA,SAAST,EAAAA,EAAyB,CAChCX,CAAAA,CAAiB,KAAA,CACjB,IAAMqB,CAAAA,CAAyB,CAC7B,QAAS,IAAA,CAAK,KAAA,CAAMtB,CAAM,CAAA,CAC1B,CAAA,CAAGJ,GACH,EAAA,CAAI,CACN,EAKIrB,CAAAA,EAAQ,QAAA,GAKR+C,EAA4C,CAAA,CAAI/C,CAAAA,CAAO,UAE3DhD,CAAAA,CACE4F,CAAAA,CAAY,eAAA,CAAiB,CAC3B,EAAA,CAAI,MAAA,CAAO,WACX,EAAA,CAAI,MAAA,CAAO,YACX,EAAA,CAAI,MAAA,CAAO,MACX,EAAA,CAAI,MAAA,CAAO,MAAA,CACX,EAAA,CAAI,SAAA,CAAU,SAAA,CACd,EAAGI,EAAAA,EAAqB,CACxB,EAAG,SAAA,CAAU,QAAA,CACb,GAAID,CACN,CAAC,CACH,EACF,CAEA,SAAST,GAA6B,CACpCd,CAAAA,CAAe,YAAY,GAAA,EAAI,CAC/BzD,EAAa,QAAA,CAAS,QAAA,CACtBf,EAAU4F,CAAAA,CAAY,WAAW,CAAC,EACpC,CAEA,SAASH,EAAAA,EAAqB,CAC5B,GAAI,CAACX,CAAAA,EAAY,CAAG,OACpB,IAAMmB,CAAAA,CAAa,KAAK,KAAA,CAAM,WAAA,CAAY,KAAI,CAAIzB,CAAY,EAC9DxE,CAAAA,CACE4F,CAAAA,CAAY,eAAgB,CAC1B,CAAA,CAAG7E,EACH,EAAA,CAAIkF,CAAAA,CACJ,GAAIC,EAAAA,EACN,CAAC,CACH,CAAA,CACAZ,CAAAA,EAAqB,CACrBZ,CAAAA,CAAiB,MACnB,CAEA,SAASgB,EAAAA,EAA2B,CAC9B,QAAA,CAAS,eAAA,GAAoB,UAAUS,EAAAA,GAC7C,CAEA,SAASR,EAAAA,EAAmB,CAC1BQ,EAAAA,GACF,CAEA,SAASA,EAAAA,EAAuB,CAE9B,GADIzB,CAAAA,EACA,CAACI,CAAAA,EAAY,CAAG,OACpBJ,EAAiB,IAAA,CACjB,IAAMuB,EAAa,IAAA,CAAK,KAAA,CAAM,YAAY,GAAA,EAAI,CAAIzB,CAAY,CAAA,CAC9DnE,CAAAA,CACEuF,CAAAA,CAAY,cAAe,CACzB,EAAA,CAAIK,EACJ,EAAA,CAAIC,EAAAA,EACN,CAAC,CACH,EACF,CAEA,SAASA,EAAAA,EAAyB,CAChC,IAAME,CAAAA,CAAY,SAAS,eAAA,CAAgB,YAAA,CACrCC,EAAa,MAAA,CAAO,WAAA,CAC1B,GAAID,CAAAA,EAAaC,CAAAA,CAAY,OAAO,GAAA,CACpC,IAAMC,EAAY,MAAA,CAAO,OAAA,EAAW,SAAS,eAAA,CAAgB,SAAA,CAC7D,OAAO,IAAA,CAAK,GAAA,CAAI,GAAA,CAAK,KAAK,KAAA,CAAA,CAAQA,CAAAA,CAAYD,GAAcD,CAAAA,CAAa,GAAG,CAAC,CAC/E,CAEA,SAASJ,EAAAA,EAA+B,CACtC,GAAI,CAEF,OAAO,UAAU,aAAA,EAAe,QAAA,EAAY,UAAU,QAAA,EAAY,EACpE,CAAA,KAAQ,CACN,OAAO,EACT,CACF,CAYO,SAASO,GAAMtG,CAAAA,CAAeuG,CAAAA,CAA4C,CAC3E,CAACzB,CAAAA,IAAe,CAACT,CAAAA,EAChBQ,GAAY,EACjB9E,CAAAA,CACE4F,EAAY,iBAAA,CAAmB,CAC7B,GAAI,CAAC,CAAE,CAAA,CAAG3F,CAAAA,CAAO,EAAA,CAAI,IAAA,CAAK,UAAUuG,CAAAA,EAAc,EAAE,CAAA,CAAG,CAAA,CAAG,EAAG,EAAA,CAAI,IAAK,CAAC,CACzE,CAAC,CACH,EACF,CAWO,SAASC,GAAQnF,CAAAA,CAA2B,CACjD,GAAI,CAACyD,CAAAA,EAAU,CAAG,OAClB,IAAM2B,CAAAA,CAAW/B,EACjBA,CAAAA,CAAenD,EAAAA,CAAeF,CAAK,CAAA,CAC/B,EAAA,CAAC0B,GAAU,CAACA,CAAAA,CAAO,iBACnB0D,CAAAA,GAAa/B,CAAAA,EAEbA,IAAiB,SAAA,EAAa+B,CAAAA,GAAa,YACzC9B,CAAAA,GAAe,EAAA,EAAI3D,EAAmB2D,CAAU,CAAA,CACpDO,EAAAA,EAAa,CACTP,CAAAA,GAAe,EAAA,EACjBxB,EAAgB,CACd,OAAA,CAASwB,EACT,MAAA,CAAQ5B,CAAAA,CAAO,MACf,SAAA,CAAWiB,CAAAA,EACb,CAAC,CAAA,CAGD,QAAA,CAAS,kBAAoB,QAAA,EAC5B,QAAA,CAAS,kBAA+B,WAAA,CAEzC,QAAA,CAAS,iBACP,kBAAA,CACA,IAAM,CACA,QAAA,CAAS,eAAA,GAAoB,SAAA,EAAWmB,IAC9C,CAAA,CACA,CAAE,IAAA,CAAM,IAAK,CACf,CAAA,CAEAA,CAAAA,IAGN,CASO,SAASuB,IAAiB,CAC3B,CAAC5B,GAAU,EAAK,CAACT,GAChBQ,CAAAA,EAAY,EACjBW,EAAAA,GACF,CAgBO,SAASmB,IAAyB,CACvCtC,CAAAA,CAAW,MACXtB,CAAAA,CAAS,IAAA,CACTuB,EAAY,IAAA,CACZC,CAAAA,CAAe,CAAA,CACfzD,CAAAA,CAAa,EAAA,CACb0D,CAAAA,CAAS,EACTC,CAAAA,CAAiB,KAAA,CACjBC,EAAe,SAAA,CACfC,CAAAA,CAAa,GACbC,CAAAA,CAAoB,KAAA,CACpB/E,CAAAA,CAAY,EAAE,EAChB","file":"index.cjs","sourcesContent":["import type { DetectionResult } from '../types'\n\n/**\n * Tier 1: Deterministic identity signals.\n * If any fire, classify immediately with high confidence.\n * Zero false positives expected.\n */\nexport function detectTier1(): DetectionResult | null {\n const signals: string[] = []\n let platform = 'unknown'\n\n // 1. navigator.webdriver — unmasked automation\n // Stealth plugins patch this to false, so absence means nothing.\n // But if true, it's definitive.\n try {\n if (navigator.webdriver === true) {\n signals.push('webdriver')\n }\n } catch {\n // Property access failed — unusual, treat as signal\n signals.push('webdriver_error')\n }\n\n // 2. Playwright globals\n if ('__playwright__binding__' in window || '__pwInitScripts' in window) {\n signals.push('playwright_globals')\n platform = 'browser_use' // Playwright-based agents (browser-use, etc.)\n }\n\n // 3. In-Chrome agent extension DOM artifacts.\n //\n // The extension-driven agents (Claude-in-Chrome, Perplexity\n // Comet, ChatGPT Atlas extension) all run inside the user's\n // own Chrome, so UA/webdriver/playwright globals are useless —\n // they're real Chrome. What they DO leave is injected DOM:\n // stop-controls, animation styles, status toasts, root\n // containers. Detecting those artifacts is the only reliable\n // way to attribute a Chrome-UA session to a specific extension.\n //\n // Registry pattern so new artifacts (different versions, other\n // extensions) can be added without changing the flow. Selectors\n // must be safe against false positives on legitimate sites —\n // stick to `[id^=\"...\"]` prefixes that match the extension's\n // own internal naming scheme and would be absurd to find on a\n // normal store.\n const EXTENSION_ARTIFACTS: ReadonlyArray<{\n selector: string\n signal: string\n platform: string\n }> = [\n /* Claude Chrome extension — `claude-agent-stop-container`,\n * `claude-agent-animation-styles`, and any future\n * `claude-agent-*` the extension might inject. Validated\n * against CHEQ's 2025 research + observed production\n * events. */\n { selector: '[id^=\"claude-agent-\"]', signal: 'claude_dom', platform: 'claude' },\n /* Perplexity Comet — the browser's extension root uses the\n * `comet-` id prefix for its overlay and toolbar containers.\n * Speculative until field-verified: selectors marked\n * TODO-research can be tightened once we have observed\n * events from Comet sessions on a production site. */\n { selector: '[id^=\"comet-agent-\"]', signal: 'comet_dom', platform: 'perplexity' },\n { selector: '[id^=\"perplexity-comet-\"]', signal: 'comet_dom_alt', platform: 'perplexity' },\n /* ChatGPT Atlas extension — injects a status indicator for\n * the agent's current action. Same TODO-research caveat. */\n { selector: '[id^=\"chatgpt-atlas-\"]', signal: 'atlas_dom', platform: 'chatgpt' },\n { selector: '[id^=\"openai-atlas-\"]', signal: 'atlas_dom_alt', platform: 'chatgpt' },\n ]\n for (const artifact of EXTENSION_ARTIFACTS) {\n if (document.querySelector(artifact.selector)) {\n signals.push(artifact.signal)\n platform = artifact.platform\n /* Keep scanning — multiple extensions CAN coexist in a\n * browser, and if we see both Claude and Atlas artifacts\n * the signals list will record both. Last-wins on\n * platform is fine because detection confidence stays at\n * 0.99 either way. */\n }\n }\n\n // 4. Known agent user-agents (self-identifying crawlers + browse\n // sessions). Order matters — most specific first so we don't\n // catch a generic substring inside a more specific UA.\n //\n // SOURCE OF TRUTH: `shared/lib/agent-ua-patterns.ts`. The\n // server-side pixel route uses the same rules. When you add\n // a new agent here, add it there too (and vice versa).\n // A future refactor will let the snippet build import from\n // shared/ directly; until then, keep both lists in sync.\n //\n // Coverage (May 2026):\n // OpenAI: GPTBot, OAI-SearchBot, ChatGPT-User, Operator\n // Anthropic: ClaudeBot, Claude-User, claude-web, Claude-SearchBot, Claude-Code\n // Perplexity: PerplexityBot, Perplexity-User\n // Google: Google-Extended (Gemini training)\n // Microsoft: bingbot, BingPreview, MicrosoftPreview\n // Apple: Applebot, Applebot-Extended (Apple Intelligence)\n // Meta: Meta-ExternalAgent, Meta-ExternalFetcher, FacebookBot\n //\n // Spoofed-UA agents (ChatGPT Atlas, Claude in Chrome via\n // extension, Perplexity Comet) use a generic Chrome UA — those\n // fall through to Tier 2's platform-mismatch heuristic and to\n // the DOM check above.\n const ua = navigator.userAgent\n if (/PerplexityBot\\/|Perplexity-User\\//i.test(ua)) {\n signals.push('perplexity_ua')\n platform = 'perplexity'\n } else if (/OAI-SearchBot\\//i.test(ua)) {\n signals.push('oai_searchbot_ua')\n platform = 'chatgpt'\n } else if (/ChatGPT-User\\//i.test(ua)) {\n signals.push('chatgpt_user_ua')\n platform = 'chatgpt'\n } else if (/Operator\\//i.test(ua)) {\n signals.push('operator_ua')\n platform = 'chatgpt'\n } else if (/GPTBot\\//i.test(ua)) {\n signals.push('gptbot_ua')\n platform = 'chatgpt'\n } else if (/ClaudeBot\\//i.test(ua)) {\n signals.push('claudebot_ua')\n platform = 'claude'\n } else if (/Claude-User\\/|claude-web\\//i.test(ua)) {\n signals.push('claude_user_ua')\n platform = 'claude'\n } else if (/Claude-SearchBot\\//i.test(ua)) {\n signals.push('claude_searchbot_ua')\n platform = 'claude'\n } else if (/Claude-Code\\//i.test(ua)) {\n signals.push('claude_code_ua')\n platform = 'claude'\n } else if (/Google-Extended\\//i.test(ua)) {\n signals.push('google_extended_ua')\n platform = 'gemini'\n } else if (/Applebot-Extended\\//i.test(ua)) {\n signals.push('applebot_extended_ua')\n platform = 'apple'\n } else if (/Applebot\\//i.test(ua)) {\n signals.push('applebot_ua')\n platform = 'apple'\n } else if (/Meta-ExternalAgent\\/|Meta-ExternalFetcher\\//i.test(ua)) {\n signals.push('meta_externalagent_ua')\n platform = 'meta'\n } else if (/bingbot\\/|BingPreview\\//i.test(ua)) {\n signals.push('bingbot_ua')\n platform = 'bing'\n }\n\n if (signals.length === 0) return null\n\n return {\n platform,\n confidence: 0.99,\n signals,\n }\n}\n","/**\n * Tier 2: Heuristic identity signals.\n * Instant checks, low false positive rate.\n * Returns signal names and a score contribution (0-1).\n */\n\ninterface Tier2Result {\n signals: string[]\n score: number // 0-1\n platform?: string\n}\n\nexport function detectTier2(): Tier2Result {\n const signals: string[] = []\n let score = 0\n let platform: string | undefined\n\n // 1. Sec-Ch-Ua-Platform mismatch (ChatGPT Agent / Operator / Atlas\n // fingerprint per Castle's research — see\n // blog.castle.io/from-puppeteer-stealth-to-nodriver/).\n //\n // The agent runs on Linux infrastructure but spoofs a desktop\n // UA so e-commerce sites don't reject it. The Sec-Ch-Ua-Platform\n // client hint isn't easily forged at the same place the UA is\n // rewritten, so a UA that claims macOS or Windows while\n // userAgentData.platform reports \"Linux\" is a near-certain tell.\n // Score is 0.5 (up from 0.4 in v1) — this signal is high\n // precision in the field and should anchor classification when\n // no Tier 1 UA fired.\n try {\n const ua = navigator.userAgent\n const claimsMac = /Macintosh|Mac OS X/i.test(ua)\n const claimsWindows = /Windows NT/i.test(ua)\n // @ts-expect-error -- userAgentData is not in all TS lib definitions\n const uaData = navigator.userAgentData\n if (uaData && uaData.platform === 'Linux' && (claimsMac || claimsWindows)) {\n signals.push('platform_mismatch')\n score += 0.5\n platform = 'chatgpt'\n }\n } catch {\n // userAgentData not available\n }\n\n // 2. Sec-Ch-Ua brand-list anomaly: ChatGPT Agent's brand list is\n // `\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\"` — no \"Google Chrome\"\n // entry even though the UA claims Chrome. Real Chrome always\n // includes a \"Google Chrome\" brand in the hint. Castle / Simon\n // Willison documented this in mid-2025.\n try {\n const ua = navigator.userAgent\n // @ts-expect-error -- userAgentData typing\n const uaData = navigator.userAgentData\n if (uaData && Array.isArray(uaData.brands) && /Chrome\\//i.test(ua)) {\n const brandNames = uaData.brands.map((b: { brand: string }) => b.brand)\n const hasGoogleChrome = brandNames.some((n: string) => /Google Chrome/i.test(n))\n const hasChromium = brandNames.some((n: string) => /Chromium/i.test(n))\n if (!hasGoogleChrome && hasChromium) {\n signals.push('brand_list_anomaly')\n score += 0.4\n platform = platform ?? 'chatgpt'\n }\n }\n } catch {\n // userAgentData.brands not available\n }\n\n // 2. maxTouchPoints + mobile UA mismatch\n try {\n const ua = navigator.userAgent\n const claimsMobile = /Mobile|Android|iPhone/i.test(ua)\n if (claimsMobile && navigator.maxTouchPoints === 0) {\n signals.push('touch_mismatch')\n score += 0.3\n }\n } catch {\n // Ignore\n }\n\n // 3. Screen resolution anomalies\n try {\n if (screen.width === 0 || screen.height === 0) {\n signals.push('zero_screen')\n score += 0.4\n }\n // Common headless default: 800x600 with 0 colorDepth\n if (screen.colorDepth === 0) {\n signals.push('zero_colordepth')\n score += 0.3\n }\n } catch {\n // Ignore\n }\n\n // 4. Viewport inconsistencies\n try {\n if (\n window.innerWidth > 0 &&\n window.outerWidth > 0 &&\n window.outerWidth < window.innerWidth\n ) {\n signals.push('viewport_mismatch')\n score += 0.2\n }\n } catch {\n // Ignore\n }\n\n // 5. Missing browser APIs that real browsers always have\n try {\n // Notification API — present in all real browsers, often missing in headless\n if (!('Notification' in window)) {\n signals.push('missing_notification_api')\n score += 0.2\n }\n } catch {\n // Ignore\n }\n\n // 6. WebGL renderer string. Real user Chrome on consumer hardware\n // exposes a concrete GPU name — \"ANGLE (Apple, Apple M3 Pro,\n // OpenGL 4.1)\", \"ANGLE (NVIDIA, NVIDIA GeForce RTX 3080...)\",\n // \"ANGLE (Intel, Intel(R) Iris Plus Graphics...)\". Managed\n // headless Chrome on server infra (Browserbase, Steel,\n // Azure-hosted Operator, Cloud Run with Xvfb) falls back to\n // software rasterizers with telltale names: \"Google\n // SwiftShader\", \"SwANGLE\", \"ANGLE (Google, Vulkan 1.3.0\n // (SwiftShader Device…\"), or \"llvmpipe\" on Mesa.\n try {\n const canvas = document.createElement('canvas')\n const gl = (canvas.getContext('webgl') ||\n canvas.getContext('experimental-webgl')) as WebGLRenderingContext | null\n if (gl) {\n const ext = gl.getExtension('WEBGL_debug_renderer_info')\n // getParameter for UNMASKED_RENDERER_WEBGL is the actual GPU\n // string; gating on ext presence keeps the type signature\n // clean across browsers that block the extension.\n const renderer =\n ext\n ? String(gl.getParameter((ext as { UNMASKED_RENDERER_WEBGL: number }).UNMASKED_RENDERER_WEBGL))\n : String(gl.getParameter(gl.RENDERER))\n const rendererLc = renderer.toLowerCase()\n if (\n rendererLc.includes('swiftshader') ||\n rendererLc.includes('swangle') ||\n rendererLc.includes('llvmpipe') ||\n rendererLc.includes('mesa offscreen')\n ) {\n signals.push('webgl_software_renderer')\n score += 0.45\n // No platform pinning — many agent fleets (Browserbase, Steel,\n // Operator infra, user's own headless Chrome) share this\n // signature. Attribution happens at the next layer up via IP\n // or referrer, with this as a boost for \"is it agent infra\".\n }\n } else if (gl === null) {\n // No WebGL at all is a strong headless tell — real Chrome has\n // WebGL on even on low-end hardware via software fallback.\n signals.push('missing_webgl')\n score += 0.25\n }\n } catch {\n // Ignore — private mode or policy can block canvas/WebGL.\n }\n\n // 7. Canvas fingerprint coarseness. Real hardware rasterization\n // produces per-GPU micro-variations in anti-aliasing. Pure\n // software rasterizers in agent infra produce a VERY small set\n // of canvas hashes — specifically, the output is indistinguishable\n // from every other SwiftShader instance running the same draw.\n // We can't ship a hash DB in the snippet, but we can probe for\n // the simpler tell: the \"consistent zero-bit at specific pixels\"\n // that indicates no sub-pixel jitter. Done inline to keep the\n // snippet tiny.\n try {\n const canvas = document.createElement('canvas')\n canvas.width = 32\n canvas.height = 16\n const ctx = canvas.getContext('2d')\n if (ctx) {\n ctx.textBaseline = 'top'\n ctx.font = '13px \"Arial\"'\n ctx.fillStyle = '#f60'\n ctx.fillRect(0, 0, 32, 16)\n ctx.fillStyle = '#069'\n ctx.fillText('serge:ag', 1, 1)\n const data = ctx.getImageData(0, 0, 32, 16).data\n let zeroCount = 0\n let maxVal = 0\n for (let i = 0; i < data.length; i += 4) {\n if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0) zeroCount++\n if (data[i] > maxVal) maxVal = data[i]\n }\n // SwiftShader outputs have a narrow distribution — far more\n // exact-zero pixels from the clear/fill boundary than GPU AA.\n const zeroRatio = zeroCount / (data.length / 4)\n if (zeroRatio > 0.95 && maxVal < 20) {\n signals.push('canvas_flat_render')\n score += 0.2\n }\n }\n } catch {\n // Ignore\n }\n\n // 8. Sec-CH-UA-Full-Version-List + Arch + Bitness + Model.\n // Requires `accept-ch` on the HTML response to be delivered —\n // when present it's a richer surface than brand+version.\n // Two patterns to flag:\n // (a) UA claims Chrome X, but the full-version-list entry for\n // \"Google Chrome\" reports a different major. Real Chrome\n // always agrees. Spoofing agents patch the UA string but\n // not the high-entropy client hint.\n // (b) The arch doesn't match the UA. \"Windows NT 10.0; Win64;\n // x64\" but arch='arm' is a headless emulation tell.\n try {\n // @ts-expect-error — client hints API typing lags in TS lib.dom.\n const uaData = navigator.userAgentData\n if (uaData && typeof uaData.getHighEntropyValues === 'function') {\n // High-entropy hints are a Promise. Fire-and-forget via an\n // unawaited call — we push synthesised signals into the shared\n // `signals` array synchronously only when already-resolved\n // hints contradict the UA. The *async* version of this check\n // folds into a Tier-2.5 observation that score.ts awaits.\n // Sync cheap path: check the brand-major against UA version.\n const ua = navigator.userAgent\n const match = /Chrome\\/(\\d+)/i.exec(ua)\n if (match && Array.isArray(uaData.brands)) {\n const claimedMajor = Number(match[1])\n const chromeBrand = uaData.brands.find((b: { brand: string }) =>\n /Chrome/i.test(b.brand) && !/Chromium/i.test(b.brand),\n )\n if (chromeBrand && typeof chromeBrand.version === 'string') {\n const brandMajor = Number(chromeBrand.version.split('.')[0])\n if (\n Number.isFinite(claimedMajor) &&\n Number.isFinite(brandMajor) &&\n Math.abs(claimedMajor - brandMajor) > 2\n ) {\n signals.push('ua_version_major_mismatch')\n score += 0.35\n }\n }\n }\n }\n } catch {\n // Ignore — client hints blocked or unavailable.\n }\n\n // Cap at 1.0\n score = Math.min(score, 1)\n\n return { signals, score, platform }\n}\n","/**\n * Tier 3: Behavioral signals.\n * Observed over a 5-second window after page load.\n * Classification happens client-side — only the score and signal names\n * are sent to the server. Raw behavioral data never leaves the browser.\n */\n\ninterface BehavioralResult {\n signals: string[]\n score: number // 0-1\n}\n\ninterface BehavioralState {\n mouseMoveCount: number\n scrollEvents: number\n clickCount: number\n clickTimestamps: number[]\n /** True if at least one hover event occurred anywhere in the session */\n anyHoverDetected: boolean\n firstInteractionMs: number | null\n pageLoadTime: number\n}\n\nlet state: BehavioralState | null = null\nlet observing = false\nlet resolvePromise: ((result: BehavioralResult) => void) | null = null\n\nfunction initState(): BehavioralState {\n return {\n mouseMoveCount: 0,\n scrollEvents: 0,\n clickCount: 0,\n clickTimestamps: [],\n anyHoverDetected: false,\n firstInteractionMs: null,\n pageLoadTime: performance.now(),\n }\n}\n\nfunction onMouseMove() {\n if (!state) return\n state.mouseMoveCount++\n}\n\nfunction onMouseOver() {\n if (!state) return\n // Track that hover occurred at any point in the session (not per-click)\n state.anyHoverDetected = true\n}\n\nfunction onScroll() {\n if (!state) return\n state.scrollEvents++\n}\n\nfunction onClick() {\n if (!state) return\n const now = performance.now()\n state.clickCount++\n state.clickTimestamps.push(now)\n if (state.firstInteractionMs === null) {\n state.firstInteractionMs = now - state.pageLoadTime\n }\n}\n\nfunction removeListeners() {\n document.removeEventListener('mousemove', onMouseMove)\n document.removeEventListener('mouseover', onMouseOver)\n document.removeEventListener('scroll', onScroll)\n document.removeEventListener('click', onClick)\n observing = false\n}\n\nfunction computeScore(): BehavioralResult {\n if (!state) return { signals: [], score: 0 }\n\n const signals: string[] = []\n let score = 0\n\n // 1. No mouse events at all (strong signal, but also touch devices)\n if (state.mouseMoveCount === 0 && state.clickCount > 0) {\n signals.push('no_mouse')\n score += 0.3\n }\n\n // 2. Impossible timing — interaction <100ms after page load\n if (state.firstInteractionMs !== null && state.firstInteractionMs < 100) {\n signals.push('fast_interaction')\n score += 0.4\n }\n\n // 3. No hover events at all before/during any clicks\n if (state.clickCount > 0 && !state.anyHoverDetected && state.mouseMoveCount === 0) {\n signals.push('no_hover')\n score += 0.2\n }\n\n // 4. Mechanical click timing — low variance between click intervals\n if (state.clickTimestamps.length >= 3) {\n const intervals: number[] = []\n for (let i = 1; i < state.clickTimestamps.length; i++) {\n intervals.push(state.clickTimestamps[i] - state.clickTimestamps[i - 1])\n }\n const mean = intervals.reduce((a, b) => a + b, 0) / intervals.length\n const variance =\n intervals.reduce((sum, v) => sum + (v - mean) ** 2, 0) / intervals.length\n const cv = mean > 0 ? Math.sqrt(variance) / mean : 0\n // Coefficient of variation < 0.1 = very regular timing (human CV is typically 0.3-0.8)\n if (cv < 0.1) {\n signals.push('mechanical_timing')\n score += 0.3\n }\n }\n\n // 5. No scroll events but page is long (agent jumped to content)\n if (\n state.scrollEvents === 0 &&\n document.documentElement.scrollHeight > window.innerHeight * 2 &&\n state.clickCount > 0\n ) {\n signals.push('no_scroll')\n score += 0.15\n }\n\n return { signals, score: Math.min(score, 1) }\n}\n\n/**\n * Start observing behavioral signals for 5 seconds.\n * Returns a promise that resolves with the classification.\n */\nexport function observeBehavior(durationMs = 5000): Promise<BehavioralResult> {\n if (observing) {\n return new Promise((resolve) => {\n resolvePromise = resolve\n })\n }\n\n state = initState()\n observing = true\n\n document.addEventListener('mousemove', onMouseMove, { passive: true })\n document.addEventListener('mouseover', onMouseOver, { passive: true })\n document.addEventListener('scroll', onScroll, { passive: true })\n document.addEventListener('click', onClick, { passive: true })\n\n return new Promise((resolve) => {\n resolvePromise = resolve\n\n setTimeout(() => {\n removeListeners()\n const result = computeScore()\n state = null\n if (resolvePromise) {\n resolvePromise(result)\n resolvePromise = null\n }\n }, durationMs)\n })\n}\n\n","import type { DetectionResult } from '../types'\nimport { detectTier1 } from './tier1'\nimport { detectTier2 } from './tier2'\nimport { observeBehavior } from './tier3'\n\n/**\n * Two-phase detection:\n * - `immediate`: Tier 1+2 results (sync, <1ms). Use this to decide whether to start tracking.\n * - `refine`: Promise that resolves after 5s with Tier 3 behavioral data folded in.\n *\n * This lets the caller send events immediately for high-confidence detections\n * without waiting 5 seconds for behavioral observation.\n */\nexport function runDetection(): {\n immediate: DetectionResult\n refine: Promise<DetectionResult>\n} {\n // Tier 1 — instant deterministic\n const tier1 = detectTier1()\n\n if (tier1 && tier1.confidence >= 0.85) {\n // High confidence from Tier 1 alone — no need for Tier 2 or 3\n return {\n immediate: tier1,\n refine: Promise.resolve(tier1),\n }\n }\n\n // Tier 2 — instant heuristic\n const tier2 = detectTier2()\n\n // Combine Tier 1 + Tier 2 signals\n const signals = [...(tier1?.signals ?? []), ...tier2.signals]\n const platform = tier1?.platform ?? tier2.platform ?? 'unknown'\n const tier12Confidence = Math.min((tier1?.confidence ?? 0) * 0.5 + tier2.score, 1)\n\n const immediate: DetectionResult = {\n platform,\n confidence: tier12Confidence,\n signals: [...signals],\n }\n\n // If already high confidence, skip behavioral\n if (tier12Confidence >= 0.85) {\n return {\n immediate,\n refine: Promise.resolve(immediate),\n }\n }\n\n // Tier 3 — 5-second behavioral observation (async)\n const refine = observeBehavior(5000).then((behavioral): DetectionResult => {\n const allSignals = [...signals, ...behavioral.signals]\n const behavioralScore = behavioral.score\n\n // Composite: weighted combination of Tier 1+2 and Tier 3\n let finalConfidence: number\n if (tier12Confidence > 0) {\n finalConfidence = Math.min(0.3 * tier12Confidence + 0.7 * behavioralScore, 1)\n } else {\n // Behavioral-only: cap at 0.85 (never high-confidence without identity signals)\n finalConfidence = behavioralScore * 0.85\n }\n\n return {\n platform,\n confidence: finalConfidence,\n signals: allSignals,\n behavioralScore,\n }\n })\n\n return { immediate, refine }\n}\n\n","import type { AgentEvent } from './types'\n\nlet endpoint = ''\n\nexport function setEndpoint(url: string) {\n endpoint = url\n}\n\n/**\n * Send an event via fetch with keepalive.\n * Content-Type: text/plain to avoid CORS preflight.\n * Single retry after 2 seconds on failure.\n */\nexport function sendEvent(event: AgentEvent): void {\n if (!endpoint) return\n\n const body = JSON.stringify(event)\n\n doSend(body).catch(() => {\n // Single retry after 2 seconds\n setTimeout(() => doSend(body).catch(() => { /* drop */ }), 2000)\n })\n}\n\nfunction doSend(body: string): Promise<void> {\n return fetch(endpoint, {\n method: 'POST',\n body,\n headers: { 'Content-Type': 'text/plain' },\n keepalive: true,\n /* Defense-in-depth on the cookieless contract. The default fetch\n * credentials policy is `same-origin` — fine for the common case\n * (cross-origin to serge.ai sends no cookies), but a customer\n * configured with first-party proxy `apiBase: '/serge'` puts the\n * request in same-origin mode where the customer's site cookies\n * WOULD be sent by default. Explicit `omit` makes the contract\n * unconditional: the SDK never carries cookies on the wire,\n * regardless of how the customer routes traffic. */\n credentials: 'omit',\n }).then((res) => {\n if (!res.ok && res.status !== 429) {\n throw new Error(`HTTP ${res.status}`)\n }\n })\n}\n\n/**\n * Send event via sendBeacon (for page unload).\n * Does not support retry — best-effort delivery.\n */\nexport function sendBeaconEvent(event: AgentEvent): void {\n if (!endpoint) return\n\n const body = JSON.stringify(event)\n\n // Try fetchLater first (98% reliability in Chrome M134+)\n // @ts-expect-error -- fetchLater is not in TS lib definitions yet\n if (typeof fetchLater === 'function') {\n try {\n // @ts-expect-error -- fetchLater is not in TS lib definitions yet\n fetchLater(endpoint, {\n method: 'POST',\n body,\n headers: { 'Content-Type': 'text/plain' },\n activateAfter: 0, // send immediately on page close\n credentials: 'omit', // cookieless contract — see doSend above\n })\n return\n } catch {\n // Fall through to sendBeacon\n }\n }\n\n /* Fallback: sendBeacon. Note: navigator.sendBeacon does NOT\n * accept a credentials option — it always uses the browser's\n * default same-origin policy. For the cross-origin case (no\n * proxy, direct to serge.ai) this means no cookies sent (correct).\n * For the first-party-proxy case (apiBase: '/serge') the request\n * is same-origin and customer-site cookies WOULD be sent on the\n * sendBeacon call. This is a browser API limitation, not an SDK\n * bug — documented in the README's first-party-proxy section. */\n try {\n navigator.sendBeacon(endpoint, body)\n } catch {\n // Last resort: fire-and-forget fetch\n fetch(endpoint, {\n method: 'POST',\n body,\n headers: { 'Content-Type': 'text/plain' },\n keepalive: true,\n credentials: 'omit', // cookieless contract — see doSend above\n }).catch(() => { /* drop */ })\n }\n}\n","/**\n * SPA navigation detection.\n * Primary: Navigation API (Baseline January 2026).\n * Fallback: History API wrapping + popstate.\n * Also handles bfcache restoration.\n */\n\nlet lastUrl = ''\nlet onNav: (() => void) | null = null\n\nexport function initSpaDetection(callback: () => void): void {\n onNav = callback\n lastUrl = location.pathname + location.search\n\n // Primary: Navigation API (Chrome, Safari, Firefox — Baseline January 2026)\n if ('navigation' in window) {\n const nav = (window as { navigation: EventTarget }).navigation\n nav.addEventListener('navigatesuccess', () => {\n // navigatesuccess fires AFTER the URL has committed — safe to read location\n handleNavigation()\n })\n } else {\n // Fallback: History API wrapping\n const originalPushState = history.pushState\n const originalReplaceState = history.replaceState\n\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState.apply(this, args)\n handleNavigation()\n }\n\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\n originalReplaceState.apply(this, args)\n handleNavigation()\n }\n\n window.addEventListener('popstate', handleNavigation)\n }\n\n // Handle bfcache restoration (back/forward cache)\n window.addEventListener('pageshow', (e) => {\n if (e.persisted) handleNavigation()\n })\n}\n\nfunction handleNavigation() {\n const currentUrl = location.pathname + location.search\n if (currentUrl === lastUrl) return // Deduplicate\n lastUrl = currentUrl\n onNav?.()\n}\n","/**\n * Signal definitions loader — fetches updated signal definitions from the server.\n *\n * Pattern: bundled fallback + async update (Cloudflare Turnstile / Fingerprint Pro).\n * - Detection runs immediately with bundled signals (zero added latency)\n * - This module async-fetches /api/v1/signals and caches in sessionStorage\n * - On subsequent page loads, sessionStorage signals are used immediately\n * - If fetch fails, bundled signals are sufficient\n *\n * The API response is CDN-cached for 24 hours, so this fetch is nearly free.\n */\n\nconst STORAGE_KEY = 'serge_signals'\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\n\ninterface CachedSignals {\n data: unknown\n fetchedAt: number\n}\n\n/**\n * Try to load cached signal definitions from sessionStorage.\n * Returns null if no cached signals or if they're expired.\n */\nexport function getCachedSignals(): unknown | null {\n try {\n const stored = sessionStorage.getItem(STORAGE_KEY)\n if (!stored) return null\n const cached = JSON.parse(stored) as CachedSignals\n if (Date.now() - cached.fetchedAt > CACHE_TTL_MS) return null\n return cached.data\n } catch {\n return null\n }\n}\n\n/**\n * Async-fetch updated signal definitions from the server.\n * Non-blocking — fire and forget. Stores result in sessionStorage for next page load.\n *\n * @param apiBase The API base URL (derived from the snippet's own origin)\n */\nexport function fetchSignalUpdates(apiBase: string): void {\n const url = `${apiBase}/api/v1/signals`\n\n fetch(url, {\n method: 'GET',\n credentials: 'omit', // No cookies needed for public endpoint\n })\n .then((res) => {\n if (!res.ok) return\n return res.json()\n })\n .then((data) => {\n if (!data) return\n try {\n const cached: CachedSignals = {\n data,\n fetchedAt: Date.now(),\n }\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(cached))\n } catch {\n // sessionStorage full or blocked — ignore\n }\n })\n .catch(() => {\n // Fetch failed — no problem, bundled signals are sufficient\n })\n}\n","/**\n * Snippet consent gating — for customers who require explicit opt-in\n * before any tracking script writes storage on their site.\n *\n * Activated by adding `data-require-consent=\"true\"` on the script tag.\n * In that mode, the snippet still loads + reads the script config but\n * does NOT call `startTracking()` or send the init ping until the\n * customer's consent management platform fires:\n *\n * window.serge('consent', 'granted') // start tracking\n * window.serge('consent', 'denied') // session-only, no cookies\n * window.serge('consent', 'unknown') // pending, snippet stays dormant\n *\n * Choice is persisted in localStorage under `_serge_consent` so\n * subsequent page loads inherit the previous answer and don't wait for\n * the CMP to fire again.\n *\n * The localStorage write itself is technically subject to ePrivacy\n * Art. 5(3), but since it stores the consent answer (the regulated\n * action's own metadata, not user data), it is treated as essential\n * by the EDPB FAQ on the EDPB Guidelines 2/2023 — same status as a\n * \"remember-my-choice\" cookie set by the CMP itself.\n */\n\nconst STORAGE_KEY = '_serge_consent'\n\nexport type ConsentValue = 'granted' | 'denied' | 'unknown'\n\nlet cached: ConsentValue | null = null\n\nfunction readStorage(): ConsentValue {\n try {\n const v = window.localStorage.getItem(STORAGE_KEY)\n if (v === 'granted' || v === 'denied') return v\n } catch {\n /* private mode / blocked storage */\n }\n return 'unknown'\n}\n\nfunction writeStorage(value: ConsentValue): void {\n if (value === 'unknown') return\n try {\n window.localStorage.setItem(STORAGE_KEY, value)\n } catch {\n /* never throw on storage rejection */\n }\n}\n\n/** Initial consent state at snippet boot. Used by init() to decide whether\n * to start tracking immediately or sit in the pending state. */\nexport function getInitialConsent(): ConsentValue {\n if (cached !== null) return cached\n cached = readStorage()\n return cached\n}\n\n/** Record the consumer's consent choice. Persists for next visits. */\nexport function setConsent(value: ConsentValue): ConsentValue {\n if (value !== 'granted' && value !== 'denied' && value !== 'unknown') {\n /* Ignore unknown action values — defensive against bad CMP wiring */\n return cached ?? 'unknown'\n }\n cached = value\n writeStorage(value)\n return value\n}\n","/**\n * Agent-detection honeypot — server-driven trap injector + watcher.\n *\n * The trap *definitions* (what to render, what to watch for) live\n * server-side in `server/services/trap-config.ts`. This module ships\n * with the snippet ONCE and renders whatever the server returns from\n * `/api/v1/trap-config?s=...&sid=...`. Updating traps = changing the\n * server-side config; no snippet redeploy.\n *\n * Safety rails:\n * - Tag list, attribute names, and style props are validated\n * against the closed enums declared by the schema. The renderer\n * uses `document.createElement` + `setAttribute` exclusively —\n * no `innerHTML`, no `insertAdjacentHTML`, no eval. Even a\n * server compromise can't escalate to XSS in the customer's\n * site through this surface.\n * - The trap roots are appended to <body> so the customer's\n * CSS / layout isn't perturbed (visually-hidden via clip-path).\n *\n * Trap fires:\n * - URL-fetch traps (kind=link/sku) fire server-side when the\n * hidden link is followed by an agent — the agent's GET to\n * /api/v1/trap/fire is the signal.\n * - DOM-interaction traps (kind=button/variant/form) fire\n * client-side: the watcher attaches event listeners to the\n * trap root and POSTs to /api/v1/trap/fire on interaction.\n *\n * Idempotency: re-injection on SPA navigations is suppressed by\n * tagging the wrapper element with `data-serge-traps=\"1\"` and\n * checking before each pass.\n */\n\nconst WRAPPER_ID = 'serge-traps-root'\nconst STORAGE_KEY = 'serge_trap_config'\nconst CACHE_TTL_MS = 60 * 60 * 1000 /* 1 hour — matches token TTL */\n\n/* ── Allowed surface — must match shared/lib/trap-config-schema.ts\n * (kept inline to avoid the cross-package import). If the server\n * ships an unknown tag/attribute we silently drop it rather than\n * crashing the snippet. */\nconst ALLOWED_TAGS = new Set([\n 'div', 'span', 'p', 'a', 'button', 'select', 'option',\n 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'ul', 'li', 'img', 'form', 'input', 'label',\n])\n\nconst ALLOWED_ATTRS = new Set([\n 'role', 'aria-label', 'aria-hidden', 'aria-describedby', 'aria-labelledby',\n 'tabindex', 'href', 'target', 'rel', 'type', 'name', 'value', 'placeholder',\n 'src', 'alt', 'data-trap', 'data-trap-kind', 'data-trap-token',\n])\n\nconst ALLOWED_STYLE = new Set([\n 'position', 'top', 'left', 'right', 'bottom',\n 'width', 'height', 'overflow', 'visibility', 'opacity',\n 'clip-path', 'clip', 'pointer-events',\n 'color', 'background', 'background-color', 'font-size',\n])\n\nconst ALLOWED_EVENTS = new Set(['click', 'change', 'submit', 'mouseover', 'focus'])\n\ninterface TrapElement {\n tag: string\n attrs?: Record<string, string>\n style?: Record<string, string>\n text?: string\n children?: TrapElement[]\n}\n\ninterface TrapTrigger {\n selector: string\n event: string\n fireKind: string\n}\n\ninterface TrapVariant {\n id: string\n kind: string\n trapUrl: string\n root: TrapElement\n triggers: TrapTrigger[]\n}\n\ninterface TrapConfig {\n version: string\n traps: TrapVariant[]\n}\n\ninterface CachedConfig {\n data: TrapConfig\n fetchedAt: number\n}\n\n// ── Safe DOM builder ──────────────────────────────────────────\n\nfunction buildElement(spec: TrapElement): HTMLElement | null {\n const tag = String(spec.tag ?? '').toLowerCase()\n if (!ALLOWED_TAGS.has(tag)) return null\n\n const el = document.createElement(tag)\n\n if (spec.attrs && typeof spec.attrs === 'object') {\n for (const [name, value] of Object.entries(spec.attrs)) {\n if (!ALLOWED_ATTRS.has(name)) continue\n if (typeof value !== 'string') continue\n try {\n el.setAttribute(name, value)\n } catch {\n /* setAttribute can throw on illegal values for some attrs;\n * silently skip */\n }\n }\n }\n\n if (spec.style && typeof spec.style === 'object') {\n for (const [prop, value] of Object.entries(spec.style)) {\n if (!ALLOWED_STYLE.has(prop)) continue\n if (typeof value !== 'string') continue\n try {\n el.style.setProperty(prop, value)\n } catch {\n /* invalid CSS value — skip */\n }\n }\n }\n\n if (typeof spec.text === 'string') {\n el.appendChild(document.createTextNode(spec.text))\n }\n\n if (Array.isArray(spec.children)) {\n for (const child of spec.children) {\n const built = buildElement(child)\n if (built) el.appendChild(built)\n }\n }\n\n return el\n}\n\n// ── Trap fire — client-side path ──────────────────────────────\n\nfunction fireTrap(trapUrl: string): void {\n /* Use sendBeacon when available (survives page navigation) and\n * fall back to a fire-and-forget fetch otherwise. The endpoint\n * returns a 1×1 PNG so we never need to read the response. */\n try {\n if ('sendBeacon' in navigator) {\n navigator.sendBeacon(trapUrl)\n return\n }\n } catch {\n /* fall through */\n }\n try {\n fetch(trapUrl, {\n method: 'GET',\n mode: 'no-cors',\n credentials: 'omit',\n keepalive: true,\n }).catch(() => undefined)\n } catch {\n /* The trap is best-effort — never throw into the customer's\n * page from this path. */\n }\n}\n\nfunction attachTriggers(root: HTMLElement, variant: TrapVariant): void {\n for (const trigger of variant.triggers) {\n if (!ALLOWED_EVENTS.has(trigger.event)) continue\n const target = root.querySelector(trigger.selector)\n if (!target) continue\n target.addEventListener(trigger.event, () => {\n fireTrap(variant.trapUrl)\n }, { once: true, capture: true })\n }\n}\n\n// ── Cache ──────────────────────────────────────────────────────\n\nfunction readCachedConfig(): TrapConfig | null {\n try {\n const stored = sessionStorage.getItem(STORAGE_KEY)\n if (!stored) return null\n const cached = JSON.parse(stored) as CachedConfig\n if (Date.now() - cached.fetchedAt > CACHE_TTL_MS) return null\n return cached.data\n } catch {\n return null\n }\n}\n\nfunction writeCachedConfig(config: TrapConfig): void {\n try {\n const cached: CachedConfig = { data: config, fetchedAt: Date.now() }\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(cached))\n } catch {\n /* QuotaExceeded / privacy mode — caching is best effort. */\n }\n}\n\n// ── Render ─────────────────────────────────────────────────────\n\nfunction alreadyInjected(): boolean {\n return Boolean(document.getElementById(WRAPPER_ID))\n}\n\nfunction injectAll(config: TrapConfig): void {\n if (alreadyInjected()) return\n if (!config || !Array.isArray(config.traps) || config.traps.length === 0) return\n\n const wrapper = document.createElement('div')\n wrapper.id = WRAPPER_ID\n wrapper.setAttribute('aria-hidden', 'true')\n wrapper.setAttribute('data-serge-traps', '1')\n /* Safety net — if the server-config style accidentally exposes\n * the wrapper, this inline style still hides it. */\n wrapper.style.setProperty('position', 'absolute')\n wrapper.style.setProperty('width', '1px')\n wrapper.style.setProperty('height', '1px')\n wrapper.style.setProperty('overflow', 'hidden')\n wrapper.style.setProperty('clip-path', 'inset(50%)')\n\n for (const variant of config.traps) {\n const root = buildElement(variant.root)\n if (!root) continue\n /* Stamp the variant id + kind so we can identify the source\n * if a trap fires through an unexpected path. */\n root.setAttribute('data-serge-trap-id', String(variant.id ?? ''))\n root.setAttribute('data-serge-trap-kind', String(variant.kind ?? ''))\n wrapper.appendChild(root)\n attachTriggers(root, variant)\n }\n\n /* Wait for body before appending — the snippet may run very early. */\n if (document.body) {\n document.body.appendChild(wrapper)\n } else {\n document.addEventListener(\n 'DOMContentLoaded',\n () => document.body && document.body.appendChild(wrapper),\n { once: true },\n )\n }\n}\n\n// ── Public entry ──────────────────────────────────────────────\n\nexport interface InjectHoneypotsOptions {\n apiBase: string\n siteId: string\n sessionId: string\n /** Set to false to skip injection entirely (consent gate, debug). */\n enabled?: boolean\n}\n\n/**\n * Fetch the latest trap config from the server, render the traps\n * into the page, and attach client-side interaction watchers.\n *\n * Idempotent — safe to call multiple times. Subsequent calls are\n * no-ops if the wrapper element is already in the DOM.\n *\n * Failures are silent — honeypots are best-effort. The snippet's\n * other detection paths still run.\n */\nexport function injectHoneypots(options: InjectHoneypotsOptions): void {\n if (options.enabled === false) return\n if (alreadyInjected()) return\n\n /* Try the cache first so subsequent page-loads in the same tab\n * inject instantly without a network round-trip. */\n const cached = readCachedConfig()\n if (cached) {\n injectAll(cached)\n }\n\n const url =\n `${options.apiBase}/api/v1/trap-config?s=${encodeURIComponent(options.siteId)}` +\n `&sid=${encodeURIComponent(options.sessionId)}`\n fetch(url, { method: 'GET', credentials: 'omit' })\n .then((res) => (res.ok ? res.json() : null))\n .then((data: TrapConfig | null) => {\n if (!data || !Array.isArray(data.traps)) return\n writeCachedConfig(data)\n /* If we already injected from cache, the new config will\n * apply on the next page in this session — re-injecting now\n * would race with running watchers. The version field lets\n * us invalidate aggressively in a future revision. */\n if (!alreadyInjected()) {\n injectAll(data)\n }\n })\n .catch(() => undefined)\n}\n","/**\n * URL query-string PII scrubber.\n *\n * Strips a denylist of well-known sensitive query keys from a URL\n * before it leaves the browser. The dominant leak vector is\n * `document.referrer` — a user landing on the customer's site from\n * a password-reset email, OAuth callback, or magic-link login\n * carries those tokens in the previous URL's query string.\n *\n * The denylist is intentionally tight. Pruning *all* unknown query\n * params would corrupt legitimate analytics signal (utm_*, ref,\n * source, etc.). We only redact keys that are reliably sensitive\n * across products in the wild.\n *\n * Case-insensitive match. Both `?Token=…` and `?TOKEN=…` are\n * stripped. Non-URL inputs are returned unchanged so the caller\n * doesn't need a try/catch on every transmission site.\n */\n\n/* Keep this list short. The cost of false-positives (dropping a\n * legit utm-style param because its name happens to contain \"key\")\n * is borne on every event in production. Match exact key names,\n * not substrings.\n *\n * Pre-publish security audit (2026-05-10) added the second cluster:\n * bearer / jwt / pin / cvv / cvc / ssn / otp / one_time_password.\n * None of those are utm-style analytics keys, so the false-positive\n * risk is effectively zero, and they cover real-world magic-link /\n * payment / one-time-password flows that occasionally land tokens\n * in URL fragments and become referer leakage. */\nconst SENSITIVE_QUERY_KEYS = new Set([\n 'access_token',\n 'api_key',\n 'apikey',\n 'auth',\n 'authorization',\n 'bearer',\n 'code',\n 'cvc',\n 'cvv',\n 'email',\n 'id_token',\n 'jwt',\n 'key',\n 'oauth_token',\n 'one_time_password',\n 'otp',\n 'password',\n 'pin',\n 'refresh_token',\n 'reset_token',\n 'secret',\n 'session',\n 'sig',\n 'signature',\n 'ssn',\n 'token',\n])\n\n/**\n * Strip sensitive query keys from a URL string. Returns the input\n * unchanged when:\n * - the input is empty / null\n * - the input is not a parseable absolute or root-relative URL\n *\n * Passes through hash fragments, path, and host; only the search\n * portion is rewritten.\n */\nexport function scrubUrl(input: string | null | undefined): string {\n if (!input) return ''\n /* Fast path: no query string, nothing to scrub. Avoids the URL\n * constructor overhead on every page_view event. */\n if (!input.includes('?')) return input\n\n /* `URL` requires an absolute URL. For inputs that come from\n * `document.referrer` this is always absolute. For root-relative\n * inputs (uncommon here) we anchor against a placeholder origin\n * so the parser succeeds, then strip the origin back off. */\n let parsed: URL\n let isRelative = false\n try {\n if (input.startsWith('/') || (!/^[a-z]+:\\/\\//i.test(input) && !input.startsWith('//'))) {\n parsed = new URL(input, 'https://placeholder.invalid')\n isRelative = true\n } else {\n parsed = new URL(input)\n }\n } catch {\n return input\n }\n\n if (parsed.searchParams.size === 0) return input\n\n let mutated = false\n for (const key of Array.from(parsed.searchParams.keys())) {\n if (SENSITIVE_QUERY_KEYS.has(key.toLowerCase())) {\n parsed.searchParams.delete(key)\n mutated = true\n }\n }\n\n if (!mutated) return input\n\n if (isRelative) {\n return parsed.pathname + parsed.search + parsed.hash\n }\n return parsed.toString()\n}\n","/**\n * Session and event identity for the snippet.\n *\n * Visitor-level identity is NOT generated client-side. It is derived\n * server-side at ingest from a daily-rotating salt and the request's\n * truncated IP + User-Agent + workspace + domain. The snippet does\n * not write a cookie, does not write to localStorage, and does not\n * send a `vid` field. See server/services/analytics-salt.ts for the\n * full protocol and CNIL/EDPB privacy posture.\n *\n * What's left here:\n * - sessionId: per-tab transient ID (sessionStorage). Dies on tab\n * close. Used to correlate events within a browsing session.\n * - generateEventId: per-event unique id, ordering-friendly.\n */\n\nconst SESSION_KEY = '_s_sid'\n\nlet sessionId: string | null = null\n\nfunction generateId(): string {\n const bytes = new Uint8Array(12)\n crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(36).padStart(2, '0')).join('')\n}\n\n/**\n * Get or create a session ID (tab-scoped, dies on tab close).\n * Uses sessionStorage with in-memory fallback.\n */\nexport function getSessionId(): string {\n if (sessionId) return sessionId\n\n try {\n const stored = sessionStorage.getItem(SESSION_KEY)\n if (stored) {\n sessionId = stored\n return stored\n }\n } catch {\n // sessionStorage blocked\n }\n\n sessionId = generateId()\n\n try {\n sessionStorage.setItem(SESSION_KEY, sessionId)\n } catch {\n // storage blocked — in-memory only\n }\n\n return sessionId\n}\n\n/** Generate a unique event ID (timestamp-prefixed for ordering) */\nexport function generateEventId(): string {\n const ts = Date.now().toString(36)\n const bytes = new Uint8Array(4)\n crypto.getRandomValues(bytes)\n const rand = Array.from(bytes, (b) => b.toString(36).padStart(2, '0')).join('')\n return `${ts}-${rand}`\n}\n","/**\n * SDK version constants.\n *\n * `SDK_VERSION` is replaced at build time by tsup's `define` (and by\n * rollup's `replace` for the IIFE build) so the published artifact\n * contains the literal string `'0.1.0'` not `__SDK_VERSION__`.\n *\n * `SCHEMA_VERSION` mirrors the snippet event schema version so the\n * server can route SDK-emitted events through the same schema-aware\n * ingest path as the snippet.\n */\n\ndeclare const __SDK_VERSION__: string\n\nexport const SDK_VERSION: string =\n typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : '0.0.0-dev'\n\n/* Mirrors snippet/server contract — bumped when AgentEvent shape changes\n * in a way the ingest API needs to know about. Kept in sync with\n * shared/lib/agent-events.ts SNIPPET_EVENT_SCHEMA_VERSION. The SDK\n * inlines the constant to avoid an external import that would break\n * the npm publish (the shared/ folder is outside this package). */\nexport const SCHEMA_VERSION = 3 as const\n","/**\n * @serge-ai/js — vanilla-JS core for the Serge tracker.\n *\n * This file exposes the public API for npm consumers. The shape mirrors\n * the snippet's `window.serge(...)` actions but with TypeScript types\n * and ESM-friendly factory ergonomics.\n *\n * SSR-safe: every browser-API access is guarded so the module can be\n * imported from a server component, Edge runtime, or Node test without\n * crashing. The actual side effects (event listeners, network requests)\n * only fire after `init()` is called in a browser context.\n *\n * Three packages, one core (this file):\n *\n * 1. Vanilla JS (this package — @serge-ai/js):\n *\n * import { init, track } from '@serge-ai/js'\n * init('srg_pub_xxxxxxxxxxxxxxxxxxxxxxxxxx')\n * track('signup_clicked', { plan: 'pro' })\n *\n * 2. React (separate package — @serge-ai/react):\n *\n * import { Analytics } from '@serge-ai/react'\n * <Analytics token=\"srg_pub_xxx\" />\n *\n * 3. Next.js App Router (separate package — @serge-ai/nextjs):\n *\n * import { Analytics } from '@serge-ai/nextjs'\n * <Analytics token=\"srg_pub_xxx\" />\n *\n * Tokens:\n * - `srg_pub_*` — public, embed-safe, the only token format the SDK\n * accepts. Mirrors PostHog's `phc_*` shape — public-by-design,\n * scoped to ingest. The (future) management API will use a\n * visually distinct `srg_sec_*` prefix so an operator who sees\n * the wrong one in logs / a tweet knows immediately whether it's\n * a leak that matters.\n * - The legacy `site_*` format from the pre-token era is still\n * accepted at ingest server-side for one major version cycle.\n *\n * Cookieless by construction:\n * - sessionStorage only (per-tab, dies on tab close — CNIL Sheet 16\n * classifies this as technically necessary, not consent-bearing).\n * - localStorage written ONLY when `requireConsent: true` is set AND\n * the user calls `consent('granted')`, and even then only the\n * consent answer itself.\n * - No cookies. No `vid` field. No persistent visitor identifier.\n * Visitor identity is derived server-side at ingest from a\n * daily-rotating salt. ePrivacy Art. 5(3) compliant by default —\n * no consent banner required for the tracker.\n *\n * Tree-shakeable: each named export is independent. Consumers that only\n * call `track()` from event handlers don't pay for the auto-init\n * lifecycle code.\n */\n\nimport { runDetection } from '../detect/score'\nimport { sendEvent, sendBeaconEvent, setEndpoint } from '../transport'\nimport { initSpaDetection } from '../spa'\nimport { fetchSignalUpdates } from '../signals'\nimport { getInitialConsent, setConsent as persistConsent, type ConsentValue } from '../consent'\nimport { injectHoneypots } from '../honeypot'\nimport { scrubUrl } from '../scrub'\nimport { getSessionId, generateEventId } from '../session'\nimport type { AgentEvent, DetectionResult } from '../types'\nimport { SDK_VERSION, SCHEMA_VERSION } from './version'\n\nexport type { ConsentValue }\nexport { SDK_VERSION }\n\n/**\n * Optional second argument to `init(token, options?)`. Every field is\n * optional; the production-safe defaults match the snippet's behavior\n * out of the box.\n */\nexport interface InitOptions {\n /** Origin of the Serge ingest API. Defaults to `https://serge.ai`.\n * Override to a first-party proxy (`/serge`) to dodge ad-blockers\n * on the customer's domain — the snippet pattern in RFC-001 §First-\n * party proxy. */\n apiBase?: string\n /** Track every session, not just agents. Development only. Mirrors\n * the snippet's `data-debug` flag. */\n debug?: boolean\n /** Hold tracking until `consent('granted')` is called. Required for\n * EU/Swiss/UK installs that need explicit opt-in for any storage\n * beyond the ePrivacy 5(3) \"technically necessary\" carve-out (we're\n * inside that carve-out by default — `requireConsent: true` is for\n * customers whose own legal team disagrees). */\n requireConsent?: boolean\n /** Pin SDK behavior to a published \"defaults date\" — same shape as\n * PostHog's `defaults` option. Lets us ship breaking-default\n * changes in future SDK versions without breaking installed tags;\n * customers who set `defaults: '2026-05-10'` keep the v0.2 behavior\n * forever. No behavioral effect today; locks the contract for\n * later. The string is sent in the session_start event meta so the\n * server can downgrade behavior per-customer if needed. */\n defaults?: string\n}\n\n/* ── Module state ─────────────────────────────────────────────\n *\n * Single-instance per page (matches the snippet's runtime model).\n * Calling `init()` twice with the same token is a no-op; calling with\n * a different token replaces the previous configuration. Same shape\n * as PostHog's posthog.init().\n *\n * State is intentionally module-level (not factory-closure) so that\n * `track()` / `consent()` / `pageview()` can be imported and called\n * from anywhere in the consumer's app without threading a `tracker`\n * instance through props. The PostHog research (May 2026) confirms\n * this is the convergent industry pattern — Sentry, Mixpanel,\n * Amplitude, Segment all do the same. */\n\ninterface ResolvedConfig {\n token: string\n apiBase: string\n debug: boolean\n requireConsent: boolean\n defaults: string | undefined\n}\n\nlet injected = false\nlet config: ResolvedConfig | null = null\nlet detection: DetectionResult | null = null\nlet pageLoadTime = 0\nlet currentUrl = ''\nlet initMs = 0\nlet sessionEndSent = false\nlet consentState: ConsentValue = 'unknown'\nlet apiBaseRef = ''\nlet listenersAttached = false\n\nfunction shouldTrack(): boolean {\n if (!config) return false\n if (config.debug) return true\n return !!detection && detection.confidence >= 0.35\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined'\n}\n\n/**\n * Initialize the tracker. Idempotent in a browser context (calling\n * `init()` again with the same token is a no-op; with a different\n * token, replaces the previous configuration).\n *\n * SSR-safe: in non-browser environments this function returns without\n * side effects, so it can be called unconditionally from any app\n * entry point that runs in both server and client contexts.\n *\n * @param token Public token from the Serge dashboard (`srg_pub_*`).\n * The legacy `site_*` format is still accepted at ingest\n * server-side for one major version cycle.\n * @param options Optional configuration. See {@link InitOptions}.\n */\nexport function init(token: string, options: InitOptions = {}): void {\n if (!isBrowser()) return\n if (!token) {\n if (options.debug) console.error('[serge] init(token) requires a token')\n return\n }\n\n const startTime = performance.now()\n const apiBase = (options.apiBase ?? 'https://serge.ai').replace(/\\/$/, '')\n apiBaseRef = apiBase\n setEndpoint(`${apiBase}/api/ingest/snippet`)\n\n config = {\n token,\n apiBase,\n debug: options.debug ?? false,\n requireConsent: options.requireConsent ?? false,\n defaults: options.defaults,\n }\n injected = true\n initMs = performance.now() - startTime\n\n if (config.requireConsent) {\n consentState = getInitialConsent()\n if (consentState === 'denied') return\n if (consentState !== 'granted') return\n }\n\n sendInitPing()\n injectHoneypots({ apiBase, siteId: token, sessionId: getSessionId() })\n fetchSignalUpdates(apiBase)\n\n if (\n document.visibilityState === 'hidden' ||\n (document.visibilityState as string) === 'prerender'\n ) {\n document.addEventListener(\n 'visibilitychange',\n () => {\n if (document.visibilityState === 'visible') startTracking()\n },\n { once: true },\n )\n } else {\n startTracking()\n }\n}\n\nfunction sendInitPing(): void {\n if (!config) return\n const event: AgentEvent = {\n s: config.token,\n sid: getSessionId(),\n eid: generateEventId(),\n ap: 'unknown',\n ac: 0,\n ds: [],\n et: 'init',\n ts: new Date().toISOString(),\n u: location.pathname,\n t: document.title || undefined,\n }\n sendEvent(event)\n}\n\nasync function startTracking(): Promise<void> {\n if (!config) return\n\n pageLoadTime = performance.now()\n currentUrl = location.pathname\n\n const { immediate, refine } = runDetection()\n detection = immediate\n\n if (config.debug || immediate.confidence >= 0.35) {\n sendSessionStart()\n sendPageviewInternal()\n setupListeners()\n }\n\n const refined = await refine\n detection = refined\n\n if (!config.debug && immediate.confidence < 0.35 && refined.confidence >= 0.35) {\n sendSessionStart()\n sendPageviewInternal()\n setupListeners()\n }\n}\n\nfunction setupListeners(): void {\n if (listenersAttached) return\n listenersAttached = true\n initSpaDetection(onNavigation)\n document.addEventListener('visibilitychange', onVisibilityChange)\n window.addEventListener('pagehide', onPageHide)\n}\n\nfunction createEvent(eventType: string, extra?: Partial<AgentEvent>): AgentEvent {\n if (!config || !detection) {\n throw new Error('[serge] tracker not initialized — call init() first')\n }\n const r = document.referrer ? scrubUrl(document.referrer) : ''\n return {\n s: config.token,\n sid: getSessionId(),\n eid: generateEventId(),\n ap: detection.platform,\n ac: Math.round(detection.confidence * 100) / 100,\n ds: detection.signals,\n bs:\n detection.behavioralScore != null\n ? Math.round(detection.behavioralScore * 100) / 100\n : undefined,\n et: eventType,\n ts: new Date().toISOString(),\n u: location.pathname,\n r: r || undefined,\n t: document.title || undefined,\n ...extra,\n }\n}\n\nfunction sendSessionStart(): void {\n sessionEndSent = false\n const meta: AgentEvent['_m'] = {\n init_ms: Math.round(initMs),\n v: SDK_VERSION,\n sv: SCHEMA_VERSION,\n }\n /* `defaults` rides along in the meta if the customer set it, so the\n * server can downgrade behavior on a per-customer basis when we\n * later ship a breaking-default change. Cheap to send (it's a date\n * string), free to ignore on the server until we need it. */\n if (config?.defaults) {\n /* `d` is a contracted wire-field — server-side ingest treats\n * unknown _m fields as opaque (RFC-001 §forward-compat) so we\n * widen via `unknown` to add it without polluting the AgentEvent\n * type. */\n ;(meta as unknown as Record<string, unknown>).d = config.defaults\n }\n sendEvent(\n createEvent('session_start', {\n vw: window.innerWidth,\n vh: window.innerHeight,\n sw: screen.width,\n sh: screen.height,\n ua: navigator.userAgent,\n p: getNavigatorPlatform(),\n l: navigator.language,\n _m: meta,\n }),\n )\n}\n\nfunction sendPageviewInternal(): void {\n pageLoadTime = performance.now()\n currentUrl = location.pathname\n sendEvent(createEvent('page_view'))\n}\n\nfunction onNavigation(): void {\n if (!shouldTrack()) return\n const timeOnPage = Math.round(performance.now() - pageLoadTime)\n sendEvent(\n createEvent('route_change', {\n u: currentUrl,\n tp: timeOnPage,\n sd: getScrollDepth(),\n }),\n )\n sendPageviewInternal()\n sessionEndSent = false\n}\n\nfunction onVisibilityChange(): void {\n if (document.visibilityState === 'hidden') sendSessionEnd()\n}\n\nfunction onPageHide(): void {\n sendSessionEnd()\n}\n\nfunction sendSessionEnd(): void {\n if (sessionEndSent) return\n if (!shouldTrack()) return\n sessionEndSent = true\n const timeOnPage = Math.round(performance.now() - pageLoadTime)\n sendBeaconEvent(\n createEvent('session_end', {\n tp: timeOnPage,\n sd: getScrollDepth(),\n }),\n )\n}\n\nfunction getScrollDepth(): number {\n const docHeight = document.documentElement.scrollHeight\n const viewHeight = window.innerHeight\n if (docHeight <= viewHeight) return 100\n const scrollTop = window.scrollY || document.documentElement.scrollTop\n return Math.min(100, Math.round(((scrollTop + viewHeight) / docHeight) * 100))\n}\n\nfunction getNavigatorPlatform(): string {\n try {\n // @ts-expect-error -- userAgentData is not in all TS lib definitions yet\n return navigator.userAgentData?.platform ?? navigator.platform ?? ''\n } catch {\n return ''\n }\n}\n\n/**\n * Track a custom event. Validated server-side at ingest:\n * - event name: `^[a-z][a-z0-9_]{0,63}$` (lowercase snake_case, ≤64 chars)\n * - properties: optional, capped at ~4 KB JSON\n *\n * Silently no-op when:\n * - The tracker hasn't been initialized yet\n * - Detection confidence is below the agent threshold (and `debug=false`)\n * - The page is server-rendered (no `window`)\n */\nexport function track(event: string, properties?: Record<string, unknown>): void {\n if (!isBrowser() || !injected) return\n if (!shouldTrack()) return\n sendEvent(\n createEvent('semantic_action', {\n ix: [{ t: event, tg: JSON.stringify(properties ?? {}), o: 0, ok: true }],\n }),\n )\n}\n\n/**\n * Update the consent state. Required when `init(token, { requireConsent: true })`\n * was used. Persisted to localStorage so the choice survives page reload — and\n * ONLY then; default installs are localStorage-free.\n *\n * - 'granted' — start tracking (idempotent if already granted)\n * - 'denied' — stop tracking; no cookies set, no events sent\n * - 'unknown' — pending; tracker stays dormant\n */\nexport function consent(value: ConsentValue): void {\n if (!isBrowser()) return\n const previous = consentState\n consentState = persistConsent(value)\n if (!config || !config.requireConsent) return\n if (previous === consentState) return\n\n if (consentState === 'granted' && previous !== 'granted') {\n if (apiBaseRef !== '') fetchSignalUpdates(apiBaseRef)\n sendInitPing()\n if (apiBaseRef !== '') {\n injectHoneypots({\n apiBase: apiBaseRef,\n siteId: config.token,\n sessionId: getSessionId(),\n })\n }\n if (\n document.visibilityState === 'hidden' ||\n (document.visibilityState as string) === 'prerender'\n ) {\n document.addEventListener(\n 'visibilitychange',\n () => {\n if (document.visibilityState === 'visible') startTracking()\n },\n { once: true },\n )\n } else {\n startTracking()\n }\n }\n}\n\n/**\n * Manual pageview emit. The tracker auto-tracks browser navigation\n * (history pushState, popstate, hashchange) — call this only when the\n * SDK can't detect a route change automatically (e.g., a custom\n * client-router that doesn't go through the History API, or the\n * Next.js App Router which we already handle in `@serge-ai/tracker/next`).\n */\nexport function pageview(): void {\n if (!isBrowser() || !injected) return\n if (!shouldTrack()) return\n onNavigation()\n}\n\n/**\n * Test-only: reset all module state. Not part of the documented API.\n * Used by Vitest tests that need a clean tracker between cases.\n *\n * The `@internal` JSDoc tag + `stripInternal: true` in tsup's dts\n * config strips this from the published `.d.ts` so consumers don't\n * see it in their IDE autocomplete or `import { ... }` suggestions.\n * The runtime export still exists (it has to — tests in other repos\n * that depend on this package would also want to call it) but it's\n * not part of the typed public API. The `_` prefix is the second\n * line of \"don't touch this\" signalling.\n *\n * @internal\n */\nexport function _resetForTesting(): void {\n injected = false\n config = null\n detection = null\n pageLoadTime = 0\n currentUrl = ''\n initMs = 0\n sessionEndSent = false\n consentState = 'unknown'\n apiBaseRef = ''\n listenersAttached = false\n setEndpoint('')\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -165,10 +165,5 @@ declare function consent(value: ConsentValue): void;
|
|
|
165
165
|
* Next.js App Router which we already handle in `@serge-ai/tracker/next`).
|
|
166
166
|
*/
|
|
167
167
|
declare function pageview(): void;
|
|
168
|
-
/**
|
|
169
|
-
* Test-only: reset all module state. Not part of the documented API.
|
|
170
|
-
* Used by Vitest tests that need a clean tracker between cases.
|
|
171
|
-
*/
|
|
172
|
-
declare function _resetForTesting(): void;
|
|
173
168
|
|
|
174
|
-
export { type ConsentValue, type InitOptions, SDK_VERSION,
|
|
169
|
+
export { type ConsentValue, type InitOptions, SDK_VERSION, consent, init, pageview, track };
|