@sentientui/core 0.23.0 → 0.25.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 (51) hide show
  1. package/dist/chunk-2NVSFI4V.mjs +2 -0
  2. package/dist/chunk-2NVSFI4V.mjs.map +1 -0
  3. package/dist/chunk-RL5B6M4G.mjs +2 -0
  4. package/dist/chunk-RL5B6M4G.mjs.map +1 -0
  5. package/dist/chunk-WKXD7KI3.mjs +2 -0
  6. package/dist/chunk-WKXD7KI3.mjs.map +1 -0
  7. package/dist/chunk-Z6ILOYBD.mjs +2 -0
  8. package/dist/chunk-Z6ILOYBD.mjs.map +1 -0
  9. package/dist/{index-BbrtAtrY.d.ts → index-BjVvN7cU.d.ts} +1 -1
  10. package/dist/{index-CXxvWxCB.d.cts → index-Cd0gMdJv.d.cts} +1 -1
  11. package/dist/index-engagement.d.cts +1 -1
  12. package/dist/index-engagement.d.ts +1 -1
  13. package/dist/index-engagement.js +1 -1
  14. package/dist/index-engagement.js.map +1 -1
  15. package/dist/index-engagement.mjs +1 -1
  16. package/dist/index-engagement.mjs.map +1 -1
  17. package/dist/index-graph.d.cts +3 -3
  18. package/dist/index-graph.d.ts +3 -3
  19. package/dist/index-graph.js +1 -1
  20. package/dist/index-graph.js.map +1 -1
  21. package/dist/index-graph.mjs +1 -1
  22. package/dist/index-local.d.cts +2 -2
  23. package/dist/index-local.d.ts +2 -2
  24. package/dist/index-server.d.cts +10 -2
  25. package/dist/index-server.d.ts +10 -2
  26. package/dist/index-server.js +1 -1
  27. package/dist/index-server.js.map +1 -1
  28. package/dist/index-server.mjs +1 -1
  29. package/dist/index-server.mjs.map +1 -1
  30. package/dist/index-topics.d.cts +16 -1
  31. package/dist/index-topics.d.ts +16 -1
  32. package/dist/index-topics.js +1 -1
  33. package/dist/index-topics.js.map +1 -1
  34. package/dist/index-topics.mjs +1 -1
  35. package/dist/index-topics.mjs.map +1 -1
  36. package/dist/index.d.cts +2 -2
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/index.mjs +1 -1
  41. package/dist/{session-meta-BVvq5RBB.d.cts → session-meta-C-bMqVeq.d.cts} +32 -1
  42. package/dist/{session-meta-BVvq5RBB.d.ts → session-meta-C-bMqVeq.d.ts} +32 -1
  43. package/package.json +2 -2
  44. package/dist/chunk-2RH7I6AP.mjs +0 -2
  45. package/dist/chunk-2RH7I6AP.mjs.map +0 -1
  46. package/dist/chunk-EWP6FTHE.mjs +0 -2
  47. package/dist/chunk-EWP6FTHE.mjs.map +0 -1
  48. package/dist/chunk-KIP52DUJ.mjs +0 -2
  49. package/dist/chunk-KIP52DUJ.mjs.map +0 -1
  50. package/dist/chunk-QNEYUJJJ.mjs +0 -2
  51. package/dist/chunk-QNEYUJJJ.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index-topics.ts","../src/engagement/classify.ts","../src/engagement/topics.ts"],"sourcesContent":["// SERVER-ONLY entry for the rich section-topic vocabulary.\n//\n// Separate from ./engagement on purpose: the topic tables cost ~1.5 KB gzip,\n// which is 7% of the always-on snippet, on every page view of every customer\n// site — and the browser never reads them (classifySection returns only the\n// parent). Only the site-audit classification job imports this.\nexport {\n classifyTopic,\n CLASSIFIER_TOPICS,\n type TopicRule,\n} from './engagement/topics';\nexport type { SectionFeatures, SemanticType } from './engagement/classify';\n","// 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","// Rich topic vocabulary for section classification (Phase 2b, spec 2026-09-04).\n//\n// SERVER-ONLY. This module is deliberately NOT imported by ./classify.ts, which\n// the browser bundles: the tables below cost ~1.5 KB gzip, which is 7% of the\n// always-on snippet, on every page view of every customer site — for a layer\n// the browser never reads (classifySection returns only the parent).\n//\n// It shares structuralTopicOf/fallbackTopicOf with ./classify.ts, so client and\n// server agree on structure and fallbacks; only vocabulary richness differs.\n//\n// The topic→parent mapping is DUPLICATED from packages/policy/src/taxonomy.ts,\n// for the same bundle reason — core must not import the policy taxonomy.\n// apps/api/src/domain/classifier-taxonomy-sync.test.ts fails if the two drift.\n\nimport {\n structuralTopicOf,\n fallbackTopicOf,\n type SectionFeatures,\n type SemanticType,\n type TopicRule,\n} from './classify.js';\n\nexport const CLASSIFIER_TOPICS: readonly TopicRule[] = [\n { topic: 'navigation', parent: 'navigation', role: 'structural' },\n { topic: 'footer', parent: 'navigation', role: 'structural' },\n { topic: 'breadcrumb', parent: 'navigation', role: 'structural' },\n { topic: 'cookie_banner', parent: 'navigation', role: 'structural' },\n { topic: 'hero', parent: 'hero', role: 'converter' },\n { topic: 'pricing_plans', parent: 'pricing', role: 'converter' },\n { topic: 'quote', parent: 'pricing', role: 'converter' },\n { topic: 'financing', parent: 'pricing', role: 'persuader' },\n { topic: 'faq', parent: 'faq', role: 'persuader' },\n { topic: 'comparison', parent: 'comparison', role: 'persuader' },\n { topic: 'reviews', parent: 'social_proof', role: 'persuader' },\n { topic: 'testimonials', parent: 'social_proof', role: 'persuader' },\n { topic: 'case_study', parent: 'social_proof', role: 'persuader' },\n { topic: 'brands', parent: 'social_proof', role: 'persuader' },\n { topic: 'awards', parent: 'social_proof', role: 'persuader' },\n { topic: 'gallery', parent: 'social_proof', role: 'persuader' },\n { topic: 'stats', parent: 'social_proof', role: 'persuader' },\n { topic: 'insurance', parent: 'trust', role: 'persuader' },\n { topic: 'warranty', parent: 'trust', role: 'persuader' },\n { topic: 'certifications', parent: 'trust', role: 'persuader' },\n { topic: 'security', parent: 'trust', role: 'persuader' },\n { topic: 'guarantee', parent: 'trust', role: 'persuader' },\n { topic: 'about', parent: 'trust', role: 'persuader' },\n { topic: 'team', parent: 'trust', role: 'persuader' },\n { topic: 'services', parent: 'features', role: 'persuader' },\n { topic: 'features', parent: 'features', role: 'persuader' },\n { topic: 'process', parent: 'features', role: 'persuader' },\n { topic: 'menu', parent: 'features', role: 'persuader' },\n { topic: 'inventory', parent: 'features', role: 'converter' },\n { topic: 'integrations', parent: 'features', role: 'persuader' },\n { topic: 'booking', parent: 'cta', role: 'converter' },\n { topic: 'contact_form', parent: 'cta', role: 'converter' },\n { topic: 'newsletter', parent: 'cta', role: 'converter' },\n { topic: 'hours', parent: 'cta', role: 'persuader' },\n { topic: 'location', parent: 'cta', role: 'persuader' },\n { topic: 'cta', parent: 'cta', role: 'converter' },\n { topic: 'blog', parent: 'generic', role: 'persuader' },\n { topic: 'careers', parent: 'generic', role: 'persuader' },\n { topic: 'legal', parent: 'generic', role: 'persuader' },\n { topic: 'generic', parent: 'generic', role: 'persuader' },\n];\n\nconst RULE = new Map(CLASSIFIER_TOPICS.map((t) => [t.topic, t]));\n\nfunction ruleFor(topic: string): TopicRule {\n return RULE.get(topic) ?? { topic: 'generic', parent: 'generic', role: 'persuader' };\n}\n\n// ORDER IS LOAD-BEARING — first hit wins, so specific must precede general:\n// * `insurance` precedes `services`, or \"Insurance Approved Repairs\" becomes\n// a services section on the strength of \"repairs\".\n// * `brands` precedes `case_study`, or \"Brands We Work With\" matches \"work\".\n// * `pricing_plans` requires real pricing context — see fix 3 in ./classify.ts.\nconst TOPIC_KEYWORDS: Array<[string, RegExp]> = [\n ['cookie_banner', /\\bcookies?\\b[^.]{0,20}\\b(banner|notice|consent|policy)\\b|consent banner/i],\n ['breadcrumb', /\\bbreadcrumbs?\\b/i],\n ['insurance', /\\b(insurance|insurer|insured)\\b/i],\n ['warranty', /\\b(warrant(y|ies)|guarantees?|guaranteed)\\b/i],\n ['certifications', /\\b(certifi|accredit|licen[cs]ed)\\b|approved by|iso ?\\d/i],\n ['security', /\\b(security|privacy|compliance|gdpr|encrypt)\\b|soc ?2/i],\n ['about', /why choose|why us|about us|our story|who we are|years? (of )?(experience|serving)/i],\n ['team', /our team|meet the team|our people|our staff/i],\n ['reviews', /\\b(reviews?|ratings?|testimonial)\\b|what our customers say/i],\n ['brands', /\\b(brands?|partners?|manufacturers?|logos)\\b|we work with|trusted by|loved by/i],\n ['gallery', /\\b(galler(y|ies)|photos?)\\b|before and after/i],\n ['case_study', /case stud|selected work|portfolio|our projects?|recent work/i],\n ['awards', /\\b(awards?|accolades?)\\b/i],\n ['pricing_plans', /\\b(pricing|price list|per month|\\/mo|subscriptions?)\\b|\\bplans?\\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],\n ['quote', /get a quote|request a quote|free estimate|get an estimate/i],\n ['financing', /\\b(financ(e|ing)|instal?ments?)\\b|payment plan|pay monthly/i],\n ['booking', /\\b(book|booking|reserve|reservation|appointments?)\\b|schedule a/i],\n ['newsletter', /\\b(newsletter|subscribe)\\b|join our (list|mailing)|mailing list/i],\n ['contact_form', /contact us|get in touch|start a project|\\benquir|\\binquir|send us/i],\n ['hours', /opening hours|\\bhours\\b|open (mon|tue|wed|thu|fri|sat|sun)/i],\n ['location', /find us|our location|directions|where we are|visit us/i],\n ['menu', /\\b(menus?|dishes)\\b|our food/i],\n ['inventory', /\\bbest ?sellers?\\b|\\b(shop|products?|collection)\\b|our range|in stock/i],\n ['process', /our process|how it works|the process|what to expect|\\bsteps?\\b/i],\n ['services', /\\b(services?|repairs?|treatments?|specialit|specialt)\\b|what we (do|offer)/i],\n ['integrations', /\\bintegrations?\\b|works with|connects? to/i],\n ['features', /\\b(features?|benefits?|capabilit)\\b|what you get/i],\n ['faq', /\\bfaq\\b|frequently asked|common questions?/i],\n ['comparison', /\\b(compare|comparison|versus)\\b|\\bvs\\./i],\n ['blog', /\\b(blog|news|articles?|insights?)\\b/i],\n ['careers', /\\b(careers?|jobs?)\\b|join (the|our) team|we.re hiring/i],\n ['legal', /\\bterms\\b|privacy policy|cookie policy|\\blegal\\b/i],\n];\n\n/**\n * schema.org `@type` → topic. The highest-precision signal available, and free:\n * the crawler already has the HTML, and a site that publishes structured data\n * has told us what a section is rather than leaving us to infer it from copy.\n *\n * Ranked BELOW structural rules deliberately — many sites emit an Organization\n * or WebSite blob site-wide in the header or footer, and letting that outrank a\n * `<nav>` would turn every such navigation bar into an `about` section.\n */\nconst SCHEMA_TOPIC: Record<string, string> = {\n FAQPage: 'faq',\n Question: 'faq',\n Review: 'reviews',\n AggregateRating: 'reviews',\n Offer: 'pricing_plans',\n AggregateOffer: 'pricing_plans',\n PriceSpecification: 'pricing_plans',\n Service: 'services',\n Product: 'inventory',\n ItemList: 'inventory',\n Menu: 'menu',\n MenuSection: 'menu',\n LocalBusiness: 'location',\n PostalAddress: 'location',\n OpeningHoursSpecification: 'hours',\n BreadcrumbList: 'breadcrumb',\n ImageGallery: 'gallery',\n JobPosting: 'careers',\n BlogPosting: 'blog',\n Article: 'blog',\n Organization: 'about',\n};\n\nconst TOPIC_CONTENT: Array<[string, RegExp]> = [\n ['pricing_plans', /(?:[$€£]\\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 ['reviews', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['guarantee', /\\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|cancel anytime)\\b/i],\n ['security', /\\b(?:encrypted|soc ?2|iso ?27001|gdpr[- ]compliant)\\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/** Which classifier stage produced the label. Persisted as provenance (the\n * `page_semantics.source` column distinguishes structured_data from\n * heuristic), so it must name the stage that actually MATCHED — a section\n * that merely CONTAINS a JSON-LD blob but was classified by its `<nav>` tag\n * is heuristic evidence, not the site telling us what it is. */\nexport type ClassifierStage = 'structural' | 'structured_data' | 'keyword' | 'content' | 'fallback';\n\n/**\n * Classify a section into a topic, its parent, and its functional role.\n * Structure and fallbacks are the shared implementation from ./classify.ts, so\n * the browser cannot disagree with the server about what a `<nav>` is.\n */\nexport function classifyTopic(\n f: SectionFeatures,\n): TopicRule & { strength: 'strong' | 'weak'; stage: ClassifierStage } {\n const structural = structuralTopicOf(f);\n if (structural) return { ...ruleFor(structural), strength: 'strong', stage: 'structural' };\n\n // Stage 0: the site told us what this is. Beats every inferred keyword.\n for (const t of f.structuredTypes ?? []) {\n const topic = SCHEMA_TOPIC[t];\n if (topic) return { ...ruleFor(topic), strength: 'strong', stage: 'structured_data' };\n }\n\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [topic, re] of TOPIC_KEYWORDS) {\n if (re.test(hay)) return { ...ruleFor(topic), strength: 'strong', stage: 'keyword' };\n }\n for (const [topic, re] of TOPIC_CONTENT) {\n if (re.test(f.bodyText)) return { ...ruleFor(topic), strength: 'strong', stage: 'content' };\n }\n return { ...ruleFor(fallbackTopicOf(f)), strength: 'weak', stage: 'fallback' };\n}\n\nexport type { SemanticType, SectionFeatures, TopicRule };\n"],"mappings":"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","names":["index_topics_exports","__export","CLASSIFIER_TOPICS","classifyTopic","__toCommonJS","ARIA_TOPIC","structuralTopicOf","f","aria","hay","fallbackTopicOf","CLASSIFIER_TOPICS","RULE","t","ruleFor","topic","_a","TOPIC_KEYWORDS","SCHEMA_TOPIC","TOPIC_CONTENT","classifyTopic","f","structural","structuralTopicOf","__spreadProps","__spreadValues","hay","re","fallbackTopicOf"]}
