@sentientui/core 0.26.0 → 0.27.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.
@@ -1,135 +1,6 @@
1
- import { b as SlotDeclInput } from './session-meta-C-bMqVeq.js';
1
+ import { W as SlotConfigEntry, V as SitePalette, X as SlotDeclInput, z as CompoundLocator } from './session-meta-dN92q19_.cjs';
2
2
  import { SlotResult } from '@sentientui/policy';
3
3
 
4
- /**
5
- * Composition Blocks (spec: 2026-08-20-nocode-composition-variants-design.md §4).
6
- *
7
- * A bounded, TYPED component tree — never HTML — that a registry arm may carry
8
- * (`published_config.arms[].blocks`). The vocabulary is the whitelist: every
9
- * prop value is an enumerated token, validated server-side before publish
10
- * (apps/api/src/domain/composition-blocks.ts) and rendered client-side through
11
- * document.createElement + property assignment only. No innerHTML/outerHTML/
12
- * insertAdjacentHTML exists anywhere on this path — the security property is
13
- * preserved by never accepting HTML, not by sanitizing it, which is why there
14
- * is no sanitizer to keep honest.
15
- *
16
- * Token lists live here (not in the API domain like SlotOps' mirrors) because
17
- * three parties must agree byte-for-byte: the server validator, the snippet
18
- * renderer, and eventually the React renderer (§11) — a drifted copy would let
19
- * a published arm fail to render, which the fail-safe turns into an invisibly
20
- * missing section, not an error.
21
- */
22
- declare const BLOCK_GAPS: readonly ["none", "sm", "md", "lg"];
23
- declare const BLOCK_ALIGNS: readonly ["start", "center", "end", "stretch"];
24
- declare const BLOCK_JUSTIFIES: readonly ["start", "center", "end", "between"];
25
- declare const BLOCK_SIZES: readonly ["sm", "md", "lg"];
26
- declare const BLOCK_WEIGHTS: readonly ["normal", "medium", "bold"];
27
- declare const BLOCK_TONES: readonly ["default", "muted", "accent"];
28
- declare const BLOCK_EMPHASES: readonly ["primary", "secondary", "ghost"];
29
- declare const BLOCK_TEXT_ALIGNS: readonly ["left", "center", "right"];
30
- declare const BLOCK_RATIOS: readonly ["auto", "square", "landscape", "wide"];
31
- declare const BLOCK_FITS: readonly ["cover", "contain"];
32
- declare const BLOCK_GRID_COLUMNS: readonly [2, 3, 4];
33
- declare const BLOCK_HEADING_LEVELS: readonly [2, 3, 4];
34
- type BlockGap = (typeof BLOCK_GAPS)[number];
35
- type BlockAlign = (typeof BLOCK_ALIGNS)[number];
36
- type BlockJustify = (typeof BLOCK_JUSTIFIES)[number];
37
- type BlockSize = (typeof BLOCK_SIZES)[number];
38
- type BlockWeight = (typeof BLOCK_WEIGHTS)[number];
39
- type BlockTone = (typeof BLOCK_TONES)[number];
40
- type BlockEmphasis = (typeof BLOCK_EMPHASES)[number];
41
- type BlockTextAlign = (typeof BLOCK_TEXT_ALIGNS)[number];
42
- type BlockRatio = (typeof BLOCK_RATIOS)[number];
43
- type BlockFit = (typeof BLOCK_FITS)[number];
44
- /** Flex row/column container. */
45
- type StackBlock = {
46
- type: 'stack';
47
- direction: 'row' | 'column';
48
- children: BlockNode[];
49
- gap?: BlockGap;
50
- align?: BlockAlign;
51
- justify?: BlockJustify;
52
- wrap?: boolean;
53
- };
54
- /** 2–4 equal-column grid container. */
55
- type GridBlock = {
56
- type: 'grid';
57
- columns: (typeof BLOCK_GRID_COLUMNS)[number];
58
- children: BlockNode[];
59
- gap?: BlockGap;
60
- align?: BlockAlign;
61
- };
62
- /** Paragraph / label. */
63
- type TextBlock = {
64
- type: 'text';
65
- value: string;
66
- size?: BlockSize;
67
- weight?: BlockWeight;
68
- tone?: BlockTone;
69
- align?: BlockTextAlign;
70
- };
71
- /** h2–h4 — never h1 (the page owns its h1). */
72
- type HeadingBlock = {
73
- type: 'heading';
74
- value: string;
75
- level: (typeof BLOCK_HEADING_LEVELS)[number];
76
- size?: BlockSize;
77
- align?: BlockTextAlign;
78
- };
79
- /** Link styled as a button. `tag` feeds agent legibility (agentDataByVariant). */
80
- type ButtonBlock = {
81
- type: 'button';
82
- label: string;
83
- href: string;
84
- emphasis?: BlockEmphasis;
85
- size?: BlockSize;
86
- tag?: string;
87
- };
88
- /** Inline text link. */
89
- type LinkBlock = {
90
- type: 'link';
91
- label: string;
92
- href: string;
93
- tag?: string;
94
- };
95
- /** Image. `alt` is required; empty only with an explicit `decorative: true`. */
96
- type ImageBlock = {
97
- type: 'image';
98
- src: string;
99
- alt: string;
100
- decorative?: boolean;
101
- ratio?: BlockRatio;
102
- fit?: BlockFit;
103
- };
104
- /** Eyebrow / pill. */
105
- type BadgeBlock = {
106
- type: 'badge';
107
- value: string;
108
- tone?: BlockTone;
109
- };
110
- /** Vertical rhythm. */
111
- type SpacerBlock = {
112
- type: 'spacer';
113
- size: BlockSize;
114
- };
115
- type BlockNode = StackBlock | GridBlock | TextBlock | HeadingBlock | ButtonBlock | LinkBlock | ImageBlock | BadgeBlock | SpacerBlock;
116
- /** The derived site palette (spec §4 "Colour and type: derived, not chosen").
117
- * Sampled by the on-site editor from the live page's own buttons — computed
118
- * styles, so values are plain colors (rgb/hex), never var()/url() — validated
119
- * server-side, stored per project, and served with the decision so injected
120
- * blocks render in the merchant's own primary color and corner radius.
121
- * Absent → the renderer's neutral inherit-first defaults. */
122
- type SitePalette = {
123
- primaryBg: string;
124
- primaryText: string;
125
- radius: string;
126
- };
127
- declare const MAX_BLOCK_NODES = 64;
128
- declare const MAX_BLOCK_DEPTH = 5;
129
- declare const MAX_BLOCK_CHILDREN = 12;
130
- declare const MAX_BLOCK_ARMS = 4;
131
- declare const MAX_BLOCK_TEXT_LEN = 500;
132
-
133
4
  /** Manages anonymous session identity with cookie + localStorage layers. */
