@sentientui/core 0.21.3 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/chunk-2RH7I6AP.mjs +2 -0
  2. package/dist/chunk-2RH7I6AP.mjs.map +1 -0
  3. package/dist/chunk-EWP6FTHE.mjs +2 -0
  4. package/dist/chunk-EWP6FTHE.mjs.map +1 -0
  5. package/dist/chunk-KIP52DUJ.mjs +2 -0
  6. package/dist/chunk-KIP52DUJ.mjs.map +1 -0
  7. package/dist/classify-DYlSjkFP.d.cts +47 -0
  8. package/dist/classify-DYlSjkFP.d.ts +47 -0
  9. package/dist/{index-BcRtGYVH.d.ts → index-BbrtAtrY.d.ts} +11 -4
  10. package/dist/{index-D3_vlZ3z.d.cts → index-CXxvWxCB.d.cts} +11 -4
  11. package/dist/index-engagement.d.cts +3 -28
  12. package/dist/index-engagement.d.ts +3 -28
  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 +15 -5
  18. package/dist/index-graph.d.ts +15 -5
  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-graph.mjs.map +1 -1
  23. package/dist/index-local.d.cts +1 -1
  24. package/dist/index-local.d.ts +1 -1
  25. package/dist/index-topics.d.cts +21 -0
  26. package/dist/index-topics.d.ts +21 -0
  27. package/dist/index-topics.js +2 -0
  28. package/dist/index-topics.js.map +1 -0
  29. package/dist/index-topics.mjs +2 -0
  30. package/dist/index-topics.mjs.map +1 -0
  31. package/dist/index.d.cts +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/index.mjs +1 -1
  36. package/package.json +7 -2
  37. package/dist/chunk-CD2A55US.mjs +0 -2
  38. package/dist/chunk-CD2A55US.mjs.map +0 -1
  39. package/dist/chunk-Q252FDO2.mjs +0 -2
  40. package/dist/chunk-Q252FDO2.mjs.map +0 -1
