@sentientui/react 0.8.5 → 0.11.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.
package/README.md CHANGED
@@ -107,6 +107,7 @@ Imported from `@sentientui/react/next`.
107
107
  | `appOrigin` | `string` *(default `http://localhost:3001`)* | Your app origin (e.g. `https://yourapp.com`). Must be on the project's allowed-origins list. Always set in production. |
108
108
  | `context` | `'landing' \| 'ecommerce' \| 'saas' \| 'marketplace'` | Type of product. Used for segment weighting and analytics grouping. |
109
109
  | `consent` | `boolean` *(default `true`)* | Set `false` to skip SDK init (no cookies, no events). Flip to `true` after the visitor accepts. |
110
+ | `respectDoNotTrack` | `boolean` *(default `true`)* | Honor the browser's Do Not Track signal. When on and DNT is enabled, the SDK sets no cookies and sends no tracking data (overriding `consent: true`), and `grantConsent()` won't re-enable it. Set `false` to make your own consent gate authoritative. |
110
111
  | `ssrFallback` | `'first' \| 'none'` *(default `'first'`)* | What to render in SSR HTML for components not in `components`. `'first'` is safe for SEO. |
111
112
  | `timeoutMs` | `number` *(default `1500`)* | Server-side fetch timeout before falling back to the first variant. |
112
113
  | `debug` | `boolean` | Log assignment and event activity to the console. |
@@ -289,6 +290,18 @@ window.__sentient_overrides = { hero_cta: 'variant_a' };
289
290
 
290
291
  Overrides bypass the bandit entirely — no events recorded, weights unchanged.
291
292
 
293
+ ## Graph mode
294
+
295
+ Pass `enableGraph` to `AdaptiveProvider` (or `AdaptiveRoot`) to turn on DOM graph scanning. The SDK captures your page structure and syncs it to power the dashboard Graph page. The graph code is loaded on demand, so apps that don't set `enableGraph` keep the lean bundle.
296
+
297
+ ```tsx
298
+ <AdaptiveProvider apiKey="pk_…" context="saas" enableGraph>
299
+ <App />
300
+ </AdaptiveProvider>
301
+ ```
302
+
303
+ Direct `@sentientui/core` users enable the same thing by importing `init` from `@sentientui/core/graph` and passing `graph: true`.
304
+
292
305
  ## Consent management (GDPR)
293
306
 
294
307
  SentientUI does not track visitors until consent is granted. To serve the best-performing variant while the cookie banner is pending (rather than freezing the UI), use `preConsentBehavior: 'statistical_winner'`:
@@ -0,0 +1,5 @@
1
+ declare function AdaptiveDevtools({ apiKey }?: {
2
+ apiKey?: string;
3
+ }): JSX.Element | null;
4
+
5
+ export { AdaptiveDevtools };
@@ -0,0 +1,5 @@
1
+ declare function AdaptiveDevtools({ apiKey }?: {
2
+ apiKey?: string;
3
+ }): JSX.Element | null;
4
+
5
+ export { AdaptiveDevtools };
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ "use strict";"use client";var l=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames,x=Object.getOwnPropertySymbols;var h=Object.prototype.hasOwnProperty,B=Object.prototype.propertyIsEnumerable;var w=(e,t,i)=>t in e?l(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i,R=(e,t)=>{for(var i in t||(t={}))h.call(t,i)&&w(e,i,t[i]);if(x)for(var i of x(t))B.call(t,i)&&w(e,i,t[i]);return e};var I=(e,t)=>{for(var i in t)l(e,i,{get:t[i],enumerable:!0})},N=(e,t,i,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of E(t))!h.call(e,r)&&r!==i&&l(e,r,{get:()=>t[r],enumerable:!(s=P(t,r))||s.enumerable});return e};var D=e=>N(l({},"__esModule",{value:!0}),e);var z={};I(z,{AdaptiveDevtools:()=>j});module.exports=D(z);var p=require("react");var V=new Map,k=new Set;function c(){return[...V.values()]}function S(e){return k.add(e),()=>{k.delete(e)}}function f(){let e=window;return e.__sentient_overrides||(e.__sentient_overrides={}),e.__sentient_overrides}function m(e,t){f()[e]=t}function O(e){delete f()[e]}function b(){return R({},f())}var y=!1,M=new Set;function u(e){if(y!==e){y=e;for(let t of M)t()}}function _(){return y}var n=require("react/jsx-runtime"),C,A=typeof process!="undefined"&&((C=process.env)==null?void 0:C.NODE_ENV)==="production",T=["browsers","buyers","researchers","deal-seekers"];async function W(e,t){let i=c().map(d=>({id:d.id})),s=c().map(d=>({id:d.id,variantIds:d.variantIds})),r=await fetch("/v1/explain",{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({persona:t,sections:i,components:s})});if(!r.ok)return;let g=await r.json();for(let[d,v]of Object.entries(g.assignments))m(d,v);u(!0)}function j({apiKey:e}={}){let[t,i]=(0,p.useState)(!1),[,s]=(0,p.useReducer)(o=>o+1,0);if((0,p.useEffect)(()=>S(s),[]),A)return null;let r=c(),g=b();function d(o,a){m(o,a),u(!0),s()}function v(o){O(o),Object.keys(b()).length===0&&u(!1),s()}return(0,n.jsxs)("div",{style:{position:"fixed",bottom:16,right:16,zIndex:2147483647,fontFamily:"system-ui"},children:[t&&(0,n.jsxs)("div",{style:{width:300,maxHeight:420,overflowY:"auto",background:"#111",color:"#eee",borderRadius:8,padding:12,marginBottom:8,boxShadow:"0 8px 30px rgba(0,0,0,.4)",fontSize:12},children:[(0,n.jsxs)("div",{style:{opacity:.7,marginBottom:8},children:[r.length," component",r.length===1?"":"s"," \xB7 ",_()?"preview \u2014 writing nothing":"live"]}),e&&(0,n.jsxs)("div",{style:{borderBottom:"1px solid #333",paddingBottom:8,marginBottom:8},children:[(0,n.jsx)("div",{style:{opacity:.7,marginBottom:4},children:"Preview persona"}),(0,n.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:4},children:T.map(o=>(0,n.jsx)("button",{onClick:()=>{W(e,o).then(s)},style:{padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:"#222",color:"#eee",cursor:"pointer"},children:o},o))})]}),r.length===0&&(0,n.jsx)("div",{style:{opacity:.6},children:"No components on this page yet."}),r.map(o=>(0,n.jsxs)("div",{style:{borderTop:"1px solid #333",padding:"8px 0"},children:[(0,n.jsxs)("div",{style:{fontWeight:600},children:[o.id,o.goal?` \xB7 goal: ${o.goal}`:""]}),(0,n.jsxs)("div",{style:{display:"flex",flexWrap:"wrap",gap:4,marginTop:4},children:[o.variantIds.map(a=>(0,n.jsx)("button",{onClick:()=>d(o.id,a),style:{padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:g[o.id]===a?"#3b82f6":"#222",color:"#eee",cursor:"pointer"},children:a},a)),g[o.id]&&(0,n.jsx)("button",{onClick:()=>v(o.id),style:{padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:"#222",color:"#aaa",cursor:"pointer"},children:"reset"})]})]},o.id))]}),(0,n.jsx)("button",{"aria-label":"Sentient DevTools",onClick:()=>i(o=>!o),style:{width:40,height:40,borderRadius:20,border:"none",background:"#3b82f6",color:"#fff",cursor:"pointer",boxShadow:"0 4px 14px rgba(0,0,0,.3)"},children:"\u25E7"})]})}0&&(module.exports={AdaptiveDevtools});
3
+ //# sourceMappingURL=devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/preview-mode.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState } from 'react';\nimport { getRegistered, subscribeRegistry } from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setPreviewMode, getPreviewMode } from '../preview-mode.js';\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\nconst IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\n\nconst SEED_PERSONAS = ['browsers', 'buyers', 'researchers', 'deal-seekers'];\n\n/** Simulate a persona via /v1/explain (read-only) and apply the result as overrides. */\nasync function forcePersona(apiKey: string, persona: string): Promise<void> {\n const sections = getRegistered().map((c) => ({ id: c.id }));\n const components = getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds }));\n const res = await fetch('/v1/explain', {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({ persona, sections, components }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as { assignments: Record<string, string> };\n for (const [id, variantId] of Object.entries(data.assignments)) setVariantOverride(id, variantId);\n setPreviewMode(true);\n}\n\nexport function AdaptiveDevtools({ apiKey }: { apiKey?: string } = {}): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n useEffect(() => subscribeRegistry(force), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of this compile-time constant.)\n if (IS_PROD) return null;\n\n const components = getRegistered();\n const overrides = getOverrides();\n\n function choose(id: string, variantId: string): void {\n setVariantOverride(id, variantId);\n setPreviewMode(true); // suppress events while previewing\n force();\n }\n function reset(id: string): void {\n clearVariantOverride(id);\n if (Object.keys(getOverrides()).length === 0) setPreviewMode(false);\n force();\n }\n\n return (\n <div style={{ position: 'fixed', bottom: 16, right: 16, zIndex: 2147483647, fontFamily: 'system-ui' }}>\n {open && (\n <div style={{ width: 300, maxHeight: 420, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, marginBottom: 8, boxShadow: '0 8px 30px rgba(0,0,0,.4)', fontSize: 12 }}>\n <div style={{ opacity: .7, marginBottom: 8 }}>\n {components.length} component{components.length === 1 ? '' : 's'} · {getPreviewMode() ? 'preview — writing nothing' : 'live'}\n </div>\n {apiKey && (\n <div style={{ borderBottom: '1px solid #333', paddingBottom: 8, marginBottom: 8 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Preview persona</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {SEED_PERSONAS.map((p) => (\n <button key={p} onClick={() => void forcePersona(apiKey, p).then(force)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#eee', cursor: 'pointer' }}>\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n {components.length === 0 && <div style={{ opacity: .6 }}>No components on this page yet.</div>}\n {components.map((c) => (\n <div key={c.id} style={{ borderTop: '1px solid #333', padding: '8px 0' }}>\n <div style={{ fontWeight: 600 }}>{c.id}{c.goal ? ` · goal: ${c.goal}` : ''}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {c.variantIds.map((v) => (\n <button key={v} onClick={() => choose(c.id, v)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444',\n background: overrides[c.id] === v ? '#3b82f6' : '#222', color: '#eee', cursor: 'pointer' }}>\n {v}\n </button>\n ))}\n {overrides[c.id] && (\n <button onClick={() => reset(c.id)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#aaa', cursor: 'pointer' }}>\n reset\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n <button aria-label=\"Sentient DevTools\" onClick={() => setOpen((o) => !o)}\n style={{ width: 40, height: 40, borderRadius: 20, border: 'none', background: '#3b82f6', color: '#fff',\n cursor: 'pointer', boxShadow: '0 4px 14px rgba(0,0,0,.3)' }}>◧</button>\n </div>\n );\n}\n","export type RegisteredComponent = { id: string; variantIds: string[]; goal?: string };\n\nconst registry = new Map<string, RegisteredComponent>();\nconst listeners = new Set<() => void>();\n\nfunction emit(): void {\n for (const fn of listeners) fn();\n}\n\n/** Register (or re-register) a component. Returns an unregister function. */\nexport function registerComponent(c: RegisteredComponent): () => void {\n registry.set(c.id, c);\n emit();\n return () => {\n registry.delete(c.id);\n emit();\n };\n}\n\nexport function getRegistered(): RegisteredComponent[] {\n return [...registry.values()];\n}\n\nexport function subscribeRegistry(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n","function store(): Record<string, string> {\n const w = window as unknown as { __sentient_overrides?: Record<string, string> };\n if (!w.__sentient_overrides) w.__sentient_overrides = {};\n return w.__sentient_overrides;\n}\n\n/** Force `componentId` to render `variantId` — read by `useAssignment`. */\nexport function setVariantOverride(componentId: string, variantId: string): void {\n store()[componentId] = variantId;\n}\n\nexport function clearVariantOverride(componentId: string): void {\n delete store()[componentId];\n}\n\nexport function getOverrides(): Record<string, string> {\n return { ...store() };\n}\n","import type { SentientClient } from '@sentientui/core';\n\nlet previewOn = false;\nconst listeners = new Set<() => void>();\n\nexport function setPreviewMode(on: boolean): void {\n if (previewOn === on) return;\n previewOn = on;\n for (const fn of listeners) fn();\n}\n\nexport function getPreviewMode(): boolean {\n return previewOn;\n}\n\nexport function subscribePreview(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n\n/**\n * Wraps a client so it writes nothing: reads pass through, all emitters no-op.\n * Used while previewing variants/personas so no `variant_assigned`, goal, or\n * session events are sent.\n */\nexport function createPreviewClient(inner: SentientClient): SentientClient {\n return {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n fetchWeights: () => Promise.resolve([]),\n getAssignment: (componentId, segment) => inner.getAssignment(componentId, segment),\n assign: (componentId, variantIds, agentData, agentDataByVariant) =>\n inner.assign(componentId, variantIds, agentData, agentDataByVariant),\n getGraph: () => inner.getGraph(),\n destroy: () => inner.destroy(),\n };\n}\n"],"mappings":"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","names":["devtools_exports","__export","AdaptiveDevtools","__toCommonJS","import_react","registry","listeners","getRegistered","registry","subscribeRegistry","fn","listeners","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","previewOn","listeners","setPreviewMode","on","fn","getPreviewMode","import_jsx_runtime","_a","IS_PROD","SEED_PERSONAS","forcePersona","apiKey","persona","sections","getRegistered","c","components","res","data","id","variantId","setVariantOverride","setPreviewMode","AdaptiveDevtools","open","setOpen","force","n","subscribeRegistry","overrides","getOverrides","choose","reset","clearVariantOverride","getPreviewMode","p","v"]}
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ "use client";var _=Object.defineProperty;var y=Object.getOwnPropertySymbols;var C=Object.prototype.hasOwnProperty,P=Object.prototype.propertyIsEnumerable;var x=(e,o,i)=>o in e?_(e,o,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[o]=i,w=(e,o)=>{for(var i in o||(o={}))C.call(o,i)&&x(e,i,o[i]);if(y)for(var i of y(o))P.call(o,i)&&x(e,i,o[i]);return e};import{useEffect as I,useReducer as N,useState as D}from"react";var E=new Map,h=new Set;function l(){return[...E.values()]}function R(e){return h.add(e),()=>{h.delete(e)}}function v(){let e=window;return e.__sentient_overrides||(e.__sentient_overrides={}),e.__sentient_overrides}function f(e,o){v()[e]=o}function k(e){delete v()[e]}function m(){return w({},v())}var b=!1,B=new Set;function c(e){if(b!==e){b=e;for(let o of B)o()}}function S(){return b}import{jsx as d,jsxs as s}from"react/jsx-runtime";var O,V=typeof process!="undefined"&&((O=process.env)==null?void 0:O.NODE_ENV)==="production",M=["browsers","buyers","researchers","deal-seekers"];async function A(e,o){let i=l().map(n=>({id:n.id})),a=l().map(n=>({id:n.id,variantIds:n.variantIds})),r=await fetch("/v1/explain",{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({persona:o,sections:i,components:a})});if(!r.ok)return;let g=await r.json();for(let[n,u]of Object.entries(g.assignments))f(n,u);c(!0)}function X({apiKey:e}={}){let[o,i]=D(!1),[,a]=N(t=>t+1,0);if(I(()=>R(a),[]),V)return null;let r=l(),g=m();function n(t,p){f(t,p),c(!0),a()}function u(t){k(t),Object.keys(m()).length===0&&c(!1),a()}return s("div",{style:{position:"fixed",bottom:16,right:16,zIndex:2147483647,fontFamily:"system-ui"},children:[o&&s("div",{style:{width:300,maxHeight:420,overflowY:"auto",background:"#111",color:"#eee",borderRadius:8,padding:12,marginBottom:8,boxShadow:"0 8px 30px rgba(0,0,0,.4)",fontSize:12},children:[s("div",{style:{opacity:.7,marginBottom:8},children:[r.length," component",r.length===1?"":"s"," \xB7 ",S()?"preview \u2014 writing nothing":"live"]}),e&&s("div",{style:{borderBottom:"1px solid #333",paddingBottom:8,marginBottom:8},children:[d("div",{style:{opacity:.7,marginBottom:4},children:"Preview persona"}),d("div",{style:{display:"flex",flexWrap:"wrap",gap:4},children:M.map(t=>d("button",{onClick:()=>{A(e,t).then(a)},style:{padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:"#222",color:"#eee",cursor:"pointer"},children:t},t))})]}),r.length===0&&d("div",{style:{opacity:.6},children:"No components on this page yet."}),r.map(t=>s("div",{style:{borderTop:"1px solid #333",padding:"8px 0"},children:[s("div",{style:{fontWeight:600},children:[t.id,t.goal?` \xB7 goal: ${t.goal}`:""]}),s("div",{style:{display:"flex",flexWrap:"wrap",gap:4,marginTop:4},children:[t.variantIds.map(p=>d("button",{onClick:()=>n(t.id,p),style:{padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:g[t.id]===p?"#3b82f6":"#222",color:"#eee",cursor:"pointer"},children:p},p)),g[t.id]&&d("button",{onClick:()=>u(t.id),style:{padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:"#222",color:"#aaa",cursor:"pointer"},children:"reset"})]})]},t.id))]}),d("button",{"aria-label":"Sentient DevTools",onClick:()=>i(t=>!t),style:{width:40,height:40,borderRadius:20,border:"none",background:"#3b82f6",color:"#fff",cursor:"pointer",boxShadow:"0 4px 14px rgba(0,0,0,.3)"},children:"\u25E7"})]})}export{X as AdaptiveDevtools};
3
+ //# sourceMappingURL=devtools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/preview-mode.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState } from 'react';\nimport { getRegistered, subscribeRegistry } from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setPreviewMode, getPreviewMode } from '../preview-mode.js';\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\nconst IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\n\nconst SEED_PERSONAS = ['browsers', 'buyers', 'researchers', 'deal-seekers'];\n\n/** Simulate a persona via /v1/explain (read-only) and apply the result as overrides. */\nasync function forcePersona(apiKey: string, persona: string): Promise<void> {\n const sections = getRegistered().map((c) => ({ id: c.id }));\n const components = getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds }));\n const res = await fetch('/v1/explain', {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({ persona, sections, components }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as { assignments: Record<string, string> };\n for (const [id, variantId] of Object.entries(data.assignments)) setVariantOverride(id, variantId);\n setPreviewMode(true);\n}\n\nexport function AdaptiveDevtools({ apiKey }: { apiKey?: string } = {}): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n useEffect(() => subscribeRegistry(force), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of this compile-time constant.)\n if (IS_PROD) return null;\n\n const components = getRegistered();\n const overrides = getOverrides();\n\n function choose(id: string, variantId: string): void {\n setVariantOverride(id, variantId);\n setPreviewMode(true); // suppress events while previewing\n force();\n }\n function reset(id: string): void {\n clearVariantOverride(id);\n if (Object.keys(getOverrides()).length === 0) setPreviewMode(false);\n force();\n }\n\n return (\n <div style={{ position: 'fixed', bottom: 16, right: 16, zIndex: 2147483647, fontFamily: 'system-ui' }}>\n {open && (\n <div style={{ width: 300, maxHeight: 420, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, marginBottom: 8, boxShadow: '0 8px 30px rgba(0,0,0,.4)', fontSize: 12 }}>\n <div style={{ opacity: .7, marginBottom: 8 }}>\n {components.length} component{components.length === 1 ? '' : 's'} · {getPreviewMode() ? 'preview — writing nothing' : 'live'}\n </div>\n {apiKey && (\n <div style={{ borderBottom: '1px solid #333', paddingBottom: 8, marginBottom: 8 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Preview persona</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {SEED_PERSONAS.map((p) => (\n <button key={p} onClick={() => void forcePersona(apiKey, p).then(force)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#eee', cursor: 'pointer' }}>\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n {components.length === 0 && <div style={{ opacity: .6 }}>No components on this page yet.</div>}\n {components.map((c) => (\n <div key={c.id} style={{ borderTop: '1px solid #333', padding: '8px 0' }}>\n <div style={{ fontWeight: 600 }}>{c.id}{c.goal ? ` · goal: ${c.goal}` : ''}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {c.variantIds.map((v) => (\n <button key={v} onClick={() => choose(c.id, v)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444',\n background: overrides[c.id] === v ? '#3b82f6' : '#222', color: '#eee', cursor: 'pointer' }}>\n {v}\n </button>\n ))}\n {overrides[c.id] && (\n <button onClick={() => reset(c.id)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#aaa', cursor: 'pointer' }}>\n reset\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n <button aria-label=\"Sentient DevTools\" onClick={() => setOpen((o) => !o)}\n style={{ width: 40, height: 40, borderRadius: 20, border: 'none', background: '#3b82f6', color: '#fff',\n cursor: 'pointer', boxShadow: '0 4px 14px rgba(0,0,0,.3)' }}>◧</button>\n </div>\n );\n}\n","export type RegisteredComponent = { id: string; variantIds: string[]; goal?: string };\n\nconst registry = new Map<string, RegisteredComponent>();\nconst listeners = new Set<() => void>();\n\nfunction emit(): void {\n for (const fn of listeners) fn();\n}\n\n/** Register (or re-register) a component. Returns an unregister function. */\nexport function registerComponent(c: RegisteredComponent): () => void {\n registry.set(c.id, c);\n emit();\n return () => {\n registry.delete(c.id);\n emit();\n };\n}\n\nexport function getRegistered(): RegisteredComponent[] {\n return [...registry.values()];\n}\n\nexport function subscribeRegistry(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n","function store(): Record<string, string> {\n const w = window as unknown as { __sentient_overrides?: Record<string, string> };\n if (!w.__sentient_overrides) w.__sentient_overrides = {};\n return w.__sentient_overrides;\n}\n\n/** Force `componentId` to render `variantId` — read by `useAssignment`. */\nexport function setVariantOverride(componentId: string, variantId: string): void {\n store()[componentId] = variantId;\n}\n\nexport function clearVariantOverride(componentId: string): void {\n delete store()[componentId];\n}\n\nexport function getOverrides(): Record<string, string> {\n return { ...store() };\n}\n","import type { SentientClient } from '@sentientui/core';\n\nlet previewOn = false;\nconst listeners = new Set<() => void>();\n\nexport function setPreviewMode(on: boolean): void {\n if (previewOn === on) return;\n previewOn = on;\n for (const fn of listeners) fn();\n}\n\nexport function getPreviewMode(): boolean {\n return previewOn;\n}\n\nexport function subscribePreview(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n\n/**\n * Wraps a client so it writes nothing: reads pass through, all emitters no-op.\n * Used while previewing variants/personas so no `variant_assigned`, goal, or\n * session events are sent.\n */\nexport function createPreviewClient(inner: SentientClient): SentientClient {\n return {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n fetchWeights: () => Promise.resolve([]),\n getAssignment: (componentId, segment) => inner.getAssignment(componentId, segment),\n assign: (componentId, variantIds, agentData, agentDataByVariant) =>\n inner.assign(componentId, variantIds, agentData, agentDataByVariant),\n getGraph: () => inner.getGraph(),\n destroy: () => inner.destroy(),\n };\n}\n"],"mappings":"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","names":["useEffect","useReducer","useState","registry","listeners","getRegistered","registry","subscribeRegistry","fn","listeners","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","previewOn","listeners","setPreviewMode","on","fn","getPreviewMode","jsx","jsxs","_a","IS_PROD","SEED_PERSONAS","forcePersona","apiKey","persona","sections","getRegistered","c","components","res","data","id","variantId","setVariantOverride","setPreviewMode","AdaptiveDevtools","open","setOpen","useState","force","useReducer","n","useEffect","subscribeRegistry","overrides","getOverrides","choose","reset","clearVariantOverride","getPreviewMode","p","v","o"]}
package/dist/index.d.cts CHANGED
@@ -41,6 +41,13 @@ type AdaptiveProviderProps = {
41
41
  * @see SentientConfig.preConsentBehavior
42
42
  */
43
43
  preConsentBehavior?: 'statistical_winner' | 'control';
44
+ /**
45
+ * Honor the browser's Do Not Track signal. Defaults to `true`: when DNT is
46
+ * enabled the SDK sets no cookies and sends no tracking data (overriding
47
+ * `consent: true`). Set `false` to make your own consent gate authoritative.
48
+ * @see SentientConfig.respectDoNotTrack
49
+ */
50
+ respectDoNotTrack?: boolean;
44
51
  /**
45
52
  * Called once per component the first time a variant is resolved for that
46
53
  * component in this session. Use to forward assignments to your own analytics
@@ -67,6 +74,14 @@ type AdaptiveProviderProps = {
67
74
  * sessions without client-side geo lookup.
68
75
  */
69
76
  country?: string;
77
+ /**
78
+ * Enable DOM graph scanning + page-structure sync. When `true`, the provider
79
+ * dynamically loads `@sentientui/core/graph` and uses its graph-capable
80
+ * `init()` for the single client, so the SDK scans your page structure and
81
+ * syncs it to power the dashboard graph page. Left off (default), the lean
82
+ * bundle is used and the graph entry is never loaded.
83
+ */
84
+ enableGraph?: boolean;
70
85
  children: ReactNode;
71
86
  };
72
87
  /**
@@ -223,4 +238,63 @@ declare function update(componentId: string, weights: ComponentWeights): void;
223
238
  */
224
239
  declare function getWeights(componentId: string): ComponentWeights | null;
225
240
 
226
- export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FireGoal, type FormSubmitGoal, type GoalConfig, type MicroSignalGoalConfig, type MicroSignalGoals, type ScrollDepthGoal, type SsrFallback, type VariantWeight, type WeightedCompositeGoal, type WeightedStep, getWeights, subscribe as subscribeWeights, update as updateWeights, useAdaptiveGoal, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
241
+ /**
242
+ * Agent-readable content feed. Merges SDK-known data (winning variants, layout
243
+ * order) with developer-supplied page content, and renders it either as a
244
+ * server-rendered inline JSON-LD block (read by passive AI crawlers, which do
245
+ * not run JS) or as Markdown (for agents that content-negotiate `text/markdown`).
246
+ *
247
+ * No React or DOM APIs — safe to import in server components, route handlers,
248
+ * and middleware.
249
+ */
250
+ type AgentBlock = {
251
+ /** Component ID. */
252
+ id: string;
253
+ /** Winning variant ID currently served. */
254
+ variant: string;
255
+ /** Agent-readable data attached to the served variant, if any. */
256
+ content?: unknown;
257
+ };
258
+ type AgentFeed = {
259
+ page: string;
260
+ title?: string;
261
+ summary?: string;
262
+ layoutOrder?: string[];
263
+ blocks: AgentBlock[];
264
+ /** Developer-supplied extra fields (products, specs, arbitrary JSON). */
265
+ [key: string]: unknown;
266
+ };
267
+ /**
268
+ * Register page-level structured content the SDK can't infer (title, summary,
269
+ * product fields, arbitrary JSON), keyed by page path. Call at module load.
270
+ */
271
+ declare function defineAgentContent(page: string, content: Record<string, unknown>): void;
272
+ /** Look up registered content for a page. */
273
+ declare function getAgentContent(page: string): Record<string, unknown> | undefined;
274
+ /**
275
+ * Merge SDK-known data with developer-supplied content into a single feed.
276
+ * Developer content fills in title/summary/etc. but can never overwrite the
277
+ * SDK-authoritative fields (`page`, `blocks`, `layoutOrder`).
278
+ */
279
+ declare function buildAgentFeed(input: {
280
+ page: string;
281
+ blocks: AgentBlock[];
282
+ layoutOrder?: string[];
283
+ content?: Record<string, unknown>;
284
+ }): AgentFeed;
285
+ /**
286
+ * Render the feed as a server-rendered inline JSON-LD `<script>` string. `<` is
287
+ * escaped to `<` so embedded content cannot break out of the script
288
+ * element. MUST be emitted on the server — passive crawlers never run client JS.
289
+ */
290
+ declare function renderAgentJsonLd(feed: AgentFeed): string;
291
+ /**
292
+ * The escaped JSON-LD body only (no `<script>` wrapper). For React server
293
+ * components, inject via `<script type="application/ld+json"
294
+ * dangerouslySetInnerHTML={{ __html: renderAgentJsonLdBody(feed) }} />`.
295
+ */
296
+ declare function renderAgentJsonLdBody(feed: AgentFeed): string;
297
+ /** Render the feed as Markdown for agents that negotiate `text/markdown`. */
298
+ declare function renderAgentMarkdown(feed: AgentFeed): string;
299
+
300
+ export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AgentBlock, type AgentFeed, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FireGoal, type FormSubmitGoal, type GoalConfig, type MicroSignalGoalConfig, type MicroSignalGoals, type ScrollDepthGoal, type SsrFallback, type VariantWeight, type WeightedCompositeGoal, type WeightedStep, buildAgentFeed, defineAgentContent, getAgentContent, getWeights, renderAgentJsonLd, renderAgentJsonLdBody, renderAgentMarkdown, subscribe as subscribeWeights, update as updateWeights, useAdaptiveGoal, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
package/dist/index.d.ts CHANGED
@@ -41,6 +41,13 @@ type AdaptiveProviderProps = {
41
41
  * @see SentientConfig.preConsentBehavior
42
42
  */
43
43
  preConsentBehavior?: 'statistical_winner' | 'control';
44
+ /**
45
+ * Honor the browser's Do Not Track signal. Defaults to `true`: when DNT is
46
+ * enabled the SDK sets no cookies and sends no tracking data (overriding
47
+ * `consent: true`). Set `false` to make your own consent gate authoritative.
48
+ * @see SentientConfig.respectDoNotTrack
49
+ */
50
+ respectDoNotTrack?: boolean;
44
51
  /**
45
52
  * Called once per component the first time a variant is resolved for that
46
53
  * component in this session. Use to forward assignments to your own analytics
@@ -67,6 +74,14 @@ type AdaptiveProviderProps = {
67
74
  * sessions without client-side geo lookup.
68
75
  */
69
76
  country?: string;
77
+ /**
78
+ * Enable DOM graph scanning + page-structure sync. When `true`, the provider
79
+ * dynamically loads `@sentientui/core/graph` and uses its graph-capable
80
+ * `init()` for the single client, so the SDK scans your page structure and
81
+ * syncs it to power the dashboard graph page. Left off (default), the lean
82
+ * bundle is used and the graph entry is never loaded.
83
+ */
84
+ enableGraph?: boolean;
70
85
  children: ReactNode;
71
86
  };
72
87
  /**
@@ -223,4 +238,63 @@ declare function update(componentId: string, weights: ComponentWeights): void;
223
238
  */
224
239
  declare function getWeights(componentId: string): ComponentWeights | null;
225
240
 
226
- export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FireGoal, type FormSubmitGoal, type GoalConfig, type MicroSignalGoalConfig, type MicroSignalGoals, type ScrollDepthGoal, type SsrFallback, type VariantWeight, type WeightedCompositeGoal, type WeightedStep, getWeights, subscribe as subscribeWeights, update as updateWeights, useAdaptiveGoal, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
241
+ /**
242
+ * Agent-readable content feed. Merges SDK-known data (winning variants, layout
243
+ * order) with developer-supplied page content, and renders it either as a
244
+ * server-rendered inline JSON-LD block (read by passive AI crawlers, which do
245
+ * not run JS) or as Markdown (for agents that content-negotiate `text/markdown`).
246
+ *
247
+ * No React or DOM APIs — safe to import in server components, route handlers,
248
+ * and middleware.
249
+ */
250
+ type AgentBlock = {
251
+ /** Component ID. */
252
+ id: string;
253
+ /** Winning variant ID currently served. */
254
+ variant: string;
255
+ /** Agent-readable data attached to the served variant, if any. */
256
+ content?: unknown;
257
+ };
258
+ type AgentFeed = {
259
+ page: string;
260
+ title?: string;
261
+ summary?: string;
262
+ layoutOrder?: string[];
263
+ blocks: AgentBlock[];
264
+ /** Developer-supplied extra fields (products, specs, arbitrary JSON). */
265
+ [key: string]: unknown;
266
+ };
267
+ /**
268
+ * Register page-level structured content the SDK can't infer (title, summary,
269
+ * product fields, arbitrary JSON), keyed by page path. Call at module load.
270
+ */
271
+ declare function defineAgentContent(page: string, content: Record<string, unknown>): void;
272
+ /** Look up registered content for a page. */
273
+ declare function getAgentContent(page: string): Record<string, unknown> | undefined;
274
+ /**
275
+ * Merge SDK-known data with developer-supplied content into a single feed.
276
+ * Developer content fills in title/summary/etc. but can never overwrite the
277
+ * SDK-authoritative fields (`page`, `blocks`, `layoutOrder`).
278
+ */
279
+ declare function buildAgentFeed(input: {
280
+ page: string;
281
+ blocks: AgentBlock[];
282
+ layoutOrder?: string[];
283
+ content?: Record<string, unknown>;
284
+ }): AgentFeed;
285
+ /**
286
+ * Render the feed as a server-rendered inline JSON-LD `<script>` string. `<` is
287
+ * escaped to `<` so embedded content cannot break out of the script
288
+ * element. MUST be emitted on the server — passive crawlers never run client JS.
289
+ */
290
+ declare function renderAgentJsonLd(feed: AgentFeed): string;
291
+ /**
292
+ * The escaped JSON-LD body only (no `<script>` wrapper). For React server
293
+ * components, inject via `<script type="application/ld+json"
294
+ * dangerouslySetInnerHTML={{ __html: renderAgentJsonLdBody(feed) }} />`.
295
+ */
296
+ declare function renderAgentJsonLdBody(feed: AgentFeed): string;
297
+ /** Render the feed as Markdown for agents that negotiate `text/markdown`. */
298
+ declare function renderAgentMarkdown(feed: AgentFeed): string;
299
+
300
+ export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AgentBlock, type AgentFeed, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FireGoal, type FormSubmitGoal, type GoalConfig, type MicroSignalGoalConfig, type MicroSignalGoals, type ScrollDepthGoal, type SsrFallback, type VariantWeight, type WeightedCompositeGoal, type WeightedStep, buildAgentFeed, defineAgentContent, getAgentContent, getWeights, renderAgentJsonLd, renderAgentJsonLdBody, renderAgentMarkdown, subscribe as subscribeWeights, update as updateWeights, useAdaptiveGoal, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  'use client';
2
- "use strict";var V=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var Se=Object.getOwnPropertyNames,ee=Object.getOwnPropertySymbols;var ne=Object.prototype.hasOwnProperty,Ae=Object.prototype.propertyIsEnumerable;var te=(e,t,n)=>t in e?V(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Y=(e,t)=>{for(var n in t||(t={}))ne.call(t,n)&&te(e,n,t[n]);if(ee)for(var n of ee(t))Ae.call(t,n)&&te(e,n,t[n]);return e};var be=(e,t)=>{for(var n in t)V(e,n,{get:t[n],enumerable:!0})},we=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Se(t))!ne.call(e,i)&&i!==n&&V(e,i,{get:()=>t[i],enumerable:!(o=he(t,i))||o.enumerable});return e};var xe=e=>we(V({},"__esModule",{value:!0}),e);var _e={};be(_e,{Adaptive:()=>de,AdaptiveProvider:()=>re,AdaptiveText:()=>fe,detectSegment:()=>Z.deriveSessionSegment,getWeights:()=>B,subscribeWeights:()=>$,updateWeights:()=>H,useAdaptiveGoal:()=>ve,useAssignment:()=>q,useInitialAssignments:()=>X,useLayoutOrder:()=>se,useSentient:()=>G});module.exports=xe(_e);var p=require("react"),D=require("@sentientui/core");var ie=new Map,j=new Map;function $(e,t){let n=j.get(e);return n||(n=new Set,j.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&j.delete(e)}}function H(e,t){ie.set(e,t);let n=j.get(e);if(n)for(let o of n)try{o(t)}catch(i){}}function B(e){var t;return(t=ie.get(e))!=null?t:null}var ae=require("react/jsx-runtime");function Ce(){var e,t;if(typeof window=="undefined")return"desktop:direct";try{let n=(0,D.detectDeviceClass)((e=navigator.userAgent)!=null?e:""),o=(0,D.detectTrafficSource)((t=document.referrer)!=null?t:"",window.location.origin);return`${n}:${o}`}catch(n){return"desktop:direct"}}var M=(0,p.createContext)({client:null,apiKey:"",initialAssignments:{},sessionSegment:"desktop:direct",ssrFallback:"first",onAssignment:void 0,initialLayoutOrder:null});function re(e){var c;let[t,n]=(0,p.useState)(null),[o]=(0,p.useState)(()=>{var a;return(a=e.sessionSegment)!=null?a:Ce()});(0,p.useEffect)(()=>{if(e.consent===!1&&!e.preConsentBehavior){n(v=>(v==null||v.destroy(),null));return}let a=(0,D.init)({apiKey:e.apiKey,context:e.context,debug:e.debug,initialAssignments:e.initialAssignments,sessionSegment:o,consent:e.consent,preConsentBehavior:e.preConsentBehavior,ssrSessionId:e.ssrSessionId,country:e.country});return n(a),()=>{a.destroy()}},[e.consent]),(0,p.useEffect)(()=>{if(!t)return;let a=!1,v=async()=>{if(a)return;let I;try{I=await t.fetchWeights()}catch(s){return}if(!a)for(let s of I){let _={componentId:s.componentId,updatedAt:s.updatedAt,variants:s.variants.map(u=>{var y;return{variantId:u.variantId,pulls:u.pulls,avgReward:(y=u.avgReward)!=null?y:0}})};H(s.componentId,_)}};v();let C=setInterval(()=>{v()},6e4);return()=>{a=!0,clearInterval(C)}},[t]);let i=(c=e.ssrFallback)!=null?c:"first",w=(0,p.useMemo)(()=>{var a,v;return{client:t,apiKey:e.apiKey,initialAssignments:(a=e.initialAssignments)!=null?a:{},sessionSegment:o,ssrFallback:i,onAssignment:e.onAssignment,initialLayoutOrder:(v=e.initialLayoutOrder)!=null?v:null}},[t,e.apiKey,e.initialAssignments,o,i,e.onAssignment,e.initialLayoutOrder]);return(0,ae.jsx)(M.Provider,{value:w,children:e.children})}function G(){return(0,p.useContext)(M).client}function J(){return(0,p.useContext)(M).apiKey}function X(){return(0,p.useContext)(M).initialAssignments}function z(){return(0,p.useContext)(M).sessionSegment}function oe(){return(0,p.useContext)(M).ssrFallback}function U(){return(0,p.useContext)(M).onAssignment}function se(){return(0,p.useContext)(M).initialLayoutOrder}var m=require("react"),ue=require("@sentientui/core");var R=require("react");function Ie(e){var n;if(typeof window=="undefined")return null;let t=(n=window.__sentient_overrides)==null?void 0:n[e];if(t)return t;try{let o=new URLSearchParams(window.location.search);for(let i of o.getAll("sentient_variant")){let w=i.indexOf(":");if(w!==-1&&i.slice(0,w)===e)return i.slice(w+1)}}catch(o){}return null}function le(e,t){var o;let n=null;for(let i of e.variants)t.includes(i.variantId)&&(!n||i.avgReward>n.avgReward)&&(n={variantId:i.variantId,avgReward:i.avgReward});return(o=n==null?void 0:n.variantId)!=null?o:null}function q(e,t,n,o){let i=X(),w=oe(),c=G(),a=z(),v=U(),C=(0,R.useRef)(null),I=Ie(e),s=I&&t.includes(I)?I:null,_=(0,R.useRef)(null);s&&_.current!==s&&(_.current=s,console.info(`[sentient] override active: ${e} -> ${s}`));let u=(()=>{var r,l;if(s)return{variantId:s,content:null,isLoading:!1};if(!c){let g=i[e];return g&&t.includes(g)?{variantId:g,content:null,isLoading:!1}:w==="first"&&t.length>0?{variantId:t[0],content:null,isLoading:!1}:{variantId:null,content:null,isLoading:!0}}let d=c.getAssignment(e,a);if(d&&(t.includes(d.variantId)||d.content))return{variantId:d.variantId,content:(r=d.content)!=null?r:null,isLoading:!1};let h=B(e);if(h){let g=le(h,t);if(g)return{variantId:g,content:null,isLoading:!1}}return{variantId:(l=t[0])!=null?l:null,content:null,isLoading:!1}})(),[y,S]=(0,R.useState)(u),O=d=>{v&&C.current!==d&&(C.current=d,v(e,d))};return(0,R.useEffect)(()=>{var h;if(s||!c)return;let d=c.getAssignment(e,a);if(d&&(t.includes(d.variantId)||d.content)){S({variantId:d.variantId,content:(h=d.content)!=null?h:null,isLoading:!1}),O(d.variantId);return}S(r=>{var l;return r.variantId?r:{variantId:(l=t[0])!=null?l:null,content:null,isLoading:!1}})},[s,c,e,a]),(0,R.useEffect)(()=>{if(s||!c)return;let d=c.getAssignment(e,a);if(d&&t.includes(d.variantId))return;let h=!1;return c.assign(e,t,n,o).then(r=>{var l;h||r&&(!t.includes(r.variantId)&&!r.content||(S({variantId:r.variantId,content:(l=r.content)!=null?l:null,isLoading:!1}),O(r.variantId)))}),()=>{h=!0}},[s,c,e,a]),(0,R.useEffect)(()=>{if(!s&&c)return $(e,d=>{var l;let h=c.getAssignment(e,a);if(h&&(t.includes(h.variantId)||h.content)){S({variantId:h.variantId,content:(l=h.content)!=null?l:null,isLoading:!1});return}let r=le(d,t);r&&S({variantId:r,content:null,isLoading:!1})})},[s,c,e,a]),s?{variantId:s,content:null,isLoading:!1}:y}var ge=require("react/jsx-runtime");function ke(e){return typeof e=="string"?{type:"click"}:e}var Ee=3e4;function Le(e,t){try{let n=`_snt_pv_${e}_${t}`;return sessionStorage.getItem(n)?!1:(sessionStorage.setItem(n,"1"),!0)}catch(n){return!0}}function Ge(e){if(!(e instanceof Element))return!1;let t=e.tagName.toLowerCase();return t==="a"||t==="button"?!0:e.getAttribute("role")==="button"}function ce(e,t,n){if(n){try{let i=e;for(;i&&i!==t;){if(i.matches(n))return!0;i=i.parentElement}}catch(i){}return!1}let o=e;for(;o&&o!==t;){if(Ge(o))return!0;o=o.parentElement}return!1}function Re(e){var d,h;let t=G(),n=J(),o=(0,m.useMemo)(()=>Object.keys(e.variants),[e.variants]),{variantId:i,content:w}=q(e.id,o,e.agentData,e.agentDataByVariant),c=(0,m.useRef)(null),[a,v]=(0,m.useState)(!1);(0,m.useEffect)(()=>{v(!0)},[]);let C=(0,m.useRef)(!1),I=(0,m.useRef)(new Set),s=(0,m.useRef)(null),_=typeof e.goal=="string"?e.goal:JSON.stringify(e.goal),u=(0,m.useMemo)(()=>ke(e.goal),[_]),y=typeof e.goal=="string"?e.goal:u.type;if((0,m.useEffect)(()=>{var g,E;if(!t||!i||!n||s.current===i)return;let r=(E=(g=c.current)==null?void 0:g.innerHTML)!=null?E:"";if(!r)return;s.current=i;let l=r!==""&&Le(e.id,i);t.track({projectId:n,componentId:e.id,variantId:i,eventType:"variant_assigned",payload:l?{previewHtml:r.slice(0,Ee)}:{}})},[t,i,n,e.id,a,w]),(0,m.useEffect)(()=>{C.current=!1,I.current=new Set},[i,u]),(0,m.useEffect)(()=>{if(!t||!i)return;let r=c.current;if(!r)return;let l=null,g=0,E=()=>{g=Date.now(),l=setTimeout(()=>{t.track({projectId:n,componentId:e.id,variantId:i,eventType:"cursor_signal",payload:{hoverDuration:Date.now()-g}}),l=null},800)},A=()=>{l!==null&&(clearTimeout(l),l=null)};return r.addEventListener("mouseenter",E),r.addEventListener("mouseleave",A),()=>{r.removeEventListener("mouseenter",E),r.removeEventListener("mouseleave",A),l!==null&&clearTimeout(l)}},[t,i,n,e.id]),(0,m.useEffect)(()=>{if(!t||!i)return;let r=c.current;if(!r)return;let l=Date.now();return(0,ue.attachMicroSignalDetectors)((g,E={})=>{var f,x,L;t.track({projectId:n,componentId:e.id,variantId:i,eventType:"micro_signal",payload:Y({signalType:g},E)});let A=(f=e.microSignalGoals)==null?void 0:f[g];if(!A||I.current.has(g))return;I.current.add(g);let P=typeof A=="string"?A:A.name,b=typeof A=="string"?1:(x=A.weight)!=null?x:1,k=typeof A=="string"?0:(L=A.stepIndex)!=null?L:0;t.goal(P,Y({signalType:g},E),b,k)},r,l)},[t,i,n,e.id,e.microSignalGoals]),(0,m.useEffect)(()=>{if(!t||!i)return;let r=c.current;if(!r)return;if(u.type==="weighted_composite"){let b=new Set,k=[];return u.steps.forEach(({goal:f,name:x,weight:L},N)=>{let Q=()=>{b.has(N)||(b.add(N),t.track({projectId:n,componentId:e.id,variantId:i,eventType:"goal_achieved",goalType:x,payload:{reward:L}}),t.goal(x,{},L,N))};if(f.type==="click"){let T=W=>{let K=W.target;K instanceof Element&&ce(K,r,f.selector)&&Q()};r.addEventListener("click",T),k.push(()=>r.removeEventListener("click",T));return}if(f.type==="form_submit"){let T=W=>{W.target instanceof HTMLFormElement&&r.contains(W.target)&&Q()};r.addEventListener("submit",T),k.push(()=>r.removeEventListener("submit",T));return}if(f.type==="scroll_depth"){let T=Math.max(0,Math.min(1,f.threshold)),W=new IntersectionObserver(K=>{for(let ye of K)if(ye.intersectionRatio>=T){Q(),W.disconnect();break}},{threshold:[T]});W.observe(r),k.push(()=>W.disconnect())}}),()=>{for(let f of k)f()}}let l=()=>{C.current||(C.current=!0,t.track({projectId:n,componentId:e.id,variantId:i,eventType:"goal_achieved",goalType:y,payload:{reward:1}}),t.goal(y,{componentId:e.id,variantId:i},1,0))},g=u.type==="composite"?u.all:[u],E=new Set(g.map((b,k)=>k)),A=b=>{E.delete(b),E.size===0&&l()},P=[];return g.forEach((b,k)=>{if(b.type==="click"){let f=x=>{let L=x.target;L instanceof Element&&ce(L,r,b.selector)&&(u.type==="composite"?A(k):l())};r.addEventListener("click",f),P.push(()=>r.removeEventListener("click",f));return}if(b.type==="form_submit"){let f=x=>{x.target instanceof HTMLFormElement&&r.contains(x.target)&&(u.type==="composite"?A(k):l())};r.addEventListener("submit",f),P.push(()=>r.removeEventListener("submit",f));return}if(b.type==="scroll_depth"){let f=Math.max(0,Math.min(1,b.threshold)),x=new IntersectionObserver(L=>{for(let N of L)if(N.intersectionRatio>=f){u.type==="composite"?A(k):l(),x.disconnect();break}},{threshold:[f]});x.observe(r),P.push(()=>x.disconnect());return}}),()=>{for(let b of P)b()}},[t,i,n,e.id,u,y]),e.clientOnly&&(!a||!t)||!i)return null;let S=(d=e.variants[i])!=null?d:null,O=S===null?w:null;return((h=process==null?void 0:process.env)==null?void 0:h.NODE_ENV)!=="production"&&S===null&&O===null&&console.warn(`[sentient] <Adaptive id="${e.id}"> was assigned variant "${i}" but no matching key exists in props.variants. If this is a dashboard-managed text variant, use <AdaptiveText id="${e.id}"> instead.`),(0,ge.jsx)("div",{ref:c,"data-sentient-id":e.id,"data-sentient-variant":i,children:S!=null?S:O})}var de=(0,m.memo)(Re,(e,t)=>{if(e.id!==t.id||JSON.stringify(e.goal)!==JSON.stringify(t.goal)||e.microSignalGoals!==t.microSignalGoals)return!1;if(e.variants===t.variants)return!0;let n=Object.keys(e.variants),o=Object.keys(t.variants);return n.length!==o.length?!1:n.every(i=>i in t.variants)});var F=require("react");var me=require("react/jsx-runtime");function fe({id:e,default:t,component:n="span",className:o}){let i=G(),w=J(),c=U(),a=z(),v=(0,F.useRef)(null),[C,I]=(0,F.useState)(()=>{var u,y;return(y=(u=i==null?void 0:i.getAssignment(e,a))==null?void 0:u.content)!=null?y:null}),[s,_]=(0,F.useState)(()=>{var u,y;return(y=(u=i==null?void 0:i.getAssignment(e,a))==null?void 0:u.variantId)!=null?y:null});return(0,F.useEffect)(()=>{var y;if(!i||((y=i.getAssignment(e,a))==null?void 0:y.content)!==void 0)return;let u=!1;return i.assign(e).then(S=>{var O;if(!u){if(!S){((O=process==null?void 0:process.env)==null?void 0:O.NODE_ENV)!=="production"&&console.warn(`[sentient] <AdaptiveText id="${e}"> assignment failed \u2014 showing default text.`);return}_(S.variantId),S.content&&I(S.content)}}),()=>{u=!0}},[i,e,a]),(0,F.useEffect)(()=>{!i||!s||!w||v.current!==s&&(v.current=s,i.track({projectId:w,componentId:e,variantId:s,eventType:"variant_assigned",payload:{}}),c==null||c(e,s))},[i,s,w,e,c]),(0,me.jsx)(n,{className:o,children:C!=null?C:t})}var pe=require("react");function ve(e){let t=G();return(0,pe.useCallback)((n,o)=>{t==null||t.componentGoal(e,n,o)},[t,e])}var Z=require("@sentientui/core");0&&(module.exports={Adaptive,AdaptiveProvider,AdaptiveText,detectSegment,getWeights,subscribeWeights,updateWeights,useAdaptiveGoal,useAssignment,useInitialAssignments,useLayoutOrder,useSentient});
2
+ "use strict";var Pe=Object.create;var K=Object.defineProperty,Me=Object.defineProperties,Te=Object.getOwnPropertyDescriptor,We=Object.getOwnPropertyDescriptors,De=Object.getOwnPropertyNames,re=Object.getOwnPropertySymbols,Ne=Object.getPrototypeOf,se=Object.prototype.hasOwnProperty,je=Object.prototype.propertyIsEnumerable;var oe=(e,t,n)=>t in e?K(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,M=(e,t)=>{for(var n in t||(t={}))se.call(t,n)&&oe(e,n,t[n]);if(re)for(var n of re(t))je.call(t,n)&&oe(e,n,t[n]);return e},$=(e,t)=>Me(e,We(t));var Ke=(e,t)=>{for(var n in t)K(e,n,{get:t[n],enumerable:!0})},ae=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of De(t))!se.call(e,i)&&i!==n&&K(e,i,{get:()=>t[i],enumerable:!(o=Te(t,i))||o.enumerable});return e};var Ve=(e,t,n)=>(n=e!=null?Pe(Ne(e)):{},ae(t||!e||!e.__esModule?K(n,"default",{value:e,enumerable:!0}):n,e)),$e=e=>ae(K({},"__esModule",{value:!0}),e);var et={};Ke(et,{Adaptive:()=>we,AdaptiveProvider:()=>ge,AdaptiveText:()=>xe,buildAgentFeed:()=>Ge,defineAgentContent:()=>Le,detectSegment:()=>te.deriveSessionSegment,getAgentContent:()=>ne,getWeights:()=>X,renderAgentJsonLd:()=>Oe,renderAgentJsonLdBody:()=>ie,renderAgentMarkdown:()=>_e,subscribeWeights:()=>B,updateWeights:()=>H,useAdaptiveGoal:()=>Ee,useAssignment:()=>Y,useInitialAssignments:()=>U,useLayoutOrder:()=>me,useSentient:()=>G});module.exports=$e(et);var m=require("react"),N=require("@sentientui/core");var le=new Map,J=new Map;function B(e,t){let n=J.get(e);return n||(n=new Set,J.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&J.delete(e)}}function H(e,t){le.set(e,t);let n=J.get(e);if(n)for(let o of n)try{o(t)}catch(i){}}function X(e){var t;return(t=le.get(e))!=null?t:null}var Je=!1,ce=new Set;function ee(){return Je}function ue(e){return ce.add(e),()=>{ce.delete(e)}}function de(e){return{track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},fetchWeights:()=>Promise.resolve([]),getAssignment:(t,n)=>e.getAssignment(t,n),assign:(t,n,o,i)=>e.assign(t,n,o,i),getGraph:()=>e.getGraph(),destroy:()=>e.destroy()}}var pe=require("react/jsx-runtime");function Be(){var e,t;if(typeof window=="undefined")return"desktop:direct";try{let n=(0,N.detectDeviceClass)((e=navigator.userAgent)!=null?e:""),o=(0,N.detectTrafficSource)((t=document.referrer)!=null?t:"",window.location.origin);return`${n}:${o}`}catch(n){return"desktop:direct"}}var T=(0,m.createContext)({client:null,apiKey:"",initialAssignments:{},sessionSegment:"desktop:direct",ssrFallback:"first",onAssignment:void 0,initialLayoutOrder:null});function ge(e){var C;let[t,n]=(0,m.useState)(null),[o]=(0,m.useState)(()=>{var d;return(d=e.sessionSegment)!=null?d:Be()}),[i,A]=(0,m.useState)(ee());(0,m.useEffect)(()=>ue(()=>A(ee())),[]),(0,m.useEffect)(()=>{if(e.consent===!1&&!e.preConsentBehavior){n(a=>(a==null||a.destroy(),null));return}let d={apiKey:e.apiKey,context:e.context,debug:e.debug,initialAssignments:e.initialAssignments,sessionSegment:o,consent:e.consent,preConsentBehavior:e.preConsentBehavior,respectDoNotTrack:e.respectDoNotTrack,ssrSessionId:e.ssrSessionId,country:e.country},s=!1,b=null;return e.enableGraph?import("@sentientui/core/graph").then(({init:a})=>{s||(b=a($(M({},d),{graph:!0})),n(b))}):(b=(0,N.init)(d),n(b)),()=>{s=!0,b==null||b.destroy()}},[e.consent]),(0,m.useEffect)(()=>{if(!t)return;let d=!1,s=async()=>{if(d)return;let a;try{a=await t.fetchWeights()}catch(g){return}if(!d)for(let g of a){let h={componentId:g.componentId,updatedAt:g.updatedAt,variants:g.variants.map(I=>{var u;return{variantId:I.variantId,pulls:I.pulls,avgReward:(u=I.avgReward)!=null?u:0}})};H(g.componentId,h)}};s();let b=setInterval(()=>{s()},6e4);return()=>{d=!0,clearInterval(b)}},[t]);let l=(C=e.ssrFallback)!=null?C:"first",p=(0,m.useMemo)(()=>t&&i?de(t):t,[t,i]),O=(0,m.useMemo)(()=>{var d,s;return{client:p,apiKey:e.apiKey,initialAssignments:(d=e.initialAssignments)!=null?d:{},sessionSegment:o,ssrFallback:l,onAssignment:e.onAssignment,initialLayoutOrder:(s=e.initialLayoutOrder)!=null?s:null}},[p,e.apiKey,e.initialAssignments,o,l,e.onAssignment,e.initialLayoutOrder]);return(0,pe.jsx)(T.Provider,{value:O,children:e.children})}function G(){return(0,m.useContext)(T).client}function z(){return(0,m.useContext)(T).apiKey}function U(){return(0,m.useContext)(T).initialAssignments}function q(){return(0,m.useContext)(T).sessionSegment}function fe(){return(0,m.useContext)(T).ssrFallback}function Q(){return(0,m.useContext)(T).onAssignment}function me(){return(0,m.useContext)(T).initialLayoutOrder}var v=require("react"),be=require("@sentientui/core");var _=require("react");function He(e){var n;if(typeof window=="undefined")return null;let t=(n=window.__sentient_overrides)==null?void 0:n[e];if(t)return t;try{let o=new URLSearchParams(window.location.search);for(let i of o.getAll("sentient_variant")){let A=i.indexOf(":");if(A!==-1&&i.slice(0,A)===e)return i.slice(A+1)}}catch(o){}return null}function ve(e,t){var o;let n=null;for(let i of e.variants)t.includes(i.variantId)&&(!n||i.avgReward>n.avgReward)&&(n={variantId:i.variantId,avgReward:i.avgReward});return(o=n==null?void 0:n.variantId)!=null?o:null}function Y(e,t,n,o){let i=U(),A=fe(),l=G(),p=q(),O=Q(),C=(0,_.useRef)(null),d=He(e),s=d&&t.includes(d)?d:null,b=(0,_.useRef)(null);s&&b.current!==s&&(b.current=s,console.info(`[sentient] override active: ${e} -> ${s}`));let a=(()=>{var r,c;if(s)return{variantId:s,content:null,isLoading:!1};if(!l){let f=i[e];return f&&t.includes(f)?{variantId:f,content:null,isLoading:!1}:A==="first"&&t.length>0?{variantId:t[0],content:null,isLoading:!1}:{variantId:null,content:null,isLoading:!0}}let u=l.getAssignment(e,p);if(u&&(t.includes(u.variantId)||u.content))return{variantId:u.variantId,content:(r=u.content)!=null?r:null,isLoading:!1};let S=X(e);if(S){let f=ve(S,t);if(f)return{variantId:f,content:null,isLoading:!1}}return{variantId:(c=t[0])!=null?c:null,content:null,isLoading:!1}})(),[g,h]=(0,_.useState)(a),I=u=>{O&&C.current!==u&&(C.current=u,O(e,u))};return(0,_.useEffect)(()=>{var S;if(s||!l)return;let u=l.getAssignment(e,p);if(u&&(t.includes(u.variantId)||u.content)){h({variantId:u.variantId,content:(S=u.content)!=null?S:null,isLoading:!1}),I(u.variantId);return}h(r=>{var c;return r.variantId?r:{variantId:(c=t[0])!=null?c:null,content:null,isLoading:!1}})},[s,l,e,p]),(0,_.useEffect)(()=>{if(s||!l)return;let u=l.getAssignment(e,p);if(u&&t.includes(u.variantId))return;let S=!1;return l.assign(e,t,n,o).then(r=>{var c;S||r&&(!t.includes(r.variantId)&&!r.content||(h({variantId:r.variantId,content:(c=r.content)!=null?c:null,isLoading:!1}),I(r.variantId)))}),()=>{S=!0}},[s,l,e,p]),(0,_.useEffect)(()=>{if(!s&&l)return B(e,u=>{var c;let S=l.getAssignment(e,p);if(S&&(t.includes(S.variantId)||S.content)){h({variantId:S.variantId,content:(c=S.content)!=null?c:null,isLoading:!1});return}let r=ve(u,t);r&&h({variantId:r,content:null,isLoading:!1})})},[s,l,e,p]),s?{variantId:s,content:null,isLoading:!1}:g}var ye=new Map,Xe=new Set;function he(){for(let e of Xe)e()}function Ae(e){return ye.set(e.id,e),he(),()=>{ye.delete(e.id),he()}}var ke=require("react/jsx-runtime");function ze(e){return typeof e=="string"?{type:"click"}:e}var Ue=3e4;function qe(e,t){try{let n=`_snt_pv_${e}_${t}`;return sessionStorage.getItem(n)?!1:(sessionStorage.setItem(n,"1"),!0)}catch(n){return!0}}function Qe(e){if(!(e instanceof Element))return!1;let t=e.tagName.toLowerCase();return t==="a"||t==="button"?!0:e.getAttribute("role")==="button"}function Se(e,t,n){if(n){try{let i=e;for(;i&&i!==t;){if(i.matches(n))return!0;i=i.parentElement}}catch(i){}return!1}let o=e;for(;o&&o!==t;){if(Qe(o))return!0;o=o.parentElement}return!1}function Ye(e){var u,S;let t=G(),n=z(),o=(0,v.useMemo)(()=>Object.keys(e.variants),[e.variants]),{variantId:i,content:A}=Y(e.id,o,e.agentData,e.agentDataByVariant),l=(0,v.useRef)(null),[p,O]=(0,v.useState)(!1);(0,v.useEffect)(()=>{O(!0)},[]);let C=(0,v.useRef)(!1),d=(0,v.useRef)(new Set),s=(0,v.useRef)(null),b=typeof e.goal=="string"?e.goal:JSON.stringify(e.goal),a=(0,v.useMemo)(()=>ze(e.goal),[b]),g=typeof e.goal=="string"?e.goal:a.type;if((0,v.useEffect)(()=>Ae({id:e.id,variantIds:o,goal:g}),[e.id,o,g]),(0,v.useEffect)(()=>{var f,R;if(!t||!i||!n||s.current===i)return;let r=(R=(f=l.current)==null?void 0:f.innerHTML)!=null?R:"";if(!r)return;s.current=i;let c=r!==""&&qe(e.id,i);t.track({projectId:n,componentId:e.id,variantId:i,eventType:"variant_assigned",payload:c?{previewHtml:r.slice(0,Ue)}:{}})},[t,i,n,e.id,p,A]),(0,v.useEffect)(()=>{C.current=!1,d.current=new Set},[i,a]),(0,v.useEffect)(()=>{if(!t||!i)return;let r=l.current;if(!r)return;let c=null,f=0,R=()=>{f=Date.now(),c=setTimeout(()=>{t.track({projectId:n,componentId:e.id,variantId:i,eventType:"cursor_signal",payload:{hoverDuration:Date.now()-f}}),c=null},800)},w=()=>{c!==null&&(clearTimeout(c),c=null)};return r.addEventListener("mouseenter",R),r.addEventListener("mouseleave",w),()=>{r.removeEventListener("mouseenter",R),r.removeEventListener("mouseleave",w),c!==null&&clearTimeout(c)}},[t,i,n,e.id]),(0,v.useEffect)(()=>{if(!t||!i)return;let r=l.current;if(!r)return;let c=Date.now();return(0,be.attachMicroSignalDetectors)((f,R={})=>{var y,x,L;t.track({projectId:n,componentId:e.id,variantId:i,eventType:"micro_signal",payload:M({signalType:f},R)});let w=(y=e.microSignalGoals)==null?void 0:y[f];if(!w||d.current.has(f))return;d.current.add(f);let D=typeof w=="string"?w:w.name,k=typeof w=="string"?1:(x=w.weight)!=null?x:1,E=typeof w=="string"?0:(L=w.stepIndex)!=null?L:0;t.goal(D,M({signalType:f},R),k,E)},r,c)},[t,i,n,e.id,e.microSignalGoals]),(0,v.useEffect)(()=>{if(!t||!i)return;let r=l.current;if(!r)return;if(a.type==="weighted_composite"){let k=new Set,E=[];return a.steps.forEach(({goal:y,name:x,weight:L},j)=>{let Z=()=>{k.has(j)||(k.add(j),t.track({projectId:n,componentId:e.id,variantId:i,eventType:"goal_achieved",goalType:x,payload:{reward:L}}),t.goal(x,{},L,j))};if(y.type==="click"){let F=P=>{let V=P.target;V instanceof Element&&Se(V,r,y.selector)&&Z()};r.addEventListener("click",F),E.push(()=>r.removeEventListener("click",F));return}if(y.type==="form_submit"){let F=P=>{P.target instanceof HTMLFormElement&&r.contains(P.target)&&Z()};r.addEventListener("submit",F),E.push(()=>r.removeEventListener("submit",F));return}if(y.type==="scroll_depth"){let F=Math.max(0,Math.min(1,y.threshold)),P=new IntersectionObserver(V=>{for(let Fe of V)if(Fe.intersectionRatio>=F){Z(),P.disconnect();break}},{threshold:[F]});P.observe(r),E.push(()=>P.disconnect())}}),()=>{for(let y of E)y()}}let c=()=>{C.current||(C.current=!0,t.track({projectId:n,componentId:e.id,variantId:i,eventType:"goal_achieved",goalType:g,payload:{reward:1}}),t.goal(g,{componentId:e.id,variantId:i},1,0))},f=a.type==="composite"?a.all:[a],R=new Set(f.map((k,E)=>E)),w=k=>{R.delete(k),R.size===0&&c()},D=[];return f.forEach((k,E)=>{if(k.type==="click"){let y=x=>{let L=x.target;L instanceof Element&&Se(L,r,k.selector)&&(a.type==="composite"?w(E):c())};r.addEventListener("click",y),D.push(()=>r.removeEventListener("click",y));return}if(k.type==="form_submit"){let y=x=>{x.target instanceof HTMLFormElement&&r.contains(x.target)&&(a.type==="composite"?w(E):c())};r.addEventListener("submit",y),D.push(()=>r.removeEventListener("submit",y));return}if(k.type==="scroll_depth"){let y=Math.max(0,Math.min(1,k.threshold)),x=new IntersectionObserver(L=>{for(let j of L)if(j.intersectionRatio>=y){a.type==="composite"?w(E):c(),x.disconnect();break}},{threshold:[y]});x.observe(r),D.push(()=>x.disconnect());return}}),()=>{for(let k of D)k()}},[t,i,n,e.id,a,g]),e.clientOnly&&(!p||!t)||!i)return null;let h=(u=e.variants[i])!=null?u:null,I=h===null?A:null;return((S=process==null?void 0:process.env)==null?void 0:S.NODE_ENV)!=="production"&&h===null&&I===null&&console.warn(`[sentient] <Adaptive id="${e.id}"> was assigned variant "${i}" but no matching key exists in props.variants. If this is a dashboard-managed text variant, use <AdaptiveText id="${e.id}"> instead.`),(0,ke.jsx)("div",{ref:l,"data-sentient-id":e.id,"data-sentient-variant":i,children:h!=null?h:I})}var we=(0,v.memo)(Ye,(e,t)=>{if(e.id!==t.id||JSON.stringify(e.goal)!==JSON.stringify(t.goal)||e.microSignalGoals!==t.microSignalGoals)return!1;if(e.variants===t.variants)return!0;let n=Object.keys(e.variants),o=Object.keys(t.variants);return n.length!==o.length?!1:n.every(i=>i in t.variants)});var W=require("react");var Ce=require("react/jsx-runtime");function xe({id:e,default:t,component:n="span",className:o}){let i=G(),A=z(),l=Q(),p=q(),O=(0,W.useRef)(null),[C,d]=(0,W.useState)(()=>{var a,g;return(g=(a=i==null?void 0:i.getAssignment(e,p))==null?void 0:a.content)!=null?g:null}),[s,b]=(0,W.useState)(()=>{var a,g;return(g=(a=i==null?void 0:i.getAssignment(e,p))==null?void 0:a.variantId)!=null?g:null});return(0,W.useEffect)(()=>{var g;if(!i||((g=i.getAssignment(e,p))==null?void 0:g.content)!==void 0)return;let a=!1;return i.assign(e).then(h=>{var I;if(!a){if(!h){((I=process==null?void 0:process.env)==null?void 0:I.NODE_ENV)!=="production"&&console.warn(`[sentient] <AdaptiveText id="${e}"> assignment failed \u2014 showing default text.`);return}b(h.variantId),h.content&&d(h.content)}}),()=>{a=!0}},[i,e,p]),(0,W.useEffect)(()=>{!i||!s||!A||O.current!==s&&(O.current=s,i.track({projectId:A,componentId:e,variantId:s,eventType:"variant_assigned",payload:{}}),l==null||l(e,s))},[i,s,A,e,l]),(0,Ce.jsx)(n,{className:o,children:C!=null?C:t})}var Ie=require("react");function Ee(e){let t=G();return(0,Ie.useCallback)((n,o)=>{t==null||t.componentGoal(e,n,o)},[t,e])}var te=require("@sentientui/core");var Ze=["page","blocks","layoutOrder"],Re=new Map;function Le(e,t){Re.set(e,t)}function ne(e){return Re.get(e)}function Ge(e){var i,A;let t=(A=(i=e.content)!=null?i:ne(e.page))!=null?A:{},n={};for(let[l,p]of Object.entries(t))Ze.includes(l)||(n[l]=p);let o=$(M({},n),{page:e.page,blocks:e.blocks});return e.layoutOrder&&(o.layoutOrder=e.layoutOrder),o}function Oe(e){return`<script type="application/ld+json">${ie(e)}</script>`}function ie(e){return JSON.stringify(M({"@context":"https://schema.org","@type":"WebPage"},e)).replace(/</g,"\\u003c")}function _e(e){let t=[];e.title&&t.push(`# ${e.title}`,""),e.summary&&t.push(String(e.summary),"");for(let[n,o]of Object.entries(e))["page","title","summary","blocks","layoutOrder"].includes(n)||t.push(`## ${n}`,"","```json",JSON.stringify(o,null,2),"```","");if(e.blocks.length>0){t.push("## blocks","");for(let n of e.blocks)t.push(`- **${n.id}** \u2192 variant \`${n.variant}\``),n.content!==void 0&&t.push(""," ```json",JSON.stringify(n.content,null,2)," ```");t.push("")}return t.join(`
3
+ `).trimEnd()+`
4
+ `}0&&(module.exports={Adaptive,AdaptiveProvider,AdaptiveText,buildAgentFeed,defineAgentContent,detectSegment,getAgentContent,getWeights,renderAgentJsonLd,renderAgentJsonLdBody,renderAgentMarkdown,subscribeWeights,updateWeights,useAdaptiveGoal,useAssignment,useInitialAssignments,useLayoutOrder,useSentient});
3
5
  //# sourceMappingURL=index.js.map