@sentientui/core 0.24.0 → 0.26.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.
Files changed (38) hide show
  1. package/dist/{chunk-RL5B6M4G.mjs → chunk-RCN2SIB6.mjs} +2 -2
  2. package/dist/chunk-RCN2SIB6.mjs.map +1 -0
  3. package/dist/chunk-WKXD7KI3.mjs +2 -0
  4. package/dist/chunk-WKXD7KI3.mjs.map +1 -0
  5. package/dist/chunk-Z6ILOYBD.mjs +2 -0
  6. package/dist/chunk-Z6ILOYBD.mjs.map +1 -0
  7. package/dist/{index-Cd0gMdJv.d.cts → index-Ce3IPLrF.d.cts} +16 -0
  8. package/dist/{index-BjVvN7cU.d.ts → index-DubrRV6l.d.ts} +16 -0
  9. package/dist/index-engagement.d.cts +1 -1
  10. package/dist/index-engagement.d.ts +1 -1
  11. package/dist/index-engagement.js +1 -1
  12. package/dist/index-engagement.js.map +1 -1
  13. package/dist/index-engagement.mjs +1 -1
  14. package/dist/index-engagement.mjs.map +1 -1
  15. package/dist/index-graph.d.cts +2 -2
  16. package/dist/index-graph.d.ts +2 -2
  17. package/dist/index-graph.js +1 -1
  18. package/dist/index-graph.js.map +1 -1
  19. package/dist/index-graph.mjs +1 -1
  20. package/dist/index-local.d.cts +1 -1
  21. package/dist/index-local.d.ts +1 -1
  22. package/dist/index-topics.d.cts +16 -1
  23. package/dist/index-topics.d.ts +16 -1
  24. package/dist/index-topics.js +1 -1
  25. package/dist/index-topics.js.map +1 -1
  26. package/dist/index-topics.mjs +1 -1
  27. package/dist/index-topics.mjs.map +1 -1
  28. package/dist/index.d.cts +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +1 -1
  33. package/package.json +2 -2
  34. package/dist/chunk-EWP6FTHE.mjs +0 -2
  35. package/dist/chunk-EWP6FTHE.mjs.map +0 -1
  36. package/dist/chunk-KIP52DUJ.mjs +0 -2
  37. package/dist/chunk-KIP52DUJ.mjs.map +0 -1
  38. package/dist/chunk-RL5B6M4G.mjs.map +0 -1