@@ -0,0 +1,47 @@
1
+ type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
+ type SectionRole = 'converter' | 'persuader' | 'structural';
3
+ /** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */
4
+ declare const SEMANTIC_TYPES: readonly SemanticType[];
5
+ type TopicRule = {
6
+ topic: string;
7
+ parent: SemanticType;
8
+ role: SectionRole;
9
+ };
10
+ /** Environment-agnostic section features — buildable from a browser Element or
11
+ * a server-parsed node (node-html-parser). */
12
+ type SectionFeatures = {
13
+ tag: string;
14
+ idClass: string;
15
+ headingText: string;
16
+ bodyText: string;
17
+ actionCount: number;
18
+ textLength: number;
19
+ /** ARIA landmark role, when the element declares one. Optional so every
20
+ * existing caller keeps compiling; a page that uses landmarks gives a
21
+ * high-precision signal for free, which the old classifier ignored — it
22
+ * tested membership of SEMANTIC_TYPES, so only role="navigation" ever hit
23
+ * and role="banner"/"contentinfo" were discarded. */
24
+ ariaRole?: string;
25
+ /** schema.org `@type` values found in `<script type="application/ld+json">`
26
+ * INSIDE this section. Highest-precision signal available and free to
27
+ * collect — the crawler already has the HTML. Type-only here; the
28
+ * `@type` → topic table is server-side in ./topics.ts, since the browser
29
+ * never reads the topic layer and the snippet has ~200 bytes of margin. */
30
+ structuredTypes?: string[];
31
+ };
32
+ /**
33
+ * Pure classification over extracted features. `strong` = keyword, content, or
34
+ * structural evidence (trustable enough to auto-apply); `weak` = a fallback
35
+ * guess (hero/cta/generic — capture-worthy but not persona evidence).
36
+ */
37
+ declare function classifyFeatures(f: SectionFeatures): {
38
+ type: SemanticType;
39
+ strength: 'strong' | 'weak';
40
+ };
41
+ /** Feature extraction from a live DOM element (browser paths). */
42
+ declare function featuresFromElement(el: Element): SectionFeatures;
43
+ /** Classify a page section into a semantic type (never null — falls back to
44
+ * 'generic' so the caller can still capture attention on it). */
45
+ declare function classifySection(el: Element): SemanticType;
46
+
47
+ export { SEMANTIC_TYPES as S, type TopicRule as T, type SectionFeatures as a, type SemanticType as b, classifyFeatures as c, classifySection as d, featuresFromElement as f };
@@ -0,0 +1,47 @@
1
+ type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
+ type SectionRole = 'converter' | 'persuader' | 'structural';
3
+ /** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */
4
+ declare const SEMANTIC_TYPES: readonly SemanticType[];
5
+ type TopicRule = {
6
+ topic: string;
7
+ parent: SemanticType;
8
+ role: SectionRole;
9
+ };
10
+ /** Environment-agnostic section features — buildable from a browser Element or
11
+ * a server-parsed node (node-html-parser). */
12
+ type SectionFeatures = {
13
+ tag: string;
14
+ idClass: string;
15
+ headingText: string;
16
+ bodyText: string;
17
+ actionCount: number;
18
+ textLength: number;
19
+ /** ARIA landmark role, when the element declares one. Optional so every
20
+ * existing caller keeps compiling; a page that uses landmarks gives a
21
+ * high-precision signal for free, which the old classifier ignored — it
22
+ * tested membership of SEMANTIC_TYPES, so only role="navigation" ever hit
23
+ * and role="banner"/"contentinfo" were discarded. */
24
+ ariaRole?: string;
25
+ /** schema.org `@type` values found in `<script type="application/ld+json">`
26
+ * INSIDE this section. Highest-precision signal available and free to
27
+ * collect — the crawler already has the HTML. Type-only here; the
28
+ * `@type` → topic table is server-side in ./topics.ts, since the browser
29
+ * never reads the topic layer and the snippet has ~200 bytes of margin. */
30
+ structuredTypes?: string[];
31
+ };
32
+ /**
33
+ * Pure classification over extracted features. `strong` = keyword, content, or
34
+ * structural evidence (trustable enough to auto-apply); `weak` = a fallback
35
+ * guess (hero/cta/generic — capture-worthy but not persona evidence).
36
+ */
37
+ declare function classifyFeatures(f: SectionFeatures): {
38
+ type: SemanticType;
39
+ strength: 'strong' | 'weak';
40
+ };
41
+ /** Feature extraction from a live DOM element (browser paths). */
42
+ declare function featuresFromElement(el: Element): SectionFeatures;
43
+ /** Classify a page section into a semantic type (never null — falls back to
44
+ * 'generic' so the caller can still capture attention on it). */
45
+ declare function classifySection(el: Element): SemanticType;
46
+
47
+ export { SEMANTIC_TYPES as S, type TopicRule as T, type SectionFeatures as a, type SemanticType as b, classifyFeatures as c, classifySection as d, featuresFromElement as f };
@@ -207,7 +207,7 @@ type Assignment = {
207
207
  type AssignmentCache = {
208
208
  get(componentId: string, segment: string): Assignment | null;
209
209
  set(componentId: string, segment: string, assignment: Assignment): void;
210
- invalidate(componentId: string): void;
210
+ /** Drops every entry, memory and localStorage — the forget-me path. */
211
211
  clear(): void;
212
212
  };
213
213
 
@@ -242,8 +242,6 @@ type GraphClient = {
242
242
  /** One-shot batch sync of all current page nodes to the backend. */
243
243
  syncOnce(): void;
244
244
  snapshot(): GraphSnapshot;
245
- serialize(): string;
246
- restore(data: string): void;
247
245
  destroy(): void;
248
246
  };
249
247
  /**
@@ -368,6 +366,15 @@ declare function attachMicroSignalDetectors(emit: MicroSignalEmitter, node: Elem
368
366
  tabLoss?: boolean;
369
367
  }): () => void;
370
368
 
369
+ /**
370
+ * @internal Wires an alternate entry point's init as grantConsent()'s upgrade
371
+ * path for `apiKey`. The /graph entry calls this when its init is gated on
372
+ * consent: without it, grantConsent() upgraded through the LEAN init, so a
373
+ * graph-configured page granted consent but never mounted the scanner (the
374
+ * graph resources exist only in the /graph entry). No-op unless the entry is
375
+ * actually upgradeable — DNT-blocked and local entries register no hook.
376
+ */
377
+ declare function _registerConsentUpgradeInit(apiKey: string, reinit: (config: SentientConfig) => SentientClient): void;
371
378
  type SentientConfig = {
372
379
  apiKey: string;
373
380
  context: 'landing' | 'ecommerce' | 'saas' | 'marketplace';
@@ -612,4 +619,4 @@ declare function grantConsent(apiKey?: string): void;
612
619
  */
613
620
  declare function init(config: SentientConfig): SentientClient;
614
621
 
615
- 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, attachMicroSignalDetectors as ah, grantConsent as ai, init as aj, isDoNotTrackEnabled as ak, readSnapshot as al, renderPrePaintScript as am, sanitizePageUrl as an, sessionCookieName as ao, writeSnapshot as ap, 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 };
622
+ 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 };
@@ -207,7 +207,7 @@ type Assignment = {
207
207
  type AssignmentCache = {
208
208
  get(componentId: string, segment: string): Assignment | null;
209
209
  set(componentId: string, segment: string, assignment: Assignment): void;
210
- invalidate(componentId: string): void;
210
+ /** Drops every entry, memory and localStorage — the forget-me path. */
211
211
  clear(): void;
212
212
  };
213
213
 
@@ -242,8 +242,6 @@ type GraphClient = {
242
242
  /** One-shot batch sync of all current page nodes to the backend. */
243
243
  syncOnce(): void;
244
244
  snapshot(): GraphSnapshot;
245
- serialize(): string;
246
- restore(data: string): void;
247
245
  destroy(): void;
248
246
  };
249
247
  /**
@@ -368,6 +366,15 @@ declare function attachMicroSignalDetectors(emit: MicroSignalEmitter, node: Elem
368
366
  tabLoss?: boolean;
369
367
  }): () => void;
370
368
 
369
+ /**
370
+ * @internal Wires an alternate entry point's init as grantConsent()'s upgrade
371
+ * path for `apiKey`. The /graph entry calls this when its init is gated on
372
+ * consent: without it, grantConsent() upgraded through the LEAN init, so a
373
+ * graph-configured page granted consent but never mounted the scanner (the
374
+ * graph resources exist only in the /graph entry). No-op unless the entry is
375
+ * actually upgradeable — DNT-blocked and local entries register no hook.
376
+ */
377
+ declare function _registerConsentUpgradeInit(apiKey: string, reinit: (config: SentientConfig) => SentientClient): void;
371
378
  type SentientConfig = {
372
379
  apiKey: string;
373
380
  context: 'landing' | 'ecommerce' | 'saas' | 'marketplace';
@@ -612,4 +619,4 @@ declare function grantConsent(apiKey?: string): void;
612
619
  */
613
620
  declare function init(config: SentientConfig): SentientClient;
614
621
 
615
- 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, attachMicroSignalDetectors as ah, grantConsent as ai, init as aj, isDoNotTrackEnabled as ak, readSnapshot as al, renderPrePaintScript as am, sanitizePageUrl as an, sessionCookieName as ao, writeSnapshot as ap, 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 };
622
+ 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 };
@@ -1,30 +1,5 @@
1
- type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
- /** Canonical vocabulary mirrors the graph_nodes semantic_type CHECK enum. */
3
- declare const SEMANTIC_TYPES: readonly SemanticType[];
4
- /** Environment-agnostic section features — buildable from a browser Element or
5
- * a server-parsed node (node-html-parser). */
6
- type SectionFeatures = {
7
- tag: string;
8
- idClass: string;
9
- headingText: string;
10
- bodyText: string;
11
- actionCount: number;
12
- textLength: number;
13
- };
14
- /**
15
- * Pure classification over extracted features. `strong` = keyword or content
16
- * evidence (trustable enough to auto-apply); `weak` = structural fallback
17
- * (cta/hero/navigation/generic — capture-worthy but not persona evidence).
18
- */
19
- declare function classifyFeatures(f: SectionFeatures): {
20
- type: SemanticType;
21
- strength: 'strong' | 'weak';
22
- };
23
- /** Feature extraction from a live DOM element (browser paths). */
24
- declare function featuresFromElement(el: Element): SectionFeatures;
25
- /** Classify a page section into a semantic type (never null — falls back to
26
- * 'generic' so the caller can still capture attention on it). */
27
- declare function classifySection(el: Element): SemanticType;
1
+ import { b as SemanticType } from './classify-DYlSjkFP.cjs';
2
+ export { S as SEMANTIC_TYPES, a as SectionFeatures, c as classifyFeatures, d as classifySection, f as featuresFromElement } from './classify-DYlSjkFP.cjs';
28
3
 
