@sentientui/core 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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"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 f(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 h(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 f(h(t)).type}export{d as a,g as b,b as c,f as d,h as e,T as f};
2
+ //# sourceMappingURL=chunk-EWP6FTHE.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 if (f.tag === 'header') return '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"]}
@@ -0,0 +1,2 @@
1
+ var E=["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 a;let e=t.tagName.toLowerCase();if(!e)return null;let i=((a=t.getAttribute("class"))!=null?a:"").split(/\s+/).filter(r=>/^[a-zA-Z][\w-]*$/.test(r)),c=[e,...i.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 o of c){let g=`${r} > ${o}`;if(u(n,g))return g}let l=Array.from(s.children).filter(o=>o.tagName.toLowerCase()===e),d=l.indexOf(t),A=l.some(o=>o!==t&&f(o)!==f(t));if(d>=0&&A){let o=`${r} > ${e}:nth-of-type(${d+1})`;if(u(n,o))return o}}}return null}function N(t,n){let e=$(t),i=t.getAttribute("id");if(i&&u(n,`#${m(i)}`))return{v:1,id:i,fingerprint:e};for(let s of E){let a=t.getAttribute(s);if(a&&u(n,`[${s}="${m(a)}"]`))return{v:1,dataAttr:{name:s,value:a},fingerprint:e}}let c=p(t,n);return c?{v:1,selector:c,fingerprint:e}:null}export{N as a};
2
+ //# sourceMappingURL=chunk-KIP52DUJ.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.\nconst STABLE_DATA_ATTRS = ['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":"AAOA,IAAMA,EAAoB,CAAC,cAAe,YAAa,UAAW,YAAa,SAAS,EAGxF,SAASC,EAAeC,EAAqB,CAV7C,IAAAC,EAWE,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,CAlCtE,IAAAL,EAmCE,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,47 @@
1
+ type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
+ type SectionRole = 'converter' | 'persuader' | 'structural';
3
+ /** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */
4
+ declare const SEMANTIC_TYPES: readonly SemanticType[];
5
+ type TopicRule = {
6
+ topic: string;
7
+ parent: SemanticType;
8
+ role: SectionRole;
9
+ };
10
+ /** Environment-agnostic section features — buildable from a browser Element or
11
+ * a server-parsed node (node-html-parser). */
12
+ type SectionFeatures = {
13
+ tag: string;
14
+ idClass: string;
15
+ headingText: string;
16
+ bodyText: string;
17
+ actionCount: number;
18
+ textLength: number;
19
+ /** ARIA landmark role, when the element declares one. Optional so every
20
+ * existing caller keeps compiling; a page that uses landmarks gives a
21
+ * high-precision signal for free, which the old classifier ignored — it
22
+ * tested membership of SEMANTIC_TYPES, so only role="navigation" ever hit
23
+ * and role="banner"/"contentinfo" were discarded. */
24
+ ariaRole?: string;
25
+ /** schema.org `@type` values found in `<script type="application/ld+json">`
26
+ * INSIDE this section. Highest-precision signal available and free to
27
+ * collect — the crawler already has the HTML. Type-only here; the
28
+ * `@type` → topic table is server-side in ./topics.ts, since the browser
29
+ * never reads the topic layer and the snippet has ~200 bytes of margin. */
30
+ structuredTypes?: string[];
31
+ };
32
+ /**
33
+ * Pure classification over extracted features. `strong` = keyword, content, or
34
+ * structural evidence (trustable enough to auto-apply); `weak` = a fallback
35
+ * guess (hero/cta/generic — capture-worthy but not persona evidence).
36
+ */
37
+ declare function classifyFeatures(f: SectionFeatures): {
38
+ type: SemanticType;
39
+ strength: 'strong' | 'weak';
40
+ };
41
+ /** Feature extraction from a live DOM element (browser paths). */
42
+ declare function featuresFromElement(el: Element): SectionFeatures;
43
+ /** Classify a page section into a semantic type (never null — falls back to
44
+ * 'generic' so the caller can still capture attention on it). */
45
+ declare function classifySection(el: Element): SemanticType;
46
+
47
+ export { SEMANTIC_TYPES as S, type TopicRule as T, type SectionFeatures as a, type SemanticType as b, classifyFeatures as c, classifySection as d, featuresFromElement as f };
@@ -0,0 +1,47 @@
1
+ type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
+ type SectionRole = 'converter' | 'persuader' | 'structural';
3
+ /** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */
4
+ declare const SEMANTIC_TYPES: readonly SemanticType[];
5
+ type TopicRule = {
6
+ topic: string;
7
+ parent: SemanticType;
8
+ role: SectionRole;
9
+ };
10
+ /** Environment-agnostic section features — buildable from a browser Element or
11
+ * a server-parsed node (node-html-parser). */
12
+ type SectionFeatures = {
13
+ tag: string;
14
+ idClass: string;
15
+ headingText: string;
16
+ bodyText: string;
17
+ actionCount: number;
18
+ textLength: number;
19
+ /** ARIA landmark role, when the element declares one. Optional so every
20
+ * existing caller keeps compiling; a page that uses landmarks gives a
21
+ * high-precision signal for free, which the old classifier ignored — it
22
+ * tested membership of SEMANTIC_TYPES, so only role="navigation" ever hit
23
+ * and role="banner"/"contentinfo" were discarded. */
24
+ ariaRole?: string;
25
+ /** schema.org `@type` values found in `<script type="application/ld+json">`
26
+ * INSIDE this section. Highest-precision signal available and free to
27
+ * collect — the crawler already has the HTML. Type-only here; the
28
+ * `@type` → topic table is server-side in ./topics.ts, since the browser
29
+ * never reads the topic layer and the snippet has ~200 bytes of margin. */
30
+ structuredTypes?: string[];
31
+ };
32
+ /**
33
+ * Pure classification over extracted features. `strong` = keyword, content, or
34
+ * structural evidence (trustable enough to auto-apply); `weak` = a fallback
35
+ * guess (hero/cta/generic — capture-worthy but not persona evidence).
36
+ */
37
+ declare function classifyFeatures(f: SectionFeatures): {
38
+ type: SemanticType;
39
+ strength: 'strong' | 'weak';
40
+ };
41
+ /** Feature extraction from a live DOM element (browser paths). */
42
+ declare function featuresFromElement(el: Element): SectionFeatures;
43
+ /** Classify a page section into a semantic type (never null — falls back to
44
+ * 'generic' so the caller can still capture attention on it). */
45
+ declare function classifySection(el: Element): SemanticType;
46
+
47
+ export { SEMANTIC_TYPES as S, type TopicRule as T, type SectionFeatures as a, type SemanticType as b, classifyFeatures as c, classifySection as d, featuresFromElement as f };
@@ -1,30 +1,5 @@
1
- type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
- /** Canonical vocabulary mirrors the graph_nodes semantic_type CHECK enum. */
3
- declare const SEMANTIC_TYPES: readonly SemanticType[];
4
- /** Environment-agnostic section features — buildable from a browser Element or
5
- * a server-parsed node (node-html-parser). */
6
- type SectionFeatures = {
7
- tag: string;
8
- idClass: string;
9
- headingText: string;
10
- bodyText: string;
11
- actionCount: number;
12
- textLength: number;
13
- };
14
- /**
15
- * Pure classification over extracted features. `strong` = keyword or content
16
- * evidence (trustable enough to auto-apply); `weak` = structural fallback
17
- * (cta/hero/navigation/generic — capture-worthy but not persona evidence).
18
- */
19
- declare function classifyFeatures(f: SectionFeatures): {
20
- type: SemanticType;
21
- strength: 'strong' | 'weak';
22
- };
23
- /** Feature extraction from a live DOM element (browser paths). */
24
- declare function featuresFromElement(el: Element): SectionFeatures;
25
- /** Classify a page section into a semantic type (never null — falls back to
26
- * 'generic' so the caller can still capture attention on it). */
27
- declare function classifySection(el: Element): SemanticType;
1
+ import { b as SemanticType } from './classify-DYlSjkFP.cjs';
2
+ export { S as SEMANTIC_TYPES, a as SectionFeatures, c as classifyFeatures, d as classifySection, f as featuresFromElement } from './classify-DYlSjkFP.cjs';
28
3
 
29
4
  type CaptureClient = {
30
5
  track(event: {
@@ -56,4 +31,4 @@ type EngagementCaptureOptions = {
56
31
  };
57
32
  declare function startEngagementCapture(client: CaptureClient, opts: EngagementCaptureOptions): () => void;
58
33
 
59
- export { type EngagementCaptureOptions, SEMANTIC_TYPES, type SectionFeatures, type SemanticType, classifyFeatures, classifySection, featuresFromElement, startEngagementCapture };
34
+ export { type EngagementCaptureOptions, SemanticType, startEngagementCapture };
@@ -1,30 +1,5 @@
1
- type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
- /** Canonical vocabulary mirrors the graph_nodes semantic_type CHECK enum. */
3
- declare const SEMANTIC_TYPES: readonly SemanticType[];
4
- /** Environment-agnostic section features — buildable from a browser Element or
5
- * a server-parsed node (node-html-parser). */
6
- type SectionFeatures = {
7
- tag: string;
8
- idClass: string;
9
- headingText: string;
10
- bodyText: string;
11
- actionCount: number;
12
- textLength: number;
13
- };
14
- /**
15
- * Pure classification over extracted features. `strong` = keyword or content
16
- * evidence (trustable enough to auto-apply); `weak` = structural fallback
17
- * (cta/hero/navigation/generic — capture-worthy but not persona evidence).
18
- */
19
- declare function classifyFeatures(f: SectionFeatures): {
20
- type: SemanticType;
21
- strength: 'strong' | 'weak';
22
- };
23
- /** Feature extraction from a live DOM element (browser paths). */
24
- declare function featuresFromElement(el: Element): SectionFeatures;
25
- /** Classify a page section into a semantic type (never null — falls back to
26
- * 'generic' so the caller can still capture attention on it). */
27
- declare function classifySection(el: Element): SemanticType;
1
+ import { b as SemanticType } from './classify-DYlSjkFP.js';
2
+ export { S as SEMANTIC_TYPES, a as SectionFeatures, c as classifyFeatures, d as classifySection, f as featuresFromElement } from './classify-DYlSjkFP.js';
28
3
 
29
4
  type CaptureClient = {
30
5
  track(event: {
@@ -56,4 +31,4 @@ type EngagementCaptureOptions = {
56
31
  };
57
32
  declare function startEngagementCapture(client: CaptureClient, opts: EngagementCaptureOptions): () => void;
58
33
 
59
- export { type EngagementCaptureOptions, SEMANTIC_TYPES, type SectionFeatures, type SemanticType, classifyFeatures, classifySection, featuresFromElement, startEngagementCapture };
34
+ export { type EngagementCaptureOptions, SemanticType, startEngagementCapture };
@@ -1,2 +1,2 @@
1
- "use strict";var b=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames,W=Object.getOwnPropertySymbols;var j=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var B=(t,e,n)=>e in t?b(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,T=(t,e)=>{for(var n in e||(e={}))j.call(e,n)&&B(t,n,e[n]);if(W)for(var n of W(e))Y.call(e,n)&&B(t,n,e[n]);return t};var z=(t,e)=>{for(var n in e)b(t,n,{get:e[n],enumerable:!0})},J=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of V(e))!j.call(t,a)&&a!==n&&b(t,a,{get:()=>e[a],enumerable:!(r=Q(e,a))||r.enumerable});return t};var X=t=>J(b({},"__esModule",{value:!0}),t);var ae={};z(ae,{SEMANTIC_TYPES:()=>E,classifyFeatures:()=>R,classifySection:()=>C,featuresFromElement:()=>I,startEngagementCapture:()=>H});module.exports=X(ae);var E=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],Z=[["pricing",/\b(pricing|price|plans?|subscriptions?|per month|\/mo|tier)\b/i],["faq",/\b(faq|frequently asked|common questions?)\b/i],["comparison",/\b(compare|comparison|versus|vs\.)\b/i],["social_proof",/\b(testimonial|reviews?|trusted by|loved by|customers?|logos|rated|case stud)\b/i],["trust",/\b(security|guarantee|privacy|compliance|secure|gdpr|soc ?2|encrypt)\b/i],["features",/\b(features?|how it works|benefits?|capabilit|what you get)\b/i]],ee=[["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|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]];function te(t){var n;let e=t.querySelector("h1, h2, h3");return((n=e==null?void 0:e.textContent)!=null?n:"").slice(0,160)}function R(t){if(t.tag==="nav"||t.tag==="footer")return{type:"navigation",strength:"weak"};let e=`${t.idClass} ${t.headingText}`.toLowerCase();for(let[n,r]of Z)if(r.test(e))return{type:n,strength:"strong"};for(let[n,r]of ee)if(r.test(t.bodyText))return{type:n,strength:"strong"};return t.actionCount>=1&&t.textLength>0&&t.textLength<200?{type:"cta",strength:"weak"}:t.tag==="header"?{type:"hero",strength:"weak"}:/\b(hero|headline|banner)\b/i.test(e)?{type:"hero",strength:"weak"}:{type:"generic",strength:"weak"}}function I(t){var n,r;let e=((n=t.textContent)!=null?n:"").replace(/\s+/g," ").trim();return{tag:t.tagName.toLowerCase(),idClass:`${t.id} ${String((r=t.className)!=null?r:"")}`,headingText:te(t),bodyText:e.slice(0,2e3),actionCount:t.querySelectorAll('a, button, [role="button"]').length,textLength:e.length}}function C(t){return R(I(t)).type}var ne=require("@sentientui/policy");function U(t,e,n,r){let a=[];{let l=!1,c=[],p=()=>{if(l)return;let m=Date.now();for(c.push(m);c.length>0&&m-c[0]>500;)c.shift();c.length>=3&&(l=!0,t("rage_click"))};e.addEventListener("click",p),a.push(()=>e.removeEventListener("click",p))}{let d=!1,g=l=>{if(d||!(l.target instanceof Node)||!e.contains(l.target)&&e!==l.target)return;d=!0;let c=typeof window!="undefined"?window.getSelection():null,p=c?c.toString().length:0;t("text_copy",{selectionLength:p})};document.addEventListener("copy",g),a.push(()=>document.removeEventListener("copy",g))}{let d=!1,g=!1,l=null,c=()=>{l!==null&&(clearTimeout(l),l=null)},p=()=>{d||!g||(c(),l=setTimeout(()=>{!d&&g&&(d=!0,t("scroll_hesitation"))},3e3))},m=()=>{c(),p()},w=v=>{for(let S of v)g=S.intersectionRatio>.3,g?p():c()},y=new IntersectionObserver(w,{threshold:[.3]});y.observe(e),window.addEventListener("scroll",m,{passive:!0}),a.push(()=>{y.disconnect(),window.removeEventListener("scroll",m),c()})}if((r==null?void 0:r.tabLoss)!==!1){let d=!1,g=n!=null?n:Date.now(),l=()=>{if(d||document.visibilityState!=="hidden")return;let c=Date.now()-g;c<15e3&&(d=!0,t("tab_loss",{timeOnPage:c}))};document.addEventListener("visibilitychange",l),a.push(()=>document.removeEventListener("visibilitychange",l))}return()=>{for(let d of a)d()}}function F(){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(e=>e==="1"||e==="yes")}var se="section, header, footer, nav, main > div, [data-sentient-section]",oe=2e4;function ie(t){let e=Array.from(t.querySelectorAll(se)),n=e.filter(r=>e.filter(a=>a!==r&&r.contains(a)).length<2);return n.filter(r=>!n.some(a=>a!==r&&a.contains(r)))}function re(t,e,n,r){try{fetch(`${e}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${t}`},body:JSON.stringify({pageUrl:n,sections:r})}).catch(()=>{})}catch(a){}}var k=()=>{};function H(t,e){var O,D,P,M,N,K,L,G,$;let n=(O=e.doc)!=null?O:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||F()||!e.apiKey||!e.apiKey.startsWith("pk_"))return k;let r=((D=e.apiBase)!=null?D:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),a=ie(n);if(a.length===0)return k;let d=new Map,g=new Map,l=new Map;for(let s of a){let o=s.getAttribute("data-sentient-type"),i=o&&E.includes(o)?o:null,u=(M=i!=null?i:(P=e.typeOf)==null?void 0:P.call(e,s))!=null?M:C(s),h=`nc-${u}`;d.set(s,h),g.set(h,u),i?l.set(h,"markup"):l.has(h)||l.set(h,"auto")}let c=(G=(L=(K=(N=n.defaultView)!=null?N:typeof window!="undefined"?window:void 0)==null?void 0:K.location)==null?void 0:L.pathname)!=null?G:"/";re(e.apiKey,r,c,[...g.entries()].map(([s,o])=>{var i;return{componentId:s,semanticType:o,source:(i=l.get(s))!=null?i:"auto"}}));let p=new Map,m=s=>{let o=p.get(s);return o||(o={ms:0,scroll:0,enterAt:null,intersecting:!1},p.set(s,o)),o},w=new IntersectionObserver(s=>{for(let o of s){let i=d.get(o.target);if(!i)continue;let u=m(i);o.isIntersecting?(u.intersecting=!0,u.enterAt=Date.now(),o.intersectionRatio>u.scroll&&(u.scroll=o.intersectionRatio)):(u.intersecting=!1,u.enterAt!=null&&(u.ms+=Date.now()-u.enterAt,u.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let s of d.keys())w.observe(s);let y=()=>{let s=Date.now();for(let[o,i]of p)if(i.enterAt!=null&&(i.ms+=s-i.enterAt,i.enterAt=null),!(i.ms<=0)){try{t.track({projectId:e.apiKey,componentId:o,eventType:"dwell",payload:{dwell_time:Math.round(i.ms),scroll_depth:Number(i.scroll.toFixed(2))}})}catch(u){}i.ms=0}},v=()=>{if(n.hidden)y();else{let s=Date.now();for(let o of p.values())o.intersecting&&(o.enterAt=s)}},S=!1,x=s=>{if(y(),s!=null&&s.persisted){S=!0;return}try{w.disconnect()}catch(o){}},_=s=>{if(!(s!=null&&s.persisted)||!S)return;S=!1;let o=Date.now();for(let i of p.values())i.enterAt=i.intersecting&&!n.hidden?o:null};n.addEventListener("visibilitychange",v);let f=($=n.defaultView)!=null?$:typeof window!="undefined"?window:void 0;f==null||f.addEventListener("pagehide",x),f==null||f.addEventListener("pageshow",_);let q=setInterval(()=>{if(n.hidden||S)return;y();let s=Date.now();for(let o of p.values())o.intersecting&&(o.enterAt=s)},oe),A=[];return e.microSignals&&[...d.entries()].forEach(([s,o],i)=>{A.push(U((u,h={})=>{try{t.track({projectId:e.apiKey,componentId:o,eventType:"micro_signal",payload:T({signalType:u},h)})}catch(le){}},s,void 0,{tabLoss:i===0}))}),()=>{y(),clearInterval(q),n.removeEventListener("visibilitychange",v),f==null||f.removeEventListener("pagehide",x),f==null||f.removeEventListener("pageshow",_);for(let s of A)s();try{w.disconnect()}catch(s){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
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:()=>A,classifySection:()=>k,featuresFromElement:()=>I,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 A(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 I(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 A(I(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,W,F;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 x=Y(r,n);d.push(w({componentId:T,semanticType:g,source:u?"markup":"auto"},x?{locator:x}:{}))}let s=(W=(G=(K=($=n.defaultView)!=null?$:typeof window!="undefined"?window:void 0)==null?void 0:K.location)==null?void 0:G.pathname)!=null?W:"/";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=(F=n.defaultView)!=null?F: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(x){}},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});
2
2
  //# sourceMappingURL=index-engagement.js.map