@sentientui/core 0.1.0

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.
@@ -0,0 +1 @@
1
+ var J=Object.defineProperty,j=Object.defineProperties;var W=Object.getOwnPropertyDescriptors;var M=Object.getOwnPropertySymbols;var Y=Object.prototype.hasOwnProperty,q=Object.prototype.propertyIsEnumerable;var K=(e,t,n)=>t in e?J(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,N=(e,t)=>{for(var n in t||(t={}))Y.call(t,n)&&K(e,n,t[n]);if(M)for(var n of M(t))q.call(t,n)&&K(e,n,t[n]);return e},Q=(e,t)=>j(e,W(t));var X="_snt_uid";var b="_snt_uid";function H(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(t){}let e=()=>Math.floor(Math.random()*4294967295).toString(16).padStart(8,"0");return`${e()}-${e()}-${e()}-${e()}`}function V(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function Z(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(a){}}function ee(e){try{return localStorage.getItem(e)}catch(t){return null}}function te(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function ne(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function re(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function se(e){try{sessionStorage.removeItem(e)}catch(t){}}function ie(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,/(?:^|; )_snt_uid_probe=1/.test(document.cookie)||document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function oe(e){try{localStorage.removeItem(e)}catch(t){}}function ae(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var ce={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function F(e){var g,d,p,v,w;if(typeof window=="undefined")return ce;let t=(g=e==null?void 0:e.cookieName)!=null?g:X,a=((d=e==null?void 0:e.cookieTTLDays)!=null?d:365)*24*60*60,r=(w=(v=(p=V(t))!=null?p:ee(b))!=null?v:ne(b))!=null?w:H();Z(t,r,a);let o=te(b,r),s=ie(t),i=o?!1:re(b,r),u=!o&&!s&&!i;return{getSessionId:()=>r,isEphemeral:()=>u,destroy:()=>{r=null,ae(t),oe(b),se(b)}}}var C="_snt_retry";var ue={push:()=>{},flush:()=>{},destroy:()=>{}};function de(e){try{let t=localStorage.getItem(C);if(!t)return[];let n=JSON.parse(t);return Array.isArray(n)?(localStorage.removeItem(C),n.slice(-e)):[]}catch(t){return[]}}function le(e,t){try{let a=[...(()=>{try{let r=localStorage.getItem(C);if(!r)return[];let o=JSON.parse(r);return Array.isArray(o)?o:[]}catch(r){return[]}})(),...e].slice(-t);localStorage.setItem(C,JSON.stringify(a))}catch(n){}}function G(e){var h,_,I;if(typeof window=="undefined")return ue;let t=(h=e.flushIntervalMs)!=null?h:5e3,n=(_=e.maxBatchSize)!=null?_:20,a=(I=e.maxRetrySize)!=null?I:100,r=e.ingestUrl,o=e.apiKey,s=[],i=new Set,u=[],g=c=>{for(let m of c)i.has(m)||(i.add(m),u.push(m));for(;u.length>500;){let m=u.shift();m&&i.delete(m)}},d=new Set,p=c=>{i.has(c.id)||d.has(c.id)||(d.add(c.id),s.push(c))},v=de(a);for(let c of v)p(c);let w=c=>{if(c.length===0)return!0;let m=JSON.stringify(c);try{return fetch(r,{method:"POST",keepalive:!0,body:m,headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`}}),g(c.map(k=>k.id)),!0}catch(k){return le(c,a),!1}},y=()=>{try{for(;s.length>0;){let m=s.splice(0,n).filter(z=>!i.has(z.id));if(m.length===0)continue;if(!w(m)){s.unshift(...m);break}}}catch(c){}},A=!0,S=null,E=()=>{if(!A)return;let c=Date.now()+t,m=()=>{if(A){if(Date.now()>=c){y(),E();return}try{S=new MessageChannel,S.port1.onmessage=m,S.port2.postMessage(null)}catch(k){}}};m()},l=()=>{document.visibilityState==="hidden"&&y()},f=()=>{y()};return document.addEventListener("visibilitychange",l),window.addEventListener("beforeunload",f),E(),{push(c){p(c),s.length>=n&&y()},flush:y,destroy(){A=!1,document.removeEventListener("visibilitychange",l),window.removeEventListener("beforeunload",f),S&&(S.port1.onmessage=null,S=null),y()}}}var D="_snt_asgn_";function x(e,t){return`${e}:${t}`}function ge(e,t){return`${D}${e}_${t}`}function P(){try{let e=[];for(let t=0;t<localStorage.length;t++){let n=localStorage.key(t);n!=null&&n.startsWith(D)&&e.push(n)}return e}catch(e){return[]}}function B(e=18e5){let t=new Map,n=r=>r.assignedAt+e<Date.now();return typeof window!="undefined"&&(()=>{for(let r of P())try{let o=localStorage.getItem(r);if(!o)continue;let s=JSON.parse(o);if(n(s)){localStorage.removeItem(r);continue}let i=r.slice(D.length),u=i.lastIndexOf("_");if(u<0)continue;let g=i.slice(0,u),d=i.slice(u+1);t.set(x(g,d),s)}catch(o){}})(),{get(r,o){let s=t.get(x(r,o));return s?n(s)?(t.delete(x(r,o)),null):s:null},set(r,o,s){let i=x(r,o);t.set(i,s);try{localStorage.setItem(ge(r,o),JSON.stringify(s))}catch(u){}},invalidate(r){let o=`${r}:`;for(let s of[...t.keys()])s.startsWith(o)&&t.delete(s);for(let s of P()){let i=s.slice(D.length),u=i.lastIndexOf("_");if(u<0)continue;if(i.slice(0,u)===r)try{localStorage.removeItem(s)}catch(d){}}},clear(){t.clear();for(let r of P())try{localStorage.removeItem(r)}catch(o){}}}}function T(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function O(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(r){}let a=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(a)?"search":/(^|\.)(twitter|x\.com|facebook|linkedin|reddit|t\.co)/.test(a)?"social":"referral"}catch(n){return"direct"}}function U(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}function R(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function me(e){let t=L("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function L(e,t){var o,s,i,u,g,d,p;let n=(s=(o=t==null?void 0:t.userAgent)==null?void 0:o.trim())!=null?s:"",a=(u=(i=t==null?void 0:t.referer)==null?void 0:i.trim())!=null?u:"",r=(g=t==null?void 0:t.now)!=null?g:new Date;return{sessionId:e,ephemeral:!1,utmParams:(d=t==null?void 0:t.utmParams)!=null?d:{},deviceClass:n?T(n):"desktop",trafficSource:a?O(a,t==null?void 0:t.appOrigin):"direct",referrerDomain:U(a),timeOfDay:R(r),dayOfWeek:(p=["sun","mon","tue","wed","thu","fri","sat"][r.getDay()])!=null?p:"sun"}}async function fe(e,t,n){let a={"Content-Type":"application/json",Authorization:`Bearer ${n.apiKey}`};n.origin&&(a.Origin=n.origin);let r=L(t,{userAgent:n.userAgent,referer:n.referer,utmParams:n.utmParams,appOrigin:n.origin});try{await fetch(`${n.baseUrl}/sessions`,{method:"POST",headers:a,body:JSON.stringify(r)})}catch(i){}let o=await Promise.allSettled(e.map(async({id:i,variantIds:u})=>{let g=await fetch(`${n.baseUrl}/assign`,{method:"POST",headers:a,body:JSON.stringify({sessionId:t,componentId:i,variantIds:u})});if(!g.ok)return null;let d=await g.json();return{id:i,variantId:d.variantId}})),s={};for(let i of o)i.status==="fulfilled"&&i.value&&(s[i.value.id]=i.value.variantId);return s}function pe(e){var t,n;return(n=(t=e.get("_snt_uid"))==null?void 0:t.value)!=null?n:null}function ye(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}return`evt-${Date.now()}-${Math.random().toString(16).slice(2)}`}var $={track:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),getGraph:()=>({pageNodes:[],userEdges:[],capturedAt:0}),destroy:()=>{}};function Se(){try{let e={},t=new URLSearchParams(window.location.search);for(let[n,a]of t)n.startsWith("utm_")&&(e[n]=a);return e}catch(e){return{}}}function he(e){return e.replace(/\/events\/?$/,"")}function Te(e){var y,A,S,E;if(typeof window=="undefined"||e.consent===!1)return $;if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),$;if(!e.ingestUrl)return console.warn("[sentient] init() called without ingestUrl. SDK disabled."),$;let t=Date.now(),n=F(),a=B(),r=G({ingestUrl:e.ingestUrl,apiKey:e.apiKey}),o=he(e.ingestUrl),s={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},i=T((y=navigator.userAgent)!=null?y:""),u=typeof window!="undefined"?window.location.origin:void 0,g=O((A=document.referrer)!=null?A:"",u),d=(S=e.sessionSegment)!=null?S:`${i}:${g}`;if(e.initialAssignments)for(let[l,f]of Object.entries(e.initialAssignments))a.set(l,d,{variantId:f,assignedAt:Date.now(),segment:d,confidence:1});let p=Promise.resolve(),v=n.getSessionId();if(v){let l=U((E=document.referrer)!=null?E:""),f={sessionId:v,deviceClass:i,trafficSource:g,referrerDomain:l,utmParams:Se(),timeOfDay:R(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:n.isEphemeral()};try{p=fetch(`${o}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(f),headers:s}).then(()=>{}).catch(()=>{})}catch(h){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:r});let w={track(l){let f=n.getSessionId();if(!f)return;let h=Q(N({},l),{id:ye(),sessionId:f,timestamp:Date.now(),timeInSession:Date.now()-t});r.push(h),e.debug&&console.log("[sentient] track",h)},getAssignment(l,f){return a.get(l,f)},async assign(l,f){let h=n.getSessionId();if(!h)return null;let _=a.get(l,d);if(_)return{variantId:_.variantId,assignmentTtlMs:0};await p;try{let I=await fetch(`${o}/assign`,{method:"POST",body:JSON.stringify({sessionId:h,componentId:l,variantIds:f}),headers:s});if(!I.ok)return null;let c=await I.json();return a.set(l,d,{variantId:c.variantId,assignedAt:Date.now(),segment:d,confidence:1}),c}catch(I){return null}},getGraph(){return{pageNodes:[],userEdges:[],capturedAt:0}},destroy(){r.destroy(),n.destroy(),e.debug&&console.log("[sentient] destroyed")}};if(e.debug){let l=window;l.__sentient&&(l.__sentient.client=w)}return w}export{N as a,Q as b,T as c,O as d,U as e,R as f,me as g,fe as h,pe as i,Te as j};
@@ -0,0 +1,31 @@
1
+ import { SentientConfig, SentientClient } from './index.cjs';
2
+ export { AssignResult, Assignment, AssignmentCache, ContentAddedEvent, DOMScanner, EventQueue, EventType, GraphClient, GraphConfig, GraphSnapshot, PageNode, QueueConfig, ScanResult, ScannedNode, SentientEvent, ServerAssignConfig, ServerAssignments, SessionConfig, SessionManager, UserEdge, deriveSessionSegment, detectDeviceClass, detectTimeOfDay, detectTrafficSource, preloadAssignments, readSessionCookie, referrerDomainFromReferer } from './index.cjs';
3
+
4
+ /**
5
+ * Graph-capable entry point for @sentientui/core.
6
+ *
7
+ * Import from `@sentientui/core/graph` when you need DOM graph scanning and
8
+ * page-structure sync. This entry pulls in `scanner.ts` and `graph.ts` at
9
+ * build time, giving bundlers a real tree-shaking boundary. Standard A/B tests
10
+ * should use `@sentientui/core` (the lean entry) instead.
11
+ */
12
+
13
+ type GraphSentientConfig = SentientConfig & {
14
+ /**
15
+ * Enable DOM graph scanning and page-structure sync. Wires a MutationObserver
16
+ * and a localStorage-backed graph. When `false` (default) this entry behaves
17
+ * identically to `@sentientui/core`.
18
+ */
19
+ graph?: boolean;
20
+ };
21
+ /**
22
+ * Graph-capable variant of `init()`. Identical to the lean `init` when
23
+ * `config.graph` is `false` (or omitted). When `config.graph: true`, mounts
24
+ * a DOM scanner + graph client and enables `client.getGraph()`.
25
+ *
26
+ * Import from `@sentientui/core/graph` — do not call the lean `init` alongside
27
+ * this function as that would initialise two clients.
28
+ */
29
+ declare function init(config: GraphSentientConfig): SentientClient;
30
+
31
+ export { type GraphSentientConfig, SentientClient, SentientConfig, init };
@@ -0,0 +1,31 @@
1
+ import { SentientConfig, SentientClient } from './index.js';
2
+ export { AssignResult, Assignment, AssignmentCache, ContentAddedEvent, DOMScanner, EventQueue, EventType, GraphClient, GraphConfig, GraphSnapshot, PageNode, QueueConfig, ScanResult, ScannedNode, SentientEvent, ServerAssignConfig, ServerAssignments, SessionConfig, SessionManager, UserEdge, deriveSessionSegment, detectDeviceClass, detectTimeOfDay, detectTrafficSource, preloadAssignments, readSessionCookie, referrerDomainFromReferer } from './index.js';
3
+
4
+ /**
5
+ * Graph-capable entry point for @sentientui/core.
6
+ *
7
+ * Import from `@sentientui/core/graph` when you need DOM graph scanning and
8
+ * page-structure sync. This entry pulls in `scanner.ts` and `graph.ts` at
9
+ * build time, giving bundlers a real tree-shaking boundary. Standard A/B tests
10
+ * should use `@sentientui/core` (the lean entry) instead.
11
+ */
12
+
13
+ type GraphSentientConfig = SentientConfig & {
14
+ /**
15
+ * Enable DOM graph scanning and page-structure sync. Wires a MutationObserver
16
+ * and a localStorage-backed graph. When `false` (default) this entry behaves
17
+ * identically to `@sentientui/core`.
18
+ */
19
+ graph?: boolean;
20
+ };
21
+ /**
22
+ * Graph-capable variant of `init()`. Identical to the lean `init` when
23
+ * `config.graph` is `false` (or omitted). When `config.graph: true`, mounts
24
+ * a DOM scanner + graph client and enables `client.getGraph()`.
25
+ *
26
+ * Import from `@sentientui/core/graph` — do not call the lean `init` alongside
27
+ * this function as that would initialise two clients.
28
+ */
29
+ declare function init(config: GraphSentientConfig): SentientClient;
30
+
31
+ export { type GraphSentientConfig, SentientClient, SentientConfig, init };
@@ -0,0 +1 @@
1
+ "use strict";var k=Object.defineProperty,ie=Object.defineProperties,ae=Object.getOwnPropertyDescriptor,ce=Object.getOwnPropertyDescriptors,de=Object.getOwnPropertyNames,q=Object.getOwnPropertySymbols;var Y=Object.prototype.hasOwnProperty,ue=Object.prototype.propertyIsEnumerable;var H=(e,t,n)=>t in e?k(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,I=(e,t)=>{for(var n in t||(t={}))Y.call(t,n)&&H(e,n,t[n]);if(q)for(var n of q(t))ue.call(t,n)&&H(e,n,t[n]);return e},U=(e,t)=>ie(e,ce(t));var ge=(e,t)=>{for(var n in t)k(e,n,{get:t[n],enumerable:!0})},le=(e,t,n,c)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of de(t))!Y.call(e,s)&&s!==n&&k(e,s,{get:()=>t[s],enumerable:!(c=ae(t,s))||c.enumerable});return e};var pe=e=>le(k({},"__esModule",{value:!0}),e);var Ye={};ge(Ye,{deriveSessionSegment:()=>K,detectDeviceClass:()=>C,detectTimeOfDay:()=>_,detectTrafficSource:()=>x,init:()=>He,preloadAssignments:()=>F,readSessionCookie:()=>j,referrerDomainFromReferer:()=>T});module.exports=pe(Ye);var me="_snt_uid";var A="_snt_uid";function fe(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(t){}let e=()=>Math.floor(Math.random()*4294967295).toString(16).padStart(8,"0");return`${e()}-${e()}-${e()}-${e()}`}function he(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function ye(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(c){}}function Se(e){try{return localStorage.getItem(e)}catch(t){return null}}function ve(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function be(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function we(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function Ee(e){try{sessionStorage.removeItem(e)}catch(t){}}function Ie(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,/(?:^|; )_snt_uid_probe=1/.test(document.cookie)||document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function Ae(e){try{localStorage.removeItem(e)}catch(t){}}function Ce(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var xe={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function V(e){var d,u,l,h,y;if(typeof window=="undefined")return xe;let t=(d=e==null?void 0:e.cookieName)!=null?d:me,c=((u=e==null?void 0:e.cookieTTLDays)!=null?u:365)*24*60*60,s=(y=(h=(l=he(t))!=null?l:Se(A))!=null?h:be(A))!=null?y:fe();ye(t,s,c);let r=ve(A,s),o=Ie(t),i=r?!1:we(A,s),a=!r&&!o&&!i;return{getSessionId:()=>s,isEphemeral:()=>a,destroy:()=>{s=null,Ce(t),Ae(A),Ee(A)}}}var R="_snt_retry";var Te={push:()=>{},flush:()=>{},destroy:()=>{}};function _e(e){try{let t=localStorage.getItem(R);if(!t)return[];let n=JSON.parse(t);return Array.isArray(n)?(localStorage.removeItem(R),n.slice(-e)):[]}catch(t){return[]}}function Ne(e,t){try{let c=[...(()=>{try{let s=localStorage.getItem(R);if(!s)return[];let r=JSON.parse(s);return Array.isArray(r)?r:[]}catch(s){return[]}})(),...e].slice(-t);localStorage.setItem(R,JSON.stringify(c))}catch(n){}}function X(e){var b,D,E;if(typeof window=="undefined")return Te;let t=(b=e.flushIntervalMs)!=null?b:5e3,n=(D=e.maxBatchSize)!=null?D:20,c=(E=e.maxRetrySize)!=null?E:100,s=e.ingestUrl,r=e.apiKey,o=[],i=new Set,a=[],d=g=>{for(let m of g)i.has(m)||(i.add(m),a.push(m));for(;a.length>500;){let m=a.shift();m&&i.delete(m)}},u=new Set,l=g=>{i.has(g.id)||u.has(g.id)||(u.add(g.id),o.push(g))},h=_e(c);for(let g of h)l(g);let y=g=>{if(g.length===0)return!0;let m=JSON.stringify(g);try{return fetch(s,{method:"POST",keepalive:!0,body:m,headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`}}),d(g.map(O=>O.id)),!0}catch(O){return Ne(g,c),!1}},S=()=>{try{for(;o.length>0;){let m=o.splice(0,n).filter(oe=>!i.has(oe.id));if(m.length===0)continue;if(!y(m)){o.unshift(...m);break}}}catch(g){}},w=!0,v=null,N=()=>{if(!w)return;let g=Date.now()+t,m=()=>{if(w){if(Date.now()>=g){S(),N();return}try{v=new MessageChannel,v.port1.onmessage=m,v.port2.postMessage(null)}catch(O){}}};m()},p=()=>{document.visibilityState==="hidden"&&S()},f=()=>{S()};return document.addEventListener("visibilitychange",p),window.addEventListener("beforeunload",f),N(),{push(g){l(g),o.length>=n&&S()},flush:S,destroy(){w=!1,document.removeEventListener("visibilitychange",p),window.removeEventListener("beforeunload",f),v&&(v.port1.onmessage=null,v=null),S()}}}var M="_snt_asgn_";function P(e,t){return`${e}:${t}`}function De(e,t){return`${M}${e}_${t}`}function $(){try{let e=[];for(let t=0;t<localStorage.length;t++){let n=localStorage.key(t);n!=null&&n.startsWith(M)&&e.push(n)}return e}catch(e){return[]}}function Z(e=18e5){let t=new Map,n=s=>s.assignedAt+e<Date.now();return typeof window!="undefined"&&(()=>{for(let s of $())try{let r=localStorage.getItem(s);if(!r)continue;let o=JSON.parse(r);if(n(o)){localStorage.removeItem(s);continue}let i=s.slice(M.length),a=i.lastIndexOf("_");if(a<0)continue;let d=i.slice(0,a),u=i.slice(a+1);t.set(P(d,u),o)}catch(r){}})(),{get(s,r){let o=t.get(P(s,r));return o?n(o)?(t.delete(P(s,r)),null):o:null},set(s,r,o){let i=P(s,r);t.set(i,o);try{localStorage.setItem(De(s,r),JSON.stringify(o))}catch(a){}},invalidate(s){let r=`${s}:`;for(let o of[...t.keys()])o.startsWith(r)&&t.delete(o);for(let o of $()){let i=o.slice(M.length),a=i.lastIndexOf("_");if(a<0)continue;if(i.slice(0,a)===s)try{localStorage.removeItem(o)}catch(u){}}},clear(){t.clear();for(let s of $())try{localStorage.removeItem(s)}catch(r){}}}}function C(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function x(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(s){}let c=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(c)?"search":/(^|\.)(twitter|x\.com|facebook|linkedin|reddit|t\.co)/.test(c)?"social":"referral"}catch(n){return"direct"}}function T(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}function _(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function K(e){let t=G("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function G(e,t){var r,o,i,a,d,u,l;let n=(o=(r=t==null?void 0:t.userAgent)==null?void 0:r.trim())!=null?o:"",c=(a=(i=t==null?void 0:t.referer)==null?void 0:i.trim())!=null?a:"",s=(d=t==null?void 0:t.now)!=null?d:new Date;return{sessionId:e,ephemeral:!1,utmParams:(u=t==null?void 0:t.utmParams)!=null?u:{},deviceClass:n?C(n):"desktop",trafficSource:c?x(c,t==null?void 0:t.appOrigin):"direct",referrerDomain:T(c),timeOfDay:_(s),dayOfWeek:(l=["sun","mon","tue","wed","thu","fri","sat"][s.getDay()])!=null?l:"sun"}}async function F(e,t,n){let c={"Content-Type":"application/json",Authorization:`Bearer ${n.apiKey}`};n.origin&&(c.Origin=n.origin);let s=G(t,{userAgent:n.userAgent,referer:n.referer,utmParams:n.utmParams,appOrigin:n.origin});try{await fetch(`${n.baseUrl}/sessions`,{method:"POST",headers:c,body:JSON.stringify(s)})}catch(i){}let r=await Promise.allSettled(e.map(async({id:i,variantIds:a})=>{let d=await fetch(`${n.baseUrl}/assign`,{method:"POST",headers:c,body:JSON.stringify({sessionId:t,componentId:i,variantIds:a})});if(!d.ok)return null;let u=await d.json();return{id:i,variantId:u.variantId}})),o={};for(let i of r)i.status==="fulfilled"&&i.value&&(o[i.value.id]=i.value.variantId);return o}function j(e){var t,n;return(n=(t=e.get("_snt_uid"))==null?void 0:t.value)!=null?n:null}function Oe(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}return`evt-${Date.now()}-${Math.random().toString(16).slice(2)}`}var L={track:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),getGraph:()=>({pageNodes:[],userEdges:[],capturedAt:0}),destroy:()=>{}};function ke(){try{let e={},t=new URLSearchParams(window.location.search);for(let[n,c]of t)n.startsWith("utm_")&&(e[n]=c);return e}catch(e){return{}}}function Ue(e){return e.replace(/\/events\/?$/,"")}function ee(e){var S,w,v,N;if(typeof window=="undefined"||e.consent===!1)return L;if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),L;if(!e.ingestUrl)return console.warn("[sentient] init() called without ingestUrl. SDK disabled."),L;let t=Date.now(),n=V(),c=Z(),s=X({ingestUrl:e.ingestUrl,apiKey:e.apiKey}),r=Ue(e.ingestUrl),o={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},i=C((S=navigator.userAgent)!=null?S:""),a=typeof window!="undefined"?window.location.origin:void 0,d=x((w=document.referrer)!=null?w:"",a),u=(v=e.sessionSegment)!=null?v:`${i}:${d}`;if(e.initialAssignments)for(let[p,f]of Object.entries(e.initialAssignments))c.set(p,u,{variantId:f,assignedAt:Date.now(),segment:u,confidence:1});let l=Promise.resolve(),h=n.getSessionId();if(h){let p=T((N=document.referrer)!=null?N:""),f={sessionId:h,deviceClass:i,trafficSource:d,referrerDomain:p,utmParams:ke(),timeOfDay:_(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:n.isEphemeral()};try{l=fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(f),headers:o}).then(()=>{}).catch(()=>{})}catch(b){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:s});let y={track(p){let f=n.getSessionId();if(!f)return;let b=U(I({},p),{id:Oe(),sessionId:f,timestamp:Date.now(),timeInSession:Date.now()-t});s.push(b),e.debug&&console.log("[sentient] track",b)},getAssignment(p,f){return c.get(p,f)},async assign(p,f){let b=n.getSessionId();if(!b)return null;let D=c.get(p,u);if(D)return{variantId:D.variantId,assignmentTtlMs:0};await l;try{let E=await fetch(`${r}/assign`,{method:"POST",body:JSON.stringify({sessionId:b,componentId:p,variantIds:f}),headers:o});if(!E.ok)return null;let g=await E.json();return c.set(p,u,{variantId:g.variantId,assignedAt:Date.now(),segment:u,confidence:1}),g}catch(E){return null}},getGraph(){return{pageNodes:[],userEdges:[],capturedAt:0}},destroy(){s.destroy(),n.destroy(),e.debug&&console.log("[sentient] destroyed")}};if(e.debug){let p=window;p.__sentient&&(p.__sentient.client=y)}return y}var Re=new Set(["SECTION","ARTICLE","MAIN","DIV"]),Pe="h1, h2, h3",Me={scan:async()=>({nodes:[],scannedAt:0}),observe:()=>{},getProminenceScore:()=>0,destroy:()=>{}};function z(e,t,n){return n<=t?0:Math.max(0,Math.min(1,(e-t)/(n-t)))}function Le(e){var t,n,c;try{let s=e;for(let r of Object.keys(s)){if(!r.startsWith("__reactFiber")&&!r.startsWith("__reactInternalInstance"))continue;let o=s[r],i=(c=(t=o==null?void 0:o.type)==null?void 0:t.displayName)!=null?c:(n=o==null?void 0:o.type)==null?void 0:n.name;if(i&&i.length>1)return i}}catch(s){}}function $e(e){let t={};for(let n of Array.from(e.attributes))n.name.startsWith("data-")&&(t[n.name]=n.value);return t}function Ke(e){let t=e.getAttribute("data-sentient-type");if(t)return t;let n=e.getAttribute("role");return n||"generic"}function Ge(e){var t,n;return(n=(t=e.getAttribute("data-sentient-id"))!=null?t:e.getAttribute("id"))!=null?n:e.tagName.toLowerCase()}function Fe(e){let t=0,n=e.parentElement;for(;n;)t++,n=n.parentElement;return t}function J(e,t){var c,s,r;let n=e.querySelector(Pe);return{componentId:Ge(e),semanticType:Ke(e),ariaLabel:(c=e.getAttribute("aria-label"))!=null?c:void 0,headingText:(r=(s=n==null?void 0:n.textContent)==null?void 0:s.trim())!=null?r:void 0,isAboveFold:e.getBoundingClientRect().top<window.innerHeight,prominenceScore:t(e),depth:Fe(e),reactComponentName:Le(e),dataAttributes:$e(e)}}function je(e){let t=[],n=new Set;return document.querySelectorAll("[data-sentient-id]").forEach(r=>{r instanceof Element&&!n.has(r)&&(n.add(r),t.push(J(r,e)))}),document.querySelectorAll("section, article, main, aside").forEach(r=>{if(!(r instanceof Element)||n.has(r))return;let o=r.hasAttribute("aria-label"),i=r.hasAttribute("data-sentient-id");!o&&!i||(n.add(r),t.push(J(r,e)))}),t}function te(){if(typeof window=="undefined")return Me;let e=null,t=0,n=null,c=i=>{try{let a=window.getComputedStyle(i),d=parseFloat(a.fontSize)||12,u=parseFloat(a.zIndex)||0,l=i.getBoundingClientRect(),h=Math.max(l.top,0),y=window.innerHeight||1,S=1/(h/y+1),w=z(d,12,48)*.4+z(S,0,1)*.4+z(u,0,100)*.2;return Math.max(0,Math.min(1,w))}catch(a){return .5}};return{scan:()=>new Promise(i=>{let a=()=>{i({nodes:je(c),scannedAt:Date.now()})};try{typeof requestIdleCallback=="function"?t=requestIdleCallback(a,{timeout:100}):a()}catch(d){a()}}),observe:i=>{n=i;try{e=new MutationObserver(a=>{let d=[];for(let u of a)u.type==="childList"&&u.addedNodes.forEach(l=>{if(!(l instanceof Element)||!Re.has(l.tagName))return;let h=l.hasAttribute("data-sentient-id"),y=l.hasAttribute("aria-label");!h&&!y||d.push(J(l,c))});d.length>0&&n&&n({nodes:d,addedAt:Date.now()})}),e.observe(document.body,{childList:!0,subtree:!0})}catch(a){}},getProminenceScore:c,destroy:()=>{if(e&&(e.disconnect(),e=null),t&&typeof cancelIdleCallback=="function")try{cancelIdleCallback(t)}catch(i){}t=0,n=null}}}var Q="_snt_graph_nodes",W="_snt_graph_edges",ze={pricing:["features","faq"],features:["pricing"],faq:["pricing"],social_proof:["cta"],cta:["social_proof","hero","trust"],hero:["cta"],comparison:["pricing"],trust:["cta"]};function B(e){return Math.max(0,Math.min(1,e))}function Je(e){var t;return(t=ze[e])!=null?t:[]}function ne(e,t){try{let n=localStorage.getItem(e);return n?JSON.parse(n):t}catch(n){return t}}function re(e,t){try{localStorage.setItem(e,JSON.stringify(t))}catch(n){}}var Qe=new Set(["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"]);function We(e){return Qe.has(e)?e:"generic"}function Be(e,t,n){let c=`${e}:${t}:${n.join(",")}`,s=5381;for(let r=0;r<c.length;r++)s=(s<<5)+s+c.charCodeAt(r)&4294967295;return(s>>>0).toString(16).padStart(8,"0")}function se(e){let t=new Map,n=new Map,c=()=>{typeof window!="undefined"&&(re(Q,[...t.values()]),re(W,[...n.values()]))},s=o=>{var i,a;try{let d=JSON.parse(o);t.clear(),n.clear();for(let u of(i=d.pageNodes)!=null?i:[])t.set(u.componentId,u);for(let u of(a=d.userEdges)!=null?a:[])n.set(u.componentId,u)}catch(d){}};if(typeof window!="undefined"){let o=ne(Q,[]),i=ne(W,[]);for(let a of o)t.set(a.componentId,a);for(let a of i)n.set(a.componentId,a)}let r=(o,i)=>{let a=n.get(o);a?(a.attentionWeight=B(a.attentionWeight+i),a.lastUpdated=Date.now()):n.set(o,{componentId:o,attentionWeight:B(i),interactionCount:0,dwellTime:0,lastUpdated:Date.now()})};return{addPageNode(o){t.set(o.componentId,o),c()},syncOnce(){if(!(e!=null&&e.syncUrl)||typeof window=="undefined")return;let o=[...t.values()];if(o.length!==0)try{let i={pageUrl:window.location.href,nodes:o.map(a=>{let d=We(a.semanticType);return{componentId:a.componentId,semanticType:d,answers:a.answers,contentHash:Be(a.componentId,d,a.answers),prominenceScore:a.prominenceScore,depthInPage:a.depth}}),edges:[]};fetch(e.syncUrl,{method:"POST",keepalive:!0,headers:I({"Content-Type":"application/json"},e.apiKey?{Authorization:`Bearer ${e.apiKey}`}:{}),body:JSON.stringify(i)}).catch(()=>{})}catch(i){}},updateUserEdge(o,i){var d;let a=(d=n.get(o))!=null?d:{componentId:o,attentionWeight:0,interactionCount:0,dwellTime:0,lastUpdated:Date.now()};a.attentionWeight=B(a.attentionWeight+i),a.interactionCount+=1,a.lastUpdated=Date.now(),n.set(o,a),c()},propagate(o,i){let a=t.get(o);if(!a){r(o,i),c();return}r(o,i);for(let d of Je(a.semanticType))for(let u of t.values())u.semanticType===d&&u.componentId!==o&&r(u.componentId,i*.4);for(let d of t.values())d.depth===a.depth+1&&r(d.componentId,i*.6);for(let d of t.values())d.semanticType===a.semanticType&&d.componentId!==o&&r(d.componentId,i*-.2);c()},snapshot(){return{pageNodes:[...t.values()],userEdges:[...n.values()],capturedAt:Date.now()}},serialize(){return JSON.stringify({pageNodes:[...t.values()],userEdges:[...n.values()]})},restore:s,destroy(){if(typeof window!="undefined")try{localStorage.removeItem(Q),localStorage.removeItem(W)}catch(o){}}}}function qe(){try{let e=document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);return e?decodeURIComponent(e[1]):void 0}catch(e){return}}function He(e){let t=ee(e);if(!e.graph||typeof window=="undefined")return t;let n=te(),c=se({syncUrl:e.ingestUrl.replace(/\/events\/?$/,"/graph/sync"),apiKey:e.apiKey,projectId:e.apiKey,sessionId:qe()});try{let s=localStorage.getItem("_snt_graph_nodes"),r=localStorage.getItem("_snt_graph_edges");(s||r)&&c.restore(JSON.stringify({pageNodes:s?JSON.parse(s):[],userEdges:r?JSON.parse(r):[]}))}catch(s){}return n.scan().then(s=>{for(let r of s.nodes)c.addPageNode({id:r.componentId,componentId:r.componentId,semanticType:r.semanticType,answers:r.headingText?[r.headingText]:[],prominenceScore:r.prominenceScore,depth:r.depth});c.syncOnce()}),n.observe(s=>{for(let r of s.nodes)c.addPageNode({id:r.componentId,componentId:r.componentId,semanticType:r.semanticType,answers:r.headingText?[r.headingText]:[],prominenceScore:r.prominenceScore,depth:r.depth})}),U(I({},t),{getGraph:()=>c.snapshot(),destroy:()=>{n.destroy(),c.destroy(),t.destroy()}})}0&&(module.exports={deriveSessionSegment,detectDeviceClass,detectTimeOfDay,detectTrafficSource,init,preloadAssignments,readSessionCookie,referrerDomainFromReferer});
@@ -0,0 +1 @@
1
+ import{a as g,b,c as x,d as O,e as _,f as U,g as D,h as R,i as P,j as E}from"./chunk-HRRGYUE4.mjs";var M=new Set(["SECTION","ARTICLE","MAIN","DIV"]),G="h1, h2, h3",k={scan:async()=>({nodes:[],scannedAt:0}),observe:()=>{},getProminenceScore:()=>0,destroy:()=>{}};function f(e,t,n){return n<=t?0:Math.max(0,Math.min(1,(e-t)/(n-t)))}function j(e){var t,n,i;try{let c=e;for(let r of Object.keys(c)){if(!r.startsWith("__reactFiber")&&!r.startsWith("__reactInternalInstance"))continue;let s=c[r],a=(i=(t=s==null?void 0:s.type)==null?void 0:t.displayName)!=null?i:(n=s==null?void 0:s.type)==null?void 0:n.name;if(a&&a.length>1)return a}}catch(c){}}function W(e){let t={};for(let n of Array.from(e.attributes))n.name.startsWith("data-")&&(t[n.name]=n.value);return t}function F(e){let t=e.getAttribute("data-sentient-type");if(t)return t;let n=e.getAttribute("role");return n||"generic"}function L(e){var t,n;return(n=(t=e.getAttribute("data-sentient-id"))!=null?t:e.getAttribute("id"))!=null?n:e.tagName.toLowerCase()}function q(e){let t=0,n=e.parentElement;for(;n;)t++,n=n.parentElement;return t}function h(e,t){var i,c,r;let n=e.querySelector(G);return{componentId:L(e),semanticType:F(e),ariaLabel:(i=e.getAttribute("aria-label"))!=null?i:void 0,headingText:(r=(c=n==null?void 0:n.textContent)==null?void 0:c.trim())!=null?r:void 0,isAboveFold:e.getBoundingClientRect().top<window.innerHeight,prominenceScore:t(e),depth:q(e),reactComponentName:j(e),dataAttributes:W(e)}}function z(e){let t=[],n=new Set;return document.querySelectorAll("[data-sentient-id]").forEach(r=>{r instanceof Element&&!n.has(r)&&(n.add(r),t.push(h(r,e)))}),document.querySelectorAll("section, article, main, aside").forEach(r=>{if(!(r instanceof Element)||n.has(r))return;let s=r.hasAttribute("aria-label"),a=r.hasAttribute("data-sentient-id");!s&&!a||(n.add(r),t.push(h(r,e)))}),t}function C(){if(typeof window=="undefined")return k;let e=null,t=0,n=null,i=a=>{try{let o=window.getComputedStyle(a),d=parseFloat(o.fontSize)||12,p=parseFloat(o.zIndex)||0,u=a.getBoundingClientRect(),l=Math.max(u.top,0),m=window.innerHeight||1,A=1/(l/m+1),T=f(d,12,48)*.4+f(A,0,1)*.4+f(p,0,100)*.2;return Math.max(0,Math.min(1,T))}catch(o){return .5}};return{scan:()=>new Promise(a=>{let o=()=>{a({nodes:z(i),scannedAt:Date.now()})};try{typeof requestIdleCallback=="function"?t=requestIdleCallback(o,{timeout:100}):o()}catch(d){o()}}),observe:a=>{n=a;try{e=new MutationObserver(o=>{let d=[];for(let p of o)p.type==="childList"&&p.addedNodes.forEach(u=>{if(!(u instanceof Element)||!M.has(u.tagName))return;let l=u.hasAttribute("data-sentient-id"),m=u.hasAttribute("aria-label");!l&&!m||d.push(h(u,i))});d.length>0&&n&&n({nodes:d,addedAt:Date.now()})}),e.observe(document.body,{childList:!0,subtree:!0})}catch(o){}},getProminenceScore:i,destroy:()=>{if(e&&(e.disconnect(),e=null),t&&typeof cancelIdleCallback=="function")try{cancelIdleCallback(t)}catch(a){}t=0,n=null}}}var y="_snt_graph_nodes",S="_snt_graph_edges",J={pricing:["features","faq"],features:["pricing"],faq:["pricing"],social_proof:["cta"],cta:["social_proof","hero","trust"],hero:["cta"],comparison:["pricing"],trust:["cta"]};function v(e){return Math.max(0,Math.min(1,e))}function K(e){var t;return(t=J[e])!=null?t:[]}function N(e,t){try{let n=localStorage.getItem(e);return n?JSON.parse(n):t}catch(n){return t}}function w(e,t){try{localStorage.setItem(e,JSON.stringify(t))}catch(n){}}var H=new Set(["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"]);function B(e){return H.has(e)?e:"generic"}function $(e,t,n){let i=`${e}:${t}:${n.join(",")}`,c=5381;for(let r=0;r<i.length;r++)c=(c<<5)+c+i.charCodeAt(r)&4294967295;return(c>>>0).toString(16).padStart(8,"0")}function I(e){let t=new Map,n=new Map,i=()=>{typeof window!="undefined"&&(w(y,[...t.values()]),w(S,[...n.values()]))},c=s=>{var a,o;try{let d=JSON.parse(s);t.clear(),n.clear();for(let p of(a=d.pageNodes)!=null?a:[])t.set(p.componentId,p);for(let p of(o=d.userEdges)!=null?o:[])n.set(p.componentId,p)}catch(d){}};if(typeof window!="undefined"){let s=N(y,[]),a=N(S,[]);for(let o of s)t.set(o.componentId,o);for(let o of a)n.set(o.componentId,o)}let r=(s,a)=>{let o=n.get(s);o?(o.attentionWeight=v(o.attentionWeight+a),o.lastUpdated=Date.now()):n.set(s,{componentId:s,attentionWeight:v(a),interactionCount:0,dwellTime:0,lastUpdated:Date.now()})};return{addPageNode(s){t.set(s.componentId,s),i()},syncOnce(){if(!(e!=null&&e.syncUrl)||typeof window=="undefined")return;let s=[...t.values()];if(s.length!==0)try{let a={pageUrl:window.location.href,nodes:s.map(o=>{let d=B(o.semanticType);return{componentId:o.componentId,semanticType:d,answers:o.answers,contentHash:$(o.componentId,d,o.answers),prominenceScore:o.prominenceScore,depthInPage:o.depth}}),edges:[]};fetch(e.syncUrl,{method:"POST",keepalive:!0,headers:g({"Content-Type":"application/json"},e.apiKey?{Authorization:`Bearer ${e.apiKey}`}:{}),body:JSON.stringify(a)}).catch(()=>{})}catch(a){}},updateUserEdge(s,a){var d;let o=(d=n.get(s))!=null?d:{componentId:s,attentionWeight:0,interactionCount:0,dwellTime:0,lastUpdated:Date.now()};o.attentionWeight=v(o.attentionWeight+a),o.interactionCount+=1,o.lastUpdated=Date.now(),n.set(s,o),i()},propagate(s,a){let o=t.get(s);if(!o){r(s,a),i();return}r(s,a);for(let d of K(o.semanticType))for(let p of t.values())p.semanticType===d&&p.componentId!==s&&r(p.componentId,a*.4);for(let d of t.values())d.depth===o.depth+1&&r(d.componentId,a*.6);for(let d of t.values())d.semanticType===o.semanticType&&d.componentId!==s&&r(d.componentId,a*-.2);i()},snapshot(){return{pageNodes:[...t.values()],userEdges:[...n.values()],capturedAt:Date.now()}},serialize(){return JSON.stringify({pageNodes:[...t.values()],userEdges:[...n.values()]})},restore:c,destroy(){if(typeof window!="undefined")try{localStorage.removeItem(y),localStorage.removeItem(S)}catch(s){}}}}function V(){try{let e=document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);return e?decodeURIComponent(e[1]):void 0}catch(e){return}}function ne(e){let t=E(e);if(!e.graph||typeof window=="undefined")return t;let n=C(),i=I({syncUrl:e.ingestUrl.replace(/\/events\/?$/,"/graph/sync"),apiKey:e.apiKey,projectId:e.apiKey,sessionId:V()});try{let c=localStorage.getItem("_snt_graph_nodes"),r=localStorage.getItem("_snt_graph_edges");(c||r)&&i.restore(JSON.stringify({pageNodes:c?JSON.parse(c):[],userEdges:r?JSON.parse(r):[]}))}catch(c){}return n.scan().then(c=>{for(let r of c.nodes)i.addPageNode({id:r.componentId,componentId:r.componentId,semanticType:r.semanticType,answers:r.headingText?[r.headingText]:[],prominenceScore:r.prominenceScore,depth:r.depth});i.syncOnce()}),n.observe(c=>{for(let r of c.nodes)i.addPageNode({id:r.componentId,componentId:r.componentId,semanticType:r.semanticType,answers:r.headingText?[r.headingText]:[],prominenceScore:r.prominenceScore,depth:r.depth})}),b(g({},t),{getGraph:()=>i.snapshot(),destroy:()=>{n.destroy(),i.destroy(),t.destroy()}})}export{D as deriveSessionSegment,x as detectDeviceClass,U as detectTimeOfDay,O as detectTrafficSource,ne as init,R as preloadAssignments,P as readSessionCookie,_ as referrerDomainFromReferer};
@@ -0,0 +1,228 @@
1
+ /** Manages anonymous session identity with cookie + localStorage layers. */
2
+ type SessionConfig = {
3
+ cookieName?: string;
4
+ cookieTTLDays?: number;
5
+ };
6
+ type SessionManager = {
7
+ getSessionId(): string | null;
8
+ /** True when neither cookie nor localStorage could be written — id is in-memory only. */
9
+ isEphemeral(): boolean;
10
+ destroy(): void;
11
+ };
12
+
13
+ /** Batched event queue with reliable transport (beacon → fetch → localStorage retry). */
14
+ type EventType = 'variant_assigned' | 'goal_achieved' | 'scroll_depth' | 'dwell' | 'cursor_signal' | 'component_visible' | 'component_exited';
15
+ type SentientEvent = {
16
+ id: string;
17
+ sessionId: string;
18
+ projectId: string;
19
+ componentId: string;
20
+ variantId?: string;
21
+ eventType: EventType;
22
+ goalType?: string;
23
+ payload: Record<string, unknown>;
24
+ timestamp: number;
25
+ timeInSession: number;
26
+ };
27
+ type QueueConfig = {
28
+ ingestUrl: string;
29
+ apiKey: string;
30
+ flushIntervalMs?: number;
31
+ maxBatchSize?: number;
32
+ maxRetrySize?: number;
33
+ };
34
+ type EventQueue = {
35
+ push(event: SentientEvent): void;
36
+ flush(): void;
37
+ destroy(): void;
38
+ };
39
+
40
+ /** Synchronous variant assignment cache (memory + localStorage). */
41
+ type Assignment = {
42
+ variantId: string;
43
+ assignedAt: number;
44
+ segment: string;
45
+ confidence: number;
46
+ };
47
+ type AssignmentCache = {
48
+ get(componentId: string, segment: string): Assignment | null;
49
+ set(componentId: string, segment: string, assignment: Assignment): void;
50
+ invalidate(componentId: string): void;
51
+ clear(): void;
52
+ };
53
+
54
+ /** Reads the rendered DOM to build the page-side context graph. */
55
+ type ScannedNode = {
56
+ componentId: string;
57
+ semanticType: string;
58
+ ariaLabel?: string;
59
+ headingText?: string;
60
+ isAboveFold: boolean;
61
+ prominenceScore: number;
62
+ depth: number;
63
+ reactComponentName?: string;
64
+ dataAttributes: Record<string, string>;
65
+ };
66
+ type ScanResult = {
67
+ nodes: ScannedNode[];
68
+ scannedAt: number;
69
+ };
70
+ type ContentAddedEvent = {
71
+ nodes: ScannedNode[];
72
+ addedAt: number;
73
+ };
74
+ type DOMScanner = {
75
+ scan(): Promise<ScanResult>;
76
+ observe(onContentAdded: (event: ContentAddedEvent) => void): void;
77
+ getProminenceScore(element: Element): number;
78
+ destroy(): void;
79
+ };
80
+
81
+ /** In-memory context graph with persistence and backend sync. */
82
+ type PageNode = {
83
+ id: string;
84
+ componentId: string;
85
+ semanticType: string;
86
+ answers: string[];
87
+ prominenceScore: number;
88
+ depth: number;
89
+ };
90
+ type UserEdge = {
91
+ componentId: string;
92
+ attentionWeight: number;
93
+ interactionCount: number;
94
+ dwellTime: number;
95
+ lastUpdated: number;
96
+ };
97
+ type GraphSnapshot = {
98
+ pageNodes: PageNode[];
99
+ userEdges: UserEdge[];
100
+ capturedAt: number;
101
+ };
102
+ type GraphConfig = {
103
+ syncUrl?: string;
104
+ apiKey?: string;
105
+ projectId?: string;
106
+ sessionId?: string;
107
+ };
108
+ type GraphClient = {
109
+ addPageNode(node: PageNode): void;
110
+ updateUserEdge(componentId: string, attentionDelta: number): void;
111
+ propagate(componentId: string, delta: number): void;
112
+ /** One-shot batch sync of all current page nodes to the backend. */
113
+ syncOnce(): void;
114
+ snapshot(): GraphSnapshot;
115
+ serialize(): string;
116
+ restore(data: string): void;
117
+ destroy(): void;
118
+ };
119
+
120
+ /** Session metadata helpers (browser + Node). No DOM APIs. */
121
+ declare function detectDeviceClass(userAgent: string): string;
122
+ declare function detectTrafficSource(referrer: string, appOrigin?: string): string;
123
+ declare function referrerDomainFromReferer(referrer: string): string | null;
124
+ declare function detectTimeOfDay(d: Date): string;
125
+ /** Bandit segment key: `<device_class>:<traffic_source>`. */
126
+ declare function deriveSessionSegment(opts?: {
127
+ userAgent?: string;
128
+ referer?: string;
129
+ appOrigin?: string;
130
+ }): string;
131
+
132
+ type ServerAssignConfig = {
133
+ /** Public API key (pk_...). */
134
+ apiKey: string;
135
+ /**
136
+ * Base URL of the Sentient API without trailing slash.
137
+ * e.g. 'https://api.yourapp.com/v1'
138
+ */
139
+ baseUrl: string;
140
+ /**
141
+ * Browser origin of your app (e.g. `http://localhost:3001`). Required for `pk_`
142
+ * keys — server-side fetch must send the same `Origin` the API allows.
143
+ */
144
+ origin?: string;
145
+ /** From `User-Agent` request header (Next.js `headers()`). */
146
+ userAgent?: string;
147
+ /** From `Referer` request header. */
148
+ referer?: string;
149
+ utmParams?: Record<string, string>;
150
+ };
151
+ /** componentId → assigned variantId */
152
+ type ServerAssignments = Record<string, string>;
153
+ /**
154
+ * Fetches variant assignments server-side for all listed components.
155
+ * Returns a map suitable for passing as `initialAssignments` to `<AdaptiveProvider>`.
156
+ *
157
+ * Call from Next.js layout, Server Component, or getServerSideProps.
158
+ *
159
+ * @example
160
+ * ```tsx
161
+ * const assignments = await preloadAssignments(
162
+ * [
163
+ * { id: 'hero', variantIds: ['hero-a', 'hero-b'] },
164
+ * { id: 'cta', variantIds: ['cta-short', 'cta-long'] },
165
+ * ],
166
+ * sessionId, // read from cookies() or req.cookies
167
+ * { apiKey: process.env.SENTIENT_API_KEY!, baseUrl: process.env.SENTIENT_API_URL! },
168
+ * );
169
+ * return <AdaptiveProvider ... initialAssignments={assignments}>{children}</AdaptiveProvider>;
170
+ * ```
171
+ */
172
+ declare function preloadAssignments(components: Array<{
173
+ id: string;
174
+ variantIds: string[];
175
+ }>, sessionId: string, config: ServerAssignConfig): Promise<ServerAssignments>;
176
+ /**
177
+ * Reads the Sentient session cookie from a Next.js `ReadonlyRequestCookies` object
178
+ * (the return value of `cookies()` from `next/headers`), or any object with a
179
+ * `get(name: string)` method. Returns null if the cookie is absent.
180
+ */
181
+ declare function readSessionCookie(cookies: {
182
+ get(name: string): {
183
+ value: string;
184
+ } | undefined;
185
+ }): string | null;
186
+
187
+ type SentientConfig = {
188
+ apiKey: string;
189
+ context: 'landing' | 'ecommerce' | 'saas' | 'marketplace';
190
+ ingestUrl: string;
191
+ debug?: boolean;
192
+ /**
193
+ * Pre-seeded assignments from `preloadAssignments()` (SSR).
194
+ * Seeds the local cache so `assign()` never makes a network call for listed components,
195
+ * guaranteeing server and client render the same variant on first paint.
196
+ */
197
+ initialAssignments?: Record<string, string>;
198
+ /**
199
+ * Segment used for SSR preload (`device:source`). When set with `initialAssignments`,
200
+ * seeds the assignment cache under this key so hydration matches the server bandit row.
201
+ */
202
+ sessionSegment?: string;
203
+ /**
204
+ * Consent gate. When `false`, returns a no-op client and performs no tracking.
205
+ * Defaults to `true`. Re-call `init()` (via `AdaptiveProvider` consent prop) when
206
+ * the user grants or revokes consent mid-session.
207
+ */
208
+ consent?: boolean;
209
+ };
210
+ type AssignResult = {
211
+ variantId: string;
212
+ assignmentTtlMs: number;
213
+ };
214
+ type SentientClient = {
215
+ track(event: Omit<SentientEvent, 'id' | 'sessionId' | 'timestamp' | 'timeInSession'>): void;
216
+ getAssignment(componentId: string, segment: string): Assignment | null;
217
+ /** Server-side variant assignment. Caches the result locally per (component, segment). */
218
+ assign(componentId: string, variantIds?: string[]): Promise<AssignResult | null>;
219
+ getGraph(): GraphSnapshot;
220
+ destroy(): void;
221
+ };
222
+
223
+ /**
224
+ * Initializes the Sentient client. Returns a no-op client during SSR.
225
+ */
226
+ declare function init(config: SentientConfig): SentientClient;
227
+
228
+ export { type AssignResult, type Assignment, type AssignmentCache, type ContentAddedEvent, type DOMScanner, type EventQueue, type EventType, type GraphClient, type GraphConfig, type GraphSnapshot, type PageNode, type QueueConfig, type ScanResult, type ScannedNode, type SentientClient, type SentientConfig, type SentientEvent, type ServerAssignConfig, type ServerAssignments, type SessionConfig, type SessionManager, type UserEdge, deriveSessionSegment, detectDeviceClass, detectTimeOfDay, detectTrafficSource, init, preloadAssignments, readSessionCookie, referrerDomainFromReferer };
@@ -0,0 +1,228 @@
1
+ /** Manages anonymous session identity with cookie + localStorage layers. */
2
+ type SessionConfig = {
3
+ cookieName?: string;
4
+ cookieTTLDays?: number;
5
+ };
6
+ type SessionManager = {
7
+ getSessionId(): string | null;
8
+ /** True when neither cookie nor localStorage could be written — id is in-memory only. */
9
+ isEphemeral(): boolean;
10
+ destroy(): void;
11
+ };
12
+
13
+ /** Batched event queue with reliable transport (beacon → fetch → localStorage retry). */
14
+ type EventType = 'variant_assigned' | 'goal_achieved' | 'scroll_depth' | 'dwell' | 'cursor_signal' | 'component_visible' | 'component_exited';
15
+ type SentientEvent = {
16
+ id: string;
17
+ sessionId: string;
18
+ projectId: string;
19
+ componentId: string;
20
+ variantId?: string;
21
+ eventType: EventType;
22
+ goalType?: string;
23
+ payload: Record<string, unknown>;
24
+ timestamp: number;
25
+ timeInSession: number;
26
+ };
27
+ type QueueConfig = {
28
+ ingestUrl: string;
29
+ apiKey: string;
30
+ flushIntervalMs?: number;
31
+ maxBatchSize?: number;
32
+ maxRetrySize?: number;
33
+ };
34
+ type EventQueue = {
35
+ push(event: SentientEvent): void;
36
+ flush(): void;
37
+ destroy(): void;
38
+ };
39
+
40
+ /** Synchronous variant assignment cache (memory + localStorage). */
41
+ type Assignment = {
42
+ variantId: string;
43
+ assignedAt: number;
44
+ segment: string;
45
+ confidence: number;
46
+ };
47
+ type AssignmentCache = {
48
+ get(componentId: string, segment: string): Assignment | null;
49
+ set(componentId: string, segment: string, assignment: Assignment): void;
50
+ invalidate(componentId: string): void;
51
+ clear(): void;
52
+ };
53
+
54
+ /** Reads the rendered DOM to build the page-side context graph. */
55
+ type ScannedNode = {
56
+ componentId: string;
57
+ semanticType: string;
58
+ ariaLabel?: string;
59
+ headingText?: string;
60
+ isAboveFold: boolean;
61
+ prominenceScore: number;
62
+ depth: number;
63
+ reactComponentName?: string;
64
+ dataAttributes: Record<string, string>;
65
+ };
66
+ type ScanResult = {
67
+ nodes: ScannedNode[];
68
+ scannedAt: number;
69
+ };
70
+ type ContentAddedEvent = {
71
+ nodes: ScannedNode[];
72
+ addedAt: number;
73
+ };
74
+ type DOMScanner = {
75
+ scan(): Promise<ScanResult>;
76
+ observe(onContentAdded: (event: ContentAddedEvent) => void): void;
77
+ getProminenceScore(element: Element): number;
78
+ destroy(): void;
79
+ };
80
+
81
+ /** In-memory context graph with persistence and backend sync. */
82
+ type PageNode = {
83
+ id: string;
84
+ componentId: string;
85
+ semanticType: string;
86
+ answers: string[];
87
+ prominenceScore: number;
88
+ depth: number;
89
+ };
90
+ type UserEdge = {
91
+ componentId: string;
92
+ attentionWeight: number;
93
+ interactionCount: number;
94
+ dwellTime: number;
95
+ lastUpdated: number;
96
+ };
97
+ type GraphSnapshot = {
98
+ pageNodes: PageNode[];
99
+ userEdges: UserEdge[];
100
+ capturedAt: number;
101
+ };
102
+ type GraphConfig = {
103
+ syncUrl?: string;
104
+ apiKey?: string;
105
+ projectId?: string;
106
+ sessionId?: string;
107
+ };
108
+ type GraphClient = {
109
+ addPageNode(node: PageNode): void;
110
+ updateUserEdge(componentId: string, attentionDelta: number): void;
111
+ propagate(componentId: string, delta: number): void;
112
+ /** One-shot batch sync of all current page nodes to the backend. */
113
+ syncOnce(): void;
114
+ snapshot(): GraphSnapshot;
115
+ serialize(): string;
116
+ restore(data: string): void;
117
+ destroy(): void;
118
+ };
119
+
120
+ /** Session metadata helpers (browser + Node). No DOM APIs. */
121
+ declare function detectDeviceClass(userAgent: string): string;
122
+ declare function detectTrafficSource(referrer: string, appOrigin?: string): string;
123
+ declare function referrerDomainFromReferer(referrer: string): string | null;
124
+ declare function detectTimeOfDay(d: Date): string;
125
+ /** Bandit segment key: `<device_class>:<traffic_source>`. */
126
+ declare function deriveSessionSegment(opts?: {
127
+ userAgent?: string;
128
+ referer?: string;
129
+ appOrigin?: string;
130
+ }): string;
131
+
132
+ type ServerAssignConfig = {
133
+ /** Public API key (pk_...). */
134
+ apiKey: string;
135
+ /**
136
+ * Base URL of the Sentient API without trailing slash.
137
+ * e.g. 'https://api.yourapp.com/v1'
138
+ */
139
+ baseUrl: string;
140
+ /**
141
+ * Browser origin of your app (e.g. `http://localhost:3001`). Required for `pk_`
142
+ * keys — server-side fetch must send the same `Origin` the API allows.
143
+ */
144
+ origin?: string;
145
+ /** From `User-Agent` request header (Next.js `headers()`). */
146
+ userAgent?: string;
147
+ /** From `Referer` request header. */
148
+ referer?: string;
149
+ utmParams?: Record<string, string>;
150
+ };
151
+ /** componentId → assigned variantId */
152
+ type ServerAssignments = Record<string, string>;
153
+ /**
154
+ * Fetches variant assignments server-side for all listed components.
155
+ * Returns a map suitable for passing as `initialAssignments` to `<AdaptiveProvider>`.
156
+ *
157
+ * Call from Next.js layout, Server Component, or getServerSideProps.
158
+ *
159
+ * @example
160
+ * ```tsx
161
+ * const assignments = await preloadAssignments(
162
+ * [
163
+ * { id: 'hero', variantIds: ['hero-a', 'hero-b'] },
164
+ * { id: 'cta', variantIds: ['cta-short', 'cta-long'] },
165
+ * ],
166
+ * sessionId, // read from cookies() or req.cookies
167
+ * { apiKey: process.env.SENTIENT_API_KEY!, baseUrl: process.env.SENTIENT_API_URL! },
168
+ * );
169
+ * return <AdaptiveProvider ... initialAssignments={assignments}>{children}</AdaptiveProvider>;
170
+ * ```
171
+ */
172
+ declare function preloadAssignments(components: Array<{
173
+ id: string;
174
+ variantIds: string[];
175
+ }>, sessionId: string, config: ServerAssignConfig): Promise<ServerAssignments>;
176
+ /**
177
+ * Reads the Sentient session cookie from a Next.js `ReadonlyRequestCookies` object
178
+ * (the return value of `cookies()` from `next/headers`), or any object with a
179
+ * `get(name: string)` method. Returns null if the cookie is absent.
180
+ */
181
+ declare function readSessionCookie(cookies: {
182
+ get(name: string): {
183
+ value: string;
184
+ } | undefined;
185
+ }): string | null;
186
+
187
+ type SentientConfig = {
188
+ apiKey: string;
189
+ context: 'landing' | 'ecommerce' | 'saas' | 'marketplace';
190
+ ingestUrl: string;
191
+ debug?: boolean;
192
+ /**
193
+ * Pre-seeded assignments from `preloadAssignments()` (SSR).
194
+ * Seeds the local cache so `assign()` never makes a network call for listed components,
195
+ * guaranteeing server and client render the same variant on first paint.
196
+ */
197
+ initialAssignments?: Record<string, string>;
198
+ /**
199
+ * Segment used for SSR preload (`device:source`). When set with `initialAssignments`,
200
+ * seeds the assignment cache under this key so hydration matches the server bandit row.
201
+ */
202
+ sessionSegment?: string;
203
+ /**
204
+ * Consent gate. When `false`, returns a no-op client and performs no tracking.
205
+ * Defaults to `true`. Re-call `init()` (via `AdaptiveProvider` consent prop) when
206
+ * the user grants or revokes consent mid-session.
207
+ */
208
+ consent?: boolean;
209
+ };
210
+ type AssignResult = {
211
+ variantId: string;
212
+ assignmentTtlMs: number;
213
+ };
214
+ type SentientClient = {
215
+ track(event: Omit<SentientEvent, 'id' | 'sessionId' | 'timestamp' | 'timeInSession'>): void;
216
+ getAssignment(componentId: string, segment: string): Assignment | null;
217
+ /** Server-side variant assignment. Caches the result locally per (component, segment). */
218
+ assign(componentId: string, variantIds?: string[]): Promise<AssignResult | null>;
219
+ getGraph(): GraphSnapshot;
220
+ destroy(): void;
221
+ };
222
+
223
+ /**
224
+ * Initializes the Sentient client. Returns a no-op client during SSR.
225
+ */
226
+ declare function init(config: SentientConfig): SentientClient;
227
+
228
+ export { type AssignResult, type Assignment, type AssignmentCache, type ContentAddedEvent, type DOMScanner, type EventQueue, type EventType, type GraphClient, type GraphConfig, type GraphSnapshot, type PageNode, type QueueConfig, type ScanResult, type ScannedNode, type SentientClient, type SentientConfig, type SentientEvent, type ServerAssignConfig, type ServerAssignments, type SessionConfig, type SessionManager, type UserEdge, deriveSessionSegment, detectDeviceClass, detectTimeOfDay, detectTrafficSource, init, preloadAssignments, readSessionCookie, referrerDomainFromReferer };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var O=Object.defineProperty,X=Object.defineProperties,H=Object.getOwnPropertyDescriptor,V=Object.getOwnPropertyDescriptors,Z=Object.getOwnPropertyNames,K=Object.getOwnPropertySymbols;var Q=Object.prototype.hasOwnProperty,ee=Object.prototype.propertyIsEnumerable;var N=(e,t,n)=>t in e?O(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,F=(e,t)=>{for(var n in t||(t={}))Q.call(t,n)&&N(e,n,t[n]);if(K)for(var n of K(t))ee.call(t,n)&&N(e,n,t[n]);return e},G=(e,t)=>X(e,V(t));var te=(e,t)=>{for(var n in t)O(e,n,{get:t[n],enumerable:!0})},ne=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Z(t))!Q.call(e,r)&&r!==n&&O(e,r,{get:()=>t[r],enumerable:!(o=H(t,r))||o.enumerable});return e};var re=e=>ne(O({},"__esModule",{value:!0}),e);var _e={};te(_e,{deriveSessionSegment:()=>j,detectDeviceClass:()=>k,detectTimeOfDay:()=>D,detectTrafficSource:()=>C,init:()=>Ee,preloadAssignments:()=>W,readSessionCookie:()=>Y,referrerDomainFromReferer:()=>x});module.exports=re(_e);var se="_snt_uid";var b="_snt_uid";function ie(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(t){}let e=()=>Math.floor(Math.random()*4294967295).toString(16).padStart(8,"0");return`${e()}-${e()}-${e()}-${e()}`}function oe(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function ae(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(o){}}function ce(e){try{return localStorage.getItem(e)}catch(t){return null}}function ue(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function de(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function le(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function ge(e){try{sessionStorage.removeItem(e)}catch(t){}}function me(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,/(?:^|; )_snt_uid_probe=1/.test(document.cookie)||document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function fe(e){try{localStorage.removeItem(e)}catch(t){}}function pe(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var ye={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function B(e){var g,d,p,v,w;if(typeof window=="undefined")return ye;let t=(g=e==null?void 0:e.cookieName)!=null?g:se,o=((d=e==null?void 0:e.cookieTTLDays)!=null?d:365)*24*60*60,r=(w=(v=(p=oe(t))!=null?p:ce(b))!=null?v:de(b))!=null?w:ie();ae(t,r,o);let a=ue(b,r),s=me(t),i=a?!1:le(b,r),u=!a&&!s&&!i;return{getSessionId:()=>r,isEphemeral:()=>u,destroy:()=>{r=null,pe(t),fe(b),ge(b)}}}var U="_snt_retry";var Se={push:()=>{},flush:()=>{},destroy:()=>{}};function he(e){try{let t=localStorage.getItem(U);if(!t)return[];let n=JSON.parse(t);return Array.isArray(n)?(localStorage.removeItem(U),n.slice(-e)):[]}catch(t){return[]}}function ve(e,t){try{let o=[...(()=>{try{let r=localStorage.getItem(U);if(!r)return[];let a=JSON.parse(r);return Array.isArray(a)?a:[]}catch(r){return[]}})(),...e].slice(-t);localStorage.setItem(U,JSON.stringify(o))}catch(n){}}function z(e){var h,_,I;if(typeof window=="undefined")return Se;let t=(h=e.flushIntervalMs)!=null?h:5e3,n=(_=e.maxBatchSize)!=null?_:20,o=(I=e.maxRetrySize)!=null?I:100,r=e.ingestUrl,a=e.apiKey,s=[],i=new Set,u=[],g=c=>{for(let m of c)i.has(m)||(i.add(m),u.push(m));for(;u.length>500;){let m=u.shift();m&&i.delete(m)}},d=new Set,p=c=>{i.has(c.id)||d.has(c.id)||(d.add(c.id),s.push(c))},v=he(o);for(let c of v)p(c);let w=c=>{if(c.length===0)return!0;let m=JSON.stringify(c);try{return fetch(r,{method:"POST",keepalive:!0,body:m,headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`}}),g(c.map(T=>T.id)),!0}catch(T){return ve(c,o),!1}},y=()=>{try{for(;s.length>0;){let m=s.splice(0,n).filter(q=>!i.has(q.id));if(m.length===0)continue;if(!w(m)){s.unshift(...m);break}}}catch(c){}},A=!0,S=null,E=()=>{if(!A)return;let c=Date.now()+t,m=()=>{if(A){if(Date.now()>=c){y(),E();return}try{S=new MessageChannel,S.port1.onmessage=m,S.port2.postMessage(null)}catch(T){}}};m()},l=()=>{document.visibilityState==="hidden"&&y()},f=()=>{y()};return document.addEventListener("visibilitychange",l),window.addEventListener("beforeunload",f),E(),{push(c){p(c),s.length>=n&&y()},flush:y,destroy(){A=!1,document.removeEventListener("visibilitychange",l),window.removeEventListener("beforeunload",f),S&&(S.port1.onmessage=null,S=null),y()}}}var $="_snt_asgn_";function R(e,t){return`${e}:${t}`}function we(e,t){return`${$}${e}_${t}`}function L(){try{let e=[];for(let t=0;t<localStorage.length;t++){let n=localStorage.key(t);n!=null&&n.startsWith($)&&e.push(n)}return e}catch(e){return[]}}function J(e=18e5){let t=new Map,n=r=>r.assignedAt+e<Date.now();return typeof window!="undefined"&&(()=>{for(let r of L())try{let a=localStorage.getItem(r);if(!a)continue;let s=JSON.parse(a);if(n(s)){localStorage.removeItem(r);continue}let i=r.slice($.length),u=i.lastIndexOf("_");if(u<0)continue;let g=i.slice(0,u),d=i.slice(u+1);t.set(R(g,d),s)}catch(a){}})(),{get(r,a){let s=t.get(R(r,a));return s?n(s)?(t.delete(R(r,a)),null):s:null},set(r,a,s){let i=R(r,a);t.set(i,s);try{localStorage.setItem(we(r,a),JSON.stringify(s))}catch(u){}},invalidate(r){let a=`${r}:`;for(let s of[...t.keys()])s.startsWith(a)&&t.delete(s);for(let s of L()){let i=s.slice($.length),u=i.lastIndexOf("_");if(u<0)continue;if(i.slice(0,u)===r)try{localStorage.removeItem(s)}catch(d){}}},clear(){t.clear();for(let r of L())try{localStorage.removeItem(r)}catch(a){}}}}function k(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function C(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(r){}let o=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(o)?"search":/(^|\.)(twitter|x\.com|facebook|linkedin|reddit|t\.co)/.test(o)?"social":"referral"}catch(n){return"direct"}}function x(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}function D(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function j(e){let t=M("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function M(e,t){var a,s,i,u,g,d,p;let n=(s=(a=t==null?void 0:t.userAgent)==null?void 0:a.trim())!=null?s:"",o=(u=(i=t==null?void 0:t.referer)==null?void 0:i.trim())!=null?u:"",r=(g=t==null?void 0:t.now)!=null?g:new Date;return{sessionId:e,ephemeral:!1,utmParams:(d=t==null?void 0:t.utmParams)!=null?d:{},deviceClass:n?k(n):"desktop",trafficSource:o?C(o,t==null?void 0:t.appOrigin):"direct",referrerDomain:x(o),timeOfDay:D(r),dayOfWeek:(p=["sun","mon","tue","wed","thu","fri","sat"][r.getDay()])!=null?p:"sun"}}async function W(e,t,n){let o={"Content-Type":"application/json",Authorization:`Bearer ${n.apiKey}`};n.origin&&(o.Origin=n.origin);let r=M(t,{userAgent:n.userAgent,referer:n.referer,utmParams:n.utmParams,appOrigin:n.origin});try{await fetch(`${n.baseUrl}/sessions`,{method:"POST",headers:o,body:JSON.stringify(r)})}catch(i){}let a=await Promise.allSettled(e.map(async({id:i,variantIds:u})=>{let g=await fetch(`${n.baseUrl}/assign`,{method:"POST",headers:o,body:JSON.stringify({sessionId:t,componentId:i,variantIds:u})});if(!g.ok)return null;let d=await g.json();return{id:i,variantId:d.variantId}})),s={};for(let i of a)i.status==="fulfilled"&&i.value&&(s[i.value.id]=i.value.variantId);return s}function Y(e){var t,n;return(n=(t=e.get("_snt_uid"))==null?void 0:t.value)!=null?n:null}function Ae(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}return`evt-${Date.now()}-${Math.random().toString(16).slice(2)}`}var P={track:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),getGraph:()=>({pageNodes:[],userEdges:[],capturedAt:0}),destroy:()=>{}};function Ie(){try{let e={},t=new URLSearchParams(window.location.search);for(let[n,o]of t)n.startsWith("utm_")&&(e[n]=o);return e}catch(e){return{}}}function be(e){return e.replace(/\/events\/?$/,"")}function Ee(e){var y,A,S,E;if(typeof window=="undefined"||e.consent===!1)return P;if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),P;if(!e.ingestUrl)return console.warn("[sentient] init() called without ingestUrl. SDK disabled."),P;let t=Date.now(),n=B(),o=J(),r=z({ingestUrl:e.ingestUrl,apiKey:e.apiKey}),a=be(e.ingestUrl),s={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},i=k((y=navigator.userAgent)!=null?y:""),u=typeof window!="undefined"?window.location.origin:void 0,g=C((A=document.referrer)!=null?A:"",u),d=(S=e.sessionSegment)!=null?S:`${i}:${g}`;if(e.initialAssignments)for(let[l,f]of Object.entries(e.initialAssignments))o.set(l,d,{variantId:f,assignedAt:Date.now(),segment:d,confidence:1});let p=Promise.resolve(),v=n.getSessionId();if(v){let l=x((E=document.referrer)!=null?E:""),f={sessionId:v,deviceClass:i,trafficSource:g,referrerDomain:l,utmParams:Ie(),timeOfDay:D(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:n.isEphemeral()};try{p=fetch(`${a}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(f),headers:s}).then(()=>{}).catch(()=>{})}catch(h){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:r});let w={track(l){let f=n.getSessionId();if(!f)return;let h=G(F({},l),{id:Ae(),sessionId:f,timestamp:Date.now(),timeInSession:Date.now()-t});r.push(h),e.debug&&console.log("[sentient] track",h)},getAssignment(l,f){return o.get(l,f)},async assign(l,f){let h=n.getSessionId();if(!h)return null;let _=o.get(l,d);if(_)return{variantId:_.variantId,assignmentTtlMs:0};await p;try{let I=await fetch(`${a}/assign`,{method:"POST",body:JSON.stringify({sessionId:h,componentId:l,variantIds:f}),headers:s});if(!I.ok)return null;let c=await I.json();return o.set(l,d,{variantId:c.variantId,assignedAt:Date.now(),segment:d,confidence:1}),c}catch(I){return null}},getGraph(){return{pageNodes:[],userEdges:[],capturedAt:0}},destroy(){r.destroy(),n.destroy(),e.debug&&console.log("[sentient] destroyed")}};if(e.debug){let l=window;l.__sentient&&(l.__sentient.client=w)}return w}0&&(module.exports={deriveSessionSegment,detectDeviceClass,detectTimeOfDay,detectTrafficSource,init,preloadAssignments,readSessionCookie,referrerDomainFromReferer});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{c as a,d as b,e as c,f as d,g as e,h as f,i as g,j as h}from"./chunk-HRRGYUE4.mjs";export{e as deriveSessionSegment,a as detectDeviceClass,d as detectTimeOfDay,b as detectTrafficSource,h as init,f as preloadAssignments,g as readSessionCookie,c as referrerDomainFromReferer};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@sentientui/core",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ },
15
+ "./graph": {
16
+ "types": "./dist/index-graph.d.ts",
17
+ "import": "./dist/index-graph.mjs",
18
+ "require": "./dist/index-graph.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "devDependencies": {
25
+ "@types/node": "^22.10.2",
26
+ "jsdom": "^25.0.1",
27
+ "tsup": "^8.3.5",
28
+ "tsx": "^4.19.2",
29
+ "typescript": "^5.7.2",
30
+ "vitest": "^2.1.8"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup",
34
+ "test": "vitest run",
35
+ "typecheck": "tsc --noEmit",
36
+ "size-check": "tsx scripts/size-check.ts",
37
+ "lint": "eslint src"
38
+ }
39
+ }