29
4
  type CaptureClient = {
30
5
  track(event: {
@@ -56,4 +31,4 @@ type EngagementCaptureOptions = {
56
31
  };
57
32
  declare function startEngagementCapture(client: CaptureClient, opts: EngagementCaptureOptions): () => void;
58
33
 
59
- export { type EngagementCaptureOptions, SEMANTIC_TYPES, type SectionFeatures, type SemanticType, classifyFeatures, classifySection, featuresFromElement, startEngagementCapture };
34
+ export { type EngagementCaptureOptions, SemanticType, startEngagementCapture };
@@ -1,30 +1,5 @@
1
- type SemanticType = 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features' | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';
2
- /** Canonical vocabulary mirrors the graph_nodes semantic_type CHECK enum. */
3
- declare const SEMANTIC_TYPES: readonly SemanticType[];
4
- /** Environment-agnostic section features — buildable from a browser Element or
5
- * a server-parsed node (node-html-parser). */
6
- type SectionFeatures = {
7
- tag: string;
8
- idClass: string;
9
- headingText: string;
10
- bodyText: string;
11
- actionCount: number;
12
- textLength: number;
13
- };
14
- /**
15
- * Pure classification over extracted features. `strong` = keyword or content
16
- * evidence (trustable enough to auto-apply); `weak` = structural fallback
17
- * (cta/hero/navigation/generic — capture-worthy but not persona evidence).
18
- */
19
- declare function classifyFeatures(f: SectionFeatures): {
20
- type: SemanticType;
21
- strength: 'strong' | 'weak';
22
- };
23
- /** Feature extraction from a live DOM element (browser paths). */
24
- declare function featuresFromElement(el: Element): SectionFeatures;
25
- /** Classify a page section into a semantic type (never null — falls back to
26
- * 'generic' so the caller can still capture attention on it). */
27
- declare function classifySection(el: Element): SemanticType;
1
+ import { b as SemanticType } from './classify-DYlSjkFP.js';
2
+ export { S as SEMANTIC_TYPES, a as SectionFeatures, c as classifyFeatures, d as classifySection, f as featuresFromElement } from './classify-DYlSjkFP.js';
28
3
 
29
4
  type CaptureClient = {
30
5
  track(event: {
@@ -56,4 +31,4 @@ type EngagementCaptureOptions = {
56
31
  };
57
32
  declare function startEngagementCapture(client: CaptureClient, opts: EngagementCaptureOptions): () => void;
58
33
 
59
- export { type EngagementCaptureOptions, SEMANTIC_TYPES, type SectionFeatures, type SemanticType, classifyFeatures, classifySection, featuresFromElement, startEngagementCapture };
34
+ export { type EngagementCaptureOptions, SemanticType, startEngagementCapture };
@@ -1,2 +1,2 @@
1
- "use strict";var v=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames,W=Object.getOwnPropertySymbols;var F=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var j=(t,e,n)=>e in t?v(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,k=(t,e)=>{for(var n in e||(e={}))F.call(e,n)&&j(t,n,e[n]);if(W)for(var n of W(e))Y.call(e,n)&&j(t,n,e[n]);return t};var z=(t,e)=>{for(var n in e)v(t,n,{get:e[n],enumerable:!0})},J=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of V(e))!F.call(t,a)&&a!==n&&v(t,a,{get:()=>e[a],enumerable:!(i=Q(e,a))||i.enumerable});return t};var X=t=>J(v({},"__esModule",{value:!0}),t);var ae={};z(ae,{SEMANTIC_TYPES:()=>E,classifyFeatures:()=>I,classifySection:()=>C,featuresFromElement:()=>x,startEngagementCapture:()=>H});module.exports=X(ae);var E=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],Z=[["pricing",/\b(pricing|price|plans?|subscriptions?|per month|\/mo|tier)\b/i],["faq",/\b(faq|frequently asked|common questions?)\b/i],["comparison",/\b(compare|comparison|versus|vs\.)\b/i],["social_proof",/\b(testimonial|reviews?|trusted by|loved by|customers?|logos|rated|case stud)\b/i],["trust",/\b(security|guarantee|privacy|compliance|secure|gdpr|soc ?2|encrypt)\b/i],["features",/\b(features?|how it works|benefits?|capabilit|what you get)\b/i]],ee=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]];function te(t){var n;let e=t.querySelector("h1, h2, h3");return((n=e==null?void 0:e.textContent)!=null?n:"").slice(0,160)}function I(t){if(t.tag==="nav"||t.tag==="footer")return{type:"navigation",strength:"weak"};let e=`${t.idClass} ${t.headingText}`.toLowerCase();for(let[n,i]of Z)if(i.test(e))return{type:n,strength:"strong"};for(let[n,i]of ee)if(i.test(t.bodyText))return{type:n,strength:"strong"};return t.actionCount>=1&&t.textLength>0&&t.textLength<200?{type:"cta",strength:"weak"}:t.tag==="header"?{type:"hero",strength:"weak"}:/\b(hero|headline|banner)\b/i.test(e)?{type:"hero",strength:"weak"}:{type:"generic",strength:"weak"}}function x(t){var n,i;let e=((n=t.textContent)!=null?n:"").replace(/\s+/g," ").trim();return{tag:t.tagName.toLowerCase(),idClass:`${t.id} ${String((i=t.className)!=null?i:"")}`,headingText:te(t),bodyText:e.slice(0,2e3),actionCount:t.querySelectorAll('a, button, [role="button"]').length,textLength:e.length}}function C(t){return I(x(t)).type}var ne=require("@sentientui/policy");function U(t,e,n,i){let a=[];{let l=!1,c=[],p=()=>{if(l)return;let m=Date.now();for(c.push(m);c.length>0&&m-c[0]>500;)c.shift();c.length>=3&&(l=!0,t("rage_click"))};e.addEventListener("click",p),a.push(()=>e.removeEventListener("click",p))}{let d=!1,g=l=>{if(d||!(l.target instanceof Node)||!e.contains(l.target)&&e!==l.target)return;d=!0;let c=typeof window!="undefined"?window.getSelection():null,p=c?c.toString().length:0;t("text_copy",{selectionLength:p})};document.addEventListener("copy",g),a.push(()=>document.removeEventListener("copy",g))}{let d=!1,g=!1,l=null,c=()=>{l!==null&&(clearTimeout(l),l=null)},p=()=>{d||!g||(c(),l=setTimeout(()=>{!d&&g&&(d=!0,t("scroll_hesitation"))},3e3))},m=()=>{c(),p()},w=b=>{for(let S of b)g=S.intersectionRatio>.3,g?p():c()},y=new IntersectionObserver(w,{threshold:[.3]});y.observe(e),window.addEventListener("scroll",m,{passive:!0}),a.push(()=>{y.disconnect(),window.removeEventListener("scroll",m),c()})}if((i==null?void 0:i.tabLoss)!==!1){let d=!1,g=n!=null?n:Date.now(),l=()=>{if(d||document.visibilityState!=="hidden")return;let c=Date.now()-g;c<15e3&&(d=!0,t("tab_loss",{timeOnPage:c}))};document.addEventListener("visibilitychange",l),a.push(()=>document.removeEventListener("visibilitychange",l))}return()=>{for(let d of a)d()}}function B(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(e=>e==="1"||e==="yes")}var se="section, header, footer, nav, main > div, [data-sentient-section]",oe=2e4;function re(t){let e=Array.from(t.querySelectorAll(se)),n=e.filter(i=>e.filter(a=>a!==i&&i.contains(a)).length<2);return n.filter(i=>!n.some(a=>a!==i&&a.contains(i)))}function ie(t,e,n,i){try{fetch(`${e}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${t}`},body:JSON.stringify({pageUrl:n,sections:i})}).catch(()=>{})}catch(a){}}var T=()=>{};function H(t,e){var A,D,M,P,N,L,K,G,$;let n=(A=e.doc)!=null?A:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||B()||!e.apiKey)return T;let i=((D=e.apiBase)!=null?D:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),a=re(n);if(a.length===0)return T;let d=new Map,g=new Map,l=new Map;for(let s of a){let o=s.getAttribute("data-sentient-type"),r=o&&E.includes(o)?o:null,u=(P=r!=null?r:(M=e.typeOf)==null?void 0:M.call(e,s))!=null?P:C(s),h=`nc-${u}`;d.set(s,h),g.set(h,u),r?l.set(h,"markup"):l.has(h)||l.set(h,"auto")}let c=(G=(K=(L=(N=n.defaultView)!=null?N:typeof window!="undefined"?window:void 0)==null?void 0:L.location)==null?void 0:K.pathname)!=null?G:"/";ie(e.apiKey,i,c,[...g.entries()].map(([s,o])=>{var r;return{componentId:s,semanticType:o,source:(r=l.get(s))!=null?r:"auto"}}));let p=new Map,m=s=>{let o=p.get(s);return o||(o={ms:0,scroll:0,enterAt:null,intersecting:!1},p.set(s,o)),o},w=new IntersectionObserver(s=>{for(let o of s){let r=d.get(o.target);if(!r)continue;let u=m(r);o.isIntersecting?(u.intersecting=!0,u.enterAt=Date.now(),o.intersectionRatio>u.scroll&&(u.scroll=o.intersectionRatio)):(u.intersecting=!1,u.enterAt!=null&&(u.ms+=Date.now()-u.enterAt,u.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let s of d.keys())w.observe(s);let y=()=>{let s=Date.now();for(let[o,r]of p)if(r.enterAt!=null&&(r.ms+=s-r.enterAt,r.enterAt=null),!(r.ms<=0)){try{t.track({projectId:e.apiKey,componentId:o,eventType:"dwell",payload:{dwell_time:Math.round(r.ms),scroll_depth:Number(r.scroll.toFixed(2))}})}catch(u){}r.ms=0}},b=()=>{if(n.hidden)y();else{let s=Date.now();for(let o of p.values())o.intersecting&&(o.enterAt=s)}},S=!1,R=s=>{if(y(),s!=null&&s.persisted){S=!0;return}try{w.disconnect()}catch(o){}},O=s=>{if(!(s!=null&&s.persisted)||!S)return;S=!1;let o=Date.now();for(let r of p.values())r.enterAt=r.intersecting&&!n.hidden?o:null};n.addEventListener("visibilitychange",b);let f=($=n.defaultView)!=null?$:typeof window!="undefined"?window:void 0;f==null||f.addEventListener("pagehide",R),f==null||f.addEventListener("pageshow",O);let q=setInterval(()=>{if(n.hidden||S)return;y();let s=Date.now();for(let o of p.values())o.intersecting&&(o.enterAt=s)},oe),_=[];return e.microSignals&&[...d.entries()].forEach(([s,o],r)=>{_.push(U((u,h={})=>{try{t.track({projectId:e.apiKey,componentId:o,eventType:"micro_signal",payload:k({signalType:u},h)})}catch(le){}},s,void 0,{tabLoss:r===0}))}),()=>{y(),clearInterval(q),n.removeEventListener("visibilitychange",b),f==null||f.removeEventListener("pagehide",R),f==null||f.removeEventListener("pageshow",O);for(let s of _)s();try{w.disconnect()}catch(s){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
1
+ "use strict";var E=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var ee=Object.getOwnPropertyNames,j=Object.getOwnPropertySymbols;var U=Object.prototype.hasOwnProperty,te=Object.prototype.propertyIsEnumerable;var B=(e,t,n)=>t in e?E(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,w=(e,t)=>{for(var n in t||(t={}))U.call(t,n)&&B(e,n,t[n]);if(j)for(var n of j(t))te.call(t,n)&&B(e,n,t[n]);return e};var ne=(e,t)=>{for(var n in t)E(e,n,{get:t[n],enumerable:!0})},oe=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ee(t))!U.call(e,o)&&o!==n&&E(e,o,{get:()=>t[o],enumerable:!(a=Z(t,o))||a.enumerable});return e};var se=e=>oe(E({},"__esModule",{value:!0}),e);var Se={};ne(Se,{SEMANTIC_TYPES:()=>C,classifyFeatures:()=>A,classifySection:()=>k,featuresFromElement:()=>I,startEngagementCapture:()=>J});module.exports=se(Se);var C=["pricing","hero","social_proof","cta","features","faq","comparison","trust","navigation","generic"],re={banner:"hero",navigation:"navigation",contentinfo:"footer"};function ie(e){let t=e.ariaRole?re[e.ariaRole.toLowerCase()]:void 0;if(t)return t;if(e.tag==="nav")return"navigation";if(e.tag==="footer")return"footer";if(e.tag==="header")return"hero";let n=`${e.idClass} ${e.headingText}`.toLowerCase();return/\b(navbar|nav-bar|navigation|site-nav|main-nav|topbar|footer)\b/.test(n)?/footer/.test(n)?"footer":"navigation":/\b(hero|masthead|jumbotron)\b/i.test(e.idClass)?"hero":null}function ae(e){return e.actionCount>=1&&e.textLength>0&&e.textLength<200?"cta":"generic"}var ce=[["pricing",/\b(pricing|price list|per month|\/mo|subscriptions?)\b|\bplans?\b(?=[^.]{0,40}(from|start|month|year|[$€£]))/i],["faq",/\bfaq\b|frequently asked|common questions?/i],["comparison",/\b(compare|comparison|versus)\b|\bvs\./i],["social_proof",/\b(reviews?|ratings?|testimonial|brands?|galler(y|ies)|logos)\b|trusted by|loved by|case stud|what our customers say/i],["trust",/\b(insurance|warrant(y|ies)|guarantees?|certifi|accredit|security|privacy|compliance|gdpr|encrypt)\b|why choose|about us|our team/i],["cta",/\b(book|booking|reserve|appointments?|newsletter|subscribe)\b|contact us|get in touch|opening hours/i],["features",/\b(features?|benefits?|capabilit|services?|repairs?|menus?|products?)\b|how it works|our process|what we (do|offer)|what you get/i]],le=[["pricing",/(?:[$€£]\s?\d[\d,.]*\s*(?:\/|per\s)\s*(?:mo|month|yr|year|seat|user))|(?:\b(?:starter|basic|pro|growth|premium|enterprise)\b[^.]{0,60}[$€£]\s?\d)/i],["social_proof",/(?:★{2,})|(?:\b\d(?:\.\d)?\s*(?:out of|\/)\s*5\b)|(?:\brated\b)|(?:["“][^"”]{20,160}["”]\s*[—–-]\s*[A-Z][a-z]+)/],["trust",/\b(?:money[- ]back guarantee|free returns?|returns? within|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\b/i],["comparison",/\b(?:vs|versus)\b\.?[^.!?]{0,80}\b(?:compare|comparison|plans?|features?|alternative)\b|\bhow (?:we|it) compares?\b/i]],H={navigation:"navigation",footer:"navigation",hero:"hero",cta:"cta",generic:"generic"};function de(e){var n;let t=e.querySelector("h1, h2, h3");return((n=t==null?void 0:t.textContent)!=null?n:"").slice(0,160)}function A(e){var o,i;let t=ie(e);if(t)return{type:(o=H[t])!=null?o:"generic",strength:"strong"};let n=`${e.idClass} ${e.headingText}`.toLowerCase();for(let[d,s]of ce)if(s.test(n))return{type:d,strength:"strong"};for(let[d,s]of le)if(s.test(e.bodyText))return{type:d,strength:"strong"};let a=ae(e);return{type:(i=H[a])!=null?i:"generic",strength:"weak"}}function I(e){var a,o;let t=((a=e.textContent)!=null?a:"").replace(/\s+/g," ").trim(),n=e.getAttribute("role");return w({tag:e.tagName.toLowerCase(),idClass:`${e.id} ${String((o=e.className)!=null?o:"")}`,headingText:de(e),bodyText:t.slice(0,2e3),actionCount:e.querySelectorAll('a, button, [role="button"]').length,textLength:t.length},n?{ariaRole:n}:{})}function k(e){return A(I(e)).type}var ue=require("@sentientui/policy");function q(e,t,n,a){let o=[];{let s=!1,c=[],f=()=>{if(s)return;let y=Date.now();for(c.push(y);c.length>0&&y-c[0]>500;)c.shift();c.length>=3&&(s=!0,e("rage_click"))};t.addEventListener("click",f),o.push(()=>t.removeEventListener("click",f))}{let i=!1,d=s=>{if(i||!(s.target instanceof Node)||!t.contains(s.target)&&t!==s.target)return;i=!0;let c=typeof window!="undefined"?window.getSelection():null,f=c?c.toString().length:0;e("text_copy",{selectionLength:f})};document.addEventListener("copy",d),o.push(()=>document.removeEventListener("copy",d))}{let i=!1,d=!1,s=null,c=()=>{s!==null&&(clearTimeout(s),s=null)},f=()=>{i||!d||(c(),s=setTimeout(()=>{!i&&d&&(i=!0,e("scroll_hesitation"))},3e3))},y=()=>{c(),f()},p=S=>{for(let v of S)d=v.intersectionRatio>.3,d?f():c()},h=new IntersectionObserver(p,{threshold:[.3]});h.observe(t),window.addEventListener("scroll",y,{passive:!0}),o.push(()=>{h.disconnect(),window.removeEventListener("scroll",y),c()})}if((a==null?void 0:a.tabLoss)!==!1){let i=!1,d=n!=null?n:Date.now(),s=()=>{if(i||document.visibilityState!=="hidden")return;let c=Date.now()-d;c<15e3&&(i=!0,e("tab_loss",{timeOnPage:c}))};document.addEventListener("visibilitychange",s),o.push(()=>document.removeEventListener("visibilitychange",s))}return()=>{for(let i of o)i()}}function Q(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}var pe=["data-testid","data-test","data-id","data-name","data-cy"];function _(e){var t;return((t=e.textContent)!=null?t:"").replace(/\s+/g," ").trim()}function ge(e){return{tag:e.tagName.toLowerCase(),text:_(e).slice(0,40)}}function V(e){return e.replace(/["\\\]]/g,"\\$&")}function b(e,t){try{return e.querySelectorAll(t).length===1}catch(n){return!1}}function z(e,t){var d;let n=e.tagName.toLowerCase();if(!n)return null;let a=((d=e.getAttribute("class"))!=null?d:"").split(/\s+/).filter(s=>/^[a-zA-Z][\w-]*$/.test(s)),o=[n,...a.map(s=>`${n}.${s}`)];for(let s of o)if(b(t,s))return s;let i=e.parentElement;if(i&&i!==t){let s=z(i,t);if(s){for(let p of o){let h=`${s} > ${p}`;if(b(t,h))return h}let c=Array.from(i.children).filter(p=>p.tagName.toLowerCase()===n),f=c.indexOf(e),y=c.some(p=>p!==e&&_(p)!==_(e));if(f>=0&&y){let p=`${s} > ${n}:nth-of-type(${f+1})`;if(b(t,p))return p}}}return null}function Y(e,t){let n=ge(e),a=e.getAttribute("id");if(a&&b(t,`#${V(a)}`))return{v:1,id:a,fingerprint:n};for(let i of pe){let d=e.getAttribute(i);if(d&&b(t,`[${i}="${V(d)}"]`))return{v:1,dataAttr:{name:i,value:d},fingerprint:n}}let o=z(e,t);return o?{v:1,selector:o,fingerprint:n}:null}var fe="section, header, footer, nav, main > div, [data-sentient-section]",me=2e4;function ye(e){let t=Array.from(e.querySelectorAll(fe)),n=t.filter(a=>t.filter(o=>o!==a&&a.contains(o)).length<2);return n.filter(a=>!n.some(o=>o!==a&&o.contains(a)))}function he(e,t,n,a){try{fetch(`${t}/v1/section-map`,{method:"POST",keepalive:!0,headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({pageUrl:n,sections:a})}).catch(()=>{})}catch(o){}}var R=()=>{};function J(e,t){var D,M,N,L,$,K,G,W,F;let n=(D=t.doc)!=null?D:typeof document!="undefined"?document:void 0;if(!n||typeof IntersectionObserver=="undefined"||Q()||!t.apiKey||!t.apiKey.startsWith("pk_"))return R;let a=((M=t.apiBase)!=null?M:"https://api.sentient-ui.com").replace(/\/+$/,"").replace(/\/v1$/,""),o=ye(n);if(o.length===0)return R;let i=new Map,d=[];for(let r of o){let l=r.getAttribute("data-sentient-type"),u=l&&C.includes(l)?l:null,g=(L=u!=null?u:(N=t.typeOf)==null?void 0:N.call(t,r))!=null?L:k(r),T=`nc-${g}`;i.set(r,T);let x=Y(r,n);d.push(w({componentId:T,semanticType:g,source:u?"markup":"auto"},x?{locator:x}:{}))}let s=(W=(G=(K=($=n.defaultView)!=null?$:typeof window!="undefined"?window:void 0)==null?void 0:K.location)==null?void 0:G.pathname)!=null?W:"/";he(t.apiKey,a,s,d);let c=new Map,f=r=>{let l=c.get(r);return l||(l={ms:0,scroll:0,enterAt:null,intersecting:!1},c.set(r,l)),l},y=new IntersectionObserver(r=>{for(let l of r){let u=i.get(l.target);if(!u)continue;let g=f(u);l.isIntersecting?(g.intersecting=!0,g.enterAt=Date.now(),l.intersectionRatio>g.scroll&&(g.scroll=l.intersectionRatio)):(g.intersecting=!1,g.enterAt!=null&&(g.ms+=Date.now()-g.enterAt,g.enterAt=null))}},{threshold:[0,.25,.5,.75,1]});for(let r of i.keys())y.observe(r);let p=()=>{let r=Date.now();for(let[l,u]of c)if(u.enterAt!=null&&(u.ms+=r-u.enterAt,u.enterAt=null),!(u.ms<=0)){try{e.track({projectId:t.apiKey,componentId:l,eventType:"dwell",payload:{dwell_time:Math.round(u.ms),scroll_depth:Number(u.scroll.toFixed(2))}})}catch(g){}u.ms=0}},h=()=>{if(n.hidden)p();else{let r=Date.now();for(let l of c.values())l.intersecting&&(l.enterAt=r)}},S=!1,v=r=>{if(p(),r!=null&&r.persisted){S=!0;return}try{y.disconnect()}catch(l){}},O=r=>{if(!(r!=null&&r.persisted)||!S)return;S=!1;let l=Date.now();for(let u of c.values())u.enterAt=u.intersecting&&!n.hidden?l:null};n.addEventListener("visibilitychange",h);let m=(F=n.defaultView)!=null?F:typeof window!="undefined"?window:void 0;m==null||m.addEventListener("pagehide",v),m==null||m.addEventListener("pageshow",O);let X=setInterval(()=>{if(n.hidden||S)return;p();let r=Date.now();for(let l of c.values())l.intersecting&&(l.enterAt=r)},me),P=[];return t.microSignals&&[...i.entries()].forEach(([r,l],u)=>{P.push(q((g,T={})=>{try{e.track({projectId:t.apiKey,componentId:l,eventType:"micro_signal",payload:w({signalType:g},T)})}catch(x){}},r,void 0,{tabLoss:u===0}))}),()=>{p(),clearInterval(X),n.removeEventListener("visibilitychange",h),m==null||m.removeEventListener("pagehide",v),m==null||m.removeEventListener("pageshow",O);for(let r of P)r();try{y.disconnect()}catch(r){}}}0&&(module.exports={SEMANTIC_TYPES,classifyFeatures,classifySection,featuresFromElement,startEngagementCapture});
2
2
  //# sourceMappingURL=index-engagement.js.map