1
+ {"version":3,"sources":["../src/index-topics.ts","../src/engagement/classify.ts","../src/engagement/topics.ts"],"sourcesContent":["// SERVER-ONLY entry for the rich section-topic vocabulary.\n//\n// Separate from ./engagement on purpose: the topic tables cost ~1.5 KB gzip,\n// which is 7% of the always-on snippet, on every page view of every customer\n// site — and the browser never reads them (classifySection returns only the\n// parent). Only the site-audit classification job imports this.\nexport {\n classifyTopic,\n CLASSIFIER_TOPICS,\n CLASSIFIER_VERSION,\n type TopicRule,\n} from './engagement/topics';\nexport type { SectionFeatures, SemanticType } from './engagement/classify';\n","// 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","// Rich topic vocabulary for section classification (Phase 2b, spec 2026-09-04).\n//\n// SERVER-ONLY. This module is deliberately NOT imported by ./classify.ts, which\n// the browser bundles: the tables below cost ~1.5 KB gzip, which is 7% of the\n// always-on snippet, on every page view of every customer site — for a layer\n// the browser never reads (classifySection returns only the parent).\n//\n// It shares structuralTopicOf/fallbackTopicOf with ./classify.ts, so client and\n// server agree on structure and fallbacks; only vocabulary richness differs.\n//\n// The topic→parent mapping is DUPLICATED from packages/policy/src/taxonomy.ts,\n// for the same bundle reason — core must not import the policy taxonomy.\n// apps/api/src/domain/classifier-taxonomy-sync.test.ts fails if the two drift.\n\nimport {\n structuralTopicOf,\n fallbackTopicOf,\n type SectionFeatures,\n type SemanticType,\n type TopicRule,\n} from './classify.js';\n\n/**\n * Version of the classification BEHAVIOUR (rules + taxonomy + stages), read by\n * the server's shadow-label rollout (section-classify.ts): a project whose\n * promoted version differs gets new labels written to SHADOW only, while its\n * old labels keep serving until an operator promotes (with one-click rollback,\n * the A4 pattern).\n *\n * BUMP THIS whenever a change here or in ./classify.ts alters what any section\n * would be labelled — rule edits, threshold changes, new stages, taxonomy\n * moves. Comment-only and refactor-only changes do not bump. The corpus gates\n * catch accuracy regressions; this constant is what keeps a deliberate\n * improvement from silently re-labelling every live project's serving rows on\n * their next audit.\n */\nexport const CLASSIFIER_VERSION = '2026-09-05';\n\nexport const CLASSIFIER_TOPICS: readonly TopicRule[] = [\n { topic: 'navigation', parent: 'navigation', role: 'structural' },\n { topic: 'footer', parent: 'navigation', role: 'structural' },\n { topic: 'breadcrumb', parent: 'navigation', role: 'structural' },\n { topic: 'cookie_banner', parent: 'navigation', role: 'structural' },\n { topic: 'hero', parent: 'hero', role: 'converter' },\n { topic: 'pricing_plans', parent: 'pricing', role: 'converter' },\n { topic: 'quote', parent: 'pricing', role: 'converter' },\n { topic: 'financing', parent: 'pricing', role: 'persuader' },\n { topic: 'faq', parent: 'faq', role: 'persuader' },\n { topic: 'comparison', parent: 'comparison', role: 'persuader' },\n { topic: 'reviews', parent: 'social_proof', role: 'persuader' },\n { topic: 'testimonials', parent: 'social_proof', role: 'persuader' },\n { topic: 'case_study', parent: 'social_proof', role: 'persuader' },\n { topic: 'brands', parent: 'social_proof', role: 'persuader' },\n { topic: 'awards', parent: 'social_proof', role: 'persuader' },\n { topic: 'gallery', parent: 'social_proof', role: 'persuader' },\n { topic: 'stats', parent: 'social_proof', role: 'persuader' },\n { topic: 'insurance', parent: 'trust', role: 'persuader' },\n { topic: 'warranty', parent: 'trust', role: 'persuader' },\n { topic: 'certifications', parent: 'trust', role: 'persuader' },\n { topic: 'security', parent: 'trust', role: 'persuader' },\n { topic: 'guarantee', parent: 'trust', role: 'persuader' },\n { topic: 'about', parent: 'trust', role: 'persuader' },\n { topic: 'team', parent: 'trust', role: 'persuader' },\n { topic: 'services', parent: 'features', role: 'persuader' },\n { topic: 'features', parent: 'features', role: 'persuader' },\n { topic: 'process', parent: 'features', role: 'persuader' },\n { topic: 'menu', parent: 'features', role: 'persuader' },\n { topic: 'inventory', parent: 'features', role: 'converter' },\n { topic: 'integrations', parent: 'features', role: 'persuader' },\n { topic: 'booking', parent: 'cta', role: 'converter' },\n { topic: 'contact_form', parent: 'cta', role: 'converter' },\n { topic: 'newsletter', parent: 'cta', role: 'converter' },\n { topic: 'hours', parent: 'cta', role: 'persuader' },\n { topic: 'location', parent: 'cta', role: 'persuader' },\n { topic: 'cta', parent: 'cta', role: 'converter' },\n { topic: 'blog', parent: 'generic', role: 'persuader' },\n { topic: 'careers', parent: 'generic', role: 'persuader' },\n { topic: 'legal', parent: 'generic', role: 'persuader' },\n { topic: 'generic', parent: 'generic', role: 'persuader' },\n];\n\nconst RULE = new Map(CLASSIFIER_TOPICS.map((t) => [t.topic, t]));\n\nfunction ruleFor(topic: string): TopicRule {\n return RULE.get(topic) ?? { topic: 'generic', parent: 'generic', role: 'persuader' };\n}\n\n// ORDER IS LOAD-BEARING — first hit wins, so specific must precede general:\n// * `insurance` precedes `services`, or \"Insurance Approved Repairs\" becomes\n// a services section on the strength of \"repairs\".\n// * `brands` precedes `case_study`, or \"Brands We Work With\" matches \"work\".\n// * `pricing_plans` requires real pricing context — see fix 3 in ./classify.ts.\nconst TOPIC_KEYWORDS: Array<[string, RegExp]> = [\n ['cookie_banner', /\\bcookies?\\b[^.]{0,20}\\b(banner|notice|consent|policy)\\b|consent banner/i],\n ['breadcrumb', /\\bbreadcrumbs?\\b/i],\n ['insurance', /\\b(insurance|insurer|insured)\\b/i],\n ['warranty', /\\b(warrant(y|ies)|guarantees?|guaranteed)\\b/i],\n ['certifications', /\\b(certifi|accredit|licen[cs]ed)\\b|approved by|iso ?\\d/i],\n ['security', /\\b(security|privacy|compliance|gdpr|encrypt)\\b|soc ?2/i],\n ['about', /why choose|why us|about us|our story|who we are|years? (of )?(experience|serving)/i],\n ['team', /our team|meet the team|our people|our staff/i],\n ['reviews', /\\b(reviews?|ratings?|testimonial)\\b|what our customers say/i],\n ['brands', /\\b(brands?|partners?|manufacturers?|logos)\\b|we work with|trusted by|loved by/i],\n ['gallery', /\\b(galler(y|ies)|photos?)\\b|before and after/i],\n ['case_study', /case stud|selected work|portfolio|our projects?|recent work/i],\n ['awards', /\\b(awards?|accolades?)\\b/i],\n ['pricing_plans', /\\b(pricing|price list|per month|\\/mo|subscriptions?)\\b|\\bplans?\\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],\n ['quote', /get a quote|request a quote|free estimate|get an estimate/i],\n ['financing', /\\b(financ(e|ing)|instal?ments?)\\b|payment plan|pay monthly/i],\n ['booking', /\\b(book|booking|reserve|reservation|appointments?)\\b|schedule a/i],\n ['newsletter', /\\b(newsletter|subscribe)\\b|join our (list|mailing)|mailing list/i],\n ['contact_form', /contact us|get in touch|start a project|\\benquir|\\binquir|send us/i],\n ['hours', /opening hours|\\bhours\\b|open (mon|tue|wed|thu|fri|sat|sun)/i],\n ['location', /find us|our location|directions|where we are|visit us/i],\n ['menu', /\\b(menus?|dishes)\\b|our food/i],\n ['inventory', /\\bbest ?sellers?\\b|\\b(shop|products?|collection)\\b|our range|in stock/i],\n ['process', /our process|how it works|the process|what to expect|\\bsteps?\\b/i],\n ['services', /\\b(services?|repairs?|treatments?|specialit|specialt)\\b|what we (do|offer)/i],\n ['integrations', /\\bintegrations?\\b|works with|connects? to/i],\n ['features', /\\b(features?|benefits?|capabilit)\\b|what you get/i],\n ['faq', /\\bfaq\\b|frequently asked|common questions?/i],\n ['comparison', /\\b(compare|comparison|versus)\\b|\\bvs\\./i],\n ['blog', /\\b(blog|news|articles?|insights?)\\b/i],\n ['careers', /\\b(careers?|jobs?)\\b|join (the|our) team|we.re hiring/i],\n ['legal', /\\bterms\\b|privacy policy|cookie policy|\\blegal\\b/i],\n];\n\n/**\n * schema.org `@type` → topic. The highest-precision signal available, and free:\n * the crawler already has the HTML, and a site that publishes structured data\n * has told us what a section is rather than leaving us to infer it from copy.\n *\n * Ranked BELOW structural rules deliberately — many sites emit an Organization\n * or WebSite blob site-wide in the header or footer, and letting that outrank a\n * `<nav>` would turn every such navigation bar into an `about` section.\n */\nconst SCHEMA_TOPIC: Record<string, string> = {\n FAQPage: 'faq',\n Question: 'faq',\n Review: 'reviews',\n AggregateRating: 'reviews',\n Offer: 'pricing_plans',\n AggregateOffer: 'pricing_plans',\n PriceSpecification: 'pricing_plans',\n Service: 'services',\n Product: 'inventory',\n ItemList: 'inventory',\n Menu: 'menu',\n MenuSection: 'menu',\n LocalBusiness: 'location',\n PostalAddress: 'location',\n OpeningHoursSpecification: 'hours',\n BreadcrumbList: 'breadcrumb',\n ImageGallery: 'gallery',\n JobPosting: 'careers',\n BlogPosting: 'blog',\n Article: 'blog',\n Organization: 'about',\n};\n\nconst TOPIC_CONTENT: Array<[string, RegExp]> = [\n ['pricing_plans', /(?:[$€£]\\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 ['reviews', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['guarantee', /\\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|cancel anytime)\\b/i],\n ['security', /\\b(?:encrypted|soc ?2|iso ?27001|gdpr[- ]compliant)\\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/** Which classifier stage produced the label. Persisted as provenance (the\n * `page_semantics.source` column distinguishes structured_data from\n * heuristic), so it must name the stage that actually MATCHED — a section\n * that merely CONTAINS a JSON-LD blob but was classified by its `<nav>` tag\n * is heuristic evidence, not the site telling us what it is. */\nexport type ClassifierStage = 'structural' | 'structured_data' | 'keyword' | 'content' | 'fallback';\n\n/**\n * Classify a section into a topic, its parent, and its functional role.\n * Structure and fallbacks are the shared implementation from ./classify.ts, so\n * the browser cannot disagree with the server about what a `<nav>` is.\n */\nexport function classifyTopic(\n f: SectionFeatures,\n): TopicRule & { strength: 'strong' | 'weak'; stage: ClassifierStage } {\n const structural = structuralTopicOf(f);\n if (structural) return { ...ruleFor(structural), strength: 'strong', stage: 'structural' };\n\n // Stage 0: the site told us what this is. Beats every inferred keyword.\n for (const t of f.structuredTypes ?? []) {\n const topic = SCHEMA_TOPIC[t];\n if (topic) return { ...ruleFor(topic), strength: 'strong', stage: 'structured_data' };\n }\n\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [topic, re] of TOPIC_KEYWORDS) {\n if (re.test(hay)) return { ...ruleFor(topic), strength: 'strong', stage: 'keyword' };\n }\n for (const [topic, re] of TOPIC_CONTENT) {\n if (re.test(f.bodyText)) return { ...ruleFor(topic), strength: 'strong', stage: 'content' };\n }\n return { ...ruleFor(fallbackTopicOf(f)), strength: 'weak', stage: 'fallback' };\n}\n\nexport type { SemanticType, SectionFeatures, TopicRule };\n"],"mappings":"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","names":["index_topics_exports","__export","CLASSIFIER_TOPICS","CLASSIFIER_VERSION","classifyTopic","__toCommonJS","ARIA_TOPIC","structuralTopicOf","f","aria","hay","fallbackTopicOf","CLASSIFIER_VERSION","CLASSIFIER_TOPICS","RULE","t","ruleFor","topic","_a","TOPIC_KEYWORDS","SCHEMA_TOPIC","TOPIC_CONTENT","classifyTopic","f","structural","structuralTopicOf","__spreadProps","__spreadValues","hay","re","fallbackTopicOf"]}
@@ -1,2 +1,2 @@
1
- import{b as c,c as p}from"./chunk-EWP6FTHE.mjs";import{a as t,b as o}from"./chunk-TMCGHANO.mjs";var u=[{topic:"navigation",parent:"navigation",role:"structural"},{topic:"footer",parent:"navigation",role:"structural"},{topic:"breadcrumb",parent:"navigation",role:"structural"},{topic:"cookie_banner",parent:"navigation",role:"structural"},{topic:"hero",parent:"hero",role:"converter"},{topic:"pricing_plans",parent:"pricing",role:"converter"},{topic:"quote",parent:"pricing",role:"converter"},{topic:"financing",parent:"pricing",role:"persuader"},{topic:"faq",parent:"faq",role:"persuader"},{topic:"comparison",parent:"comparison",role:"persuader"},{topic:"reviews",parent:"social_proof",role:"persuader"},{topic:"testimonials",parent:"social_proof",role:"persuader"},{topic:"case_study",parent:"social_proof",role:"persuader"},{topic:"brands",parent:"social_proof",role:"persuader"},{topic:"awards",parent:"social_proof",role:"persuader"},{topic:"gallery",parent:"social_proof",role:"persuader"},{topic:"stats",parent:"social_proof",role:"persuader"},{topic:"insurance",parent:"trust",role:"persuader"},{topic:"warranty",parent:"trust",role:"persuader"},{topic:"certifications",parent:"trust",role:"persuader"},{topic:"security",parent:"trust",role:"persuader"},{topic:"guarantee",parent:"trust",role:"persuader"},{topic:"about",parent:"trust",role:"persuader"},{topic:"team",parent:"trust",role:"persuader"},{topic:"services",parent:"features",role:"persuader"},{topic:"features",parent:"features",role:"persuader"},{topic:"process",parent:"features",role:"persuader"},{topic:"menu",parent:"features",role:"persuader"},{topic:"inventory",parent:"features",role:"converter"},{topic:"integrations",parent:"features",role:"persuader"},{topic:"booking",parent:"cta",role:"converter"},{topic:"contact_form",parent:"cta",role:"converter"},{topic:"newsletter",parent:"cta",role:"converter"},{topic:"hours",parent:"cta",role:"persuader"},{topic:"location",parent:"cta",role:"persuader"},{topic:"cta",parent:"cta",role:"converter"},{topic:"blog",parent:"generic",role:"persuader"},{topic:"careers",parent:"generic",role:"persuader"},{topic:"legal",parent:"generic",role:"persuader"},{topic:"generic",parent:"generic",role:"persuader"}],b=new Map(u.map(e=>[e.topic,e]));function s(e){var a;return(a=b.get(e))!=null?a:{topic:"generic",parent:"generic",role:"persuader"}}var g=[["cookie_banner",/\bcookies?\b[^.]{0,20}\b(banner|notice|consent|policy)\b|consent banner/i],["breadcrumb",/\bbreadcrumbs?\b/i],["insurance",/\b(insurance|insurer|insured)\b/i],["warranty",/\b(warrant(y|ies)|guarantees?|guaranteed)\b/i],["certifications",/\b(certifi|accredit|licen[cs]ed)\b|approved by|iso ?\d/i],["security",/\b(security|privacy|compliance|gdpr|encrypt)\b|soc ?2/i],["about",/why choose|why us|about us|our story|who we are|years? (of )?(experience|serving)/i],["team",/our team|meet the team|our people|our staff/i],["reviews",/\b(reviews?|ratings?|testimonial)\b|what our customers say/i],["brands",/\b(brands?|partners?|manufacturers?|logos)\b|we work with|trusted by|loved by/i],["gallery",/\b(galler(y|ies)|photos?)\b|before and after/i],["case_study",/case stud|selected work|portfolio|our projects?|recent work/i],["awards",/\b(awards?|accolades?)\b/i],["pricing_plans",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["quote",/get a quote|request a quote|free estimate|get an estimate/i],["financing",/\b(financ(e|ing)|instal?ments?)\b|payment plan|pay monthly/i],["booking",/\b(book|booking|reserve|reservation|appointments?)\b|schedule a/i],["newsletter",/\b(newsletter|subscribe)\b|join our (list|mailing)|mailing list/i],["contact_form",/contact us|get in touch|start a project|\benquir|\binquir|send us/i],["hours",/opening hours|\bhours\b|open (mon|tue|wed|thu|fri|sat|sun)/i],["location",/find us|our location|directions|where we are|visit us/i],["menu",/\b(menus?|dishes)\b|our food/i],["inventory",/\bbest ?sellers?\b|\b(shop|products?|collection)\b|our range|in stock/i],["process",/our process|how it works|the process|what to expect|\bsteps?\b/i],["services",/\b(services?|repairs?|treatments?|specialit|specialt)\b|what we (do|offer)/i],["integrations",/\bintegrations?\b|works with|connects? to/i],["features",/\b(features?|benefits?|capabilit)\b|what you get/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["blog",/\b(blog|news|articles?|insights?)\b/i],["careers",/\b(careers?|jobs?)\b|join (the|our) team|we.re hiring/i],["legal",/\bterms\b|privacy policy|cookie policy|\blegal\b/i]],d={FAQPage:"faq",Question:"faq",Review:"reviews",AggregateRating:"reviews",Offer:"pricing_plans",AggregateOffer:"pricing_plans",PriceSpecification:"pricing_plans",Service:"services",Product:"inventory",ItemList:"inventory",Menu:"menu",MenuSection:"menu",LocalBusiness:"location",PostalAddress:"location",OpeningHoursSpecification:"hours",BreadcrumbList:"breadcrumb",ImageGallery:"gallery",JobPosting:"careers",BlogPosting:"blog",Article:"blog",Organization:"about"},f=[["pricing_plans",/(?:[$€£]\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],["reviews",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["guarantee",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|cancel anytime)\b/i],["security",/\b(?:encrypted|soc ?2|iso ?27001|gdpr[- ]compliant)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]];function m(e){var n;let a=c(e);if(a)return o(t({},s(a)),{strength:"strong",stage:"structural"});for(let i of(n=e.structuredTypes)!=null?n:[]){let r=d[i];if(r)return o(t({},s(r)),{strength:"strong",stage:"structured_data"})}let l=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[i,r]of g)if(r.test(l))return o(t({},s(i)),{strength:"strong",stage:"keyword"});for(let[i,r]of f)if(r.test(e.bodyText))return o(t({},s(i)),{strength:"strong",stage:"content"});return o(t({},s(p(e))),{strength:"weak",stage:"fallback"})}export{u as CLASSIFIER_TOPICS,m as classifyTopic};
1
+ import{b as c,c as p}from"./chunk-Z6ILOYBD.mjs";import{a as t,b as o}from"./chunk-TMCGHANO.mjs";var b="2026-09-05",u=[{topic:"navigation",parent:"navigation",role:"structural"},{topic:"footer",parent:"navigation",role:"structural"},{topic:"breadcrumb",parent:"navigation",role:"structural"},{topic:"cookie_banner",parent:"navigation",role:"structural"},{topic:"hero",parent:"hero",role:"converter"},{topic:"pricing_plans",parent:"pricing",role:"converter"},{topic:"quote",parent:"pricing",role:"converter"},{topic:"financing",parent:"pricing",role:"persuader"},{topic:"faq",parent:"faq",role:"persuader"},{topic:"comparison",parent:"comparison",role:"persuader"},{topic:"reviews",parent:"social_proof",role:"persuader"},{topic:"testimonials",parent:"social_proof",role:"persuader"},{topic:"case_study",parent:"social_proof",role:"persuader"},{topic:"brands",parent:"social_proof",role:"persuader"},{topic:"awards",parent:"social_proof",role:"persuader"},{topic:"gallery",parent:"social_proof",role:"persuader"},{topic:"stats",parent:"social_proof",role:"persuader"},{topic:"insurance",parent:"trust",role:"persuader"},{topic:"warranty",parent:"trust",role:"persuader"},{topic:"certifications",parent:"trust",role:"persuader"},{topic:"security",parent:"trust",role:"persuader"},{topic:"guarantee",parent:"trust",role:"persuader"},{topic:"about",parent:"trust",role:"persuader"},{topic:"team",parent:"trust",role:"persuader"},{topic:"services",parent:"features",role:"persuader"},{topic:"features",parent:"features",role:"persuader"},{topic:"process",parent:"features",role:"persuader"},{topic:"menu",parent:"features",role:"persuader"},{topic:"inventory",parent:"features",role:"converter"},{topic:"integrations",parent:"features",role:"persuader"},{topic:"booking",parent:"cta",role:"converter"},{topic:"contact_form",parent:"cta",role:"converter"},{topic:"newsletter",parent:"cta",role:"converter"},{topic:"hours",parent:"cta",role:"persuader"},{topic:"location",parent:"cta",role:"persuader"},{topic:"cta",parent:"cta",role:"converter"},{topic:"blog",parent:"generic",role:"persuader"},{topic:"careers",parent:"generic",role:"persuader"},{topic:"legal",parent:"generic",role:"persuader"},{topic:"generic",parent:"generic",role:"persuader"}],g=new Map(u.map(e=>[e.topic,e]));function s(e){var a;return(a=g.get(e))!=null?a:{topic:"generic",parent:"generic",role:"persuader"}}var d=[["cookie_banner",/\bcookies?\b[^.]{0,20}\b(banner|notice|consent|policy)\b|consent banner/i],["breadcrumb",/\bbreadcrumbs?\b/i],["insurance",/\b(insurance|insurer|insured)\b/i],["warranty",/\b(warrant(y|ies)|guarantees?|guaranteed)\b/i],["certifications",/\b(certifi|accredit|licen[cs]ed)\b|approved by|iso ?\d/i],["security",/\b(security|privacy|compliance|gdpr|encrypt)\b|soc ?2/i],["about",/why choose|why us|about us|our story|who we are|years? (of )?(experience|serving)/i],["team",/our team|meet the team|our people|our staff/i],["reviews",/\b(reviews?|ratings?|testimonial)\b|what our customers say/i],["brands",/\b(brands?|partners?|manufacturers?|logos)\b|we work with|trusted by|loved by/i],["gallery",/\b(galler(y|ies)|photos?)\b|before and after/i],["case_study",/case stud|selected work|portfolio|our projects?|recent work/i],["awards",/\b(awards?|accolades?)\b/i],["pricing_plans",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["quote",/get a quote|request a quote|free estimate|get an estimate/i],["financing",/\b(financ(e|ing)|instal?ments?)\b|payment plan|pay monthly/i],["booking",/\b(book|booking|reserve|reservation|appointments?)\b|schedule a/i],["newsletter",/\b(newsletter|subscribe)\b|join our (list|mailing)|mailing list/i],["contact_form",/contact us|get in touch|start a project|\benquir|\binquir|send us/i],["hours",/opening hours|\bhours\b|open (mon|tue|wed|thu|fri|sat|sun)/i],["location",/find us|our location|directions|where we are|visit us/i],["menu",/\b(menus?|dishes)\b|our food/i],["inventory",/\bbest ?sellers?\b|\b(shop|products?|collection)\b|our range|in stock/i],["process",/our process|how it works|the process|what to expect|\bsteps?\b/i],["services",/\b(services?|repairs?|treatments?|specialit|specialt)\b|what we (do|offer)/i],["integrations",/\bintegrations?\b|works with|connects? to/i],["features",/\b(features?|benefits?|capabilit)\b|what you get/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["blog",/\b(blog|news|articles?|insights?)\b/i],["careers",/\b(careers?|jobs?)\b|join (the|our) team|we.re hiring/i],["legal",/\bterms\b|privacy policy|cookie policy|\blegal\b/i]],f={FAQPage:"faq",Question:"faq",Review:"reviews",AggregateRating:"reviews",Offer:"pricing_plans",AggregateOffer:"pricing_plans",PriceSpecification:"pricing_plans",Service:"services",Product:"inventory",ItemList:"inventory",Menu:"menu",MenuSection:"menu",LocalBusiness:"location",PostalAddress:"location",OpeningHoursSpecification:"hours",BreadcrumbList:"breadcrumb",ImageGallery:"gallery",JobPosting:"careers",BlogPosting:"blog",Article:"blog",Organization:"about"},m=[["pricing_plans",/(?:[$€£]\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],["reviews",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["guarantee",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|cancel anytime)\b/i],["security",/\b(?:encrypted|soc ?2|iso ?27001|gdpr[- ]compliant)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]];function y(e){var n;let a=c(e);if(a)return o(t({},s(a)),{strength:"strong",stage:"structural"});for(let i of(n=e.structuredTypes)!=null?n:[]){let r=f[i];if(r)return o(t({},s(r)),{strength:"strong",stage:"structured_data"})}let l=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[i,r]of d)if(r.test(l))return o(t({},s(i)),{strength:"strong",stage:"keyword"});for(let[i,r]of m)if(r.test(e.bodyText))return o(t({},s(i)),{strength:"strong",stage:"content"});return o(t({},s(p(e))),{strength:"weak",stage:"fallback"})}export{u as CLASSIFIER_TOPICS,b as CLASSIFIER_VERSION,y as classifyTopic};
2
2
  //# sourceMappingURL=index-topics.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engagement/topics.ts"],"sourcesContent":["// Rich topic vocabulary for section classification (Phase 2b, spec 2026-09-04).\n//\n// SERVER-ONLY. This module is deliberately NOT imported by ./classify.ts, which\n// the browser bundles: the tables below cost ~1.5 KB gzip, which is 7% of the\n// always-on snippet, on every page view of every customer site — for a layer\n// the browser never reads (classifySection returns only the parent).\n//\n// It shares structuralTopicOf/fallbackTopicOf with ./classify.ts, so client and\n// server agree on structure and fallbacks; only vocabulary richness differs.\n//\n// The topic→parent mapping is DUPLICATED from packages/policy/src/taxonomy.ts,\n// for the same bundle reason — core must not import the policy taxonomy.\n// apps/api/src/domain/classifier-taxonomy-sync.test.ts fails if the two drift.\n\nimport {\n structuralTopicOf,\n fallbackTopicOf,\n type SectionFeatures,\n type SemanticType,\n type TopicRule,\n} from './classify.js';\n\nexport const CLASSIFIER_TOPICS: readonly TopicRule[] = [\n { topic: 'navigation', parent: 'navigation', role: 'structural' },\n { topic: 'footer', parent: 'navigation', role: 'structural' },\n { topic: 'breadcrumb', parent: 'navigation', role: 'structural' },\n { topic: 'cookie_banner', parent: 'navigation', role: 'structural' },\n { topic: 'hero', parent: 'hero', role: 'converter' },\n { topic: 'pricing_plans', parent: 'pricing', role: 'converter' },\n { topic: 'quote', parent: 'pricing', role: 'converter' },\n { topic: 'financing', parent: 'pricing', role: 'persuader' },\n { topic: 'faq', parent: 'faq', role: 'persuader' },\n { topic: 'comparison', parent: 'comparison', role: 'persuader' },\n { topic: 'reviews', parent: 'social_proof', role: 'persuader' },\n { topic: 'testimonials', parent: 'social_proof', role: 'persuader' },\n { topic: 'case_study', parent: 'social_proof', role: 'persuader' },\n { topic: 'brands', parent: 'social_proof', role: 'persuader' },\n { topic: 'awards', parent: 'social_proof', role: 'persuader' },\n { topic: 'gallery', parent: 'social_proof', role: 'persuader' },\n { topic: 'stats', parent: 'social_proof', role: 'persuader' },\n { topic: 'insurance', parent: 'trust', role: 'persuader' },\n { topic: 'warranty', parent: 'trust', role: 'persuader' },\n { topic: 'certifications', parent: 'trust', role: 'persuader' },\n { topic: 'security', parent: 'trust', role: 'persuader' },\n { topic: 'guarantee', parent: 'trust', role: 'persuader' },\n { topic: 'about', parent: 'trust', role: 'persuader' },\n { topic: 'team', parent: 'trust', role: 'persuader' },\n { topic: 'services', parent: 'features', role: 'persuader' },\n { topic: 'features', parent: 'features', role: 'persuader' },\n { topic: 'process', parent: 'features', role: 'persuader' },\n { topic: 'menu', parent: 'features', role: 'persuader' },\n { topic: 'inventory', parent: 'features', role: 'converter' },\n { topic: 'integrations', parent: 'features', role: 'persuader' },\n { topic: 'booking', parent: 'cta', role: 'converter' },\n { topic: 'contact_form', parent: 'cta', role: 'converter' },\n { topic: 'newsletter', parent: 'cta', role: 'converter' },\n { topic: 'hours', parent: 'cta', role: 'persuader' },\n { topic: 'location', parent: 'cta', role: 'persuader' },\n { topic: 'cta', parent: 'cta', role: 'converter' },\n { topic: 'blog', parent: 'generic', role: 'persuader' },\n { topic: 'careers', parent: 'generic', role: 'persuader' },\n { topic: 'legal', parent: 'generic', role: 'persuader' },\n { topic: 'generic', parent: 'generic', role: 'persuader' },\n];\n\nconst RULE = new Map(CLASSIFIER_TOPICS.map((t) => [t.topic, t]));\n\nfunction ruleFor(topic: string): TopicRule {\n return RULE.get(topic) ?? { topic: 'generic', parent: 'generic', role: 'persuader' };\n}\n\n// ORDER IS LOAD-BEARING — first hit wins, so specific must precede general:\n// * `insurance` precedes `services`, or \"Insurance Approved Repairs\" becomes\n// a services section on the strength of \"repairs\".\n// * `brands` precedes `case_study`, or \"Brands We Work With\" matches \"work\".\n// * `pricing_plans` requires real pricing context — see fix 3 in ./classify.ts.\nconst TOPIC_KEYWORDS: Array<[string, RegExp]> = [\n ['cookie_banner', /\\bcookies?\\b[^.]{0,20}\\b(banner|notice|consent|policy)\\b|consent banner/i],\n ['breadcrumb', /\\bbreadcrumbs?\\b/i],\n ['insurance', /\\b(insurance|insurer|insured)\\b/i],\n ['warranty', /\\b(warrant(y|ies)|guarantees?|guaranteed)\\b/i],\n ['certifications', /\\b(certifi|accredit|licen[cs]ed)\\b|approved by|iso ?\\d/i],\n ['security', /\\b(security|privacy|compliance|gdpr|encrypt)\\b|soc ?2/i],\n ['about', /why choose|why us|about us|our story|who we are|years? (of )?(experience|serving)/i],\n ['team', /our team|meet the team|our people|our staff/i],\n ['reviews', /\\b(reviews?|ratings?|testimonial)\\b|what our customers say/i],\n ['brands', /\\b(brands?|partners?|manufacturers?|logos)\\b|we work with|trusted by|loved by/i],\n ['gallery', /\\b(galler(y|ies)|photos?)\\b|before and after/i],\n ['case_study', /case stud|selected work|portfolio|our projects?|recent work/i],\n ['awards', /\\b(awards?|accolades?)\\b/i],\n ['pricing_plans', /\\b(pricing|price list|per month|\\/mo|subscriptions?)\\b|\\bplans?\\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],\n ['quote', /get a quote|request a quote|free estimate|get an estimate/i],\n ['financing', /\\b(financ(e|ing)|instal?ments?)\\b|payment plan|pay monthly/i],\n ['booking', /\\b(book|booking|reserve|reservation|appointments?)\\b|schedule a/i],\n ['newsletter', /\\b(newsletter|subscribe)\\b|join our (list|mailing)|mailing list/i],\n ['contact_form', /contact us|get in touch|start a project|\\benquir|\\binquir|send us/i],\n ['hours', /opening hours|\\bhours\\b|open (mon|tue|wed|thu|fri|sat|sun)/i],\n ['location', /find us|our location|directions|where we are|visit us/i],\n ['menu', /\\b(menus?|dishes)\\b|our food/i],\n ['inventory', /\\bbest ?sellers?\\b|\\b(shop|products?|collection)\\b|our range|in stock/i],\n ['process', /our process|how it works|the process|what to expect|\\bsteps?\\b/i],\n ['services', /\\b(services?|repairs?|treatments?|specialit|specialt)\\b|what we (do|offer)/i],\n ['integrations', /\\bintegrations?\\b|works with|connects? to/i],\n ['features', /\\b(features?|benefits?|capabilit)\\b|what you get/i],\n ['faq', /\\bfaq\\b|frequently asked|common questions?/i],\n ['comparison', /\\b(compare|comparison|versus)\\b|\\bvs\\./i],\n ['blog', /\\b(blog|news|articles?|insights?)\\b/i],\n ['careers', /\\b(careers?|jobs?)\\b|join (the|our) team|we.re hiring/i],\n ['legal', /\\bterms\\b|privacy policy|cookie policy|\\blegal\\b/i],\n];\n\n/**\n * schema.org `@type` → topic. The highest-precision signal available, and free:\n * the crawler already has the HTML, and a site that publishes structured data\n * has told us what a section is rather than leaving us to infer it from copy.\n *\n * Ranked BELOW structural rules deliberately — many sites emit an Organization\n * or WebSite blob site-wide in the header or footer, and letting that outrank a\n * `<nav>` would turn every such navigation bar into an `about` section.\n */\nconst SCHEMA_TOPIC: Record<string, string> = {\n FAQPage: 'faq',\n Question: 'faq',\n Review: 'reviews',\n AggregateRating: 'reviews',\n Offer: 'pricing_plans',\n AggregateOffer: 'pricing_plans',\n PriceSpecification: 'pricing_plans',\n Service: 'services',\n Product: 'inventory',\n ItemList: 'inventory',\n Menu: 'menu',\n MenuSection: 'menu',\n LocalBusiness: 'location',\n PostalAddress: 'location',\n OpeningHoursSpecification: 'hours',\n BreadcrumbList: 'breadcrumb',\n ImageGallery: 'gallery',\n JobPosting: 'careers',\n BlogPosting: 'blog',\n Article: 'blog',\n Organization: 'about',\n};\n\nconst TOPIC_CONTENT: Array<[string, RegExp]> = [\n ['pricing_plans', /(?:[$€£]\\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 ['reviews', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['guarantee', /\\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|cancel anytime)\\b/i],\n ['security', /\\b(?:encrypted|soc ?2|iso ?27001|gdpr[- ]compliant)\\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/** Which classifier stage produced the label. Persisted as provenance (the\n * `page_semantics.source` column distinguishes structured_data from\n * heuristic), so it must name the stage that actually MATCHED — a section\n * that merely CONTAINS a JSON-LD blob but was classified by its `<nav>` tag\n * is heuristic evidence, not the site telling us what it is. */\nexport type ClassifierStage = 'structural' | 'structured_data' | 'keyword' | 'content' | 'fallback';\n\n/**\n * Classify a section into a topic, its parent, and its functional role.\n * Structure and fallbacks are the shared implementation from ./classify.ts, so\n * the browser cannot disagree with the server about what a `<nav>` is.\n */\nexport function classifyTopic(\n f: SectionFeatures,\n): TopicRule & { strength: 'strong' | 'weak'; stage: ClassifierStage } {\n const structural = structuralTopicOf(f);\n if (structural) return { ...ruleFor(structural), strength: 'strong', stage: 'structural' };\n\n // Stage 0: the site told us what this is. Beats every inferred keyword.\n for (const t of f.structuredTypes ?? []) {\n const topic = SCHEMA_TOPIC[t];\n if (topic) return { ...ruleFor(topic), strength: 'strong', stage: 'structured_data' };\n }\n\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [topic, re] of TOPIC_KEYWORDS) {\n if (re.test(hay)) return { ...ruleFor(topic), strength: 'strong', stage: 'keyword' };\n }\n for (const [topic, re] of TOPIC_CONTENT) {\n if (re.test(f.bodyText)) return { ...ruleFor(topic), strength: 'strong', stage: 'content' };\n }\n return { ...ruleFor(fallbackTopicOf(f)), strength: 'weak', stage: 'fallback' };\n}\n\nexport type { SemanticType, SectionFeatures, TopicRule };\n"],"mappings":"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","names":["CLASSIFIER_TOPICS","RULE","t","ruleFor","topic","_a","TOPIC_KEYWORDS","SCHEMA_TOPIC","TOPIC_CONTENT","classifyTopic","f","structural","structuralTopicOf","__spreadProps","__spreadValues","hay","re","fallbackTopicOf"]}
1
+ {"version":3,"sources":["../src/engagement/topics.ts"],"sourcesContent":["// Rich topic vocabulary for section classification (Phase 2b, spec 2026-09-04).\n//\n// SERVER-ONLY. This module is deliberately NOT imported by ./classify.ts, which\n// the browser bundles: the tables below cost ~1.5 KB gzip, which is 7% of the\n// always-on snippet, on every page view of every customer site — for a layer\n// the browser never reads (classifySection returns only the parent).\n//\n// It shares structuralTopicOf/fallbackTopicOf with ./classify.ts, so client and\n// server agree on structure and fallbacks; only vocabulary richness differs.\n//\n// The topic→parent mapping is DUPLICATED from packages/policy/src/taxonomy.ts,\n// for the same bundle reason — core must not import the policy taxonomy.\n// apps/api/src/domain/classifier-taxonomy-sync.test.ts fails if the two drift.\n\nimport {\n structuralTopicOf,\n fallbackTopicOf,\n type SectionFeatures,\n type SemanticType,\n type TopicRule,\n} from './classify.js';\n\n/**\n * Version of the classification BEHAVIOUR (rules + taxonomy + stages), read by\n * the server's shadow-label rollout (section-classify.ts): a project whose\n * promoted version differs gets new labels written to SHADOW only, while its\n * old labels keep serving until an operator promotes (with one-click rollback,\n * the A4 pattern).\n *\n * BUMP THIS whenever a change here or in ./classify.ts alters what any section\n * would be labelled — rule edits, threshold changes, new stages, taxonomy\n * moves. Comment-only and refactor-only changes do not bump. The corpus gates\n * catch accuracy regressions; this constant is what keeps a deliberate\n * improvement from silently re-labelling every live project's serving rows on\n * their next audit.\n */\nexport const CLASSIFIER_VERSION = '2026-09-05';\n\nexport const CLASSIFIER_TOPICS: readonly TopicRule[] = [\n { topic: 'navigation', parent: 'navigation', role: 'structural' },\n { topic: 'footer', parent: 'navigation', role: 'structural' },\n { topic: 'breadcrumb', parent: 'navigation', role: 'structural' },\n { topic: 'cookie_banner', parent: 'navigation', role: 'structural' },\n { topic: 'hero', parent: 'hero', role: 'converter' },\n { topic: 'pricing_plans', parent: 'pricing', role: 'converter' },\n { topic: 'quote', parent: 'pricing', role: 'converter' },\n { topic: 'financing', parent: 'pricing', role: 'persuader' },\n { topic: 'faq', parent: 'faq', role: 'persuader' },\n { topic: 'comparison', parent: 'comparison', role: 'persuader' },\n { topic: 'reviews', parent: 'social_proof', role: 'persuader' },\n { topic: 'testimonials', parent: 'social_proof', role: 'persuader' },\n { topic: 'case_study', parent: 'social_proof', role: 'persuader' },\n { topic: 'brands', parent: 'social_proof', role: 'persuader' },\n { topic: 'awards', parent: 'social_proof', role: 'persuader' },\n { topic: 'gallery', parent: 'social_proof', role: 'persuader' },\n { topic: 'stats', parent: 'social_proof', role: 'persuader' },\n { topic: 'insurance', parent: 'trust', role: 'persuader' },\n { topic: 'warranty', parent: 'trust', role: 'persuader' },\n { topic: 'certifications', parent: 'trust', role: 'persuader' },\n { topic: 'security', parent: 'trust', role: 'persuader' },\n { topic: 'guarantee', parent: 'trust', role: 'persuader' },\n { topic: 'about', parent: 'trust', role: 'persuader' },\n { topic: 'team', parent: 'trust', role: 'persuader' },\n { topic: 'services', parent: 'features', role: 'persuader' },\n { topic: 'features', parent: 'features', role: 'persuader' },\n { topic: 'process', parent: 'features', role: 'persuader' },\n { topic: 'menu', parent: 'features', role: 'persuader' },\n { topic: 'inventory', parent: 'features', role: 'converter' },\n { topic: 'integrations', parent: 'features', role: 'persuader' },\n { topic: 'booking', parent: 'cta', role: 'converter' },\n { topic: 'contact_form', parent: 'cta', role: 'converter' },\n { topic: 'newsletter', parent: 'cta', role: 'converter' },\n { topic: 'hours', parent: 'cta', role: 'persuader' },\n { topic: 'location', parent: 'cta', role: 'persuader' },\n { topic: 'cta', parent: 'cta', role: 'converter' },\n { topic: 'blog', parent: 'generic', role: 'persuader' },\n { topic: 'careers', parent: 'generic', role: 'persuader' },\n { topic: 'legal', parent: 'generic', role: 'persuader' },\n { topic: 'generic', parent: 'generic', role: 'persuader' },\n];\n\nconst RULE = new Map(CLASSIFIER_TOPICS.map((t) => [t.topic, t]));\n\nfunction ruleFor(topic: string): TopicRule {\n return RULE.get(topic) ?? { topic: 'generic', parent: 'generic', role: 'persuader' };\n}\n\n// ORDER IS LOAD-BEARING — first hit wins, so specific must precede general:\n// * `insurance` precedes `services`, or \"Insurance Approved Repairs\" becomes\n// a services section on the strength of \"repairs\".\n// * `brands` precedes `case_study`, or \"Brands We Work With\" matches \"work\".\n// * `pricing_plans` requires real pricing context — see fix 3 in ./classify.ts.\nconst TOPIC_KEYWORDS: Array<[string, RegExp]> = [\n ['cookie_banner', /\\bcookies?\\b[^.]{0,20}\\b(banner|notice|consent|policy)\\b|consent banner/i],\n ['breadcrumb', /\\bbreadcrumbs?\\b/i],\n ['insurance', /\\b(insurance|insurer|insured)\\b/i],\n ['warranty', /\\b(warrant(y|ies)|guarantees?|guaranteed)\\b/i],\n ['certifications', /\\b(certifi|accredit|licen[cs]ed)\\b|approved by|iso ?\\d/i],\n ['security', /\\b(security|privacy|compliance|gdpr|encrypt)\\b|soc ?2/i],\n ['about', /why choose|why us|about us|our story|who we are|years? (of )?(experience|serving)/i],\n ['team', /our team|meet the team|our people|our staff/i],\n ['reviews', /\\b(reviews?|ratings?|testimonial)\\b|what our customers say/i],\n ['brands', /\\b(brands?|partners?|manufacturers?|logos)\\b|we work with|trusted by|loved by/i],\n ['gallery', /\\b(galler(y|ies)|photos?)\\b|before and after/i],\n ['case_study', /case stud|selected work|portfolio|our projects?|recent work/i],\n ['awards', /\\b(awards?|accolades?)\\b/i],\n ['pricing_plans', /\\b(pricing|price list|per month|\\/mo|subscriptions?)\\b|\\bplans?\\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],\n ['quote', /get a quote|request a quote|free estimate|get an estimate/i],\n ['financing', /\\b(financ(e|ing)|instal?ments?)\\b|payment plan|pay monthly/i],\n ['booking', /\\b(book|booking|reserve|reservation|appointments?)\\b|schedule a/i],\n ['newsletter', /\\b(newsletter|subscribe)\\b|join our (list|mailing)|mailing list/i],\n ['contact_form', /contact us|get in touch|start a project|\\benquir|\\binquir|send us/i],\n ['hours', /opening hours|\\bhours\\b|open (mon|tue|wed|thu|fri|sat|sun)/i],\n ['location', /find us|our location|directions|where we are|visit us/i],\n ['menu', /\\b(menus?|dishes)\\b|our food/i],\n ['inventory', /\\bbest ?sellers?\\b|\\b(shop|products?|collection)\\b|our range|in stock/i],\n ['process', /our process|how it works|the process|what to expect|\\bsteps?\\b/i],\n ['services', /\\b(services?|repairs?|treatments?|specialit|specialt)\\b|what we (do|offer)/i],\n ['integrations', /\\bintegrations?\\b|works with|connects? to/i],\n ['features', /\\b(features?|benefits?|capabilit)\\b|what you get/i],\n ['faq', /\\bfaq\\b|frequently asked|common questions?/i],\n ['comparison', /\\b(compare|comparison|versus)\\b|\\bvs\\./i],\n ['blog', /\\b(blog|news|articles?|insights?)\\b/i],\n ['careers', /\\b(careers?|jobs?)\\b|join (the|our) team|we.re hiring/i],\n ['legal', /\\bterms\\b|privacy policy|cookie policy|\\blegal\\b/i],\n];\n\n/**\n * schema.org `@type` → topic. The highest-precision signal available, and free:\n * the crawler already has the HTML, and a site that publishes structured data\n * has told us what a section is rather than leaving us to infer it from copy.\n *\n * Ranked BELOW structural rules deliberately — many sites emit an Organization\n * or WebSite blob site-wide in the header or footer, and letting that outrank a\n * `<nav>` would turn every such navigation bar into an `about` section.\n */\nconst SCHEMA_TOPIC: Record<string, string> = {\n FAQPage: 'faq',\n Question: 'faq',\n Review: 'reviews',\n AggregateRating: 'reviews',\n Offer: 'pricing_plans',\n AggregateOffer: 'pricing_plans',\n PriceSpecification: 'pricing_plans',\n Service: 'services',\n Product: 'inventory',\n ItemList: 'inventory',\n Menu: 'menu',\n MenuSection: 'menu',\n LocalBusiness: 'location',\n PostalAddress: 'location',\n OpeningHoursSpecification: 'hours',\n BreadcrumbList: 'breadcrumb',\n ImageGallery: 'gallery',\n JobPosting: 'careers',\n BlogPosting: 'blog',\n Article: 'blog',\n Organization: 'about',\n};\n\nconst TOPIC_CONTENT: Array<[string, RegExp]> = [\n ['pricing_plans', /(?:[$€£]\\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 ['reviews', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['guarantee', /\\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|cancel anytime)\\b/i],\n ['security', /\\b(?:encrypted|soc ?2|iso ?27001|gdpr[- ]compliant)\\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/** Which classifier stage produced the label. Persisted as provenance (the\n * `page_semantics.source` column distinguishes structured_data from\n * heuristic), so it must name the stage that actually MATCHED — a section\n * that merely CONTAINS a JSON-LD blob but was classified by its `<nav>` tag\n * is heuristic evidence, not the site telling us what it is. */\nexport type ClassifierStage = 'structural' | 'structured_data' | 'keyword' | 'content' | 'fallback';\n\n/**\n * Classify a section into a topic, its parent, and its functional role.\n * Structure and fallbacks are the shared implementation from ./classify.ts, so\n * the browser cannot disagree with the server about what a `<nav>` is.\n */\nexport function classifyTopic(\n f: SectionFeatures,\n): TopicRule & { strength: 'strong' | 'weak'; stage: ClassifierStage } {\n const structural = structuralTopicOf(f);\n if (structural) return { ...ruleFor(structural), strength: 'strong', stage: 'structural' };\n\n // Stage 0: the site told us what this is. Beats every inferred keyword.\n for (const t of f.structuredTypes ?? []) {\n const topic = SCHEMA_TOPIC[t];\n if (topic) return { ...ruleFor(topic), strength: 'strong', stage: 'structured_data' };\n }\n\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [topic, re] of TOPIC_KEYWORDS) {\n if (re.test(hay)) return { ...ruleFor(topic), strength: 'strong', stage: 'keyword' };\n }\n for (const [topic, re] of TOPIC_CONTENT) {\n if (re.test(f.bodyText)) return { ...ruleFor(topic), strength: 'strong', stage: 'content' };\n }\n return { ...ruleFor(fallbackTopicOf(f)), strength: 'weak', stage: 'fallback' };\n}\n\nexport type { SemanticType, SectionFeatures, TopicRule };\n"],"mappings":"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","names":["CLASSIFIER_VERSION","CLASSIFIER_TOPICS","RULE","t","ruleFor","topic","_a","TOPIC_KEYWORDS","SCHEMA_TOPIC","TOPIC_CONTENT","classifyTopic","f","structural","structuralTopicOf","__spreadProps","__spreadValues","hay","re","fallbackTopicOf"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as AssignResult, a as Assignment, B as BLOCK_ALIGNS, c as BLOCK_EMPHASES, d as BLOCK_FITS, e as BLOCK_GAPS, f as BLOCK_GRID_COLUMNS, g as BLOCK_HEADING_LEVELS, h as BLOCK_JUSTIFIES, i as BLOCK_RATIOS, j as BLOCK_SIZES, k as BLOCK_TEXT_ALIGNS, l as BLOCK_TONES, m as BLOCK_WEIGHTS, n as BadgeBlock, o as BlockAlign, p as BlockEmphasis, q as BlockFit, r as BlockGap, s as BlockJustify, t as BlockNode, u as BlockRatio, v as BlockSize, w as BlockTextAlign, x as BlockTone, y as BlockWeight, z as ButtonBlock, C as ComponentGoalOptions, D as ComponentWeightEntry, E as CompoundLocator, F as DecideInput, G as DecideOutcome, H as DecisionSnapshot, J as EventType, K as GoalDefinition, L as GoalOptions, N as GraphConfig, O as GraphSnapshot, P as GridBlock, Q as HeadingBlock, R as ImageBlock, S as LEGACY_SESSION_COOKIE_NAME, T as LOCAL_MODE_BANNER, U as LinkBlock, V as MAX_BLOCK_ARMS, W as MAX_BLOCK_CHILDREN, X as MAX_BLOCK_DEPTH, Y as MAX_BLOCK_NODES, Z as MAX_BLOCK_TEXT_LEN, _ as MicroSignalEmitter, $ as MicroSignalType, a0 as PROD_KEYLESS_ERROR, a2 as QueueConfig, a3 as SNAPSHOT_STORAGE_KEY_PREFIX, a4 as SectionMapEntry, a5 as SentientClient, a6 as SentientConfig, a7 as SentientEvent, a8 as SessionConfig, a9 as SessionManager, aa as SitePalette, ab as SlotConfigEntry, ac as SlotOps, ad as SpacerBlock, ae as StackBlock, af as TextBlock, ag as WeightEntry, ah as _registerConsentUpgradeInit, ai as attachMicroSignalDetectors, aj as grantConsent, ak as init, al as isDoNotTrackEnabled, am as readSnapshot, an as renderPrePaintScript, ap as sessionCookieName, aq as writeSnapshot } from './index-CXxvWxCB.cjs';
2
- export { A as AGENT_INTENTS, a as AgentIntent, b as SlotDeclInput, c as agentIntent, d as agentUaList, e as armOfResult, f as baselineResultFor, g as baselineSlots, i as classifiedAgents, j as deriveSessionSegment, k as detectDeviceClass, l as detectTimeOfDay, m as detectTrafficSource, n as matchedAgentToken, r as referrerDomainFromReferer, t as toWireSlot, u as uaTokenMatch } from './session-meta-BVvq5RBB.cjs';
1
+ export { A as AssignResult, a as Assignment, B as BLOCK_ALIGNS, c as BLOCK_EMPHASES, d as BLOCK_FITS, e as BLOCK_GAPS, f as BLOCK_GRID_COLUMNS, g as BLOCK_HEADING_LEVELS, h as BLOCK_JUSTIFIES, i as BLOCK_RATIOS, j as BLOCK_SIZES, k as BLOCK_TEXT_ALIGNS, l as BLOCK_TONES, m as BLOCK_WEIGHTS, n as BadgeBlock, o as BlockAlign, p as BlockEmphasis, q as BlockFit, r as BlockGap, s as BlockJustify, t as BlockNode, u as BlockRatio, v as BlockSize, w as BlockTextAlign, x as BlockTone, y as BlockWeight, z as ButtonBlock, C as ComponentGoalOptions, D as ComponentWeightEntry, E as CompoundLocator, F as DecideInput, G as DecideOutcome, H as DecisionSnapshot, J as EventType, K as GoalDefinition, L as GoalOptions, N as GraphConfig, O as GraphSnapshot, P as GridBlock, Q as HeadingBlock, R as ImageBlock, S as LEGACY_SESSION_COOKIE_NAME, T as LOCAL_MODE_BANNER, U as LinkBlock, V as MAX_BLOCK_ARMS, W as MAX_BLOCK_CHILDREN, X as MAX_BLOCK_DEPTH, Y as MAX_BLOCK_NODES, Z as MAX_BLOCK_TEXT_LEN, _ as MicroSignalEmitter, $ as MicroSignalType, a0 as PROD_KEYLESS_ERROR, a2 as QueueConfig, a3 as SNAPSHOT_STORAGE_KEY_PREFIX, a4 as SectionMapEntry, a5 as SentientClient, a6 as SentientConfig, a7 as SentientEvent, a8 as SessionConfig, a9 as SessionManager, aa as SitePalette, ab as SlotConfigEntry, ac as SlotOps, ad as SpacerBlock, ae as StackBlock, af as TextBlock, ag as WeightEntry, ah as _registerConsentUpgradeInit, ai as attachMicroSignalDetectors, aj as grantConsent, ak as init, al as isDoNotTrackEnabled, am as readSnapshot, an as renderPrePaintScript, ap as sessionCookieName, aq as writeSnapshot } from './index-Cd0gMdJv.cjs';
2
+ export { A as AGENT_INTENTS, a as AgentIntent, C as CLICK_ID_KEYS, b as SlotDeclInput, c as agentIntent, d as agentUaList, e as armOfResult, f as baselineResultFor, g as baselineSlots, i as classifiedAgents, j as deriveSessionSegment, k as detectDeviceClass, l as detectTimeOfDay, m as detectTrafficSource, n as extractTrackedParams, o as matchedAgentToken, r as referrerDomainFromReferer, t as toWireSlot, u as uaTokenMatch } from './session-meta-C-bMqVeq.cjs';
3
3
  export { SlotResult } from '@sentientui/policy';
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as AssignResult, a as Assignment, B as BLOCK_ALIGNS, c as BLOCK_EMPHASES, d as BLOCK_FITS, e as BLOCK_GAPS, f as BLOCK_GRID_COLUMNS, g as BLOCK_HEADING_LEVELS, h as BLOCK_JUSTIFIES, i as BLOCK_RATIOS, j as BLOCK_SIZES, k as BLOCK_TEXT_ALIGNS, l as BLOCK_TONES, m as BLOCK_WEIGHTS, n as BadgeBlock, o as BlockAlign, p as BlockEmphasis, q as BlockFit, r as BlockGap, s as BlockJustify, t as BlockNode, u as BlockRatio, v as BlockSize, w as BlockTextAlign, x as BlockTone, y as BlockWeight, z as ButtonBlock, C as ComponentGoalOptions, D as ComponentWeightEntry, E as CompoundLocator, F as DecideInput, G as DecideOutcome, H as DecisionSnapshot, J as EventType, K as GoalDefinition, L as GoalOptions, N as GraphConfig, O as GraphSnapshot, P as GridBlock, Q as HeadingBlock, R as ImageBlock, S as LEGACY_SESSION_COOKIE_NAME, T as LOCAL_MODE_BANNER, U as LinkBlock, V as MAX_BLOCK_ARMS, W as MAX_BLOCK_CHILDREN, X as MAX_BLOCK_DEPTH, Y as MAX_BLOCK_NODES, Z as MAX_BLOCK_TEXT_LEN, _ as MicroSignalEmitter, $ as MicroSignalType, a0 as PROD_KEYLESS_ERROR, a2 as QueueConfig, a3 as SNAPSHOT_STORAGE_KEY_PREFIX, a4 as SectionMapEntry, a5 as SentientClient, a6 as SentientConfig, a7 as SentientEvent, a8 as SessionConfig, a9 as SessionManager, aa as SitePalette, ab as SlotConfigEntry, ac as SlotOps, ad as SpacerBlock, ae as StackBlock, af as TextBlock, ag as WeightEntry, ah as _registerConsentUpgradeInit, ai as attachMicroSignalDetectors, aj as grantConsent, ak as init, al as isDoNotTrackEnabled, am as readSnapshot, an as renderPrePaintScript, ap as sessionCookieName, aq as writeSnapshot } from './index-BbrtAtrY.js';
2
- export { A as AGENT_INTENTS, a as AgentIntent, b as SlotDeclInput, c as agentIntent, d as agentUaList, e as armOfResult, f as baselineResultFor, g as baselineSlots, i as classifiedAgents, j as deriveSessionSegment, k as detectDeviceClass, l as detectTimeOfDay, m as detectTrafficSource, n as matchedAgentToken, r as referrerDomainFromReferer, t as toWireSlot, u as uaTokenMatch } from './session-meta-BVvq5RBB.js';
1
+ export { A as AssignResult, a as Assignment, B as BLOCK_ALIGNS, c as BLOCK_EMPHASES, d as BLOCK_FITS, e as BLOCK_GAPS, f as BLOCK_GRID_COLUMNS, g as BLOCK_HEADING_LEVELS, h as BLOCK_JUSTIFIES, i as BLOCK_RATIOS, j as BLOCK_SIZES, k as BLOCK_TEXT_ALIGNS, l as BLOCK_TONES, m as BLOCK_WEIGHTS, n as BadgeBlock, o as BlockAlign, p as BlockEmphasis, q as BlockFit, r as BlockGap, s as BlockJustify, t as BlockNode, u as BlockRatio, v as BlockSize, w as BlockTextAlign, x as BlockTone, y as BlockWeight, z as ButtonBlock, C as ComponentGoalOptions, D as ComponentWeightEntry, E as CompoundLocator, F as DecideInput, G as DecideOutcome, H as DecisionSnapshot, J as EventType, K as GoalDefinition, L as GoalOptions, N as GraphConfig, O as GraphSnapshot, P as GridBlock, Q as HeadingBlock, R as ImageBlock, S as LEGACY_SESSION_COOKIE_NAME, T as LOCAL_MODE_BANNER, U as LinkBlock, V as MAX_BLOCK_ARMS, W as MAX_BLOCK_CHILDREN, X as MAX_BLOCK_DEPTH, Y as MAX_BLOCK_NODES, Z as MAX_BLOCK_TEXT_LEN, _ as MicroSignalEmitter, $ as MicroSignalType, a0 as PROD_KEYLESS_ERROR, a2 as QueueConfig, a3 as SNAPSHOT_STORAGE_KEY_PREFIX, a4 as SectionMapEntry, a5 as SentientClient, a6 as SentientConfig, a7 as SentientEvent, a8 as SessionConfig, a9 as SessionManager, aa as SitePalette, ab as SlotConfigEntry, ac as SlotOps, ad as SpacerBlock, ae as StackBlock, af as TextBlock, ag as WeightEntry, ah as _registerConsentUpgradeInit, ai as attachMicroSignalDetectors, aj as grantConsent, ak as init, al as isDoNotTrackEnabled, am as readSnapshot, an as renderPrePaintScript, ap as sessionCookieName, aq as writeSnapshot } from './index-BjVvN7cU.js';
2
+ export { A as AGENT_INTENTS, a as AgentIntent, C as CLICK_ID_KEYS, b as SlotDeclInput, c as agentIntent, d as agentUaList, e as armOfResult, f as baselineResultFor, g as baselineSlots, i as classifiedAgents, j as deriveSessionSegment, k as detectDeviceClass, l as detectTimeOfDay, m as detectTrafficSource, n as extractTrackedParams, o as matchedAgentToken, r as referrerDomainFromReferer, t as toWireSlot, u as uaTokenMatch } from './session-meta-C-bMqVeq.js';
3
3
  export { SlotResult } from '@sentientui/policy';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var wt=Object.create;var fe=Object.defineProperty,xt=Object.defineProperties,bt=Object.getOwnPropertyDescriptor,kt=Object.getOwnPropertyDescriptors,Et=Object.getOwnPropertyNames,Ye=Object.getOwnPropertySymbols,Ct=Object.getPrototypeOf,Ze=Object.prototype.hasOwnProperty,It=Object.prototype.propertyIsEnumerable;var qe=(e,t,n)=>t in e?fe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,k=(e,t)=>{for(var n in t||(t={}))Ze.call(t,n)&&qe(e,n,t[n]);if(Ye)for(var n of Ye(t))It.call(t,n)&&qe(e,n,t[n]);return e},se=(e,t)=>xt(e,kt(t));var _t=(e,t)=>{for(var n in t)fe(e,n,{get:t[n],enumerable:!0})},et=(e,t,n,l)=>{if(t&&typeof t=="object"||typeof t=="function")for(let p of Et(t))!Ze.call(e,p)&&p!==n&&fe(e,p,{get:()=>t[p],enumerable:!(l=bt(t,p))||l.enumerable});return e};var At=(e,t,n)=>(n=e!=null?wt(Ct(e)):{},et(t||!e||!e.__esModule?fe(n,"default",{value:e,enumerable:!0}):n,e)),Ot=e=>et(fe({},"__esModule",{value:!0}),e);var xn={};_t(xn,{AGENT_INTENTS:()=>Be,BLOCK_ALIGNS:()=>Xt,BLOCK_EMPHASES:()=>tn,BLOCK_FITS:()=>rn,BLOCK_GAPS:()=>Vt,BLOCK_GRID_COLUMNS:()=>sn,BLOCK_HEADING_LEVELS:()=>an,BLOCK_JUSTIFIES:()=>Yt,BLOCK_RATIOS:()=>on,BLOCK_SIZES:()=>qt,BLOCK_TEXT_ALIGNS:()=>nn,BLOCK_TONES:()=>en,BLOCK_WEIGHTS:()=>Zt,LEGACY_SESSION_COOKIE_NAME:()=>Ke,LOCAL_MODE_BANNER:()=>We,MAX_BLOCK_ARMS:()=>un,MAX_BLOCK_CHILDREN:()=>dn,MAX_BLOCK_DEPTH:()=>cn,MAX_BLOCK_NODES:()=>ln,MAX_BLOCK_TEXT_LEN:()=>pn,PROD_KEYLESS_ERROR:()=>Me,SNAPSHOT_STORAGE_KEY_PREFIX:()=>te,_registerConsentUpgradeInit:()=>gn,agentIntent:()=>at,agentUaList:()=>ye,armOfResult:()=>De,attachMicroSignalDetectors:()=>ft,baselineResultFor:()=>le,baselineSlots:()=>dt,classifiedAgents:()=>lt,deriveSessionSegment:()=>ct,detectDeviceClass:()=>Se,detectTimeOfDay:()=>xe,detectTrafficSource:()=>ve,grantConsent:()=>vn,init:()=>vt,isDoNotTrackEnabled:()=>Qe,matchedAgentToken:()=>Fe,readSnapshot:()=>ce,referrerDomainFromReferer:()=>we,renderPrePaintScript:()=>ut,sessionCookieName:()=>Ie,toWireSlot:()=>be,uaTokenMatch:()=>he,writeSnapshot:()=>de});module.exports=Ot(xn);function Ce(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}try{if(typeof crypto!="undefined"&&typeof crypto.getRandomValues=="function"){let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n<16;n++)t+=e[n].toString(16).padStart(2,"0"),(n===3||n===5||n===7||n===9)&&(t+="-");return t}}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function me(e){return e?`_${e.slice(0,12)}`:""}var Ke="_snt_uid";function Ie(e){return`${Ke}${me(e)}`}var Rt="_snt_uid",Tt=365,_e="_snt_uid";function Bt(){return Ce()}function tt(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function Dt(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(l){}}function Ge(e){try{return localStorage.getItem(e)}catch(t){return null}}function nt(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function ot(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function Lt(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function Pt(e){try{sessionStorage.removeItem(e)}catch(t){}}function Mt(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function Nt(e){try{localStorage.removeItem(e)}catch(t){}}function Kt(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var Gt={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function Ae(e){var E,R,T,C,G,$,_;if(typeof window=="undefined")return Gt;let t=me(e==null?void 0:e.apiKey),n=(E=e==null?void 0:e.cookieName)!=null?E:Ie(e==null?void 0:e.apiKey),l=`${_e}${t}`,p=`${_e}_tomb${t}`,c=((R=e==null?void 0:e.cookieTTLDays)!=null?R:Tt)*24*60*60,i=v=>v&&v.length>0?v:null,u=()=>{var v,D;return t&&!(e!=null&&e.cookieName)&&Ge(p)===null?(D=(v=i(tt(Rt)))!=null?v:i(Ge(_e)))!=null?D:i(ot(_e)):null},s=(_=($=(G=(C=(T=i(tt(n)))!=null?T:i(Ge(l)))!=null?C:i(ot(l)))!=null?G:u())!=null?$:i(e==null?void 0:e.ssrSessionId))!=null?_:Bt();Dt(n,s,c);let o=nt(l,s),r=Mt(n),y=o?!1:Lt(l,s),h=!o&&!r&&!y;return{getSessionId:()=>s,isEphemeral:()=>h,destroy:()=>{s=null,Kt(n),Nt(l),Pt(l),t&&!(e!=null&&e.cookieName)&&nt(p,"1")}}}function X(e){return Math.min(6e4,1e3*2**Math.min(e,6))}function ie(e){if(e.ok===!0)return"delivered";let{status:t}=e;return typeof t!="number"?"retry":t>=200&&t<300?"delivered":t>=400&&t<500&&t!==429?"dropped":"retry"}function Oe(e,t){try{let n=localStorage.getItem(e);if(!n)return[];let l=JSON.parse(n);return Array.isArray(l)?(localStorage.removeItem(e),l.slice(-t)):[]}catch(n){return[]}}function Y(e,t,n){try{let l=(()=>{try{let a=localStorage.getItem(n);if(!a)return[];let c=JSON.parse(a);return Array.isArray(c)?c:[]}catch(a){return[]}})(),p=new Map;for(let a of l)p.set(a.id,a);for(let a of e)p.set(a.id,a);localStorage.setItem(n,JSON.stringify([...p.values()].slice(-t)))}catch(l){}}function Re(e,t){try{let n=localStorage.getItem(t);if(!n)return;let l=JSON.parse(n);if(!Array.isArray(l))return;let p=new Set(e),a=l.filter(c=>!p.has(c.id));if(a.length===l.length)return;a.length===0?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(a))}catch(n){}}var Ut=500,$t=56*1024;function Ue(e){return`_snt_retry_${e.slice(0,12)}`}var Ft={push:()=>{},flush:()=>{},destroy:()=>{}};function rt(e){var q,M,ne;if(typeof window=="undefined")return Ft;let t=(q=e.flushIntervalMs)!=null?q:5e3,n=(M=e.maxBatchSize)!=null?M:20,l=(ne=e.maxRetrySize)!=null?ne:100,p=e.ingestUrl,a=e.apiKey,c=Ue(a),i=[],u=new Set,s=[],o=f=>{for(let x of f)r.delete(x),!u.has(x)&&(u.add(x),s.push(x));for(;s.length>Ut;){let x=s.shift();x&&u.delete(x)}Re(f,c)},r=new Set,y=()=>{for(;i.length>l;){let f=i.shift();f&&r.delete(f.id)}},h=f=>{u.has(f.id)||r.has(f.id)||(r.add(f.id),i.push(f),y())},E=f=>{for(let x of f)u.has(x.id)||(r.add(x.id),i.push(x));y()},R=Oe(c,l);for(let f of R)h(f);let T=0,C=0,G=(f,x=!0)=>{if(f.length===0)return;let j=JSON.stringify(f),Z=f.map(z=>z.id),F;try{F=fetch(p,{method:"POST",keepalive:!0,body:j,headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`}})}catch(z){Y(f,l,c),E(f),C++,T=Date.now()+X(C);return}let ue=z=>{if(ie(z)!=="retry"){if(!z.ok&&x){let oe=f.filter(Q=>Q.eventType!=="pageview");if(oe.length>0&&oe.length<f.length){o(f.filter(Q=>Q.eventType==="pageview").map(Q=>Q.id)),G(oe,!1);return}}o(Z),C=0,T=0;return}Y(f,l,c),E(f),C++,T=Date.now()+X(C)};F instanceof Promise?F.then(ue).catch(()=>{Y(f,l,c),E(f),C++,T=Date.now()+X(C)}):ue(F)},$=typeof TextEncoder!="undefined"?new TextEncoder:null,_=f=>$?$.encode(f).length:f.length,v=f=>{let x=[],j=2;for(let Z of f){let F=_(JSON.stringify(Z))+1;if(x.length>0&&j+F>$t||x.length>=n)break;x.push(Z),j+=F}return x},D=()=>{try{if(Date.now()<T)return;for(;i.length>0&&!(Date.now()<T);){let f=i.filter(j=>!u.has(j.id));if(i.length=0,f.length===0)break;let x=v(f);if(x.length===0)break;x.length<f.length&&i.push(...f.slice(x.length)),G(x)}}catch(f){}},m=!0,O=null;O=setInterval(()=>{m&&D()},t);let P=()=>{document.visibilityState==="hidden"&&D()},W=()=>{D()};return document.addEventListener("visibilitychange",P),window.addEventListener("pagehide",W),{push(f){h(f),i.length>=n&&D()},flush:D,destroy(){m=!1,O!==null&&(clearInterval(O),O=null),document.removeEventListener("visibilitychange",P),window.removeEventListener("pagehide",W),D()}}}var Wt=200;function $e(e){return`_snt_goal_retry_${e.slice(0,12)}`}var jt={send:()=>{},flush:()=>{},destroy:()=>{}};function st(e){var _,v,D;if(typeof window=="undefined")return jt;let t=(_=e.flushIntervalMs)!=null?_:5e3,n=(v=e.maxRetrySize)!=null?v:100,l=(D=e.maxPerFlush)!=null?D:5,p=$e(e.apiKey),a=[],c=new Set,i=new Set,u=[],s=0,o=0,r=!1,y=m=>{for(c.delete(m.id),i.has(m.id)||(i.add(m.id),u.push(m.id));u.length>Wt;){let O=u.shift();O&&i.delete(O)}Re([m.id],p)},h=m=>{Y([m],n,p),!i.has(m.id)&&!c.has(m.id)&&(c.add(m.id),a.push(m)),Date.now()>=s&&(o++,s=Date.now()+X(o))},E=m=>{let O;try{O=fetch(e.url,{method:"POST",keepalive:!0,body:m.body,headers:e.headers})}catch(W){h(m);return}let P=W=>{var M;let q=ie(W);if(q==="retry"){h(m);return}q==="dropped"&&((M=e.onDrop)==null||M.call(e,m,W.status)),y(m),o=0,s=0};O instanceof Promise?O.then(P).catch(()=>h(m)):P(O)},R=()=>{try{if(r||Date.now()<s)return;let m=0;for(;a.length>0&&m<l&&!(Date.now()<s);){let O=a.shift();c.delete(O.id),!i.has(O.id)&&(m++,E(O))}}catch(m){}},T=Oe(p,n);for(let m of T)c.has(m.id)||(c.add(m.id),a.push(m));T.length>0&&Y(T,n,p);let C=setInterval(R,t),G=()=>{document.visibilityState==="hidden"&&R()},$=()=>R();return document.addEventListener("visibilitychange",G),window.addEventListener("pagehide",$),{send(m){if(!r&&!(i.has(m.id)||c.has(m.id))){if(Date.now()<s){Y([m],n,p),c.add(m.id),a.push(m);return}E(m)}},flush:R,destroy(){clearInterval(C),document.removeEventListener("visibilitychange",G),window.removeEventListener("pagehide",$),R(),r=!0}}}var Ht=1800*1e3;function Te(e,t){return`${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function it(e=Ht,t){let n=new Map,l=`_snt_asgn${me(t)}_`,p=(s,o)=>`${l}${encodeURIComponent(s)}:${encodeURIComponent(o)}`,a=s=>{let o=s.slice(l.length),r=o.indexOf(":");if(r<0)return null;try{return{componentId:decodeURIComponent(o.slice(0,r)),segment:decodeURIComponent(o.slice(r+1))}}catch(y){return null}},c=()=>{try{let s=[];for(let o=0;o<localStorage.length;o++){let r=localStorage.key(o);r!=null&&r.startsWith(l)&&s.push(r)}return s}catch(s){return[]}},i=s=>s.assignedAt+(s.ttlMs&&s.ttlMs>0?s.ttlMs:e)<Date.now();return typeof window!="undefined"&&(()=>{for(let s of c())try{let o=localStorage.getItem(s);if(!o)continue;let r=JSON.parse(o);if(i(r)){localStorage.removeItem(s);continue}let y=a(s);if(!y)continue;n.set(Te(y.componentId,y.segment),r)}catch(o){}})(),{get(s,o){let r=n.get(Te(s,o));return r?i(r)?(n.delete(Te(s,o)),null):r:null},set(s,o,r){let y=Te(s,o);n.set(y,r);try{localStorage.setItem(p(s,o),JSON.stringify(r))}catch(h){}},clear(){n.clear();for(let s of c())try{localStorage.removeItem(s)}catch(o){}}}}var ye=["GPTBot","ChatGPT-User","OAI-SearchBot","ClaudeBot","Claude-User","Claude-SearchBot","PerplexityBot","Perplexity-User","Google-Extended","Applebot-Extended","Meta-ExternalAgent","Bytespider","CCBot","Amazonbot","cohere-ai","Diffbot"];function he(e){return Fe(e)!==null}function Fe(e){var n;if(!e)return null;let t=e.toLowerCase();return(n=ye.find(l=>t.includes(l.toLowerCase())))!=null?n:null}var Be={GPTBot:"training","ChatGPT-User":"user","OAI-SearchBot":"search",ClaudeBot:"training","Claude-User":"user","Claude-SearchBot":"search",PerplexityBot:"search","Perplexity-User":"user","Google-Extended":"training","Applebot-Extended":"training","Meta-ExternalAgent":"training",Bytespider:"training",CCBot:"training",Amazonbot:"other","cohere-ai":"training",Diffbot:"other"};function at(e){if(!e)return"other";let t=ye.find(n=>n.toLowerCase()===e.toLowerCase());return t&&Be[t]||"other"}function lt(){var t;let e={user:[],search:[],training:[],other:[]};for(let n of ye)e[(t=Be[n])!=null?t:"other"].push(n);return e}function Se(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function ve(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(p){}let l=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(l)?"search":/(^|\.)(twitter\.com|x\.com|facebook\.com|linkedin\.com|reddit\.com|t\.co)$/.test(l)?"social":"referral"}catch(n){return"direct"}}function we(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}function xe(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function ct(e){let t=zt("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function zt(e,t){var a,c,i,u,s,o,r;let n=(c=(a=t==null?void 0:t.userAgent)==null?void 0:a.trim())!=null?c:"",l=(u=(i=t==null?void 0:t.referer)==null?void 0:i.trim())!=null?u:"",p=(s=t==null?void 0:t.now)!=null?s:new Date;return{sessionId:e,ephemeral:!1,utmParams:(o=t==null?void 0:t.utmParams)!=null?o:{},deviceClass:n?Se(n):"desktop",trafficSource:l?ve(l,t==null?void 0:t.appOrigin):"direct",referrerDomain:we(l),timeOfDay:xe(p),dayOfWeek:(r=["sun","mon","tue","wed","thu","fri","sat"][p.getDay()])!=null?r:"sun",automation:(t==null?void 0:t.webdriver)===!0||he(n)}}var ae=require("@sentientui/policy");function be(e){return k(k(k({id:e.id},e.arms?{arms:[...e.arms]}:{}),e.dims?{dims:Object.fromEntries(Object.entries(e.dims).map(([t,n])=>[t,[...n]]))}:{}),e.baseline!==void 0?{baseline:e.baseline}:{})}function le(e){let t=be(e);return(0,ae.slotResultFor)(t,(0,ae.slotBaselineArm)(t))}function dt(e){let t={};for(let n of e)t[n.id]=le(n);return t}function De(e){return typeof e=="string"?e:(0,ae.canonicalArm)(e)}var te="_snt_snap:",Qt=["low","medium","high"];function ce(e){try{let t=localStorage.getItem(te+e);if(!t)return null;let n=JSON.parse(t);return!n||typeof n!="object"||n.v!==1||typeof n.persona!="string"||typeof n.band!="string"||!Qt.includes(n.band)||typeof n.slots!="object"||n.slots===null||Array.isArray(n.slots)||!(n.layoutOrder===null||Array.isArray(n.layoutOrder))||typeof n.savedAt!="number"||!(n.slotConfig===void 0||typeof n.slotConfig=="object"&&n.slotConfig!==null&&!Array.isArray(n.slotConfig))?null:n}catch(t){return null}}function de(e,t){try{localStorage.setItem(te+e,JSON.stringify(t))}catch(n){}}function ut(e){return"(function(){try{var r=localStorage.getItem("+JSON.stringify(te+e).replace(/</g,"\\u003c")+');if(!r)return;var s=JSON.parse(r);if(!s||s.v!==1||typeof s.persona!=="string"||typeof s.band!=="string")return;var d=document.documentElement;if(d.hasAttribute("data-sentient-persona"))return;d.setAttribute("data-sentient-persona",s.persona);d.setAttribute("data-sentient-confidence",s.band);}catch(e){}})();'}var He=require("@sentientui/policy");var Pe=require("@sentientui/policy");var Me="[sentient] No API key configured \u2014 nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.",We="[sentient] Local mode \u2014 decisions are simulated on-device and nothing is sent over the network. Add an API key to learn from real traffic.",pt=!1,Le=!1;function Jt(){var e;try{return(e=new URLSearchParams(window.location.search).get("sentient_persona"))!=null?e:void 0}catch(t){return}}function gt(e){var s;let t=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),n=(s=t.getSessionId())!=null?s:"local",l=Jt(),p=import("@sentientui/core/local").then(o=>{let r=o;return r.LOCAL_ENGINE_AVAILABLE?(pt||(pt=!0,console.info(We)),r):(Le||(Le=!0,console.error(Me)),null)}).catch(()=>(Le||(Le=!0,console.error(Me)),null)),a=null,c={low:.15,medium:.5,high:.85},i=(()=>{var r;if(e.initialPersona)return k({},e.initialPersona);let o=ce(e.apiKey||"local");return o?{persona:o.persona,confidence:(r=c[o.band])!=null?r:.15}:null})();function u(o){let r=document.documentElement;r.dataset.sentientPersona===void 0&&(r.dataset.sentientPersona=o.persona,r.dataset.sentientConfidence=(0,Pe.confidenceBand)(o.confidence))}return{isLocal:!0,async decide(o){var h,E,R;let r=await p;if(!r)return null;let y=r.createLocalEngine({sessionId:n,forcedPersona:l}).decide(o);return a=se(k({},y),{layoutOrder:(E=(h=y.layoutOrder)!=null?h:a==null?void 0:a.layoutOrder)!=null?E:null,slots:k(k({},(R=a==null?void 0:a.slots)!=null?R:{}),y.slots)}),de(e.apiKey||"local",{v:1,persona:a.persona,band:(0,Pe.confidenceBand)(a.confidence),slots:a.slots,layoutOrder:a.layoutOrder,savedAt:Date.now()}),u(y),y},getSlotResult(o){var r,y,h;return(h=(y=a==null?void 0:a.slots[o])!=null?y:(r=e.initialSlots)==null?void 0:r[o])!=null?h:null},getPersona(){let o=a!=null?a:i;return o?{persona:o.persona,confidence:o.confidence,band:(0,Pe.confidenceBand)(o.confidence)}:null},async assign(o,r){var E;let y=await p;return!y||!r||r.length===0?r!=null&&r[0]?{variantId:r[0],assignmentTtlMs:0}:null:{variantId:(E=y.createLocalEngine({sessionId:n,forcedPersona:l}).decide({components:[{id:o,variantIds:r}]}).assignments[o])!=null?E:r[0],assignmentTtlMs:0}},track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>t.destroy()}}var Vt=["none","sm","md","lg"],Xt=["start","center","end","stretch"],Yt=["start","center","end","between"],qt=["sm","md","lg"],Zt=["normal","medium","bold"],en=["default","muted","accent"],tn=["primary","secondary","ghost"],nn=["left","center","right"],on=["auto","square","landscape","wide"],rn=["cover","contain"],sn=[2,3,4],an=[2,3,4],ln=64,cn=5,dn=12,un=4,pn=500;function ft(e,t,n,l){let p=[];{let i=!1,u=[],s=()=>{if(i)return;let o=Date.now();for(u.push(o);u.length>0&&o-u[0]>500;)u.shift();u.length>=3&&(i=!0,e("rage_click"))};t.addEventListener("click",s),p.push(()=>t.removeEventListener("click",s))}{let a=!1,c=i=>{if(a||!(i.target instanceof Node)||!t.contains(i.target)&&t!==i.target)return;a=!0;let u=typeof window!="undefined"?window.getSelection():null,s=u?u.toString().length:0;e("text_copy",{selectionLength:s})};document.addEventListener("copy",c),p.push(()=>document.removeEventListener("copy",c))}{let a=!1,c=!1,i=null,u=()=>{i!==null&&(clearTimeout(i),i=null)},s=()=>{a||!c||(u(),i=setTimeout(()=>{!a&&c&&(a=!0,e("scroll_hesitation"))},3e3))},o=()=>{u(),s()},r=h=>{for(let E of h)c=E.intersectionRatio>.3,c?s():u()},y=new IntersectionObserver(r,{threshold:[.3]});y.observe(t),window.addEventListener("scroll",o,{passive:!0}),p.push(()=>{y.disconnect(),window.removeEventListener("scroll",o),u()})}if((l==null?void 0:l.tabLoss)!==!1){let a=!1,c=n!=null?n:Date.now(),i=()=>{if(a||document.visibilityState!=="hidden")return;let u=Date.now()-c;u<15e3&&(a=!0,e("tab_loss",{timeOnPage:u}))};document.addEventListener("visibilitychange",i),p.push(()=>document.removeEventListener("visibilitychange",i))}return()=>{for(let a of p)a()}}var mt="https://api.sentient-ui.com/v1/events",K=new Map,yt=null;function gn(e,t){let n=K.get(e);n&&n.upgrade&&(n.reinit=t)}function fn(e){return"value"in e||"currency"in e||"externalId"in e||"metadata"in e||"weight"in e||"stepIndex"in e}var mn=3;function je(){return Ce()}function Ne(){var e;if(typeof window!="undefined")return((e=window.location)==null?void 0:e.pathname)||void 0}function yn(){let e=new WeakMap,t=new Map,n=!1,l=(i,u,s)=>{let o=i.get(u);return s===null?o?!0:(i.set(u,new Set),!1):o?o.has(s)?!0:(o.add(s),!1):(i.set(u,new Set([s])),!1)},p=typeof window!="undefined"&&"event"in window,a=()=>{if(!p)return;let i=window.event;return typeof Event=="function"&&i instanceof Event?i:void 0},c=()=>{if(p){Promise.resolve().then(()=>{n=!1,t.clear()});return}let i=!1,u=()=>{i||(i=!0,n=!1,t.clear())};if(setTimeout(u,0),typeof MessageChannel=="function"){let s=new MessageChannel;s.port1.onmessage=()=>{s.port1.close(),s.port2.close(),u()},s.port2.postMessage(0)}};return{firedBefore(i,u){let s=a();if(s){let r=e.get(s);return r||(r=new Map,e.set(s,r)),l(r,i,u)}let o=l(t,i,u);return!o&&!n&&(n=!0,c()),o}}}var ht=new Set;function hn(e,t,n){let l=typeof window=="undefined"?null:window.history;if(!l)return()=>{};let p=!1,a,c=()=>{if(p)return;let s=Ne();!s||s===a||(a=s,e.track({projectId:t,componentId:"__page__",eventType:"pageview",payload:{}}),n(`${t}:${s}`))},i=[];for(let s of["pushState","replaceState"]){let o=l[s],r=function(...y){let h=o.apply(this,y);return c(),h};l[s]=r,i.push([s,o,r])}window.addEventListener("popstate",c);let u=Ne();return u&&ht.has(`${t}:${u}`)?a=u:c(),()=>{if(!p){p=!0,window.removeEventListener("popstate",c);for(let[s,o,r]of i)l[s]===r&&(l[s]=o)}}}var ke={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),decide:()=>Promise.resolve(null),getSlotResult:()=>null,getPersona:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}};function Sn(){try{let e={},t=new URLSearchParams(window.location.search);for(let[n,l]of t)n.startsWith("utm_")&&(e[n]=l);return e}catch(e){return{}}}function St(e){return e.replace(/\/events\/?$/,"")}function Qe(){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")}function vn(e){var u;if(typeof window=="undefined")return;let t=e!=null?e:yt;if(!t){console.warn("[sentient] grantConsent() called before init()");return}let n=K.get(t);if(!n){console.warn("[sentient] grantConsent() called before init()");return}let{config:l,upgrade:p,reinit:a}=n;if(!p){n.upgradeBlockedReason&&console.warn(n.upgradeBlockedReason);return}if(l.respectDoNotTrack!==!1&&Qe())return;let c=(a!=null?a:vt)(se(k({},l),{consent:!0}));p(c);let i=(u=K.get(t))==null?void 0:u.dispose;K.set(t,{config:se(k({},l),{consent:!0}),upgrade:null,dispose:i})}function wn(e){var s;let t=e.preConsentBehavior==="statistical_winner",n=St((s=e.ingestUrl)!=null?s:mt),l={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},p=new Map,a=new Map,c={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),assign(o,r,y){if(!t)return Promise.resolve(null);let h=p.get(o);return h?Promise.resolve(h):ze(a,o,async()=>{try{let E=new URLSearchParams({componentId:o});for(let G of r!=null?r:[])E.append("variantIds[]",G);let R=await fetch(`${n}/winner?${E.toString()}`,{headers:l});if(!R.ok)return r!=null&&r[0]?{variantId:r[0],assignmentTtlMs:0}:null;let C={variantId:(await R.json()).variantId,assignmentTtlMs:0};return p.set(o,C),C}catch(E){return r!=null&&r[0]?{variantId:r[0],assignmentTtlMs:0}:null}})},decide:()=>Promise.resolve(null),getSlotResult:()=>null,getPersona:()=>null,getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}},i={track:o=>c.track(o),goal:((o,r,y,h)=>c.goal(o,r,y,h)),componentGoal:(o,r,y)=>c.componentGoal(o,r,y),identify:o=>c.identify(o),getAssignment:(o,r)=>c.getAssignment(o,r),assign:(o,r,y,h)=>c.assign(o,r,y,h),decide:o=>c.decide(o),getSlotResult:o=>c.getSlotResult(o),getPersona:()=>c.getPersona(),fetchWeights:()=>c.fetchWeights(),getGraph:()=>c.getGraph(),dispose:()=>c.dispose(),destroy:()=>c.destroy()};function u(o){c=o}return{proxy:i,setInner:u}}function ze(e,t,n){let l=e.get(t);if(l)return l;let p=(async()=>{try{return await n()}finally{e.delete(t)}})();return e.set(t,p),p}function vt(e){var x,j,Z,F,ue,z,oe,Q,Je;if(typeof window=="undefined")return ke;yt=e.apiKey||"local";let t=K.get(e.apiKey||"local");if(t!=null&&t.dispose)try{t.dispose()}catch(d){}let n=e.respectDoNotTrack!==!1&&Qe(),l=e.consent===!1||n,p=typeof e.apiKey=="string"&&e.apiKey.startsWith("pk_");if(e.localMode===!0||!p&&e.localMode!==!1)return K.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): this client is keyless/local \u2014 there is no hosted client to upgrade to. Configure a pk_ API key to enable tracking."}),l?ke:gt(e);if(l){if(!e.apiKey||!e.apiKey.startsWith("pk_"))return e.preConsentBehavior==="statistical_winner"&&console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),K.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): the client was initialized with an invalid apiKey (expected a pk_ public key) \u2014 consent cannot enable tracking."}),ke;let{proxy:d,setInner:g}=wn(e);return K.set(e.apiKey,{config:e,upgrade:n?null:g}),d}if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),ke;if(e.ingestUrl==="")return console.warn("[sentient] init() called with an empty ingestUrl. SDK disabled."),ke;let a=(x=e.ingestUrl)!=null?x:mt,c=Date.now(),i=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),u=it(void 0,e.apiKey),s=rt({ingestUrl:a,apiKey:e.apiKey}),o=St(a),r={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},y=new Set,h=st({url:`${o}/goals`,apiKey:e.apiKey,headers:r,onDrop:(d,g)=>{if(!e.debug){if(y.has(g))return;y.add(g)}console.warn(`[sentient] goal dropped (HTTP ${g}) \u2014 this will not be retried. `+(g===400?"The session was not found: call init() and let the session upsert complete before firing goals.":g===401||g===403?"Check the API key and that this origin is on the project allowlist.":"See the response status for the cause."),d)}}),E=Se((j=navigator.userAgent)!=null?j:""),R=window.location.origin,T=ve((Z=document.referrer)!=null?Z:"",R),C=(F=e.sessionSegment)!=null?F:`${E}:${T}`,G=new Map,$=new Map,_=new Map,v=null,D=d=>{for(let g of d)_.has(g.id)||_.set(g.id,le(g))};if(e.initialSlots)for(let[d,g]of Object.entries(e.initialSlots))_.set(d,g);let m=ce(e.apiKey);if(m)for(let[d,g]of Object.entries(m.slots))_.has(d)||_.set(d,g);let O={low:.15,medium:.5,high:.85};if(e.initialPersona)v=k({},e.initialPersona);else{let d=document.documentElement.dataset;d.sentientPersona?v={persona:d.sentientPersona,confidence:(z=O[(ue=d.sentientConfidence)!=null?ue:"low"])!=null?z:.15}:m&&(v={persona:m.persona,confidence:(oe=O[m.band])!=null?oe:.15})}if(e.initialAssignments)for(let[d,g]of Object.entries(e.initialAssignments))u.set(d,C,{variantId:g,assignedAt:Date.now(),segment:C,confidence:1});let P=Promise.resolve(),W=i.getSessionId();if(W){let d=we((Q=document.referrer)!=null?Q:""),g=k(k(k({sessionId:W,deviceClass:E,trafficSource:T,referrerDomain:d,utmParams:Sn(),timeOfDay:xe(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:i.isEphemeral(),automation:typeof navigator!="undefined"&&navigator.webdriver===!0||he((Je=navigator.userAgent)!=null?Je:"")},e.userId?{userId:e.userId}:{}),e.persona?{persona:e.persona}:{}),e.country?{country:e.country}:{}),S=async()=>{for(let I=0;;I++){try{let A=await fetch(`${o}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(g),headers:r});if(A.status===402){console.warn("[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing");return}if(A.ok||ie(A)==="dropped")return}catch(A){}if(I>=mn){console.warn("[SentientUI] Could not register the session after retries. Conversions in this visit may not be recorded.");return}await new Promise(A=>setTimeout(A,X(I+1)))}};try{P=S()}catch(I){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:s});let q=yn(),M=null,ne=!1,f={goal(d,g={},S=1,I=0){var pe,ge,re,b,H,Ee,B;let A=i.getSessionId();if(!A)return;let w=fn(g)?g:{metadata:g},N=[d,(pe=w.externalId)!=null?pe:"",(ge=w.stepIndex)!=null?ge:I,(re=w.weight)!=null?re:S].join("\0"),U=w.value!==void 0?`${w.value}\0${(b=w.currency)!=null?b:""}`:null;if(q.firedBefore(N,U)){e.debug&&console.log(`[sentient] goal("${d}") already recorded for this action \u2014 not sent twice`);return}let L=je(),J={sessionId:A,name:d,metadata:(H=w.metadata)!=null?H:{},weight:(Ee=w.weight)!=null?Ee:S,stepIndex:(B=w.stepIndex)!=null?B:I,goalId:L,value:w.value,currency:w.currency,externalId:w.externalId};e.debug&&console.log("[sentient] goal",J);let ee={id:L,body:JSON.stringify(J)};P.then(()=>h.send(ee))},componentGoal(d,g,S){var L,J,ee;let I=i.getSessionId();if(!I)return;let A=u.get(d,C),w=A?null:(L=_.get(d))!=null?L:null;if(!A&&w===null){e.debug&&console.warn(`[sentient] componentGoal("${d}"): no assignment or slot decision yet \u2014 render its <Adaptive>/adaptive hook or call assign()/decide() before recording a goal.`);return}let N=A?A.variantId:De(w),U={id:je(),sessionId:I,projectId:e.apiKey,componentId:d,variantId:N,eventType:"goal_achieved",goalType:g,payload:k({reward:(J=S==null?void 0:S.reward)!=null?J:1,goalValue:S==null?void 0:S.value,currency:S==null?void 0:S.currency},(ee=S==null?void 0:S.metadata)!=null?ee:{}),timestamp:Date.now(),timeInSession:Date.now()-c,path:Ne()};e.debug&&console.log("[sentient] componentGoal",U),P.then(()=>s.push(U))},identify(d){let g=i.getSessionId();g&&P.then(()=>{fetch(`${o}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify({sessionId:g,userId:d,ephemeral:i.isEphemeral()}),headers:r}).catch(()=>{})})},track(d){let g=i.getSessionId();if(!g)return;let S=se(k({path:Ne()},d),{id:je(),sessionId:g,timestamp:Date.now(),timeInSession:Date.now()-c});e.debug&&console.log("[sentient] track",S),P.then(()=>s.push(S))},getAssignment(d,g){return u.get(d,g)},async assign(d,g,S,I){let A=i.getSessionId();if(!A)return null;let w=u.get(d,C);if(w&&(g!=null&&g.length||w.content!==void 0)){let N=w.ttlMs&&w.ttlMs>0?Math.max(0,w.assignedAt+w.ttlMs-Date.now()):0;return{variantId:w.variantId,assignmentTtlMs:N,content:w.content}}return ze(G,d,async()=>{await P;try{let N={sessionId:A,componentId:d,variantIds:g};I!==void 0?N.agentDataByVariant=I:S!==void 0&&(N.agentData=S);let U=await fetch(`${o}/assign`,{method:"POST",body:JSON.stringify(N),headers:r});if(!U.ok)return null;let L=await U.json();return u.set(d,C,k({variantId:L.variantId,assignedAt:Date.now(),segment:C,confidence:1,content:L.content},L.assignmentTtlMs&&L.assignmentTtlMs>0?{ttlMs:L.assignmentTtlMs}:{})),L}catch(N){return null}})},async decide(d){var w,N;let g=i.getSessionId();if(!g)return null;let S=(w=d.slots)!=null?w:[],I={sessionId:g};d.sections&&d.sections.length>0&&(I.sections=d.sections.map(U=>({id:U}))),I.components=(N=d.components)!=null?N:[],S.length>0&&(I.slots=S.map(be)),d.slotsFrom==="registry"&&(I.slotsFrom="registry"),d.v&&(I.v=d.v),e.persona&&(I.persona=e.persona);let A=JSON.stringify(I);return ze($,A,async()=>{var U,L,J,ee,pe,ge;await P;try{let re=await fetch(`${o}/decide`,{method:"POST",body:A,headers:r});if(!re.ok)return D(S),null;let b=await re.json(),H={};for(let B of S){let V=(U=b.slots)==null?void 0:U[B.id];if(V!==void 0){H[B.id]=V,_.set(B.id,V);continue}let Ve=_.get(B.id);if(Ve!==void 0)H[B.id]=Ve;else{let Xe=le(B);H[B.id]=Xe,_.set(B.id,Xe)}}if(b.slots)for(let[B,V]of Object.entries(b.slots))B in H||(H[B]=V,_.set(B,V));let Ee=v!=null&&v.persona!=="unknown";b.persona&&!(b.persona==="unknown"&&Ee)?v={persona:b.persona,confidence:(L=b.confidence)!=null?L:0}:v||(v={persona:"unknown",confidence:0});for(let[B,V]of Object.entries((J=b.assignments)!=null?J:{}))u.set(B,C,{variantId:V,assignedAt:Date.now(),segment:C,confidence:1});return de(e.apiKey,k(k({v:1,persona:v.persona,band:(0,He.confidenceBand)(v.confidence),slots:Object.fromEntries(_),layoutOrder:(ee=b.layoutOrder)!=null?ee:null,savedAt:Date.now()},b.slotConfig?{slotConfig:b.slotConfig}:{}),b.palette?{palette:b.palette}:{})),k(k(k(k({layoutOrder:(pe=b.layoutOrder)!=null?pe:null,assignments:(ge=b.assignments)!=null?ge:{},slots:H,persona:v.persona,confidence:v.confidence},b.slotConfig?{slotConfig:b.slotConfig}:{}),b.goals?{goals:b.goals}:{}),b.sectionMap?{sectionMap:b.sectionMap}:{}),b.palette?{palette:b.palette}:{})}catch(re){return D(S),null}})},getSlotResult(d){var g;return(g=_.get(d))!=null?g:null},getPersona(){return v?{persona:v.persona,confidence:v.confidence,band:(0,He.confidenceBand)(v.confidence)}:null},async fetchWeights(){var d;try{let g=await fetch(`${o}/weights`,{headers:r});return g.ok?(d=(await g.json()).components)!=null?d:[]:[]}catch(g){return[]}},getGraph(){return{pageNodes:[],capturedAt:0}},dispose(){var d;M==null||M(),ne=!0,s.destroy(),h.destroy(),((d=K.get(e.apiKey))==null?void 0:d.dispose)===f.dispose&&K.delete(e.apiKey),e.debug&&console.log("[sentient] disposed")},destroy(){var d;M==null||M(),ne=!0,s.destroy(),h.destroy(),i.destroy(),u.clear(),((d=K.get(e.apiKey))==null?void 0:d.dispose)===f.dispose&&K.delete(e.apiKey);try{localStorage.removeItem(te+e.apiKey),localStorage.removeItem(Ue(e.apiKey)),localStorage.removeItem($e(e.apiKey))}catch(g){}e.debug&&console.log("[sentient] destroyed")}};if(K.set(e.apiKey,{config:e,upgrade:null,dispose:f.dispose}),M=hn(f,e.apiKey,d=>{P.then(()=>{ne||ht.add(d)})}),e.debug){let d=window;d.__sentient&&(d.__sentient.client=f)}return f}0&&(module.exports={AGENT_INTENTS,BLOCK_ALIGNS,BLOCK_EMPHASES,BLOCK_FITS,BLOCK_GAPS,BLOCK_GRID_COLUMNS,BLOCK_HEADING_LEVELS,BLOCK_JUSTIFIES,BLOCK_RATIOS,BLOCK_SIZES,BLOCK_TEXT_ALIGNS,BLOCK_TONES,BLOCK_WEIGHTS,LEGACY_SESSION_COOKIE_NAME,LOCAL_MODE_BANNER,MAX_BLOCK_ARMS,MAX_BLOCK_CHILDREN,MAX_BLOCK_DEPTH,MAX_BLOCK_NODES,MAX_BLOCK_TEXT_LEN,PROD_KEYLESS_ERROR,SNAPSHOT_STORAGE_KEY_PREFIX,_registerConsentUpgradeInit,agentIntent,agentUaList,armOfResult,attachMicroSignalDetectors,baselineResultFor,baselineSlots,classifiedAgents,deriveSessionSegment,detectDeviceClass,detectTimeOfDay,detectTrafficSource,grantConsent,init,isDoNotTrackEnabled,matchedAgentToken,readSnapshot,referrerDomainFromReferer,renderPrePaintScript,sessionCookieName,toWireSlot,uaTokenMatch,writeSnapshot});
1
+ "use strict";var kt=Object.create;var fe=Object.defineProperty,bt=Object.defineProperties,Et=Object.getOwnPropertyDescriptor,It=Object.getOwnPropertyDescriptors,Ct=Object.getOwnPropertyNames,Ze=Object.getOwnPropertySymbols,_t=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty,At=Object.prototype.propertyIsEnumerable;var et=(e,t,n)=>t in e?fe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,b=(e,t)=>{for(var n in t||(t={}))tt.call(t,n)&&et(e,n,t[n]);if(Ze)for(var n of Ze(t))At.call(t,n)&&et(e,n,t[n]);return e},se=(e,t)=>bt(e,It(t));var Rt=(e,t)=>{for(var n in t)fe(e,n,{get:t[n],enumerable:!0})},nt=(e,t,n,l)=>{if(t&&typeof t=="object"||typeof t=="function")for(let d of Ct(t))!tt.call(e,d)&&d!==n&&fe(e,d,{get:()=>t[d],enumerable:!(l=Et(t,d))||l.enumerable});return e};var Tt=(e,t,n)=>(n=e!=null?kt(_t(e)):{},nt(t||!e||!e.__esModule?fe(n,"default",{value:e,enumerable:!0}):n,e)),Ot=e=>nt(fe({},"__esModule",{value:!0}),e);var bn={};Rt(bn,{AGENT_INTENTS:()=>Be,BLOCK_ALIGNS:()=>qt,BLOCK_EMPHASES:()=>rn,BLOCK_FITS:()=>an,BLOCK_GAPS:()=>Yt,BLOCK_GRID_COLUMNS:()=>ln,BLOCK_HEADING_LEVELS:()=>cn,BLOCK_JUSTIFIES:()=>Zt,BLOCK_RATIOS:()=>sn,BLOCK_SIZES:()=>en,BLOCK_TEXT_ALIGNS:()=>on,BLOCK_TONES:()=>nn,BLOCK_WEIGHTS:()=>tn,CLICK_ID_KEYS:()=>je,LEGACY_SESSION_COOKIE_NAME:()=>Ge,LOCAL_MODE_BANNER:()=>He,MAX_BLOCK_ARMS:()=>gn,MAX_BLOCK_CHILDREN:()=>pn,MAX_BLOCK_DEPTH:()=>un,MAX_BLOCK_NODES:()=>dn,MAX_BLOCK_TEXT_LEN:()=>fn,PROD_KEYLESS_ERROR:()=>Ke,SNAPSHOT_STORAGE_KEY_PREFIX:()=>te,_registerConsentUpgradeInit:()=>mn,agentIntent:()=>ct,agentUaList:()=>ye,armOfResult:()=>Le,attachMicroSignalDetectors:()=>yt,baselineResultFor:()=>le,baselineSlots:()=>pt,classifiedAgents:()=>dt,deriveSessionSegment:()=>ut,detectDeviceClass:()=>Se,detectTimeOfDay:()=>xe,detectTrafficSource:()=>ve,extractTrackedParams:()=>Pe,grantConsent:()=>xn,init:()=>xt,isDoNotTrackEnabled:()=>Ve,matchedAgentToken:()=>We,readSnapshot:()=>ce,referrerDomainFromReferer:()=>we,renderPrePaintScript:()=>gt,sessionCookieName:()=>Ce,toWireSlot:()=>ke,uaTokenMatch:()=>he,writeSnapshot:()=>de});module.exports=Ot(bn);function Ie(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}try{if(typeof crypto!="undefined"&&typeof crypto.getRandomValues=="function"){let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n<16;n++)t+=e[n].toString(16).padStart(2,"0"),(n===3||n===5||n===7||n===9)&&(t+="-");return t}}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function me(e){return e?`_${e.slice(0,12)}`:""}var Ge="_snt_uid";function Ce(e){return`${Ge}${me(e)}`}var Bt="_snt_uid",Pt=365,_e="_snt_uid";function Lt(){return Ie()}function rt(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function Dt(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(l){}}function Ue(e){try{return localStorage.getItem(e)}catch(t){return null}}function ot(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function st(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function Mt(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function Kt(e){try{sessionStorage.removeItem(e)}catch(t){}}function Nt(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function Gt(e){try{localStorage.removeItem(e)}catch(t){}}function Ut(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var $t={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function Ae(e){var E,R,O,I,G,$,C;if(typeof window=="undefined")return $t;let t=me(e==null?void 0:e.apiKey),n=(E=e==null?void 0:e.cookieName)!=null?E:Ce(e==null?void 0:e.apiKey),l=`${_e}${t}`,d=`${_e}_tomb${t}`,c=((R=e==null?void 0:e.cookieTTLDays)!=null?R:Pt)*24*60*60,a=w=>w&&w.length>0?w:null,p=()=>{var w,P;return t&&!(e!=null&&e.cookieName)&&Ue(d)===null?(P=(w=a(rt(Bt)))!=null?w:a(Ue(_e)))!=null?P:a(st(_e)):null},s=(C=($=(G=(I=(O=a(rt(n)))!=null?O:a(Ue(l)))!=null?I:a(st(l)))!=null?G:p())!=null?$:a(e==null?void 0:e.ssrSessionId))!=null?C:Lt();Dt(n,s,c);let r=ot(l,s),o=Nt(n),m=r?!1:Mt(l,s),S=!r&&!o&&!m;return{getSessionId:()=>s,isEphemeral:()=>S,destroy:()=>{s=null,Ut(n),Gt(l),Kt(l),t&&!(e!=null&&e.cookieName)&&ot(d,"1")}}}function X(e){return Math.min(6e4,1e3*2**Math.min(e,6))}function ie(e){if(e.ok===!0)return"delivered";let{status:t}=e;return typeof t!="number"?"retry":t>=200&&t<300?"delivered":t>=400&&t<500&&t!==429?"dropped":"retry"}function Re(e,t){try{let n=localStorage.getItem(e);if(!n)return[];let l=JSON.parse(n);return Array.isArray(l)?(localStorage.removeItem(e),l.slice(-t)):[]}catch(n){return[]}}function Y(e,t,n){try{let l=(()=>{try{let i=localStorage.getItem(n);if(!i)return[];let c=JSON.parse(i);return Array.isArray(c)?c:[]}catch(i){return[]}})(),d=new Map;for(let i of l)d.set(i.id,i);for(let i of e)d.set(i.id,i);localStorage.setItem(n,JSON.stringify([...d.values()].slice(-t)))}catch(l){}}function Te(e,t){try{let n=localStorage.getItem(t);if(!n)return;let l=JSON.parse(n);if(!Array.isArray(l))return;let d=new Set(e),i=l.filter(c=>!d.has(c.id));if(i.length===l.length)return;i.length===0?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(i))}catch(n){}}var Ft=500,Wt=56*1024;function $e(e){return`_snt_retry_${e.slice(0,12)}`}var jt={push:()=>{},flush:()=>{},destroy:()=>{}};function it(e){var q,K,ne;if(typeof window=="undefined")return jt;let t=(q=e.flushIntervalMs)!=null?q:5e3,n=(K=e.maxBatchSize)!=null?K:20,l=(ne=e.maxRetrySize)!=null?ne:100,d=e.ingestUrl,i=e.apiKey,c=$e(i),a=[],p=new Set,s=[],r=f=>{for(let x of f)o.delete(x),!p.has(x)&&(p.add(x),s.push(x));for(;s.length>Ft;){let x=s.shift();x&&p.delete(x)}Te(f,c)},o=new Set,m=()=>{for(;a.length>l;){let f=a.shift();f&&o.delete(f.id)}},S=f=>{p.has(f.id)||o.has(f.id)||(o.add(f.id),a.push(f),m())},E=f=>{for(let x of f)p.has(x.id)||(o.add(x.id),a.push(x));m()},R=Re(c,l);for(let f of R)S(f);let O=0,I=0,G=(f,x=!0)=>{if(f.length===0)return;let j=JSON.stringify(f),Z=f.map(z=>z.id),F;try{F=fetch(d,{method:"POST",keepalive:!0,body:j,headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`}})}catch(z){Y(f,l,c),E(f),I++,O=Date.now()+X(I);return}let ue=z=>{if(ie(z)!=="retry"){if(!z.ok&&x){let re=f.filter(Q=>Q.eventType!=="pageview");if(re.length>0&&re.length<f.length){r(f.filter(Q=>Q.eventType==="pageview").map(Q=>Q.id)),G(re,!1);return}}r(Z),I=0,O=0;return}Y(f,l,c),E(f),I++,O=Date.now()+X(I)};F instanceof Promise?F.then(ue).catch(()=>{Y(f,l,c),E(f),I++,O=Date.now()+X(I)}):ue(F)},$=typeof TextEncoder!="undefined"?new TextEncoder:null,C=f=>$?$.encode(f).length:f.length,w=f=>{let x=[],j=2;for(let Z of f){let F=C(JSON.stringify(Z))+1;if(x.length>0&&j+F>Wt||x.length>=n)break;x.push(Z),j+=F}return x},P=()=>{try{if(Date.now()<O)return;for(;a.length>0&&!(Date.now()<O);){let f=a.filter(j=>!p.has(j.id));if(a.length=0,f.length===0)break;let x=w(f);if(x.length===0)break;x.length<f.length&&a.push(...f.slice(x.length)),G(x)}}catch(f){}},y=!0,A=null;A=setInterval(()=>{y&&P()},t);let M=()=>{document.visibilityState==="hidden"&&P()},W=()=>{P()};return document.addEventListener("visibilitychange",M),window.addEventListener("pagehide",W),{push(f){S(f),a.length>=n&&P()},flush:P,destroy(){y=!1,A!==null&&(clearInterval(A),A=null),document.removeEventListener("visibilitychange",M),window.removeEventListener("pagehide",W),P()}}}var Ht=200;function Fe(e){return`_snt_goal_retry_${e.slice(0,12)}`}var zt={send:()=>{},flush:()=>{},destroy:()=>{}};function at(e){var C,w,P;if(typeof window=="undefined")return zt;let t=(C=e.flushIntervalMs)!=null?C:5e3,n=(w=e.maxRetrySize)!=null?w:100,l=(P=e.maxPerFlush)!=null?P:5,d=Fe(e.apiKey),i=[],c=new Set,a=new Set,p=[],s=0,r=0,o=!1,m=y=>{for(c.delete(y.id),a.has(y.id)||(a.add(y.id),p.push(y.id));p.length>Ht;){let A=p.shift();A&&a.delete(A)}Te([y.id],d)},S=y=>{Y([y],n,d),!a.has(y.id)&&!c.has(y.id)&&(c.add(y.id),i.push(y)),Date.now()>=s&&(r++,s=Date.now()+X(r))},E=y=>{let A;try{A=fetch(e.url,{method:"POST",keepalive:!0,body:y.body,headers:e.headers})}catch(W){S(y);return}let M=W=>{var K;let q=ie(W);if(q==="retry"){S(y);return}q==="dropped"&&((K=e.onDrop)==null||K.call(e,y,W.status)),m(y),r=0,s=0};A instanceof Promise?A.then(M).catch(()=>S(y)):M(A)},R=()=>{try{if(o||Date.now()<s)return;let y=0;for(;i.length>0&&y<l&&!(Date.now()<s);){let A=i.shift();c.delete(A.id),!a.has(A.id)&&(y++,E(A))}}catch(y){}},O=Re(d,n);for(let y of O)c.has(y.id)||(c.add(y.id),i.push(y));O.length>0&&Y(O,n,d);let I=setInterval(R,t),G=()=>{document.visibilityState==="hidden"&&R()},$=()=>R();return document.addEventListener("visibilitychange",G),window.addEventListener("pagehide",$),{send(y){if(!o&&!(a.has(y.id)||c.has(y.id))){if(Date.now()<s){Y([y],n,d),c.add(y.id),i.push(y);return}E(y)}},flush:R,destroy(){clearInterval(I),document.removeEventListener("visibilitychange",G),window.removeEventListener("pagehide",$),R(),o=!0}}}var Qt=1800*1e3;function Oe(e,t){return`${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function lt(e=Qt,t){let n=new Map,l=`_snt_asgn${me(t)}_`,d=(s,r)=>`${l}${encodeURIComponent(s)}:${encodeURIComponent(r)}`,i=s=>{let r=s.slice(l.length),o=r.indexOf(":");if(o<0)return null;try{return{componentId:decodeURIComponent(r.slice(0,o)),segment:decodeURIComponent(r.slice(o+1))}}catch(m){return null}},c=()=>{try{let s=[];for(let r=0;r<localStorage.length;r++){let o=localStorage.key(r);o!=null&&o.startsWith(l)&&s.push(o)}return s}catch(s){return[]}},a=s=>s.assignedAt+(s.ttlMs&&s.ttlMs>0?s.ttlMs:e)<Date.now();return typeof window!="undefined"&&(()=>{for(let s of c())try{let r=localStorage.getItem(s);if(!r)continue;let o=JSON.parse(r);if(a(o)){localStorage.removeItem(s);continue}let m=i(s);if(!m)continue;n.set(Oe(m.componentId,m.segment),o)}catch(r){}})(),{get(s,r){let o=n.get(Oe(s,r));return o?a(o)?(n.delete(Oe(s,r)),null):o:null},set(s,r,o){let m=Oe(s,r);n.set(m,o);try{localStorage.setItem(d(s,r),JSON.stringify(o))}catch(S){}},clear(){n.clear();for(let s of c())try{localStorage.removeItem(s)}catch(r){}}}}var ye=["GPTBot","ChatGPT-User","OAI-SearchBot","ClaudeBot","Claude-User","Claude-SearchBot","PerplexityBot","Perplexity-User","Google-Extended","Applebot-Extended","Meta-ExternalAgent","Bytespider","CCBot","Amazonbot","cohere-ai","Diffbot"];function he(e){return We(e)!==null}function We(e){var n;if(!e)return null;let t=e.toLowerCase();return(n=ye.find(l=>t.includes(l.toLowerCase())))!=null?n:null}var Be={GPTBot:"training","ChatGPT-User":"user","OAI-SearchBot":"search",ClaudeBot:"training","Claude-User":"user","Claude-SearchBot":"search",PerplexityBot:"search","Perplexity-User":"user","Google-Extended":"training","Applebot-Extended":"training","Meta-ExternalAgent":"training",Bytespider:"training",CCBot:"training",Amazonbot:"other","cohere-ai":"training",Diffbot:"other"};function ct(e){if(!e)return"other";let t=ye.find(n=>n.toLowerCase()===e.toLowerCase());return t&&Be[t]||"other"}function dt(){var t;let e={user:[],search:[],training:[],other:[]};for(let n of ye)e[(t=Be[n])!=null?t:"other"].push(n);return e}function Se(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function ve(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(d){}let l=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(l)?"search":/(^|\.)(twitter\.com|x\.com|facebook\.com|linkedin\.com|reddit\.com|t\.co)$/.test(l)?"social":"referral"}catch(n){return"direct"}}function we(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}var je=["gclid","gbraid","wbraid","fbclid","ttclid","msclkid","twclid","li_fat_id"];function Pe(e){let t={},n={};try{let l=typeof e=="string"||e instanceof URLSearchParams?new URLSearchParams(e):Object.entries(e).flatMap(([d,i])=>{let c=Array.isArray(i)?i[0]:i;return c===void 0?[]:[[d,c]]});for(let[d,i]of l)d.startsWith("utm_")?d in t||(t[d]=i):je.includes(d)&&(d in n||(n[d]=i))}catch(l){}return{utmParams:t,clickIds:n}}function xe(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function ut(e){let t=Jt("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function Jt(e,t){var i,c,a,p,s,r,o,m;let n=(c=(i=t==null?void 0:t.userAgent)==null?void 0:i.trim())!=null?c:"",l=(p=(a=t==null?void 0:t.referer)==null?void 0:a.trim())!=null?p:"",d=(s=t==null?void 0:t.now)!=null?s:new Date;return{sessionId:e,ephemeral:!1,utmParams:(r=t==null?void 0:t.utmParams)!=null?r:{},clickIds:(o=t==null?void 0:t.clickIds)!=null?o:{},deviceClass:n?Se(n):"desktop",trafficSource:l?ve(l,t==null?void 0:t.appOrigin):"direct",referrerDomain:we(l),timeOfDay:xe(d),dayOfWeek:(m=["sun","mon","tue","wed","thu","fri","sat"][d.getDay()])!=null?m:"sun",automation:(t==null?void 0:t.webdriver)===!0||he(n)}}var ae=require("@sentientui/policy");function ke(e){return b(b(b({id:e.id},e.arms?{arms:[...e.arms]}:{}),e.dims?{dims:Object.fromEntries(Object.entries(e.dims).map(([t,n])=>[t,[...n]]))}:{}),e.baseline!==void 0?{baseline:e.baseline}:{})}function le(e){let t=ke(e);return(0,ae.slotResultFor)(t,(0,ae.slotBaselineArm)(t))}function pt(e){let t={};for(let n of e)t[n.id]=le(n);return t}function Le(e){return typeof e=="string"?e:(0,ae.canonicalArm)(e)}var te="_snt_snap:",Vt=["low","medium","high"];function ce(e){try{let t=localStorage.getItem(te+e);if(!t)return null;let n=JSON.parse(t);return!n||typeof n!="object"||n.v!==1||typeof n.persona!="string"||typeof n.band!="string"||!Vt.includes(n.band)||typeof n.slots!="object"||n.slots===null||Array.isArray(n.slots)||!(n.layoutOrder===null||Array.isArray(n.layoutOrder))||typeof n.savedAt!="number"||!(n.slotConfig===void 0||typeof n.slotConfig=="object"&&n.slotConfig!==null&&!Array.isArray(n.slotConfig))?null:n}catch(t){return null}}function de(e,t){try{localStorage.setItem(te+e,JSON.stringify(t))}catch(n){}}function gt(e){return"(function(){try{var r=localStorage.getItem("+JSON.stringify(te+e).replace(/</g,"\\u003c")+');if(!r)return;var s=JSON.parse(r);if(!s||s.v!==1||typeof s.persona!=="string"||typeof s.band!=="string")return;var d=document.documentElement;if(d.hasAttribute("data-sentient-persona"))return;d.setAttribute("data-sentient-persona",s.persona);d.setAttribute("data-sentient-confidence",s.band);}catch(e){}})();'}var Qe=require("@sentientui/policy");var Me=require("@sentientui/policy");var Ke="[sentient] No API key configured \u2014 nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.",He="[sentient] Local mode \u2014 decisions are simulated on-device and nothing is sent over the network. Add an API key to learn from real traffic.",ft=!1,De=!1;function Xt(){var e;try{return(e=new URLSearchParams(window.location.search).get("sentient_persona"))!=null?e:void 0}catch(t){return}}function mt(e){var s;let t=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),n=(s=t.getSessionId())!=null?s:"local",l=Xt(),d=import("@sentientui/core/local").then(r=>{let o=r;return o.LOCAL_ENGINE_AVAILABLE?(ft||(ft=!0,console.info(He)),o):(De||(De=!0,console.error(Ke)),null)}).catch(()=>(De||(De=!0,console.error(Ke)),null)),i=null,c={low:.15,medium:.5,high:.85},a=(()=>{var o;if(e.initialPersona)return b({},e.initialPersona);let r=ce(e.apiKey||"local");return r?{persona:r.persona,confidence:(o=c[r.band])!=null?o:.15}:null})();function p(r){let o=document.documentElement;o.dataset.sentientPersona===void 0&&(o.dataset.sentientPersona=r.persona,o.dataset.sentientConfidence=(0,Me.confidenceBand)(r.confidence))}return{isLocal:!0,async decide(r){var S,E,R;let o=await d;if(!o)return null;let m=o.createLocalEngine({sessionId:n,forcedPersona:l}).decide(r);return i=se(b({},m),{layoutOrder:(E=(S=m.layoutOrder)!=null?S:i==null?void 0:i.layoutOrder)!=null?E:null,slots:b(b({},(R=i==null?void 0:i.slots)!=null?R:{}),m.slots)}),de(e.apiKey||"local",{v:1,persona:i.persona,band:(0,Me.confidenceBand)(i.confidence),slots:i.slots,layoutOrder:i.layoutOrder,savedAt:Date.now()}),p(m),m},getSlotResult(r){var o,m,S;return(S=(m=i==null?void 0:i.slots[r])!=null?m:(o=e.initialSlots)==null?void 0:o[r])!=null?S:null},getPersona(){let r=i!=null?i:a;return r?{persona:r.persona,confidence:r.confidence,band:(0,Me.confidenceBand)(r.confidence)}:null},async assign(r,o){var E;let m=await d;return!m||!o||o.length===0?o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null:{variantId:(E=m.createLocalEngine({sessionId:n,forcedPersona:l}).decide({components:[{id:r,variantIds:o}]}).assignments[r])!=null?E:o[0],assignmentTtlMs:0}},track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>t.destroy()}}var Yt=["none","sm","md","lg"],qt=["start","center","end","stretch"],Zt=["start","center","end","between"],en=["sm","md","lg"],tn=["normal","medium","bold"],nn=["default","muted","accent"],rn=["primary","secondary","ghost"],on=["left","center","right"],sn=["auto","square","landscape","wide"],an=["cover","contain"],ln=[2,3,4],cn=[2,3,4],dn=64,un=5,pn=12,gn=4,fn=500;function yt(e,t,n,l){let d=[];{let a=!1,p=[],s=()=>{if(a)return;let r=Date.now();for(p.push(r);p.length>0&&r-p[0]>500;)p.shift();p.length>=3&&(a=!0,e("rage_click"))};t.addEventListener("click",s),d.push(()=>t.removeEventListener("click",s))}{let i=!1,c=a=>{if(i||!(a.target instanceof Node)||!t.contains(a.target)&&t!==a.target)return;i=!0;let p=typeof window!="undefined"?window.getSelection():null,s=p?p.toString().length:0;e("text_copy",{selectionLength:s})};document.addEventListener("copy",c),d.push(()=>document.removeEventListener("copy",c))}{let i=!1,c=!1,a=null,p=()=>{a!==null&&(clearTimeout(a),a=null)},s=()=>{i||!c||(p(),a=setTimeout(()=>{!i&&c&&(i=!0,e("scroll_hesitation"))},3e3))},r=()=>{p(),s()},o=S=>{for(let E of S)c=E.intersectionRatio>.3,c?s():p()},m=new IntersectionObserver(o,{threshold:[.3]});m.observe(t),window.addEventListener("scroll",r,{passive:!0}),d.push(()=>{m.disconnect(),window.removeEventListener("scroll",r),p()})}if((l==null?void 0:l.tabLoss)!==!1){let i=!1,c=n!=null?n:Date.now(),a=()=>{if(i||document.visibilityState!=="hidden")return;let p=Date.now()-c;p<15e3&&(i=!0,e("tab_loss",{timeOnPage:p}))};document.addEventListener("visibilitychange",a),d.push(()=>document.removeEventListener("visibilitychange",a))}return()=>{for(let i of d)i()}}var ht="https://api.sentient-ui.com/v1/events",N=new Map,St=null;function mn(e,t){let n=N.get(e);n&&n.upgrade&&(n.reinit=t)}function yn(e){return"value"in e||"currency"in e||"externalId"in e||"metadata"in e||"weight"in e||"stepIndex"in e}var hn=3;function ze(){return Ie()}function Ne(){var e;if(typeof window!="undefined")return((e=window.location)==null?void 0:e.pathname)||void 0}function Sn(){let e=new WeakMap,t=new Map,n=!1,l=(a,p,s)=>{let r=a.get(p);return s===null?r?!0:(a.set(p,new Set),!1):r?r.has(s)?!0:(r.add(s),!1):(a.set(p,new Set([s])),!1)},d=typeof window!="undefined"&&"event"in window,i=()=>{if(!d)return;let a=window.event;return typeof Event=="function"&&a instanceof Event?a:void 0},c=()=>{if(d){Promise.resolve().then(()=>{n=!1,t.clear()});return}let a=!1,p=()=>{a||(a=!0,n=!1,t.clear())};if(setTimeout(p,0),typeof MessageChannel=="function"){let s=new MessageChannel;s.port1.onmessage=()=>{s.port1.close(),s.port2.close(),p()},s.port2.postMessage(0)}};return{firedBefore(a,p){let s=i();if(s){let o=e.get(s);return o||(o=new Map,e.set(s,o)),l(o,a,p)}let r=l(t,a,p);return!r&&!n&&(n=!0,c()),r}}}var vt=new Set;function vn(e,t,n){let l=typeof window=="undefined"?null:window.history;if(!l)return()=>{};let d=!1,i,c=()=>{if(d)return;let s=Ne();!s||s===i||(i=s,e.track({projectId:t,componentId:"__page__",eventType:"pageview",payload:{}}),n(`${t}:${s}`))},a=[];for(let s of["pushState","replaceState"]){let r=l[s],o=function(...m){let S=r.apply(this,m);return c(),S};l[s]=o,a.push([s,r,o])}window.addEventListener("popstate",c);let p=Ne();return p&&vt.has(`${t}:${p}`)?i=p:c(),()=>{if(!d){d=!0,window.removeEventListener("popstate",c);for(let[s,r,o]of a)l[s]===o&&(l[s]=r)}}}var be={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),decide:()=>Promise.resolve(null),getSlotResult:()=>null,getPersona:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}};function wn(){try{return Pe(window.location.search)}catch(e){return{utmParams:{},clickIds:{}}}}function wt(e){return e.replace(/\/events\/?$/,"")}function Ve(){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")}function xn(e){var p;if(typeof window=="undefined")return;let t=e!=null?e:St;if(!t){console.warn("[sentient] grantConsent() called before init()");return}let n=N.get(t);if(!n){console.warn("[sentient] grantConsent() called before init()");return}let{config:l,upgrade:d,reinit:i}=n;if(!d){n.upgradeBlockedReason&&console.warn(n.upgradeBlockedReason);return}if(l.respectDoNotTrack!==!1&&Ve())return;let c=(i!=null?i:xt)(se(b({},l),{consent:!0}));d(c);let a=(p=N.get(t))==null?void 0:p.dispose;N.set(t,{config:se(b({},l),{consent:!0}),upgrade:null,dispose:a})}function kn(e){var s;let t=e.preConsentBehavior==="statistical_winner",n=wt((s=e.ingestUrl)!=null?s:ht),l={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},d=new Map,i=new Map,c={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),assign(r,o,m){if(!t)return Promise.resolve(null);let S=d.get(r);return S?Promise.resolve(S):Je(i,r,async()=>{try{let E=new URLSearchParams({componentId:r});for(let G of o!=null?o:[])E.append("variantIds[]",G);let R=await fetch(`${n}/winner?${E.toString()}`,{headers:l});if(!R.ok)return o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null;let I={variantId:(await R.json()).variantId,assignmentTtlMs:0};return d.set(r,I),I}catch(E){return o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null}})},decide:()=>Promise.resolve(null),getSlotResult:()=>null,getPersona:()=>null,getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}},a={track:r=>c.track(r),goal:((r,o,m,S)=>c.goal(r,o,m,S)),componentGoal:(r,o,m)=>c.componentGoal(r,o,m),identify:r=>c.identify(r),getAssignment:(r,o)=>c.getAssignment(r,o),assign:(r,o,m,S)=>c.assign(r,o,m,S),decide:r=>c.decide(r),getSlotResult:r=>c.getSlotResult(r),getPersona:()=>c.getPersona(),fetchWeights:()=>c.fetchWeights(),getGraph:()=>c.getGraph(),dispose:()=>c.dispose(),destroy:()=>c.destroy()};function p(r){c=r}return{proxy:a,setInner:p}}function Je(e,t,n){let l=e.get(t);if(l)return l;let d=(async()=>{try{return await n()}finally{e.delete(t)}})();return e.set(t,d),d}function xt(e){var x,j,Z,F,ue,z,re,Q,Xe;if(typeof window=="undefined")return be;St=e.apiKey||"local";let t=N.get(e.apiKey||"local");if(t!=null&&t.dispose)try{t.dispose()}catch(u){}let n=e.respectDoNotTrack!==!1&&Ve(),l=e.consent===!1||n,d=typeof e.apiKey=="string"&&e.apiKey.startsWith("pk_");if(e.localMode===!0||!d&&e.localMode!==!1)return N.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): this client is keyless/local \u2014 there is no hosted client to upgrade to. Configure a pk_ API key to enable tracking."}),l?be:mt(e);if(l){if(!e.apiKey||!e.apiKey.startsWith("pk_"))return e.preConsentBehavior==="statistical_winner"&&console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),N.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): the client was initialized with an invalid apiKey (expected a pk_ public key) \u2014 consent cannot enable tracking."}),be;let{proxy:u,setInner:g}=kn(e);return N.set(e.apiKey,{config:e,upgrade:n?null:g}),u}if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),be;if(e.ingestUrl==="")return console.warn("[sentient] init() called with an empty ingestUrl. SDK disabled."),be;let i=(x=e.ingestUrl)!=null?x:ht,c=Date.now(),a=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),p=lt(void 0,e.apiKey),s=it({ingestUrl:i,apiKey:e.apiKey}),r=wt(i),o={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},m=new Set,S=at({url:`${r}/goals`,apiKey:e.apiKey,headers:o,onDrop:(u,g)=>{if(!e.debug){if(m.has(g))return;m.add(g)}console.warn(`[sentient] goal dropped (HTTP ${g}) \u2014 this will not be retried. `+(g===400?"The session was not found: call init() and let the session upsert complete before firing goals.":g===401||g===403?"Check the API key and that this origin is on the project allowlist.":"See the response status for the cause."),u)}}),E=Se((j=navigator.userAgent)!=null?j:""),R=window.location.origin,O=ve((Z=document.referrer)!=null?Z:"",R),I=(F=e.sessionSegment)!=null?F:`${E}:${O}`,G=new Map,$=new Map,C=new Map,w=null,P=u=>{for(let g of u)C.has(g.id)||C.set(g.id,le(g))};if(e.initialSlots)for(let[u,g]of Object.entries(e.initialSlots))C.set(u,g);let y=ce(e.apiKey);if(y)for(let[u,g]of Object.entries(y.slots))C.has(u)||C.set(u,g);let A={low:.15,medium:.5,high:.85};if(e.initialPersona)w=b({},e.initialPersona);else{let u=document.documentElement.dataset;u.sentientPersona?w={persona:u.sentientPersona,confidence:(z=A[(ue=u.sentientConfidence)!=null?ue:"low"])!=null?z:.15}:y&&(w={persona:y.persona,confidence:(re=A[y.band])!=null?re:.15})}if(e.initialAssignments)for(let[u,g]of Object.entries(e.initialAssignments))p.set(u,I,{variantId:g,assignedAt:Date.now(),segment:I,confidence:1});let M=Promise.resolve(),W=a.getSessionId();if(W){let u=we((Q=document.referrer)!=null?Q:""),{utmParams:g,clickIds:v}=wn(),T=b(b(b({sessionId:W,deviceClass:E,trafficSource:O,referrerDomain:u,utmParams:g,clickIds:v,timeOfDay:xe(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:a.isEphemeral(),automation:typeof navigator!="undefined"&&navigator.webdriver===!0||he((Xe=navigator.userAgent)!=null?Xe:"")},e.userId?{userId:e.userId}:{}),e.persona?{persona:e.persona}:{}),e.country?{country:e.country}:{}),L=async()=>{for(let h=0;;h++){try{let _=await fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(T),headers:o});if(_.status===402){console.warn("[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing");return}if(_.ok||ie(_)==="dropped")return}catch(_){}if(h>=hn){console.warn("[SentientUI] Could not register the session after retries. Conversions in this visit may not be recorded.");return}await new Promise(_=>setTimeout(_,X(h+1)))}};try{M=L()}catch(h){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:s});let q=Sn(),K=null,ne=!1,f={goal(u,g={},v=1,T=0){var pe,ge,oe,k,H,Ee,B;let L=a.getSessionId();if(!L)return;let h=yn(g)?g:{metadata:g},_=[u,(pe=h.externalId)!=null?pe:"",(ge=h.stepIndex)!=null?ge:T,(oe=h.weight)!=null?oe:v].join("\0"),U=h.value!==void 0?`${h.value}\0${(k=h.currency)!=null?k:""}`:null;if(q.firedBefore(_,U)){e.debug&&console.log(`[sentient] goal("${u}") already recorded for this action \u2014 not sent twice`);return}let D=ze(),J={sessionId:L,name:u,metadata:(H=h.metadata)!=null?H:{},weight:(Ee=h.weight)!=null?Ee:v,stepIndex:(B=h.stepIndex)!=null?B:T,goalId:D,value:h.value,currency:h.currency,externalId:h.externalId};e.debug&&console.log("[sentient] goal",J);let ee={id:D,body:JSON.stringify(J)};M.then(()=>S.send(ee))},componentGoal(u,g,v){var D,J,ee;let T=a.getSessionId();if(!T)return;let L=p.get(u,I),h=L?null:(D=C.get(u))!=null?D:null;if(!L&&h===null){e.debug&&console.warn(`[sentient] componentGoal("${u}"): no assignment or slot decision yet \u2014 render its <Adaptive>/adaptive hook or call assign()/decide() before recording a goal.`);return}let _=L?L.variantId:Le(h),U={id:ze(),sessionId:T,projectId:e.apiKey,componentId:u,variantId:_,eventType:"goal_achieved",goalType:g,payload:b({reward:(J=v==null?void 0:v.reward)!=null?J:1,goalValue:v==null?void 0:v.value,currency:v==null?void 0:v.currency},(ee=v==null?void 0:v.metadata)!=null?ee:{}),timestamp:Date.now(),timeInSession:Date.now()-c,path:Ne()};e.debug&&console.log("[sentient] componentGoal",U),M.then(()=>s.push(U))},identify(u){let g=a.getSessionId();g&&M.then(()=>{fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify({sessionId:g,userId:u,ephemeral:a.isEphemeral()}),headers:o}).catch(()=>{})})},track(u){let g=a.getSessionId();if(!g)return;let v=se(b({path:Ne()},u),{id:ze(),sessionId:g,timestamp:Date.now(),timeInSession:Date.now()-c});e.debug&&console.log("[sentient] track",v),M.then(()=>s.push(v))},getAssignment(u,g){return p.get(u,g)},async assign(u,g,v,T){let L=a.getSessionId();if(!L)return null;let h=p.get(u,I);if(h&&(g!=null&&g.length||h.content!==void 0)){let _=h.ttlMs&&h.ttlMs>0?Math.max(0,h.assignedAt+h.ttlMs-Date.now()):0;return{variantId:h.variantId,assignmentTtlMs:_,content:h.content}}return Je(G,u,async()=>{await M;try{let _={sessionId:L,componentId:u,variantIds:g};T!==void 0?_.agentDataByVariant=T:v!==void 0&&(_.agentData=v);let U=await fetch(`${r}/assign`,{method:"POST",body:JSON.stringify(_),headers:o});if(!U.ok)return null;let D=await U.json();return p.set(u,I,b({variantId:D.variantId,assignedAt:Date.now(),segment:I,confidence:1,content:D.content},D.assignmentTtlMs&&D.assignmentTtlMs>0?{ttlMs:D.assignmentTtlMs}:{})),D}catch(_){return null}})},async decide(u){var h,_;let g=a.getSessionId();if(!g)return null;let v=(h=u.slots)!=null?h:[],T={sessionId:g};u.sections&&u.sections.length>0&&(T.sections=u.sections.map(U=>({id:U}))),T.components=(_=u.components)!=null?_:[],v.length>0&&(T.slots=v.map(ke)),u.slotsFrom==="registry"&&(T.slotsFrom="registry"),u.v&&(T.v=u.v),e.persona&&(T.persona=e.persona);let L=JSON.stringify(T);return Je($,L,async()=>{var U,D,J,ee,pe,ge;await M;try{let oe=await fetch(`${r}/decide`,{method:"POST",body:L,headers:o});if(!oe.ok)return P(v),null;let k=await oe.json(),H={};for(let B of v){let V=(U=k.slots)==null?void 0:U[B.id];if(V!==void 0){H[B.id]=V,C.set(B.id,V);continue}let Ye=C.get(B.id);if(Ye!==void 0)H[B.id]=Ye;else{let qe=le(B);H[B.id]=qe,C.set(B.id,qe)}}if(k.slots)for(let[B,V]of Object.entries(k.slots))B in H||(H[B]=V,C.set(B,V));let Ee=w!=null&&w.persona!=="unknown";k.persona&&!(k.persona==="unknown"&&Ee)?w={persona:k.persona,confidence:(D=k.confidence)!=null?D:0}:w||(w={persona:"unknown",confidence:0});for(let[B,V]of Object.entries((J=k.assignments)!=null?J:{}))p.set(B,I,{variantId:V,assignedAt:Date.now(),segment:I,confidence:1});return de(e.apiKey,b(b({v:1,persona:w.persona,band:(0,Qe.confidenceBand)(w.confidence),slots:Object.fromEntries(C),layoutOrder:(ee=k.layoutOrder)!=null?ee:null,savedAt:Date.now()},k.slotConfig?{slotConfig:k.slotConfig}:{}),k.palette?{palette:k.palette}:{})),b(b(b(b({layoutOrder:(pe=k.layoutOrder)!=null?pe:null,assignments:(ge=k.assignments)!=null?ge:{},slots:H,persona:w.persona,confidence:w.confidence},k.slotConfig?{slotConfig:k.slotConfig}:{}),k.goals?{goals:k.goals}:{}),k.sectionMap?{sectionMap:k.sectionMap}:{}),k.palette?{palette:k.palette}:{})}catch(oe){return P(v),null}})},getSlotResult(u){var g;return(g=C.get(u))!=null?g:null},getPersona(){return w?{persona:w.persona,confidence:w.confidence,band:(0,Qe.confidenceBand)(w.confidence)}:null},async fetchWeights(){var u;try{let g=await fetch(`${r}/weights`,{headers:o});return g.ok?(u=(await g.json()).components)!=null?u:[]:[]}catch(g){return[]}},getGraph(){return{pageNodes:[],capturedAt:0}},dispose(){var u;K==null||K(),ne=!0,s.destroy(),S.destroy(),((u=N.get(e.apiKey))==null?void 0:u.dispose)===f.dispose&&N.delete(e.apiKey),e.debug&&console.log("[sentient] disposed")},destroy(){var u;K==null||K(),ne=!0,s.destroy(),S.destroy(),a.destroy(),p.clear(),((u=N.get(e.apiKey))==null?void 0:u.dispose)===f.dispose&&N.delete(e.apiKey);try{localStorage.removeItem(te+e.apiKey),localStorage.removeItem($e(e.apiKey)),localStorage.removeItem(Fe(e.apiKey))}catch(g){}e.debug&&console.log("[sentient] destroyed")}};if(N.set(e.apiKey,{config:e,upgrade:null,dispose:f.dispose}),K=vn(f,e.apiKey,u=>{M.then(()=>{ne||vt.add(u)})}),e.debug){let u=window;u.__sentient&&(u.__sentient.client=f)}return f}0&&(module.exports={AGENT_INTENTS,BLOCK_ALIGNS,BLOCK_EMPHASES,BLOCK_FITS,BLOCK_GAPS,BLOCK_GRID_COLUMNS,BLOCK_HEADING_LEVELS,BLOCK_JUSTIFIES,BLOCK_RATIOS,BLOCK_SIZES,BLOCK_TEXT_ALIGNS,BLOCK_TONES,BLOCK_WEIGHTS,CLICK_ID_KEYS,LEGACY_SESSION_COOKIE_NAME,LOCAL_MODE_BANNER,MAX_BLOCK_ARMS,MAX_BLOCK_CHILDREN,MAX_BLOCK_DEPTH,MAX_BLOCK_NODES,MAX_BLOCK_TEXT_LEN,PROD_KEYLESS_ERROR,SNAPSHOT_STORAGE_KEY_PREFIX,_registerConsentUpgradeInit,agentIntent,agentUaList,armOfResult,attachMicroSignalDetectors,baselineResultFor,baselineSlots,classifiedAgents,deriveSessionSegment,detectDeviceClass,detectTimeOfDay,detectTrafficSource,extractTrackedParams,grantConsent,init,isDoNotTrackEnabled,matchedAgentToken,readSnapshot,referrerDomainFromReferer,renderPrePaintScript,sessionCookieName,toWireSlot,uaTokenMatch,writeSnapshot});
2
2
  //# sourceMappingURL=index.js.map