134
5
  type SessionConfig = {
135
6
  cookieName?: string;
@@ -250,90 +121,6 @@ type GraphClient = {
250
121
  */
251
122
  declare function sanitizePageUrl(href: string): string;
252
123
 
253
- /**
254
- * Decision snapshot: the SPA / return-visit pre-paint source. Written after
255
- * every successful decide; read by the inline pre-paint script (before any
256
- * framework code runs) and by init() to seed slot/persona state.
257
- */
258
-
259
- declare const SNAPSHOT_STORAGE_KEY_PREFIX = "_snt_snap:";
260
- /** Versioned compound locator: resolve id → dataAttr → selector, then verify
261
- * against fingerprint. Lets a slot survive DOM/markup drift. */
262
- type CompoundLocator = {
263
- v?: number;
264
- id?: string;
265
- dataAttr?: {
266
- name: string;
267
- value: string;
268
- };
269
- selector?: string;
270
- urlMatch?: string;
271
- fingerprint?: {
272
- tag?: string;
273
- text?: string;
274
- };
275
- semanticId?: string;
276
- };
277
- /** Bounded, declarative operations a registry arm may apply to its element.
278
- * The style set is a fixed whitelist (validated server-side); no arbitrary CSS,
279
- * HTML, or JS ever. `text` is applied via textContent; https-only URLs.
280
- * moveBefore/moveAfter (exactly one) reposition the element relative to a
281
- * uniquely-resolving sibling anchor — post-decide only, never pre-paint. */
282
- type SlotOps = {
283
- text?: string;
284
- style?: Record<string, string>;
285
- hidden?: boolean;
286
- href?: string;
287
- imageSrc?: string;
288
- imageAlt?: string;
289
- moveBefore?: CompoundLocator;
290
- moveAfter?: CompoundLocator;
291
- };
292
- /** Registry-mode apply info per slot: where to apply and what to set. Stored so
293
- * a returning visitor's pre-paint can reapply it. `target` is the Phase-2 bare
294
- * selector; `locator` (Phase 3) is the compound locator, preferred when present. */
295
- type SlotConfigEntry = {
296
- kind: 'tokens' | 'arms';
297
- target?: string;
298
- locator?: CompoundLocator;
299
- content?: string;
300
- ops?: SlotOps;
301
- /** Composition Blocks per arm — ALL arms, not just the served one, because
302
- * Option-B rendering pre-paints every arm hidden and reveals the served one
303
- * (spec §6). Holdout sessions receive the baseline arm's tree only, so the
304
- * control group's DOM stays meaningful. Absent for non-composition slots. */
305
- blocks?: Record<string, BlockNode>;
306
- };
307
- type DecisionSnapshot = {
308
- v: 1;
309
- persona: string;
310
- band: 'low' | 'medium' | 'high';
311
- slots: Record<string, SlotResult>;
312
- layoutOrder: string[] | null;
313
- savedAt: number;
314
- slotConfig?: Record<string, SlotConfigEntry>;
315
- /** Derived site palette for Composition Block rendering — cached so the
316
- * pre-paint render already looks native (a palette that pops in post-decide
317
- * would be its own flash). */
318
- palette?: SitePalette;
319
- };
320
- /** Returns null on missing, corrupt, or wrong-version data — never throws. */
321
- declare function readSnapshot(apiKey: string): DecisionSnapshot | null;
322
- /** Best-effort persist — storage failures are swallowed. */
323
- declare function writeSnapshot(apiKey: string, snap: DecisionSnapshot): void;
324
- /**
325
- * Inline pre-paint script (Rung 1a): reads the snapshot and sets
326
- * `data-sentient-persona` / `data-sentient-confidence` on <html> before
327
- * first paint. Single-writer: it never overwrites attributes already set.
328
- *
329
- * Safety properties (pinned by tests):
330
- * - apiKey goes through JSON.stringify, then '<' is escaped to <, so a
331
- * hostile key can neither break the JS string nor terminate the <script>.
332
- * - Built by string concatenation and contains no backticks, so the output
333
- * survives being embedded in template-literal-based renderers.
334
- */
335
- declare function renderPrePaintScript(apiKey: string): string;
336
-
337
124
  declare const PROD_KEYLESS_ERROR = "[sentient] No API key configured \u2014 nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.";
338
125
  declare const LOCAL_MODE_BANNER = "[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.";
339
126
 
@@ -445,6 +232,13 @@ type SentientConfig = {
445
232
  * server-rendered markup on first paint.
446
233
  */
447
234
  initialSlots?: Record<string, SlotResult>;
235
+ /**
236
+ * Registry slot config decided outside the browser (SSR). Seeds
237
+ * `getSlotConfig()` so server-rendered block/content arms survive hydration.
238
+ */
239
+ initialSlotConfig?: Record<string, SlotConfigEntry>;
240
+ /** Site palette decided outside the browser (SSR). Seeds `getSitePalette()`. */
241
+ initialPalette?: SitePalette;
448
242
  /**
449
243
  * Persona decided during SSR. Takes priority over the html-attribute
450
244
  * adoption and the local snapshot.
@@ -529,6 +323,14 @@ type DecideInput = {
529
323
  * entirely on older deployments. Omit if the caller has no version to report.
530
324
  */
531
325
  v?: string;
326
+ /**
327
+ * Snippet only: which inline pre-paint contract the install carries
328
+ * (`window.__sntPP.v`), or 0 for the two-tag install with no inline script.
329
+ * Sent as `pp` alongside `v`; the server persists it for the dashboard's
330
+ * install-health nudge and no serving behaviour depends on it. Additive —
331
+ * older deployments ignore it entirely.
332
+ */
333
+ pp?: number;
532
334
  };
533
335
  type WeightEntry = {
534
336
  variantId: string;
@@ -588,6 +390,15 @@ type SentientClient = {
588
390
  decide(input: DecideInput): Promise<DecideOutcome | null>;
589
391
  /** Slot result served this session (decide result, SSR seed, snapshot, or failure baseline). Null when unknown. */
590
392
  getSlotResult(slotId: string): SlotResult | null;
393
+ /** Registry slot config served this session (content/ops/blocks for the slot).
394
+ * Null until a registry-mode decide, SSR seed, or snapshot provides it. */
395
+ getSlotConfig(slotId: string): SlotConfigEntry | null;
396
+ /** Report mounted AdaptiveSlot ids the server has no config for, so they
397
+ * auto-register as draft slots. Fire-and-forget, batched, deduped per
398
+ * client — never blocks rendering and never throws. */
399
+ reportSlots(slotIds: string[]): void;
400
+ /** Site palette served with registry block decisions. Null when absent. */
401
+ getSitePalette(): SitePalette | null;
591
402
  /** Current persona estimate. Band is always `confidenceBand(confidence)`. Null when nothing is known yet. */
592
403
  getPersona(): {
593
404
  persona: string;
@@ -635,4 +446,4 @@ declare function grantConsent(apiKey?: string): void;
635
446
  */
636
447
  declare function init(config: SentientConfig): SentientClient;
637
448
 
638
- export { type MicroSignalType as $, type AssignResult as A, BLOCK_ALIGNS as B, type ComponentGoalOptions as C, type ComponentWeightEntry as D, type CompoundLocator as E, type DecideInput as F, type DecideOutcome as G, type DecisionSnapshot as H, type EventQueue as I, type EventType as J, type GoalDefinition as K, type GoalOptions as L, type GraphClient as M, type GraphConfig as N, type GraphSnapshot as O, type GridBlock as P, type HeadingBlock as Q, type ImageBlock as R, LEGACY_SESSION_COOKIE_NAME as S, LOCAL_MODE_BANNER as T, type LinkBlock as U, MAX_BLOCK_ARMS as V, MAX_BLOCK_CHILDREN as W, MAX_BLOCK_DEPTH as X, MAX_BLOCK_NODES as Y, MAX_BLOCK_TEXT_LEN as Z, type MicroSignalEmitter as _, type Assignment as a, PROD_KEYLESS_ERROR as a0, type PageNode as a1, type QueueConfig as a2, SNAPSHOT_STORAGE_KEY_PREFIX as a3, type SectionMapEntry as a4, type SentientClient as a5, type SentientConfig as a6, type SentientEvent as a7, type SessionConfig as a8, type SessionManager as a9, type SitePalette as aa, type SlotConfigEntry as ab, type SlotOps as ac, type SpacerBlock as ad, type StackBlock as ae, type TextBlock as af, type WeightEntry as ag, _registerConsentUpgradeInit as ah, attachMicroSignalDetectors as ai, grantConsent as aj, init as ak, isDoNotTrackEnabled as al, readSnapshot as am, renderPrePaintScript as an, sanitizePageUrl as ao, sessionCookieName as ap, writeSnapshot as aq, type AssignmentCache as b, BLOCK_EMPHASES as c, BLOCK_FITS as d, BLOCK_GAPS as e, BLOCK_GRID_COLUMNS as f, BLOCK_HEADING_LEVELS as g, BLOCK_JUSTIFIES as h, BLOCK_RATIOS as i, BLOCK_SIZES as j, BLOCK_TEXT_ALIGNS as k, BLOCK_TONES as l, BLOCK_WEIGHTS as m, type BadgeBlock as n, type BlockAlign as o, type BlockEmphasis as p, type BlockFit as q, type BlockGap as r, type BlockJustify as s, type BlockNode as t, type BlockRatio as u, type BlockSize as v, type BlockTextAlign as w, type BlockTone as x, type BlockWeight as y, type ButtonBlock as z };
449
+ export { type AssignResult as A, type ComponentGoalOptions as C, type DecideInput as D, type EventQueue as E, type GoalDefinition as G, LEGACY_SESSION_COOKIE_NAME as L, type MicroSignalEmitter as M, PROD_KEYLESS_ERROR as P, type QueueConfig as Q, type SectionMapEntry as S, type WeightEntry as W, _registerConsentUpgradeInit as _, type Assignment as a, type AssignmentCache as b, type ComponentWeightEntry as c, type DecideOutcome as d, type EventType as e, type GoalOptions as f, type GraphClient as g, type GraphConfig as h, type GraphSnapshot as i, LOCAL_MODE_BANNER as j, type MicroSignalType as k, type PageNode as l, type SentientClient as m, type SentientConfig as n, type SentientEvent as o, type SessionConfig as p, type SessionManager as q, attachMicroSignalDetectors as r, grantConsent as s, init as t, isDoNotTrackEnabled as u, sanitizePageUrl as v, sessionCookieName as w };
@@ -1,135 +1,6 @@
1
- import { b as SlotDeclInput } from './session-meta-C-bMqVeq.cjs';
1
+ import { W as SlotConfigEntry, V as SitePalette, X as SlotDeclInput, z as CompoundLocator } from './session-meta-dN92q19_.js';
2
2
  import { SlotResult } from '@sentientui/policy';
3
3
 
4
- /**
5
- * Composition Blocks (spec: 2026-08-20-nocode-composition-variants-design.md §4).
6
- *
7
- * A bounded, TYPED component tree — never HTML — that a registry arm may carry
8
- * (`published_config.arms[].blocks`). The vocabulary is the whitelist: every
9
- * prop value is an enumerated token, validated server-side before publish
10
- * (apps/api/src/domain/composition-blocks.ts) and rendered client-side through
11
- * document.createElement + property assignment only. No innerHTML/outerHTML/
12
- * insertAdjacentHTML exists anywhere on this path — the security property is
13
- * preserved by never accepting HTML, not by sanitizing it, which is why there
14
- * is no sanitizer to keep honest.
15
- *
16
- * Token lists live here (not in the API domain like SlotOps' mirrors) because
17
- * three parties must agree byte-for-byte: the server validator, the snippet
18
- * renderer, and eventually the React renderer (§11) — a drifted copy would let
19
- * a published arm fail to render, which the fail-safe turns into an invisibly
20
- * missing section, not an error.
21
- */
22
- declare const BLOCK_GAPS: readonly ["none", "sm", "md", "lg"];
23
- declare const BLOCK_ALIGNS: readonly ["start", "center", "end", "stretch"];
24
- declare const BLOCK_JUSTIFIES: readonly ["start", "center", "end", "between"];
25
- declare const BLOCK_SIZES: readonly ["sm", "md", "lg"];
26
- declare const BLOCK_WEIGHTS: readonly ["normal", "medium", "bold"];
27
- declare const BLOCK_TONES: readonly ["default", "muted", "accent"];
28
- declare const BLOCK_EMPHASES: readonly ["primary", "secondary", "ghost"];
29
- declare const BLOCK_TEXT_ALIGNS: readonly ["left", "center", "right"];
30
- declare const BLOCK_RATIOS: readonly ["auto", "square", "landscape", "wide"];
31
- declare const BLOCK_FITS: readonly ["cover", "contain"];
32
- declare const BLOCK_GRID_COLUMNS: readonly [2, 3, 4];
33
- declare const BLOCK_HEADING_LEVELS: readonly [2, 3, 4];
34
- type BlockGap = (typeof BLOCK_GAPS)[number];
35
- type BlockAlign = (typeof BLOCK_ALIGNS)[number];
36
- type BlockJustify = (typeof BLOCK_JUSTIFIES)[number];
37
- type BlockSize = (typeof BLOCK_SIZES)[number];
38
- type BlockWeight = (typeof BLOCK_WEIGHTS)[number];
39
- type BlockTone = (typeof BLOCK_TONES)[number];
40
- type BlockEmphasis = (typeof BLOCK_EMPHASES)[number];
41
- type BlockTextAlign = (typeof BLOCK_TEXT_ALIGNS)[number];
42
- type BlockRatio = (typeof BLOCK_RATIOS)[number];
43
- type BlockFit = (typeof BLOCK_FITS)[number];
44
- /** Flex row/column container. */
45
- type StackBlock = {
46
- type: 'stack';
47
- direction: 'row' | 'column';
48
- children: BlockNode[];
49
- gap?: BlockGap;
50
- align?: BlockAlign;
51
- justify?: BlockJustify;
52
- wrap?: boolean;
53
- };
54
- /** 2–4 equal-column grid container. */
55
- type GridBlock = {
56
- type: 'grid';
57
- columns: (typeof BLOCK_GRID_COLUMNS)[number];
58
- children: BlockNode[];
59
- gap?: BlockGap;
60
- align?: BlockAlign;
61
- };
62
- /** Paragraph / label. */
63
- type TextBlock = {
64
- type: 'text';
65
- value: string;
66
- size?: BlockSize;
67
- weight?: BlockWeight;
68
- tone?: BlockTone;
69
- align?: BlockTextAlign;
70
- };
71
- /** h2–h4 — never h1 (the page owns its h1). */
72
- type HeadingBlock = {
73
- type: 'heading';
74
- value: string;
75
- level: (typeof BLOCK_HEADING_LEVELS)[number];
76
- size?: BlockSize;
77
- align?: BlockTextAlign;
78
- };
79
- /** Link styled as a button. `tag` feeds agent legibility (agentDataByVariant). */
80
- type ButtonBlock = {
81
- type: 'button';
82
- label: string;
83
- href: string;
84
- emphasis?: BlockEmphasis;
85
- size?: BlockSize;
86
- tag?: string;
87
- };
88
- /** Inline text link. */
89
- type LinkBlock = {
90
- type: 'link';
91
- label: string;
92
- href: string;
93
- tag?: string;
94
- };
95
- /** Image. `alt` is required; empty only with an explicit `decorative: true`. */
96
- type ImageBlock = {
97
- type: 'image';
98
- src: string;
99
- alt: string;
100
- decorative?: boolean;
101
- ratio?: BlockRatio;
102
- fit?: BlockFit;
103
- };
104
- /** Eyebrow / pill. */
105
- type BadgeBlock = {
106
- type: 'badge';
107
- value: string;
108
- tone?: BlockTone;
109
- };
110
- /** Vertical rhythm. */
111
- type SpacerBlock = {
112
- type: 'spacer';
113
- size: BlockSize;
114
- };
115
- type BlockNode = StackBlock | GridBlock | TextBlock | HeadingBlock | ButtonBlock | LinkBlock | ImageBlock | BadgeBlock | SpacerBlock;
116
- /** The derived site palette (spec §4 "Colour and type: derived, not chosen").
117
- * Sampled by the on-site editor from the live page's own buttons — computed
118
- * styles, so values are plain colors (rgb/hex), never var()/url() — validated
119
- * server-side, stored per project, and served with the decision so injected
120
- * blocks render in the merchant's own primary color and corner radius.
121
- * Absent → the renderer's neutral inherit-first defaults. */
122
- type SitePalette = {
123
- primaryBg: string;
124
- primaryText: string;
125
- radius: string;
126
- };
127
- declare const MAX_BLOCK_NODES = 64;
128
- declare const MAX_BLOCK_DEPTH = 5;
129
- declare const MAX_BLOCK_CHILDREN = 12;
130
- declare const MAX_BLOCK_ARMS = 4;
131
- declare const MAX_BLOCK_TEXT_LEN = 500;
132
-
133
4
  /** Manages anonymous session identity with cookie + localStorage layers. */
134
5
  type SessionConfig = {
135
6
  cookieName?: string;
@@ -250,90 +121,6 @@ type GraphClient = {
250
121
  */
251
122
  declare function sanitizePageUrl(href: string): string;
252
123
 
253
- /**
254
- * Decision snapshot: the SPA / return-visit pre-paint source. Written after
255
- * every successful decide; read by the inline pre-paint script (before any
256
- * framework code runs) and by init() to seed slot/persona state.
257
- */
258
-
259
- declare const SNAPSHOT_STORAGE_KEY_PREFIX = "_snt_snap:";
260
- /** Versioned compound locator: resolve id → dataAttr → selector, then verify
261
- * against fingerprint. Lets a slot survive DOM/markup drift. */
262
- type CompoundLocator = {
263
- v?: number;
264
- id?: string;
265
- dataAttr?: {
266
- name: string;
267
- value: string;
268
- };
269
- selector?: string;
270
- urlMatch?: string;
271
- fingerprint?: {
272
- tag?: string;
273
- text?: string;
274
- };
275
- semanticId?: string;
276
- };
277
- /** Bounded, declarative operations a registry arm may apply to its element.
278
- * The style set is a fixed whitelist (validated server-side); no arbitrary CSS,
279
- * HTML, or JS ever. `text` is applied via textContent; https-only URLs.
280
- * moveBefore/moveAfter (exactly one) reposition the element relative to a
281
- * uniquely-resolving sibling anchor — post-decide only, never pre-paint. */
282
- type SlotOps = {
283
- text?: string;
284
- style?: Record<string, string>;
285
- hidden?: boolean;
286
- href?: string;
287
- imageSrc?: string;
288
- imageAlt?: string;
289
- moveBefore?: CompoundLocator;
290
- moveAfter?: CompoundLocator;
291
- };
292
- /** Registry-mode apply info per slot: where to apply and what to set. Stored so
293
- * a returning visitor's pre-paint can reapply it. `target` is the Phase-2 bare
294
- * selector; `locator` (Phase 3) is the compound locator, preferred when present. */
295
- type SlotConfigEntry = {
296
- kind: 'tokens' | 'arms';
297
- target?: string;
298
- locator?: CompoundLocator;
299
- content?: string;
300
- ops?: SlotOps;
301
- /** Composition Blocks per arm — ALL arms, not just the served one, because
302
- * Option-B rendering pre-paints every arm hidden and reveals the served one
303
- * (spec §6). Holdout sessions receive the baseline arm's tree only, so the
304
- * control group's DOM stays meaningful. Absent for non-composition slots. */
305
- blocks?: Record<string, BlockNode>;
306
- };
307
- type DecisionSnapshot = {
308
- v: 1;
309
- persona: string;
310
- band: 'low' | 'medium' | 'high';
311
- slots: Record<string, SlotResult>;
312
- layoutOrder: string[] | null;
313
- savedAt: number;
314
- slotConfig?: Record<string, SlotConfigEntry>;
315
- /** Derived site palette for Composition Block rendering — cached so the
316
- * pre-paint render already looks native (a palette that pops in post-decide
317
- * would be its own flash). */
318
- palette?: SitePalette;
319
- };
320
- /** Returns null on missing, corrupt, or wrong-version data — never throws. */
321
- declare function readSnapshot(apiKey: string): DecisionSnapshot | null;
322
- /** Best-effort persist — storage failures are swallowed. */
323
- declare function writeSnapshot(apiKey: string, snap: DecisionSnapshot): void;
324
- /**
325
- * Inline pre-paint script (Rung 1a): reads the snapshot and sets
326
- * `data-sentient-persona` / `data-sentient-confidence` on <html> before
327
- * first paint. Single-writer: it never overwrites attributes already set.
328
- *
329
- * Safety properties (pinned by tests):
330
- * - apiKey goes through JSON.stringify, then '<' is escaped to <, so a
331
- * hostile key can neither break the JS string nor terminate the <script>.
332
- * - Built by string concatenation and contains no backticks, so the output
333
- * survives being embedded in template-literal-based renderers.
334
- */
335
- declare function renderPrePaintScript(apiKey: string): string;
336
-
337
124
  declare const PROD_KEYLESS_ERROR = "[sentient] No API key configured \u2014 nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.";
338
125
  declare const LOCAL_MODE_BANNER = "[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.";
339
126
 
@@ -445,6 +232,13 @@ type SentientConfig = {
445
232
  * server-rendered markup on first paint.
446
233
  */
447
234
  initialSlots?: Record<string, SlotResult>;
235
+ /**
236
+ * Registry slot config decided outside the browser (SSR). Seeds
237
+ * `getSlotConfig()` so server-rendered block/content arms survive hydration.
238
+ */
239
+ initialSlotConfig?: Record<string, SlotConfigEntry>;
240
+ /** Site palette decided outside the browser (SSR). Seeds `getSitePalette()`. */
241
+ initialPalette?: SitePalette;
448
242
  /**
449
243
  * Persona decided during SSR. Takes priority over the html-attribute
450
244
  * adoption and the local snapshot.
@@ -529,6 +323,14 @@ type DecideInput = {
529
323
  * entirely on older deployments. Omit if the caller has no version to report.
530
324
  */
531
325
  v?: string;
326
+ /**
327
+ * Snippet only: which inline pre-paint contract the install carries
328
+ * (`window.__sntPP.v`), or 0 for the two-tag install with no inline script.
329
+ * Sent as `pp` alongside `v`; the server persists it for the dashboard's
330
+ * install-health nudge and no serving behaviour depends on it. Additive —
331
+ * older deployments ignore it entirely.
332
+ */
333
+ pp?: number;
532
334
  };
533
335
  type WeightEntry = {
534
336
  variantId: string;
@@ -588,6 +390,15 @@ type SentientClient = {
588
390
  decide(input: DecideInput): Promise<DecideOutcome | null>;
589
391
  /** Slot result served this session (decide result, SSR seed, snapshot, or failure baseline). Null when unknown. */
590
392
  getSlotResult(slotId: string): SlotResult | null;
393
+ /** Registry slot config served this session (content/ops/blocks for the slot).
394
+ * Null until a registry-mode decide, SSR seed, or snapshot provides it. */
395
+ getSlotConfig(slotId: string): SlotConfigEntry | null;
396
+ /** Report mounted AdaptiveSlot ids the server has no config for, so they
397
+ * auto-register as draft slots. Fire-and-forget, batched, deduped per
398
+ * client — never blocks rendering and never throws. */
399
+ reportSlots(slotIds: string[]): void;
400
+ /** Site palette served with registry block decisions. Null when absent. */
401
+ getSitePalette(): SitePalette | null;
591
402
  /** Current persona estimate. Band is always `confidenceBand(confidence)`. Null when nothing is known yet. */
592
403
  getPersona(): {
593
404
  persona: string;
@@ -635,4 +446,4 @@ declare function grantConsent(apiKey?: string): void;
635
446
  */
636
447
  declare function init(config: SentientConfig): SentientClient;
637
448
 
638
- export { type MicroSignalType as $, type AssignResult as A, BLOCK_ALIGNS as B, type ComponentGoalOptions as C, type ComponentWeightEntry as D, type CompoundLocator as E, type DecideInput as F, type DecideOutcome as G, type DecisionSnapshot as H, type EventQueue as I, type EventType as J, type GoalDefinition as K, type GoalOptions as L, type GraphClient as M, type GraphConfig as N, type GraphSnapshot as O, type GridBlock as P, type HeadingBlock as Q, type ImageBlock as R, LEGACY_SESSION_COOKIE_NAME as S, LOCAL_MODE_BANNER as T, type LinkBlock as U, MAX_BLOCK_ARMS as V, MAX_BLOCK_CHILDREN as W, MAX_BLOCK_DEPTH as X, MAX_BLOCK_NODES as Y, MAX_BLOCK_TEXT_LEN as Z, type MicroSignalEmitter as _, type Assignment as a, PROD_KEYLESS_ERROR as a0, type PageNode as a1, type QueueConfig as a2, SNAPSHOT_STORAGE_KEY_PREFIX as a3, type SectionMapEntry as a4, type SentientClient as a5, type SentientConfig as a6, type SentientEvent as a7, type SessionConfig as a8, type SessionManager as a9, type SitePalette as aa, type SlotConfigEntry as ab, type SlotOps as ac, type SpacerBlock as ad, type StackBlock as ae, type TextBlock as af, type WeightEntry as ag, _registerConsentUpgradeInit as ah, attachMicroSignalDetectors as ai, grantConsent as aj, init as ak, isDoNotTrackEnabled as al, readSnapshot as am, renderPrePaintScript as an, sanitizePageUrl as ao, sessionCookieName as ap, writeSnapshot as aq, type AssignmentCache as b, BLOCK_EMPHASES as c, BLOCK_FITS as d, BLOCK_GAPS as e, BLOCK_GRID_COLUMNS as f, BLOCK_HEADING_LEVELS as g, BLOCK_JUSTIFIES as h, BLOCK_RATIOS as i, BLOCK_SIZES as j, BLOCK_TEXT_ALIGNS as k, BLOCK_TONES as l, BLOCK_WEIGHTS as m, type BadgeBlock as n, type BlockAlign as o, type BlockEmphasis as p, type BlockFit as q, type BlockGap as r, type BlockJustify as s, type BlockNode as t, type BlockRatio as u, type BlockSize as v, type BlockTextAlign as w, type BlockTone as x, type BlockWeight as y, type ButtonBlock as z };
449
+ export { type AssignResult as A, type ComponentGoalOptions as C, type DecideInput as D, type EventQueue as E, type GoalDefinition as G, LEGACY_SESSION_COOKIE_NAME as L, type MicroSignalEmitter as M, PROD_KEYLESS_ERROR as P, type QueueConfig as Q, type SectionMapEntry as S, type WeightEntry as W, _registerConsentUpgradeInit as _, type Assignment as a, type AssignmentCache as b, type ComponentWeightEntry as c, type DecideOutcome as d, type EventType as e, type GoalOptions as f, type GraphClient as g, type GraphConfig as h, type GraphSnapshot as i, LOCAL_MODE_BANNER as j, type MicroSignalType as k, type PageNode as l, type SentientClient as m, type SentientConfig as n, type SentientEvent as o, type SessionConfig as p, type SessionManager as q, attachMicroSignalDetectors as r, grantConsent as s, init as t, isDoNotTrackEnabled as u, sanitizePageUrl as v, sessionCookieName as w };
@@ -1,2 +1,2 @@
1
- "use strict";var R=Object.defineProperty,le=Object.defineProperties,ce=Object.getOwnPropertyDescriptor,de=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,z=Object.getOwnPropertySymbols;var J=Object.prototype.hasOwnProperty,pe=Object.prototype.propertyIsEnumerable;var Y=(e,t,n)=>t in e?R(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,S=(e,t)=>{for(var n in t||(t={}))J.call(t,n)&&Y(e,n,t[n]);if(z)for(var n of z(t))pe.call(t,n)&&Y(e,n,t[n]);return e},_=(e,t)=>le(e,de(t));var ge=(e,t)=>{for(var n in t)R(e,n,{get:t[n],enumerable:!0})},fe=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ue(t))!J.call(e,o)&&o!==n&&R(e,o,{get:()=>t[o],enumerable:!(a=ce(t,o))||a.enumerable});return e};var me=e=>fe(R({},"__esModule",{value:!0}),e);var Ie={};ge(Ie,{SEMANTIC_TYPES:()=>x,classifyFeatures:()=>T,classifySection:()=>Z,featuresFromElement:()=>C,startEngagementCapture:()=>ie});module.exports=me(Ie);var x=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],ye={banner:"hero",navigation:"navigation",contentinfo:"footer"};function he(e){let t=e.ariaRole?ye[e.ariaRole.toLowerCase()]:void 0;if(t)return t;if(e.tag==="nav")return"navigation";if(e.tag==="footer")return"footer";if(e.tag==="header")return e.actionCount>=5?"navigation":"hero";let n=`${e.idClass} ${e.headingText}`.toLowerCase();return/\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\b/.test(n)?/footer/.test(n)?"footer":"navigation":/\b(hero|masthead|jumbotron)\b/i.test(e.idClass)?"hero":null}function Se(e){return e.actionCount>=1&&e.textLength>0&&e.textLength<200?"cta":"generic"}var we=[["pricing",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["social_proof",/\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\b|trusted by|loved by|case stud|what our customers say/i],["trust",/\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\b|why choose|about us|our team/i],["cta",/\b(book|booking|reserve|appointments?|newsletter|subscribe)\b|contact us|get in touch|opening hours/i],["features",/\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\b|how it works|our process|what we (do|offer)|what you get/i]],O=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]],X={navigation:"navigation",footer:"navigation",hero:"hero",cta:"cta",generic:"generic"};function be(e){var n;let t=e.querySelector("h1, h2, h3");return((n=t==null?void 0:t.textContent)!=null?n:"").slice(0,160)}function T(e){var o,i;let t=he(e);if(t)return{type:(o=X[t])!=null?o:"generic",strength:"strong"};let n=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[d,s]of we)if(s.test(n))return{type:d,strength:"strong"};for(let[d,s]of O)if(s.test(e.bodyText))return{type:d,strength:"strong"};let a=Se(e);return{type:(i=X[a])!=null?i:"generic",strength:"weak"}}function C(e){var a,o;let t=((a=e.textContent)!=null?a:"").replace(/\s+/g," ").trim(),n=e.getAttribute("role");return S({tag:e.tagName.toLowerCase(),idClass:`${e.id} ${String((o=e.className)!=null?o:"")}`,headingText:be(e),bodyText:t.slice(0,2e3),actionCount:e.querySelectorAll('a, button, [role="button"]').length,textLength:t.length},n?{ariaRole:n}:{})}function Z(e){return T(C(e)).type}var re=require("@sentientui/policy");var ve=require("@sentientui/policy");function ee(e,t,n,a){let o=[];{let s=!1,l=[],f=()=>{if(s)return;let y=Date.now();for(l.push(y);l.length>0&&y-l[0]>500;)l.shift();l.length>=3&&(s=!0,e("rage_click"))};t.addEventListener("click",f),o.push(()=>t.removeEventListener("click",f))}{let i=!1,d=s=>{if(i||!(s.target instanceof Node)||!t.contains(s.target)&&t!==s.target)return;i=!0;let l=typeof window!="undefined"?window.getSelection():null,f=l?l.toString().length:0;e("text_copy",{selectionLength:f})};document.addEventListener("copy",d),o.push(()=>document.removeEventListener("copy",d))}{let i=!1,d=!1,s=null,l=()=>{s!==null&&(clearTimeout(s),s=null)},f=()=>{i||!d||(l(),s=setTimeout(()=>{!i&&d&&(i=!0,e("scroll_hesitation"))},3e3))},y=()=>{l(),f()},g=b=>{for(let k of b)d=k.intersectionRatio>.3,d?f():l()},h=new IntersectionObserver(g,{threshold:[.3]});h.observe(t),window.addEventListener("scroll",y,{passive:!0}),o.push(()=>{h.disconnect(),window.removeEventListener("scroll",y),l()})}if((a==null?void 0:a.tabLoss)!==!1){let i=!1,d=n!=null?n:Date.now(),s=()=>{if(i||document.visibilityState!=="hidden")return;let l=Date.now()-d;l<15e3&&(i=!0,e("tab_loss",{timeOnPage:l}))};document.addEventListener("visibilitychange",s),o.push(()=>document.removeEventListener("visibilitychange",s))}return()=>{for(let i of o)i()}}function te(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}var Te=["data-sentient-id","data-testid","data-test","data-id","data-name","data-cy"];function P(e){var t;return((t=e.textContent)!=null?t:"").replace(/\s+/g," ").trim()}function Ce(e){return{tag:e.tagName.toLowerCase(),text:P(e).slice(0,40)}}function ne(e){return e.replace(/["\\\]]/g,"\\$&")}function E(e,t){try{return e.querySelectorAll(t).length===1}catch(n){return!1}}function oe(e,t){var d;let n=e.tagName.toLowerCase();if(!n)return null;let a=((d=e.getAttribute("class"))!=null?d:"").split(/\s+/).filter(s=>/^[a-zA-Z][\w-]*$/.test(s)),o=[n,...a.map(s=>`${n}.${s}`)];for(let s of o)if(E(t,s))return s;let i=e.parentElement;if(i&&i!==t){let s=oe(i,t);if(s){for(let g of o){let h=`${s} > ${g}`;if(E(t,h))return h}let l=Array.from(i.children).filter(g=>g.tagName.toLowerCase()===n),f=l.indexOf(e),y=l.some(g=>g!==e&&P(g)!==P(e));if(f>=0&&y){let g=`${s} > ${n}:nth-of-type(${f+1})`;if(E(t,g))return g}}}return null}function se(e,t){let n=Ce(e),a=e.getAttribute("id");if(a&&E(t,`#${ne(a)}`))return{v:1,id:a,fingerprint:n};for(let i of Te){let d=e.getAttribute(i);if(d&&E(t,`[${i}="${ne(d)}"]`))return{v:1,dataAttr:{name:i,value:d},fingerprint:n}}let o=oe(e,t);return o?{v:1,selector:o,fingerprint:n}:null}var Ee="section, header, footer, nav, main > div, [data-sentient-section]",ke=2e4;function Re(e){let t=Array.from(e.querySelectorAll(Ee)),n=t.filter(a=>t.filter(o=>o!==a&&a.contains(o)).length<2);return n.filter(a=>!n.some(o=>o!==a&&o.contains(a)))}function xe(e,t,n,a){try{fetch(`${t}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({pageUrl:n,sections:a})}).catch(()=>{})}catch(o){}}var I=()=>{};function ie(e,t){var N,L,$,K,G,F,W,j,B,U,H,q;let n=(N=t.doc)!=null?N:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||te()||!t.apiKey||!t.apiKey.startsWith("pk_"))return I;let a=((L=t.apiBase)!=null?L:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),o=Re(n);if(o.length===0)return I;let i=new Map,d=[];for(let r of o){let c=r.getAttribute("data-sentient-type"),p=c&&x.includes(c)?c:null,u=C(r),v=(K=p!=null?p:($=t.typeOf)==null?void 0:$.call(t,r))!=null?K:T(u).type,w=se(r,n),V=w?`nc-${v}-${(0,re.fnv1a)(JSON.stringify({id:(G=w.id)!=null?G:null,dataAttr:(F=w.dataAttr)!=null?F:null,selector:(W=w.selector)!=null?W:null})).toString(36)}`:`nc-${v}`;i.set(r,V);let Q=O.filter(([,A])=>A.test(u.bodyText)).map(([A])=>A);d.push(_(S({componentId:V,semanticType:v,source:p?"markup":"auto"},w?{locator:w}:{}),{observation:S(S({tag:u.tag,idClass:u.idClass.slice(0,200),headingText:u.headingText,textLength:u.textLength,actionCount:u.actionCount},u.ariaRole?{ariaRole:u.ariaRole}:{}),Q.length>0?{patternFlags:Q}:{})}))}let s=(H=(U=(B=(j=n.defaultView)!=null?j:typeof window!="undefined"?window:void 0)==null?void 0:B.location)==null?void 0:U.pathname)!=null?H:"/";xe(t.apiKey,a,s,d);let l=new Map,f=r=>{let c=l.get(r);return c||(c={ms:0,scroll:0,enterAt:null,intersecting:!1},l.set(r,c)),c},y=new IntersectionObserver(r=>{for(let c of r){let p=i.get(c.target);if(!p)continue;let u=f(p);c.isIntersecting?(u.intersecting=!0,u.enterAt=Date.now(),c.intersectionRatio>u.scroll&&(u.scroll=c.intersectionRatio)):(u.intersecting=!1,u.enterAt!=null&&(u.ms+=Date.now()-u.enterAt,u.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let r of i.keys())y.observe(r);let g=()=>{let r=Date.now();for(let[c,p]of l)if(p.enterAt!=null&&(p.ms+=r-p.enterAt,p.enterAt=null),!(p.ms<=0)){try{e.track({projectId:t.apiKey,componentId:c,eventType:"dwell",payload:{dwell_time:Math.round(p.ms),scroll_depth:Number(p.scroll.toFixed(2))}})}catch(u){}p.ms=0}},h=()=>{if(n.hidden)g();else{let r=Date.now();for(let c of l.values())c.intersecting&&(c.enterAt=r)}},b=!1,k=r=>{if(g(),r!=null&&r.persisted){b=!0;return}try{y.disconnect()}catch(c){}},D=r=>{if(!(r!=null&&r.persisted)||!b)return;b=!1;let c=Date.now();for(let p of l.values())p.enterAt=p.intersecting&&!n.hidden?c:null};n.addEventListener("visibilitychange",h);let m=(q=n.defaultView)!=null?q:typeof window!="undefined"?window:void 0;m==null||m.addEventListener("pagehide",k),m==null||m.addEventListener("pageshow",D);let ae=setInterval(()=>{if(n.hidden||b)return;g();let r=Date.now();for(let c of l.values())c.intersecting&&(c.enterAt=r)},ke),M=[];return t.microSignals&&[...i.entries()].forEach(([r,c],p)=>{M.push(ee((u,v={})=>{try{e.track({projectId:t.apiKey,componentId:c,eventType:"micro_signal",payload:S({signalType:u},v)})}catch(w){}},r,void 0,{tabLoss:p===0}))}),()=>{g(),clearInterval(ae),n.removeEventListener("visibilitychange",h),m==null||m.removeEventListener("pagehide",k),m==null||m.removeEventListener("pageshow",D);for(let r of M)r();try{y.disconnect()}catch(r){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
1
+ "use strict";var R=Object.defineProperty,le=Object.defineProperties,ce=Object.getOwnPropertyDescriptor,de=Object.getOwnPropertyDescriptors,ue=Object.getOwnPropertyNames,Q=Object.getOwnPropertySymbols;var J=Object.prototype.hasOwnProperty,pe=Object.prototype.propertyIsEnumerable;var Y=(e,t,n)=>t in e?R(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,S=(e,t)=>{for(var n in t||(t={}))J.call(t,n)&&Y(e,n,t[n]);if(Q)for(var n of Q(t))pe.call(t,n)&&Y(e,n,t[n]);return e},P=(e,t)=>le(e,de(t));var ge=(e,t)=>{for(var n in t)R(e,n,{get:t[n],enumerable:!0})},fe=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ue(t))!J.call(e,o)&&o!==n&&R(e,o,{get:()=>t[o],enumerable:!(a=ce(t,o))||a.enumerable});return e};var me=e=>fe(R({},"__esModule",{value:!0}),e);var xe={};ge(xe,{SEMANTIC_TYPES:()=>I,classifyFeatures:()=>C,classifySection:()=>Z,featuresFromElement:()=>T,startEngagementCapture:()=>ie});module.exports=me(xe);var I=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],ye={banner:"hero",navigation:"navigation",contentinfo:"footer"};function he(e){let t=e.ariaRole?ye[e.ariaRole.toLowerCase()]:void 0;if(t)return t;if(e.tag==="nav")return"navigation";if(e.tag==="footer")return"footer";if(e.tag==="header")return e.actionCount>=5?"navigation":"hero";let n=`${e.idClass} ${e.headingText}`.toLowerCase();return/\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\b/.test(n)?/footer/.test(n)?"footer":"navigation":/\b(hero|masthead|jumbotron)\b/i.test(e.idClass)?"hero":null}function Se(e){return e.actionCount>=1&&e.textLength>0&&e.textLength<200?"cta":"generic"}var be=[["pricing",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["social_proof",/\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\b|trusted by|loved by|case stud|what our customers say/i],["trust",/\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\b|why choose|about us|our team/i],["cta",/\b(book|booking|reserve|appointments?|newsletter|subscribe)\b|contact us|get in touch|opening hours/i],["features",/\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\b|how it works|our process|what we (do|offer)|what you get/i]],O=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]],X={navigation:"navigation",footer:"navigation",hero:"hero",cta:"cta",generic:"generic"};function we(e){var n;let t=e.querySelector("h1, h2, h3");return((n=t==null?void 0:t.textContent)!=null?n:"").slice(0,160)}function C(e){var o,i;let t=he(e);if(t)return{type:(o=X[t])!=null?o:"generic",strength:"strong"};let n=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[d,s]of be)if(s.test(n))return{type:d,strength:"strong"};for(let[d,s]of O)if(s.test(e.bodyText))return{type:d,strength:"strong"};let a=Se(e);return{type:(i=X[a])!=null?i:"generic",strength:"weak"}}function T(e){var a,o;let t=((a=e.textContent)!=null?a:"").replace(/\s+/g," ").trim(),n=e.getAttribute("role");return S({tag:e.tagName.toLowerCase(),idClass:`${e.id} ${String((o=e.className)!=null?o:"")}`,headingText:we(e),bodyText:t.slice(0,2e3),actionCount:e.querySelectorAll('a, button, [role="button"]').length,textLength:t.length},n?{ariaRole:n}:{})}function Z(e){return C(T(e)).type}var re=require("@sentientui/policy");var ve=require("@sentientui/policy");function ee(e,t,n,a){let o=[];{let s=!1,l=[],f=()=>{if(s)return;let y=Date.now();for(l.push(y);l.length>0&&y-l[0]>500;)l.shift();l.length>=3&&(s=!0,e("rage_click"))};t.addEventListener("click",f),o.push(()=>t.removeEventListener("click",f))}{let i=!1,d=s=>{if(i||!(s.target instanceof Node)||!t.contains(s.target)&&t!==s.target)return;i=!0;let l=typeof window!="undefined"?window.getSelection():null,f=l?l.toString().length:0;e("text_copy",{selectionLength:f})};document.addEventListener("copy",d),o.push(()=>document.removeEventListener("copy",d))}{let i=!1,d=!1,s=null,l=()=>{s!==null&&(clearTimeout(s),s=null)},f=()=>{i||!d||(l(),s=setTimeout(()=>{!i&&d&&(i=!0,e("scroll_hesitation"))},3e3))},y=()=>{l(),f()},g=w=>{for(let k of w)d=k.intersectionRatio>.3,d?f():l()},h=new IntersectionObserver(g,{threshold:[.3]});h.observe(t),window.addEventListener("scroll",y,{passive:!0}),o.push(()=>{h.disconnect(),window.removeEventListener("scroll",y),l()})}if((a==null?void 0:a.tabLoss)!==!1){let i=!1,d=n!=null?n:Date.now(),s=()=>{if(i||document.visibilityState!=="hidden")return;let l=Date.now()-d;l<15e3&&(i=!0,e("tab_loss",{timeOnPage:l}))};document.addEventListener("visibilitychange",s),o.push(()=>document.removeEventListener("visibilitychange",s))}return()=>{for(let i of o)i()}}function te(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}var Ce=["data-sentient-id","data-testid","data-test","data-id","data-name","data-cy"];function _(e){var t;return((t=e.textContent)!=null?t:"").replace(/\s+/g," ").trim()}function Te(e){return{tag:e.tagName.toLowerCase(),text:_(e).slice(0,40)}}function ne(e){return e.replace(/["\\\]]/g,"\\$&")}function E(e,t){try{return e.querySelectorAll(t).length===1}catch(n){return!1}}function oe(e,t){var d;let n=e.tagName.toLowerCase();if(!n)return null;let a=((d=e.getAttribute("class"))!=null?d:"").split(/\s+/).filter(s=>/^[a-zA-Z][\w-]*$/.test(s)),o=[n,...a.map(s=>`${n}.${s}`)];for(let s of o)if(E(t,s))return s;let i=e.parentElement;if(i&&i!==t){let s=oe(i,t);if(s){for(let g of o){let h=`${s} > ${g}`;if(E(t,h))return h}let l=Array.from(i.children).filter(g=>g.tagName.toLowerCase()===n),f=l.indexOf(e),y=l.some(g=>g!==e&&_(g)!==_(e));if(f>=0&&y){let g=`${s} > ${n}:nth-of-type(${f+1})`;if(E(t,g))return g}}}return null}function se(e,t){let n=Te(e),a=e.getAttribute("id");if(a&&E(t,`#${ne(a)}`))return{v:1,id:a,fingerprint:n};for(let i of Ce){let d=e.getAttribute(i);if(d&&E(t,`[${i}="${ne(d)}"]`))return{v:1,dataAttr:{name:i,value:d},fingerprint:n}}let o=oe(e,t);return o?{v:1,selector:o,fingerprint:n}:null}var Ee="section, header, footer, nav, main > div, [data-sentient-section]",ke=2e4;function Re(e){let t=Array.from(e.querySelectorAll(Ee)),n=t.filter(a=>t.filter(o=>o!==a&&a.contains(o)).length<2);return n.filter(a=>!n.some(o=>o!==a&&o.contains(a)))}function Ie(e,t,n,a){try{fetch(`${t}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({pageUrl:n,sections:a})}).catch(()=>{})}catch(o){}}var x=()=>{};function ie(e,t){var N,L,$,K,G,j,F,W,B,U,H,q;let n=(N=t.doc)!=null?N:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||te()||!t.apiKey||!t.apiKey.startsWith("pk_"))return x;let a=((L=t.apiBase)!=null?L:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),o=Re(n);if(o.length===0)return x;let i=new Map,d=[];for(let r of o){let c=r.getAttribute("data-sentient-type"),p=c&&I.includes(c)?c:null,u=T(r),v=(K=p!=null?p:($=t.typeOf)==null?void 0:$.call(t,r))!=null?K:C(u).type,b=se(r,n),z=b?`nc-${v}-${(0,re.fnv1a)(JSON.stringify({id:(G=b.id)!=null?G:null,dataAttr:(j=b.dataAttr)!=null?j:null,selector:(F=b.selector)!=null?F:null})).toString(36)}`:`nc-${v}`;i.set(r,z);let V=O.filter(([,A])=>A.test(u.bodyText)).map(([A])=>A);d.push(P(S({componentId:z,semanticType:v,source:p?"markup":"auto"},b?{locator:b}:{}),{observation:S(S({tag:u.tag,idClass:u.idClass.slice(0,200),headingText:u.headingText,textLength:u.textLength,actionCount:u.actionCount},u.ariaRole?{ariaRole:u.ariaRole}:{}),V.length>0?{patternFlags:V}:{})}))}let s=(H=(U=(B=(W=n.defaultView)!=null?W:typeof window!="undefined"?window:void 0)==null?void 0:B.location)==null?void 0:U.pathname)!=null?H:"/";Ie(t.apiKey,a,s,d);let l=new Map,f=r=>{let c=l.get(r);return c||(c={ms:0,scroll:0,enterAt:null,intersecting:!1},l.set(r,c)),c},y=new IntersectionObserver(r=>{for(let c of r){let p=i.get(c.target);if(!p)continue;let u=f(p);c.isIntersecting?(u.intersecting=!0,u.enterAt=Date.now(),c.intersectionRatio>u.scroll&&(u.scroll=c.intersectionRatio)):(u.intersecting=!1,u.enterAt!=null&&(u.ms+=Date.now()-u.enterAt,u.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let r of i.keys())y.observe(r);let g=()=>{let r=Date.now();for(let[c,p]of l)if(p.enterAt!=null&&(p.ms+=r-p.enterAt,p.enterAt=null),!(p.ms<=0)){try{e.track({projectId:t.apiKey,componentId:c,eventType:"dwell",payload:{dwell_time:Math.round(p.ms),scroll_depth:Number(p.scroll.toFixed(2))}})}catch(u){}p.ms=0}},h=()=>{if(n.hidden)g();else{let r=Date.now();for(let c of l.values())c.intersecting&&(c.enterAt=r)}},w=!1,k=r=>{if(g(),r!=null&&r.persisted){w=!0;return}try{y.disconnect()}catch(c){}},D=r=>{if(!(r!=null&&r.persisted)||!w)return;w=!1;let c=Date.now();for(let p of l.values())p.enterAt=p.intersecting&&!n.hidden?c:null};n.addEventListener("visibilitychange",h);let m=(q=n.defaultView)!=null?q:typeof window!="undefined"?window:void 0;m==null||m.addEventListener("pagehide",k),m==null||m.addEventListener("pageshow",D);let ae=setInterval(()=>{if(n.hidden||w)return;g();let r=Date.now();for(let c of l.values())c.intersecting&&(c.enterAt=r)},ke),M=[];return t.microSignals&&[...i.entries()].forEach(([r,c],p)=>{M.push(ee((u,v={})=>{try{e.track({projectId:t.apiKey,componentId:c,eventType:"micro_signal",payload:S({signalType:u},v)})}catch(b){}},r,void 0,{tabLoss:p===0}))}),()=>{g(),clearInterval(ae),n.removeEventListener("visibilitychange",h),m==null||m.removeEventListener("pagehide",k),m==null||m.removeEventListener("pageshow",D);for(let r of M)r();try{y.disconnect()}catch(r){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
2
2
  //# sourceMappingURL=index-engagement.js.map