@tokenoftrust/storefront-runner 2.0.29 → 2.0.30

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.0.29",
3
+ "version": "2.0.30",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
6
6
  "packageManager": "pnpm@11.9.0",
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The platform-owned browser binding for the two reviewed contact-sales CTAs.
3
+ *
4
+ * This is deliberately not a general interaction adapter. Both DOM identifiers and
5
+ * the emitted semantic name are fixed below; the source reads no labels, URLs,
6
+ * selectors supplied by a caller, event detail, or arbitrary DOM data.
7
+ */
8
+ import type { PlatformManagedActionActivation, PlatformManagedActionSource } from "./managed-analytics-bridge.js";
9
+
10
+ /** The only reviewed CTA identities that may raise this marketing action. */
11
+ export const CONTACT_SALES_ACTION_SOURCES = [
12
+ { ctaId: "cta-book-demo", logicalId: "contact-sales-book-demo" },
13
+ { ctaId: "contact-sales", logicalId: "contact-sales-lower" },
14
+ ] as const;
15
+
16
+ const CONTACT_SALES_ACTION_NAME = "user_clicked_contact_sales_mktg" as const;
17
+ const CORRELATION_TTL_MS = 60 * 60 * 1000;
18
+
19
+ export interface ContactSalesActionElement {
20
+ addEventListener(type: "click", listener: () => void): void;
21
+ removeEventListener(type: "click", listener: () => void): void;
22
+ }
23
+
24
+ /** The narrow DOM seam used only to find the two fixed platform CTA identities. */
25
+ export interface ContactSalesActionDocument {
26
+ querySelectorAll(selector: string): ArrayLike<ContactSalesActionElement>;
27
+ }
28
+
29
+ export interface ContactSalesActionSourceOptions {
30
+ document?: ContactSalesActionDocument;
31
+ planRevision: string;
32
+ now?: () => number;
33
+ }
34
+
35
+ /**
36
+ * Bind literal `data-cta-id` values to a typed source for the managed-analytics
37
+ * bridge. A keyboard activation of an anchor/button produces its native `click`, so
38
+ * it follows this same semantic path without a separate key listener.
39
+ */
40
+ export function createContactSalesActionSource(
41
+ options: ContactSalesActionSourceOptions,
42
+ ): PlatformManagedActionSource {
43
+ const document = options.document;
44
+ const now = options.now ?? Date.now;
45
+ let sequence = 0;
46
+
47
+ return {
48
+ subscribe(listener) {
49
+ if (!document || typeof document.querySelectorAll !== "function") return () => {};
50
+ const removals: Array<() => void> = [];
51
+ for (const source of CONTACT_SALES_ACTION_SOURCES) {
52
+ const nodes = document.querySelectorAll(`[data-cta-id="${source.ctaId}"]`);
53
+ for (const node of Array.from(nodes)) {
54
+ const emit = (): void => {
55
+ sequence += 1;
56
+ const at = now();
57
+ const activation: PlatformManagedActionActivation = {
58
+ logicalId: `${source.logicalId}-${options.planRevision}-${sequence}`,
59
+ name: CONTACT_SALES_ACTION_NAME,
60
+ correlation: {
61
+ id: `contact-sales-${options.planRevision}`,
62
+ expiresAt: new Date(at + CORRELATION_TTL_MS).toISOString(),
63
+ },
64
+ sequence,
65
+ };
66
+ listener(activation);
67
+ };
68
+ node.addEventListener("click", emit);
69
+ removals.push(() => node.removeEventListener("click", emit));
70
+ }
71
+ }
72
+ return () => removals.splice(0).forEach((remove) => remove());
73
+ },
74
+ };
75
+ }
@@ -76,6 +76,7 @@ export * from "./events-catalog.js";
76
76
  export * from "./commerce-broker.js";
77
77
  export * from "./managed-analytics.js";
78
78
  export * from "./managed-analytics-bridge.js";
79
+ export * from "./contact-sales-action-source.js";
79
80
  export * from "./g2-action-boundary.js";
80
81
  export * from "./correlation-token.js";
81
82
  export * from "./order-conversion.js";
@@ -45,6 +45,10 @@ import { validateBrowserEnvironment, BROWSER_ENVIRONMENT_GLOBAL, type BrowserEnv
45
45
  import { makePlatformAdapterRuntimeLoader } from "./platform-adapter-runtime.js";
46
46
  import { ManagedAnalyticsEventBrokerBridge } from "./managed-analytics-bridge.js";
47
47
  import { minimizePageAttribution, type ManagedPageViewInput } from "./managed-analytics.js";
48
+ import {
49
+ createContactSalesActionSource,
50
+ type ContactSalesActionDocument,
51
+ } from "./contact-sales-action-source.js";
48
52
 
49
53
  /** The window property holding the single per-document lifecycle (idempotent boot guard). */
50
54
  export const LIFECYCLE_SINGLETON_GLOBAL = "__TOT_INTEGRATION_LIFECYCLE__";
@@ -68,7 +72,7 @@ export interface LifecycleWindow {
68
72
  * analytics data allowlist accepts no other document state — no title, no DOM, no
69
73
  * globals — so this is deliberately the narrowest slice that supports attribution.
70
74
  */
71
- document?: { referrer?: string };
75
+ document?: { referrer?: string; querySelectorAll?: ContactSalesActionDocument["querySelectorAll"] };
72
76
  [key: string]: unknown;
73
77
  }
74
78
 
@@ -337,7 +341,16 @@ export function bootIntegrationLifecycle(
337
341
  commerceBroker,
338
342
  managedAnalytics: {
339
343
  bridge: options.managedAnalyticsBridge ??
340
- new ManagedAnalyticsEventBrokerBridge({ broker: commerceBroker }),
344
+ new ManagedAnalyticsEventBrokerBridge({
345
+ broker: commerceBroker,
346
+ actionSource: createContactSalesActionSource({
347
+ document: typeof win.document?.querySelectorAll === "function"
348
+ ? win.document as ContactSalesActionDocument
349
+ : undefined,
350
+ planRevision: descriptor.planRevision,
351
+ now: options.now ?? Date.now,
352
+ }),
353
+ }),
341
354
  pageView: () => documentPageView(win, descriptor, options.now ?? Date.now),
342
355
  },
343
356
  });