@@ -0,0 +1,2 @@
1
+ var E=["data-sentient-id","data-testid","data-test","data-id","data-name","data-cy"];function f(t){var n;return((n=t.textContent)!=null?n:"").replace(/\s+/g," ").trim()}function $(t){return{tag:t.tagName.toLowerCase(),text:f(t).slice(0,40)}}function m(t){return t.replace(/["\\\]]/g,"\\$&")}function u(t,n){try{return t.querySelectorAll(n).length===1}catch(e){return!1}}function p(t,n){var i;let e=t.tagName.toLowerCase();if(!e)return null;let o=((i=t.getAttribute("class"))!=null?i:"").split(/\s+/).filter(r=>/^[a-zA-Z][\w-]*$/.test(r)),c=[e,...o.map(r=>`${e}.${r}`)];for(let r of c)if(u(n,r))return r;let s=t.parentElement;if(s&&s!==n){let r=p(s,n);if(r){for(let a of c){let g=`${r} > ${a}`;if(u(n,g))return g}let l=Array.from(s.children).filter(a=>a.tagName.toLowerCase()===e),d=l.indexOf(t),A=l.some(a=>a!==t&&f(a)!==f(t));if(d>=0&&A){let a=`${r} > ${e}:nth-of-type(${d+1})`;if(u(n,a))return a}}}return null}function N(t,n){let e=$(t),o=t.getAttribute("id");if(o&&u(n,`#${m(o)}`))return{v:1,id:o,fingerprint:e};for(let s of E){let i=t.getAttribute(s);if(i&&u(n,`[${s}="${m(i)}"]`))return{v:1,dataAttr:{name:s,value:i},fingerprint:e}}let c=p(t,n);return c?{v:1,selector:c,fingerprint:e}:null}export{N as a};
2
+ //# sourceMappingURL=chunk-WKXD7KI3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/locator-from-dom.ts"],"sourcesContent":["import type { CompoundLocator } from './snapshot.js';\n\n// Live-DOM twin of apps/api/src/domain/locator-from-html.ts. The two MUST stay\n// in step: section_key is a hash of this object, so any divergence silently\n// splits one physical section into two identities — a crawl-derived one and a\n// client-derived one — and every per-section number downstream halves.\n// Cross-implementation parity is locked by apps/api/src/domain/locator-parity.test.ts.\n// data-sentient-id FIRST (added 2026-09-05, both generators together): it is\n// the one attribute a site authors specifically to name a section for us, and\n// it is exactly what hydration and redesigns do NOT rewrite. Elements with a\n// unique `id` are unaffected (id outranks data attrs); for the rest the change\n// moves section_key once — ingest-time fingerprint reconciliation\n// (section-key-reconcile.ts) writes the alias, and no offline backfill is\n// possible because pre-change locators never captured the attribute.\nconst STABLE_DATA_ATTRS = ['data-sentient-id', 'data-testid', 'data-test', 'data-id', 'data-name', 'data-cy'];\nconst FINGERPRINT_TEXT_MAX = 40;\n\nfunction normalizedText(el: Element): string {\n return (el.textContent ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction fingerprintOf(el: Element): { tag: string; text: string } {\n return {\n tag: el.tagName.toLowerCase(),\n text: normalizedText(el).slice(0, FINGERPRINT_TEXT_MAX),\n };\n}\n\nfunction cssEscape(v: string): string {\n return v.replace(/[\"\\\\\\]]/g, '\\\\$&');\n}\n\nfunction unique(root: ParentNode, selector: string): boolean {\n try {\n return root.querySelectorAll(selector).length === 1;\n } catch {\n return false;\n }\n}\n\n/** Shortest unique selector: tag, tag.class, then parent-qualified, then nth-of-type chain. */\nfunction uniqueSelector(el: Element, root: ParentNode): string | null {\n const tag = el.tagName.toLowerCase();\n if (!tag) return null;\n const classes = (el.getAttribute('class') ?? '').split(/\\s+/).filter((c) => /^[a-zA-Z][\\w-]*$/.test(c));\n const candidates = [tag, ...classes.map((c) => `${tag}.${c}`)];\n for (const c of candidates) if (unique(root, c)) return c;\n const parent = el.parentElement;\n if (parent && (parent as ParentNode) !== root) {\n const parentSel = uniqueSelector(parent, root);\n if (parentSel) {\n for (const c of candidates) {\n const combined = `${parentSel} > ${c}`;\n if (unique(root, combined)) return combined;\n }\n // Element-only children: matches the server's rawTagName filter over\n // childNodes, which excludes text nodes.\n const siblings = Array.from(parent.children).filter((n) => n.tagName.toLowerCase() === tag);\n const idx = siblings.indexOf(el);\n // An nth-of-type selector is only trustworthy when it pairs with a\n // fingerprint that could actually catch drift: if every same-tag sibling\n // has identical text, the node is a true duplicate and the {tag, text}\n // check can never distinguish \"still the right one\" from \"DOM reordered,\n // now pointing at the wrong duplicate\". Refuse it rather than return a\n // false sense of precision.\n const distinguishable = siblings.some((s) => s !== el && normalizedText(s) !== normalizedText(el));\n if (idx >= 0 && distinguishable) {\n const nth = `${parentSel} > ${tag}:nth-of-type(${idx + 1})`;\n if (unique(root, nth)) return nth;\n }\n }\n }\n return null;\n}\n\n/** Build a compound locator for a live DOM element. Returns null when nothing\n * resolves uniquely — the runtime never guesses an identity. */\nexport function locatorFromElement(el: Element, root: ParentNode): CompoundLocator | null {\n const fingerprint = fingerprintOf(el);\n const id = el.getAttribute('id');\n if (id && unique(root, `#${cssEscape(id)}`)) return { v: 1, id, fingerprint };\n for (const name of STABLE_DATA_ATTRS) {\n const value = el.getAttribute(name);\n if (value && unique(root, `[${name}=\"${cssEscape(value)}\"]`)) {\n return { v: 1, dataAttr: { name, value }, fingerprint };\n }\n }\n const selector = uniqueSelector(el, root);\n if (selector) return { v: 1, selector, fingerprint };\n return null;\n}\n"],"mappings":"AAcA,IAAMA,EAAoB,CAAC,mBAAoB,cAAe,YAAa,UAAW,YAAa,SAAS,EAG5G,SAASC,EAAeC,EAAqB,CAjB7C,IAAAC,EAkBE,QAAQA,EAAAD,EAAG,cAAH,KAAAC,EAAkB,IAAI,QAAQ,OAAQ,GAAG,EAAE,KAAK,CAC1D,CAEA,SAASC,EAAcF,EAA4C,CACjE,MAAO,CACL,IAAKA,EAAG,QAAQ,YAAY,EAC5B,KAAMD,EAAeC,CAAE,EAAE,MAAM,EAAG,EAAoB,CACxD,CACF,CAEA,SAASG,EAAUC,EAAmB,CACpC,OAAOA,EAAE,QAAQ,WAAY,MAAM,CACrC,CAEA,SAASC,EAAOC,EAAkBC,EAA2B,CAC3D,GAAI,CACF,OAAOD,EAAK,iBAAiBC,CAAQ,EAAE,SAAW,CACpD,OAAQ,GACN,MAAO,EACT,CACF,CAGA,SAASC,EAAeR,EAAaM,EAAiC,CAzCtE,IAAAL,EA0CE,IAAMQ,EAAMT,EAAG,QAAQ,YAAY,EACnC,GAAI,CAACS,EAAK,OAAO,KACjB,IAAMC,IAAWT,EAAAD,EAAG,aAAa,OAAO,IAAvB,KAAAC,EAA4B,IAAI,MAAM,KAAK,EAAE,OAAQU,GAAM,mBAAmB,KAAKA,CAAC,CAAC,EAChGC,EAAa,CAACH,EAAK,GAAGC,EAAQ,IAAKC,GAAM,GAAGF,CAAG,IAAIE,CAAC,EAAE,CAAC,EAC7D,QAAWA,KAAKC,EAAY,GAAIP,EAAOC,EAAMK,CAAC,EAAG,OAAOA,EACxD,IAAME,EAASb,EAAG,cAClB,GAAIa,GAAWA,IAA0BP,EAAM,CAC7C,IAAMQ,EAAYN,EAAeK,EAAQP,CAAI,EAC7C,GAAIQ,EAAW,CACb,QAAWH,KAAKC,EAAY,CAC1B,IAAMG,EAAW,GAAGD,CAAS,MAAMH,CAAC,GACpC,GAAIN,EAAOC,EAAMS,CAAQ,EAAG,OAAOA,CACrC,CAGA,IAAMC,EAAW,MAAM,KAAKH,EAAO,QAAQ,EAAE,OAAQI,GAAMA,EAAE,QAAQ,YAAY,IAAMR,CAAG,EACpFS,EAAMF,EAAS,QAAQhB,CAAE,EAOzBmB,EAAkBH,EAAS,KAAMI,GAAMA,IAAMpB,GAAMD,EAAeqB,CAAC,IAAMrB,EAAeC,CAAE,CAAC,EACjG,GAAIkB,GAAO,GAAKC,EAAiB,CAC/B,IAAME,EAAM,GAAGP,CAAS,MAAML,CAAG,gBAAgBS,EAAM,CAAC,IACxD,GAAIb,EAAOC,EAAMe,CAAG,EAAG,OAAOA,CAChC,CACF,CACF,CACA,OAAO,IACT,CAIO,SAASC,EAAmBtB,EAAaM,EAA0C,CACxF,IAAMiB,EAAcrB,EAAcF,CAAE,EAC9BwB,EAAKxB,EAAG,aAAa,IAAI,EAC/B,GAAIwB,GAAMnB,EAAOC,EAAM,IAAIH,EAAUqB,CAAE,CAAC,EAAE,EAAG,MAAO,CAAE,EAAG,EAAG,GAAAA,EAAI,YAAAD,CAAY,EAC5E,QAAWE,KAAQC,EAAmB,CACpC,IAAMC,EAAQ3B,EAAG,aAAayB,CAAI,EAClC,GAAIE,GAAStB,EAAOC,EAAM,IAAImB,CAAI,KAAKtB,EAAUwB,CAAK,CAAC,IAAI,EACzD,MAAO,CAAE,EAAG,EAAG,SAAU,CAAE,KAAAF,EAAM,MAAAE,CAAM,EAAG,YAAAJ,CAAY,CAE1D,CACA,IAAMhB,EAAWC,EAAeR,EAAIM,CAAI,EACxC,OAAIC,EAAiB,CAAE,EAAG,EAAG,SAAAA,EAAU,YAAAgB,CAAY,EAC5C,IACT","names":["STABLE_DATA_ATTRS","normalizedText","el","_a","fingerprintOf","cssEscape","v","unique","root","selector","uniqueSelector","tag","classes","c","candidates","parent","parentSel","combined","siblings","n","idx","distinguishable","s","nth","locatorFromElement","fingerprint","id","name","STABLE_DATA_ATTRS","value"]}
@@ -0,0 +1,2 @@
1
+ import{a as c}from"./chunk-TMCGHANO.mjs";var d=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],p={banner:"hero",navigation:"navigation",contentinfo:"footer"};function g(t){let e=t.ariaRole?p[t.ariaRole.toLowerCase()]:void 0;if(e)return e;if(t.tag==="nav")return"navigation";if(t.tag==="footer")return"footer";if(t.tag==="header")return t.actionCount>=5?"navigation":"hero";let r=`${t.idClass} ${t.headingText}`.toLowerCase();return/\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\b/.test(r)?/footer/.test(r)?"footer":"navigation":/\b(hero|masthead|jumbotron)\b/i.test(t.idClass)?"hero":null}function b(t){return t.actionCount>=1&&t.textLength>0&&t.textLength<200?"cta":"generic"}var l=[["pricing",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["social_proof",/\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\b|trusted by|loved by|case stud|what our customers say/i],["trust",/\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\b|why choose|about us|our team/i],["cta",/\b(book|booking|reserve|appointments?|newsletter|subscribe)\b|contact us|get in touch|opening hours/i],["features",/\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\b|how it works|our process|what we (do|offer)|what you get/i]],m=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]],u={navigation:"navigation",footer:"navigation",hero:"hero",cta:"cta",generic:"generic"};function y(t){var r;let e=t.querySelector("h1, h2, h3");return((r=e==null?void 0:e.textContent)!=null?r:"").slice(0,160)}function h(t){var n,s;let e=g(t);if(e)return{type:(n=u[e])!=null?n:"generic",strength:"strong"};let r=`${t.idClass} ${t.headingText}`.toLowerCase();for(let[i,a]of l)if(a.test(r))return{type:i,strength:"strong"};for(let[i,a]of m)if(a.test(t.bodyText))return{type:i,strength:"strong"};let o=b(t);return{type:(s=u[o])!=null?s:"generic",strength:"weak"}}function f(t){var o,n;let e=((o=t.textContent)!=null?o:"").replace(/\s+/g," ").trim(),r=t.getAttribute("role");return c({tag:t.tagName.toLowerCase(),idClass:`${t.id} ${String((n=t.className)!=null?n:"")}`,headingText:y(t),bodyText:e.slice(0,2e3),actionCount:t.querySelectorAll('a, button, [role="button"]').length,textLength:e.length},r?{ariaRole:r}:{})}function T(t){return h(f(t)).type}export{d as a,g as b,b as c,m as d,h as e,f,T as g};
2
+ //# sourceMappingURL=chunk-Z6ILOYBD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engagement/classify.ts"],"sourcesContent":["// Semantic section classification for no-code section capture (Phase 3 §2.4).\n// Pure heuristic: element → one of the graph_nodes semantic_type enum. Mirrors\n// the SDK graph scanner's vocabulary so the persona × section matrix consumes\n// snippet-captured sections unchanged.\n//\n// SPLIT (Phase 2b, spec 2026-09-04). This module is the BROWSER-SAFE core:\n// structural rules, the small parent-level keyword table, and the fallbacks.\n// The rich ~40-topic vocabulary lives in ./topics.ts, which only the server\n// imports — shipping it here cost 1.5 KB gzip in every bundle (7% of the\n// always-on snippet) for a layer the browser never reads, since classifySection\n// returns only the parent.\n//\n// Both paths share structuralTopicOf/fallbackTopicOf, so the three fixes below\n// apply identically on client and server; only vocabulary richness differs.\n//\n// Fixes, each reproduced against the shipped classifier before being changed:\n// 1. structural before the converter fallback — `<div class=\"navbar\">` used\n// to become `cta`, because `navigation` was reachable only via tag\n// nav/footer.\n// 2. hero before the converter fallback — the cta rule sat above both hero\n// rules, so a `<header>` with a button and <200 chars could never be hero.\n// 3. tightened pricing/social keywords — `plans?` and `customers?` fired on\n// unrelated copy AND were `strong`, so they auto-applied at confidence 0.9\n// and were never sent to the LLM fallback.\n\nexport type SemanticType =\n | 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features'\n | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';\n\nexport type SectionRole = 'converter' | 'persuader' | 'structural';\n\n/** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */\nexport const SEMANTIC_TYPES: readonly SemanticType[] = [\n 'pricing', 'hero', 'social_proof', 'cta', 'features',\n 'faq', 'comparison', 'trust', 'navigation', 'generic',\n] as const;\n\nexport type TopicRule = { topic: string; parent: SemanticType; role: SectionRole };\n\n/** Environment-agnostic section features — buildable from a browser Element or\n * a server-parsed node (node-html-parser). */\nexport type SectionFeatures = {\n tag: string; // lowercase tag name\n idClass: string; // `${id} ${className}`\n headingText: string;\n bodyText: string; // normalized text content, first 2000 chars\n actionCount: number;\n textLength: number;\n /** ARIA landmark role, when the element declares one. Optional so every\n * existing caller keeps compiling; a page that uses landmarks gives a\n * high-precision signal for free, which the old classifier ignored — it\n * tested membership of SEMANTIC_TYPES, so only role=\"navigation\" ever hit\n * and role=\"banner\"/\"contentinfo\" were discarded. */\n ariaRole?: string;\n /** schema.org `@type` values found in `<script type=\"application/ld+json\">`\n * INSIDE this section. Highest-precision signal available and free to\n * collect — the crawler already has the HTML. Type-only here; the\n * `@type` → topic table is server-side in ./topics.ts, since the browser\n * never reads the topic layer and the snippet has ~200 bytes of margin. */\n structuredTypes?: string[];\n};\n\n// ARIA landmark → topic. Structural facts, not guesses.\nconst ARIA_TOPIC: Record<string, string> = {\n banner: 'hero',\n navigation: 'navigation',\n contentinfo: 'footer',\n};\n\n/**\n * Structural identification, shared by the browser and server classifiers.\n * Returns a topic name, or null when the section is not structural furniture.\n * Everything matched here is definitive (tag or authored marker), so callers\n * treat it as `strong`.\n */\nexport function structuralTopicOf(f: SectionFeatures): string | null {\n const aria = f.ariaRole ? ARIA_TOPIC[f.ariaRole.toLowerCase()] : undefined;\n if (aria) return aria;\n if (f.tag === 'nav') return 'navigation';\n if (f.tag === 'footer') return 'footer';\n // A page-level <header> IS the banner landmark (HTML-AAM maps it to\n // role=\"banner\", which this function already treats as hero), so it is a\n // structural fact rather than a keyword guess. Without this, a header whose\n // headline happens to contain a content word loses to the keyword table:\n // \"We build brands that move\" scored social_proof and \"Winter collection\"\n // scored features, purely on words inside the hero copy.\n //\n // …EXCEPT when the header IS the site's navigation. The hold-out corpus\n // (2026-09-05, six real unseen sites) showed most real pages wrap their nav\n // in <header> — mega-menus with 10–114 links and almost no prose — and\n // hero-typing those cost the classifier a 0.11 hero precision. A hero header\n // carries a headline and one or two calls to action; a nav header is\n // link-dominated, so the action count is the discriminator. Measured on the\n // hold-out: nav headers had 10+ actions (one at 4), authored hero headers\n // have 0–2.\n if (f.tag === 'header') return f.actionCount >= 5 ? 'navigation' : 'hero';\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n if (/\\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\\b/.test(hay)) {\n return /footer/.test(hay) ? 'footer' : 'navigation';\n }\n // Explicit authoring marker beats any inferred keyword, and is matched on\n // idClass ONLY: a `<header class=\"hero\">` headlined \"Expert Car Repair\" is a\n // hero, not a services section — but matching \"hero\" in heading text would\n // also catch \"Hero of the story\".\n if (/\\b(hero|masthead|jumbotron)\\b/i.test(f.idClass)) return 'hero';\n return null;\n}\n\n/**\n * Last-resort classification once no keyword or content evidence matched.\n * Hero is checked BEFORE the converter fallback — fix 2 above.\n */\nexport function fallbackTopicOf(f: SectionFeatures): string {\n // `tag === 'header'` is handled in structuralTopicOf, which runs first.\n if (f.actionCount >= 1 && f.textLength > 0 && f.textLength < 200) return 'cta';\n return 'generic';\n}\n\n// Parent-level keyword table for the BROWSER path. Deliberately close to the\n// original size — the rich topic vocabulary is in ./topics.ts. `plans?` now\n// requires adjacent pricing context, and bare `customers?` is gone (it fired on\n// navigation furniture like a \"Customer Service\" footer block).\nconst KEYWORDS: Array<[SemanticType, RegExp]> = [\n ['pricing', /\\b(pricing|price list|per month|\\/mo|subscriptions?)\\b|\\bplans?\\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],\n ['faq', /\\bfaq\\b|frequently asked|common questions?/i],\n ['comparison', /\\b(compare|comparison|versus)\\b|\\bvs\\./i],\n ['social_proof', /\\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\\b|trusted by|loved by|case stud|what our customers say/i],\n ['trust', /\\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\\b|why choose|about us|our team/i],\n ['cta', /\\b(book|booking|reserve|appointments?|newsletter|subscribe)\\b|contact us|get in touch|opening hours/i],\n ['features', /\\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\\b|how it works|our process|what we (do|offer)|what you get/i],\n];\n\n// Content-evidence patterns — run against bodyText when the heading gave us\n// nothing. Deliberately conservative: pricing needs per-period/plan context next\n// to money so an article mentioning \"$5 million\" stays generic.\nexport const CONTENT_PATTERNS: Array<[SemanticType, RegExp]> = [\n ['pricing', /(?:[$€£]\\s?\\d[\\d,.]*\\s*(?:\\/|per\\s)\\s*(?:mo|month|yr|year|seat|user))|(?:\\b(?:starter|basic|pro|growth|premium|enterprise)\\b[^.]{0,60}[$€£]\\s?\\d)/i],\n ['social_proof', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['trust', /\\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\\b/i],\n ['comparison', /\\b(?:vs|versus)\\b\\.?[^.!?]{0,80}\\b(?:compare|comparison|plans?|features?|alternative)\\b|\\bhow (?:we|it) compares?\\b/i],\n];\n\n/** Parent for the handful of topics the shared structural/fallback helpers\n * emit. The server maps the full vocabulary via CLASSIFIER_TOPICS instead. */\nconst SHARED_TOPIC_PARENT: Record<string, SemanticType> = {\n navigation: 'navigation',\n footer: 'navigation',\n hero: 'hero',\n cta: 'cta',\n generic: 'generic',\n};\n\nfunction headingText(el: Element): string {\n const h = el.querySelector('h1, h2, h3');\n return (h?.textContent ?? '').slice(0, 160);\n}\n\n/**\n * Pure classification over extracted features. `strong` = keyword, content, or\n * structural evidence (trustable enough to auto-apply); `weak` = a fallback\n * guess (hero/cta/generic — capture-worthy but not persona evidence).\n */\nexport function classifyFeatures(f: SectionFeatures): { type: SemanticType; strength: 'strong' | 'weak' } {\n const structural = structuralTopicOf(f);\n if (structural) return { type: SHARED_TOPIC_PARENT[structural] ?? 'generic', strength: 'strong' };\n\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [type, re] of KEYWORDS) if (re.test(hay)) return { type, strength: 'strong' };\n for (const [type, re] of CONTENT_PATTERNS) if (re.test(f.bodyText)) return { type, strength: 'strong' };\n\n const fb = fallbackTopicOf(f);\n return { type: SHARED_TOPIC_PARENT[fb] ?? 'generic', strength: 'weak' };\n}\n\n/** Feature extraction from a live DOM element (browser paths). */\nexport function featuresFromElement(el: Element): SectionFeatures {\n const text = (el.textContent ?? '').replace(/\\s+/g, ' ').trim();\n const role = el.getAttribute('role');\n return {\n tag: el.tagName.toLowerCase(),\n idClass: `${el.id} ${String(el.className ?? '')}`,\n headingText: headingText(el),\n bodyText: text.slice(0, 2000),\n actionCount: el.querySelectorAll('a, button, [role=\"button\"]').length,\n textLength: text.length,\n ...(role ? { ariaRole: role } : {}),\n };\n}\n\n/** Classify a page section into a semantic type (never null — falls back to\n * 'generic' so the caller can still capture attention on it). */\nexport function classifySection(el: Element): SemanticType {\n return classifyFeatures(featuresFromElement(el)).type;\n}\n"],"mappings":"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","names":["SEMANTIC_TYPES","ARIA_TOPIC","structuralTopicOf","f","aria","hay","fallbackTopicOf","KEYWORDS","CONTENT_PATTERNS","SHARED_TOPIC_PARENT","headingText","el","_a","h","classifyFeatures","_b","structural","type","re","fb","featuresFromElement","text","role","__spreadValues","classifySection"]}
@@ -461,6 +461,22 @@ type SentientConfig = {
461
461
  * `false` restores the silent keyless no-op.
462
462
  */
463
463
  localMode?: 'auto' | boolean;
464
+ /**
465
+ * Which integration surface is driving this client, and its released build
466
+ * version — `{ name: 'react', version: '0.27.0' }`. Set by the wrapper
467
+ * package (@sentientui/react, @sentientui/snippet), never by application
468
+ * code: core is a dependency of both, so its own version says nothing about
469
+ * what the customer installed.
470
+ *
471
+ * Rides the session upsert (the one call EVERY integration makes, unlike
472
+ * decide, which only the slot paths use) so the dashboard can tell a project
473
+ * its SDK is behind. Additive and best-effort: older API deployments ignore
474
+ * the fields, and omitting it changes nothing.
475
+ */
476
+ sdk?: {
477
+ name: string;
478
+ version: string;
479
+ };
464
480
  };
465
481
  type AssignResult = {
466
482
  variantId: string;
@@ -461,6 +461,22 @@ type SentientConfig = {
461
461
  * `false` restores the silent keyless no-op.
462
462
  */
463
463
  localMode?: 'auto' | boolean;
464
+ /**
465
+ * Which integration surface is driving this client, and its released build
466
+ * version — `{ name: 'react', version: '0.27.0' }`. Set by the wrapper
467
+ * package (@sentientui/react, @sentientui/snippet), never by application
468
+ * code: core is a dependency of both, so its own version says nothing about
469
+ * what the customer installed.
470
+ *
471
+ * Rides the session upsert (the one call EVERY integration makes, unlike
472
+ * decide, which only the slot paths use) so the dashboard can tell a project
473
+ * its SDK is behind. Additive and best-effort: older API deployments ignore
474
+ * the fields, and omitting it changes nothing.
475
+ */
476
+ sdk?: {
477
+ name: string;
478
+ version: string;
479
+ };
464
480
  };
465
481
  type AssignResult = {
466
482
  variantId: string;
@@ -16,7 +16,7 @@ type EngagementCaptureOptions = {
16
16
  doc?: Document;
17
17
  /**
18
18
  * Also attach per-section micro-signal detectors (rage click, text copy,
19
- * scroll hesitation, tab loss), attributed to the section's `nc-<type>`
19
+ * scroll hesitation, tab loss), attributed to the section's per-element `nc-*`
20
20
  * component. For the no-code snippet, whose pages have no `<Adaptive>`
21
21
  * components carrying their own detectors. Default false — the React SDK
22
22
  * keeps its per-component detectors and must not double-attach.
@@ -16,7 +16,7 @@ type EngagementCaptureOptions = {
16
16
  doc?: Document;
17
17
  /**
18
18
  * Also attach per-section micro-signal detectors (rage click, text copy,
19
- * scroll hesitation, tab loss), attributed to the section's `nc-<type>`
19
+ * scroll hesitation, tab loss), attributed to the section's per-element `nc-*`
20
20
  * component. For the no-code snippet, whose pages have no `<Adaptive>`
21
21
  * components carrying their own detectors. Default false — the React SDK
22
22
  * keeps its per-component detectors and must not double-attach.
@@ -1,2 +1,2 @@
1
- "use strict";var E=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var ee=Object.getOwnPropertyNames,j=Object.getOwnPropertySymbols;var U=Object.prototype.hasOwnProperty,te=Object.prototype.propertyIsEnumerable;var B=(e,t,n)=>t in e?E(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,w=(e,t)=>{for(var n in t||(t={}))U.call(t,n)&&B(e,n,t[n]);if(j)for(var n of j(t))te.call(t,n)&&B(e,n,t[n]);return e};var ne=(e,t)=>{for(var n in t)E(e,n,{get:t[n],enumerable:!0})},oe=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ee(t))!U.call(e,o)&&o!==n&&E(e,o,{get:()=>t[o],enumerable:!(a=Z(t,o))||a.enumerable});return e};var se=e=>oe(E({},"__esModule",{value:!0}),e);var Se={};ne(Se,{SEMANTIC_TYPES:()=>C,classifyFeatures:()=>x,classifySection:()=>k,featuresFromElement:()=>A,startEngagementCapture:()=>J});module.exports=se(Se);var C=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],re={banner:"hero",navigation:"navigation",contentinfo:"footer"};function ie(e){let t=e.ariaRole?re[e.ariaRole.toLowerCase()]:void 0;if(t)return t;if(e.tag==="nav")return"navigation";if(e.tag==="footer")return"footer";if(e.tag==="header")return"hero";let n=`${e.idClass} ${e.headingText}`.toLowerCase();return/\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\b/.test(n)?/footer/.test(n)?"footer":"navigation":/\b(hero|masthead|jumbotron)\b/i.test(e.idClass)?"hero":null}function ae(e){return e.actionCount>=1&&e.textLength>0&&e.textLength<200?"cta":"generic"}var ce=[["pricing",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["social_proof",/\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\b|trusted by|loved by|case stud|what our customers say/i],["trust",/\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\b|why choose|about us|our team/i],["cta",/\b(book|booking|reserve|appointments?|newsletter|subscribe)\b|contact us|get in touch|opening hours/i],["features",/\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\b|how it works|our process|what we (do|offer)|what you get/i]],le=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]],H={navigation:"navigation",footer:"navigation",hero:"hero",cta:"cta",generic:"generic"};function de(e){var n;let t=e.querySelector("h1, h2, h3");return((n=t==null?void 0:t.textContent)!=null?n:"").slice(0,160)}function x(e){var o,i;let t=ie(e);if(t)return{type:(o=H[t])!=null?o:"generic",strength:"strong"};let n=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[d,s]of ce)if(s.test(n))return{type:d,strength:"strong"};for(let[d,s]of le)if(s.test(e.bodyText))return{type:d,strength:"strong"};let a=ae(e);return{type:(i=H[a])!=null?i:"generic",strength:"weak"}}function A(e){var a,o;let t=((a=e.textContent)!=null?a:"").replace(/\s+/g," ").trim(),n=e.getAttribute("role");return w({tag:e.tagName.toLowerCase(),idClass:`${e.id} ${String((o=e.className)!=null?o:"")}`,headingText:de(e),bodyText:t.slice(0,2e3),actionCount:e.querySelectorAll('a, button, [role="button"]').length,textLength:t.length},n?{ariaRole:n}:{})}function k(e){return x(A(e)).type}var ue=require("@sentientui/policy");function q(e,t,n,a){let o=[];{let s=!1,c=[],f=()=>{if(s)return;let y=Date.now();for(c.push(y);c.length>0&&y-c[0]>500;)c.shift();c.length>=3&&(s=!0,e("rage_click"))};t.addEventListener("click",f),o.push(()=>t.removeEventListener("click",f))}{let i=!1,d=s=>{if(i||!(s.target instanceof Node)||!t.contains(s.target)&&t!==s.target)return;i=!0;let c=typeof window!="undefined"?window.getSelection():null,f=c?c.toString().length:0;e("text_copy",{selectionLength:f})};document.addEventListener("copy",d),o.push(()=>document.removeEventListener("copy",d))}{let i=!1,d=!1,s=null,c=()=>{s!==null&&(clearTimeout(s),s=null)},f=()=>{i||!d||(c(),s=setTimeout(()=>{!i&&d&&(i=!0,e("scroll_hesitation"))},3e3))},y=()=>{c(),f()},p=S=>{for(let v of S)d=v.intersectionRatio>.3,d?f():c()},h=new IntersectionObserver(p,{threshold:[.3]});h.observe(t),window.addEventListener("scroll",y,{passive:!0}),o.push(()=>{h.disconnect(),window.removeEventListener("scroll",y),c()})}if((a==null?void 0:a.tabLoss)!==!1){let i=!1,d=n!=null?n:Date.now(),s=()=>{if(i||document.visibilityState!=="hidden")return;let c=Date.now()-d;c<15e3&&(i=!0,e("tab_loss",{timeOnPage:c}))};document.addEventListener("visibilitychange",s),o.push(()=>document.removeEventListener("visibilitychange",s))}return()=>{for(let i of o)i()}}function Q(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}var pe=["data-testid","data-test","data-id","data-name","data-cy"];function _(e){var t;return((t=e.textContent)!=null?t:"").replace(/\s+/g," ").trim()}function ge(e){return{tag:e.tagName.toLowerCase(),text:_(e).slice(0,40)}}function V(e){return e.replace(/["\\\]]/g,"\\$&")}function b(e,t){try{return e.querySelectorAll(t).length===1}catch(n){return!1}}function z(e,t){var d;let n=e.tagName.toLowerCase();if(!n)return null;let a=((d=e.getAttribute("class"))!=null?d:"").split(/\s+/).filter(s=>/^[a-zA-Z][\w-]*$/.test(s)),o=[n,...a.map(s=>`${n}.${s}`)];for(let s of o)if(b(t,s))return s;let i=e.parentElement;if(i&&i!==t){let s=z(i,t);if(s){for(let p of o){let h=`${s} > ${p}`;if(b(t,h))return h}let c=Array.from(i.children).filter(p=>p.tagName.toLowerCase()===n),f=c.indexOf(e),y=c.some(p=>p!==e&&_(p)!==_(e));if(f>=0&&y){let p=`${s} > ${n}:nth-of-type(${f+1})`;if(b(t,p))return p}}}return null}function Y(e,t){let n=ge(e),a=e.getAttribute("id");if(a&&b(t,`#${V(a)}`))return{v:1,id:a,fingerprint:n};for(let i of pe){let d=e.getAttribute(i);if(d&&b(t,`[${i}="${V(d)}"]`))return{v:1,dataAttr:{name:i,value:d},fingerprint:n}}let o=z(e,t);return o?{v:1,selector:o,fingerprint:n}:null}var fe="section, header, footer, nav, main > div, [data-sentient-section]",me=2e4;function ye(e){let t=Array.from(e.querySelectorAll(fe)),n=t.filter(a=>t.filter(o=>o!==a&&a.contains(o)).length<2);return n.filter(a=>!n.some(o=>o!==a&&o.contains(a)))}function he(e,t,n,a){try{fetch(`${t}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({pageUrl:n,sections:a})}).catch(()=>{})}catch(o){}}var R=()=>{};function J(e,t){var D,M,N,L,K,$,G,F,W;let n=(D=t.doc)!=null?D:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||Q()||!t.apiKey||!t.apiKey.startsWith("pk_"))return R;let a=((M=t.apiBase)!=null?M:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),o=ye(n);if(o.length===0)return R;let i=new Map,d=[];for(let r of o){let l=r.getAttribute("data-sentient-type"),u=l&&C.includes(l)?l:null,g=(L=u!=null?u:(N=t.typeOf)==null?void 0:N.call(t,r))!=null?L:k(r),T=`nc-${g}`;i.set(r,T);let I=Y(r,n);d.push(w({componentId:T,semanticType:g,source:u?"markup":"auto"},I?{locator:I}:{}))}let s=(F=(G=($=(K=n.defaultView)!=null?K:typeof window!="undefined"?window:void 0)==null?void 0:$.location)==null?void 0:G.pathname)!=null?F:"/";he(t.apiKey,a,s,d);let c=new Map,f=r=>{let l=c.get(r);return l||(l={ms:0,scroll:0,enterAt:null,intersecting:!1},c.set(r,l)),l},y=new IntersectionObserver(r=>{for(let l of r){let u=i.get(l.target);if(!u)continue;let g=f(u);l.isIntersecting?(g.intersecting=!0,g.enterAt=Date.now(),l.intersectionRatio>g.scroll&&(g.scroll=l.intersectionRatio)):(g.intersecting=!1,g.enterAt!=null&&(g.ms+=Date.now()-g.enterAt,g.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let r of i.keys())y.observe(r);let p=()=>{let r=Date.now();for(let[l,u]of c)if(u.enterAt!=null&&(u.ms+=r-u.enterAt,u.enterAt=null),!(u.ms<=0)){try{e.track({projectId:t.apiKey,componentId:l,eventType:"dwell",payload:{dwell_time:Math.round(u.ms),scroll_depth:Number(u.scroll.toFixed(2))}})}catch(g){}u.ms=0}},h=()=>{if(n.hidden)p();else{let r=Date.now();for(let l of c.values())l.intersecting&&(l.enterAt=r)}},S=!1,v=r=>{if(p(),r!=null&&r.persisted){S=!0;return}try{y.disconnect()}catch(l){}},O=r=>{if(!(r!=null&&r.persisted)||!S)return;S=!1;let l=Date.now();for(let u of c.values())u.enterAt=u.intersecting&&!n.hidden?l:null};n.addEventListener("visibilitychange",h);let m=(W=n.defaultView)!=null?W:typeof window!="undefined"?window:void 0;m==null||m.addEventListener("pagehide",v),m==null||m.addEventListener("pageshow",O);let X=setInterval(()=>{if(n.hidden||S)return;p();let r=Date.now();for(let l of c.values())l.intersecting&&(l.enterAt=r)},me),P=[];return t.microSignals&&[...i.entries()].forEach(([r,l],u)=>{P.push(q((g,T={})=>{try{e.track({projectId:t.apiKey,componentId:l,eventType:"micro_signal",payload:w({signalType:g},T)})}catch(I){}},r,void 0,{tabLoss:u===0}))}),()=>{p(),clearInterval(X),n.removeEventListener("visibilitychange",h),m==null||m.removeEventListener("pagehide",v),m==null||m.removeEventListener("pageshow",O);for(let r of P)r();try{y.disconnect()}catch(r){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
1
+ "use strict";var R=Object.defineProperty,le=Object.defineProperties,ce=Object.getOwnPropertyDescriptor,de=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,z=Object.getOwnPropertySymbols;var J=Object.prototype.hasOwnProperty,pe=Object.prototype.propertyIsEnumerable;var Y=(e,t,n)=>t in e?R(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,S=(e,t)=>{for(var n in t||(t={}))J.call(t,n)&&Y(e,n,t[n]);if(z)for(var n of z(t))pe.call(t,n)&&Y(e,n,t[n]);return e},_=(e,t)=>le(e,de(t));var ge=(e,t)=>{for(var n in t)R(e,n,{get:t[n],enumerable:!0})},fe=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ue(t))!J.call(e,o)&&o!==n&&R(e,o,{get:()=>t[o],enumerable:!(a=ce(t,o))||a.enumerable});return e};var me=e=>fe(R({},"__esModule",{value:!0}),e);var Ie={};ge(Ie,{SEMANTIC_TYPES:()=>x,classifyFeatures:()=>T,classifySection:()=>Z,featuresFromElement:()=>C,startEngagementCapture:()=>ie});module.exports=me(Ie);var x=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],ye={banner:"hero",navigation:"navigation",contentinfo:"footer"};function he(e){let t=e.ariaRole?ye[e.ariaRole.toLowerCase()]:void 0;if(t)return t;if(e.tag==="nav")return"navigation";if(e.tag==="footer")return"footer";if(e.tag==="header")return e.actionCount>=5?"navigation":"hero";let n=`${e.idClass} ${e.headingText}`.toLowerCase();return/\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\b/.test(n)?/footer/.test(n)?"footer":"navigation":/\b(hero|masthead|jumbotron)\b/i.test(e.idClass)?"hero":null}function Se(e){return e.actionCount>=1&&e.textLength>0&&e.textLength<200?"cta":"generic"}var we=[["pricing",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["social_proof",/\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\b|trusted by|loved by|case stud|what our customers say/i],["trust",/\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\b|why choose|about us|our team/i],["cta",/\b(book|booking|reserve|appointments?|newsletter|subscribe)\b|contact us|get in touch|opening hours/i],["features",/\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\b|how it works|our process|what we (do|offer)|what you get/i]],O=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]],X={navigation:"navigation",footer:"navigation",hero:"hero",cta:"cta",generic:"generic"};function be(e){var n;let t=e.querySelector("h1, h2, h3");return((n=t==null?void 0:t.textContent)!=null?n:"").slice(0,160)}function T(e){var o,i;let t=he(e);if(t)return{type:(o=X[t])!=null?o:"generic",strength:"strong"};let n=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[d,s]of we)if(s.test(n))return{type:d,strength:"strong"};for(let[d,s]of O)if(s.test(e.bodyText))return{type:d,strength:"strong"};let a=Se(e);return{type:(i=X[a])!=null?i:"generic",strength:"weak"}}function C(e){var a,o;let t=((a=e.textContent)!=null?a:"").replace(/\s+/g," ").trim(),n=e.getAttribute("role");return S({tag:e.tagName.toLowerCase(),idClass:`${e.id} ${String((o=e.className)!=null?o:"")}`,headingText:be(e),bodyText:t.slice(0,2e3),actionCount:e.querySelectorAll('a, button, [role="button"]').length,textLength:t.length},n?{ariaRole:n}:{})}function Z(e){return T(C(e)).type}var re=require("@sentientui/policy");var ve=require("@sentientui/policy");function ee(e,t,n,a){let o=[];{let s=!1,l=[],f=()=>{if(s)return;let y=Date.now();for(l.push(y);l.length>0&&y-l[0]>500;)l.shift();l.length>=3&&(s=!0,e("rage_click"))};t.addEventListener("click",f),o.push(()=>t.removeEventListener("click",f))}{let i=!1,d=s=>{if(i||!(s.target instanceof Node)||!t.contains(s.target)&&t!==s.target)return;i=!0;let l=typeof window!="undefined"?window.getSelection():null,f=l?l.toString().length:0;e("text_copy",{selectionLength:f})};document.addEventListener("copy",d),o.push(()=>document.removeEventListener("copy",d))}{let i=!1,d=!1,s=null,l=()=>{s!==null&&(clearTimeout(s),s=null)},f=()=>{i||!d||(l(),s=setTimeout(()=>{!i&&d&&(i=!0,e("scroll_hesitation"))},3e3))},y=()=>{l(),f()},g=b=>{for(let k of b)d=k.intersectionRatio>.3,d?f():l()},h=new IntersectionObserver(g,{threshold:[.3]});h.observe(t),window.addEventListener("scroll",y,{passive:!0}),o.push(()=>{h.disconnect(),window.removeEventListener("scroll",y),l()})}if((a==null?void 0:a.tabLoss)!==!1){let i=!1,d=n!=null?n:Date.now(),s=()=>{if(i||document.visibilityState!=="hidden")return;let l=Date.now()-d;l<15e3&&(i=!0,e("tab_loss",{timeOnPage:l}))};document.addEventListener("visibilitychange",s),o.push(()=>document.removeEventListener("visibilitychange",s))}return()=>{for(let i of o)i()}}function te(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}var Te=["data-sentient-id","data-testid","data-test","data-id","data-name","data-cy"];function P(e){var t;return((t=e.textContent)!=null?t:"").replace(/\s+/g," ").trim()}function Ce(e){return{tag:e.tagName.toLowerCase(),text:P(e).slice(0,40)}}function ne(e){return e.replace(/["\\\]]/g,"\\$&")}function E(e,t){try{return e.querySelectorAll(t).length===1}catch(n){return!1}}function oe(e,t){var d;let n=e.tagName.toLowerCase();if(!n)return null;let a=((d=e.getAttribute("class"))!=null?d:"").split(/\s+/).filter(s=>/^[a-zA-Z][\w-]*$/.test(s)),o=[n,...a.map(s=>`${n}.${s}`)];for(let s of o)if(E(t,s))return s;let i=e.parentElement;if(i&&i!==t){let s=oe(i,t);if(s){for(let g of o){let h=`${s} > ${g}`;if(E(t,h))return h}let l=Array.from(i.children).filter(g=>g.tagName.toLowerCase()===n),f=l.indexOf(e),y=l.some(g=>g!==e&&P(g)!==P(e));if(f>=0&&y){let g=`${s} > ${n}:nth-of-type(${f+1})`;if(E(t,g))return g}}}return null}function se(e,t){let n=Ce(e),a=e.getAttribute("id");if(a&&E(t,`#${ne(a)}`))return{v:1,id:a,fingerprint:n};for(let i of Te){let d=e.getAttribute(i);if(d&&E(t,`[${i}="${ne(d)}"]`))return{v:1,dataAttr:{name:i,value:d},fingerprint:n}}let o=oe(e,t);return o?{v:1,selector:o,fingerprint:n}:null}var Ee="section, header, footer, nav, main > div, [data-sentient-section]",ke=2e4;function Re(e){let t=Array.from(e.querySelectorAll(Ee)),n=t.filter(a=>t.filter(o=>o!==a&&a.contains(o)).length<2);return n.filter(a=>!n.some(o=>o!==a&&o.contains(a)))}function xe(e,t,n,a){try{fetch(`${t}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({pageUrl:n,sections:a})}).catch(()=>{})}catch(o){}}var I=()=>{};function ie(e,t){var N,L,$,K,G,F,W,j,B,U,H,q;let n=(N=t.doc)!=null?N:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||te()||!t.apiKey||!t.apiKey.startsWith("pk_"))return I;let a=((L=t.apiBase)!=null?L:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),o=Re(n);if(o.length===0)return I;let i=new Map,d=[];for(let r of o){let c=r.getAttribute("data-sentient-type"),p=c&&x.includes(c)?c:null,u=C(r),v=(K=p!=null?p:($=t.typeOf)==null?void 0:$.call(t,r))!=null?K:T(u).type,w=se(r,n),V=w?`nc-${v}-${(0,re.fnv1a)(JSON.stringify({id:(G=w.id)!=null?G:null,dataAttr:(F=w.dataAttr)!=null?F:null,selector:(W=w.selector)!=null?W:null})).toString(36)}`:`nc-${v}`;i.set(r,V);let Q=O.filter(([,A])=>A.test(u.bodyText)).map(([A])=>A);d.push(_(S({componentId:V,semanticType:v,source:p?"markup":"auto"},w?{locator:w}:{}),{observation:S(S({tag:u.tag,idClass:u.idClass.slice(0,200),headingText:u.headingText,textLength:u.textLength,actionCount:u.actionCount},u.ariaRole?{ariaRole:u.ariaRole}:{}),Q.length>0?{patternFlags:Q}:{})}))}let s=(H=(U=(B=(j=n.defaultView)!=null?j:typeof window!="undefined"?window:void 0)==null?void 0:B.location)==null?void 0:U.pathname)!=null?H:"/";xe(t.apiKey,a,s,d);let l=new Map,f=r=>{let c=l.get(r);return c||(c={ms:0,scroll:0,enterAt:null,intersecting:!1},l.set(r,c)),c},y=new IntersectionObserver(r=>{for(let c of r){let p=i.get(c.target);if(!p)continue;let u=f(p);c.isIntersecting?(u.intersecting=!0,u.enterAt=Date.now(),c.intersectionRatio>u.scroll&&(u.scroll=c.intersectionRatio)):(u.intersecting=!1,u.enterAt!=null&&(u.ms+=Date.now()-u.enterAt,u.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let r of i.keys())y.observe(r);let g=()=>{let r=Date.now();for(let[c,p]of l)if(p.enterAt!=null&&(p.ms+=r-p.enterAt,p.enterAt=null),!(p.ms<=0)){try{e.track({projectId:t.apiKey,componentId:c,eventType:"dwell",payload:{dwell_time:Math.round(p.ms),scroll_depth:Number(p.scroll.toFixed(2))}})}catch(u){}p.ms=0}},h=()=>{if(n.hidden)g();else{let r=Date.now();for(let c of l.values())c.intersecting&&(c.enterAt=r)}},b=!1,k=r=>{if(g(),r!=null&&r.persisted){b=!0;return}try{y.disconnect()}catch(c){}},D=r=>{if(!(r!=null&&r.persisted)||!b)return;b=!1;let c=Date.now();for(let p of l.values())p.enterAt=p.intersecting&&!n.hidden?c:null};n.addEventListener("visibilitychange",h);let m=(q=n.defaultView)!=null?q:typeof window!="undefined"?window:void 0;m==null||m.addEventListener("pagehide",k),m==null||m.addEventListener("pageshow",D);let ae=setInterval(()=>{if(n.hidden||b)return;g();let r=Date.now();for(let c of l.values())c.intersecting&&(c.enterAt=r)},ke),M=[];return t.microSignals&&[...i.entries()].forEach(([r,c],p)=>{M.push(ee((u,v={})=>{try{e.track({projectId:t.apiKey,componentId:c,eventType:"micro_signal",payload:S({signalType:u},v)})}catch(w){}},r,void 0,{tabLoss:p===0}))}),()=>{g(),clearInterval(ae),n.removeEventListener("visibilitychange",h),m==null||m.removeEventListener("pagehide",k),m==null||m.removeEventListener("pageshow",D);for(let r of M)r();try{y.disconnect()}catch(r){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
2
2
  //# sourceMappingURL=index-engagement.js.map