@active-reach/web-sdk 1.27.3 → 1.29.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Storage, l as logger, A as Aegis } from "./analytics-0p0p56H0.mjs";
2
- import { B, E, N, R, U, m } from "./analytics-0p0p56H0.mjs";
1
+ import { S as Storage, l as logger, A as Aegis } from "./analytics-CCDZCnut.mjs";
2
+ import { B, E, N, R, U, m } from "./analytics-CCDZCnut.mjs";
3
3
  import { AegisWebPush } from "./push/AegisWebPush.js";
4
4
  function debounce(func, wait) {
5
5
  let timeoutId = null;
@@ -25,6 +25,94 @@ function throttle(func, limit) {
25
25
  }
26
26
  };
27
27
  }
28
+ const PDP_SKU_RE = /\/p\/([^/?#]+)/;
29
+ function parseAction(actionUrl, meta) {
30
+ const url = (actionUrl ?? "").trim();
31
+ const m2 = meta ?? {};
32
+ if (url.startsWith("aegis://")) {
33
+ const rest = url.slice("aegis://".length);
34
+ const [verb, qs] = rest.split("?");
35
+ const params = new URLSearchParams(qs || "");
36
+ switch (verb) {
37
+ case "dismiss":
38
+ return { type: "dismiss" };
39
+ case "request-push":
40
+ case "request_push":
41
+ return { type: "request_push" };
42
+ case "cart-add":
43
+ case "add-to-cart":
44
+ return {
45
+ type: "add_to_cart",
46
+ sku: params.get("sku") || m2.sku || void 0,
47
+ productId: params.get("product_id") || m2.product_id || void 0,
48
+ platformProductId: m2.platform_product_id,
49
+ qty: Number(params.get("qty")) || 1,
50
+ title: m2.title,
51
+ price: m2.price,
52
+ imageUrl: m2.image_url,
53
+ url: m2.platform_url
54
+ };
55
+ case "open-cart":
56
+ case "cart":
57
+ return { type: "open_cart" };
58
+ case "open-login":
59
+ case "login":
60
+ return { type: "open_login" };
61
+ case "open-product":
62
+ case "product": {
63
+ const sku = params.get("sku") || m2.sku || void 0;
64
+ return { type: "open_product", sku, productId: m2.product_id, url: sku ? `/p/${sku}` : void 0 };
65
+ }
66
+ default:
67
+ return { type: "custom", name: verb || "unknown", payload: Object.fromEntries(params) };
68
+ }
69
+ }
70
+ const pdp = PDP_SKU_RE.exec(url);
71
+ if (pdp) {
72
+ return {
73
+ type: "open_product",
74
+ sku: decodeURIComponent(pdp[1]),
75
+ productId: m2.product_id,
76
+ url,
77
+ platformProductId: m2.platform_product_id,
78
+ platformUrl: m2.platform_url
79
+ };
80
+ }
81
+ if (url) {
82
+ return { type: "navigate", url };
83
+ }
84
+ if (m2.sku || m2.product_id) {
85
+ return {
86
+ type: "add_to_cart",
87
+ sku: m2.sku,
88
+ productId: m2.product_id,
89
+ platformProductId: m2.platform_product_id,
90
+ qty: 1,
91
+ title: m2.title,
92
+ price: m2.price,
93
+ imageUrl: m2.image_url,
94
+ url: m2.platform_url
95
+ };
96
+ }
97
+ return { type: "dismiss" };
98
+ }
99
+ function fallbackUrlForAction(a, productUrl = (sku) => `/p/${encodeURIComponent(sku)}`) {
100
+ switch (a.type) {
101
+ case "navigate":
102
+ return a.url;
103
+ case "open_product":
104
+ return a.url || (a.sku ? productUrl(a.sku) : void 0);
105
+ case "add_to_cart":
106
+ return a.url || (a.sku ? productUrl(a.sku) : void 0);
107
+ case "open_cart":
108
+ return "/cart";
109
+ case "open_login":
110
+ return void 0;
111
+ // host-only; no neutral fallback
112
+ default:
113
+ return void 0;
114
+ }
115
+ }
28
116
  const ICONS = {
29
117
  x: [
30
118
  ["path", { d: "M18 6 6 18" }],
@@ -943,6 +1031,7 @@ function renderCardFromTemplate(card, opts) {
943
1031
  const fg = contrastFg(frame.bg ? sanitizeColor(frame.bg) : "#ffffff");
944
1032
  const muted = fg === "#ffffff" ? "rgba(255,255,255,0.72)" : "#6b7280";
945
1033
  const tile = document.createElement("div");
1034
+ tile.setAttribute("data-aegis-reaction-anchor", "");
946
1035
  const cardFont = resolveCardFont(frame.fontFamily);
947
1036
  ensureCardFontLoaded(cardFont);
948
1037
  const radius = frame.radius !== void 0 ? `${parseInt(String(frame.radius), 10) || 12}px` : "12px";
@@ -957,7 +1046,7 @@ function renderCardFromTemplate(card, opts) {
957
1046
  background:${frame.bg ? sanitizeColor(frame.bg) : "#ffffff"};
958
1047
  border:1px solid ${frame.border ? sanitizeColor(frame.border) : "transparent"};
959
1048
  border-radius:${radius}; padding:8px;
960
- ${fLayout === "row" ? "" : "box-shadow:0 2px 10px rgba(0,0,0,0.12);"}
1049
+ ${fLayout === "row" ? "" : "box-shadow:0 4px 16px rgba(0,0,0,0.18);"}
961
1050
  ${frame.max_width ? `max-width:${frame.max_width}px;` : ""}${frame.min_width ? `min-width:${frame.min_width}px;` : ""}
962
1051
  ${frame.height ? `height:${frame.height}px;` : ""}${frame.min_height ? `min-height:${frame.min_height}px;` : ""}
963
1052
  ${sizing}
@@ -1123,15 +1212,30 @@ function renderCardFromTemplate(card, opts) {
1123
1212
  e.stopPropagation();
1124
1213
  trackEvent(campaign.id, "clicked", { stepId: el.id || "add" });
1125
1214
  if (inline) {
1126
- if (typeof window !== "undefined") {
1215
+ const m2 = meta;
1216
+ if (ctx.dispatchAction) {
1217
+ ctx.dispatchAction({
1218
+ type: "add_to_cart",
1219
+ productId: m2.product_id,
1220
+ sku: m2.sku,
1221
+ // Platform-native id (catalog-sync C.2) — plugins use this for a
1222
+ // native add; undefined today → graceful product-search fallback.
1223
+ platformProductId: m2.platform_product_id,
1224
+ title: card.title,
1225
+ price: m2.price ?? card.body,
1226
+ imageUrl: card.image_url,
1227
+ url: m2.platform_url || card.cta_url,
1228
+ qty: 1
1229
+ });
1230
+ } else if (typeof window !== "undefined") {
1127
1231
  window.dispatchEvent(
1128
1232
  new CustomEvent("aegis:add-to-cart", {
1129
1233
  detail: {
1130
1234
  campaign_id: campaign.id,
1131
- product_id: meta.product_id,
1132
- sku: meta.sku,
1235
+ product_id: m2.product_id,
1236
+ sku: m2.sku,
1133
1237
  title: card.title,
1134
- price: meta.price ?? card.body,
1238
+ price: m2.price ?? card.body,
1135
1239
  image_url: card.image_url,
1136
1240
  cta_url: card.cta_url
1137
1241
  }
@@ -1156,13 +1260,23 @@ function renderCardFromTemplate(card, opts) {
1156
1260
  e.stopPropagation();
1157
1261
  trackEvent(campaign.id, "clicked");
1158
1262
  if (inline) {
1159
- if (typeof window !== "undefined") {
1263
+ const bm = meta;
1264
+ if (ctx.dispatchAction) {
1265
+ ctx.dispatchAction({
1266
+ type: "open_product",
1267
+ productId: bm.product_id,
1268
+ sku: bm.sku,
1269
+ url: bm.platform_url || card.cta_url,
1270
+ platformProductId: bm.platform_product_id,
1271
+ platformUrl: bm.platform_url
1272
+ });
1273
+ } else if (typeof window !== "undefined") {
1160
1274
  window.dispatchEvent(
1161
1275
  new CustomEvent("aegis:open-product", {
1162
1276
  detail: {
1163
1277
  campaign_id: campaign.id,
1164
- product_id: meta.product_id,
1165
- sku: meta.sku,
1278
+ product_id: bm.product_id,
1279
+ sku: bm.sku,
1166
1280
  cta_url: card.cta_url
1167
1281
  }
1168
1282
  })
@@ -1256,7 +1370,8 @@ function renderCarouselCards(ctx) {
1256
1370
  track.style.cssText = `
1257
1371
  display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;
1258
1372
  scroll-padding-left: 0; scrollbar-width: none; -ms-overflow-style: none;
1259
- padding-bottom: 4px; -webkit-overflow-scrolling: touch;
1373
+ /* room so overflow-x: auto doesn't clip the card drop-shadow */
1374
+ padding: 8px 6px 16px; margin: -8px -6px -6px; -webkit-overflow-scrolling: touch;
1260
1375
  `;
1261
1376
  track.style.msOverflowStyle = "none";
1262
1377
  const template = resolveGlobalTemplate(ic);
@@ -1921,12 +2036,12 @@ function renderProductRecommendation(ctx) {
1921
2036
  if (layout === "row") {
1922
2037
  grid.style.cssText = `
1923
2038
  display: flex; gap: 10px; overflow-x: auto;
1924
- scroll-snap-type: x mandatory; padding: 14px 0 4px;
2039
+ scroll-snap-type: x mandatory; padding: 14px 2px 18px;
1925
2040
  `;
1926
2041
  } else {
1927
2042
  grid.style.cssText = `
1928
2043
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;
1929
- padding-top: 14px;
2044
+ padding: 14px 2px 6px;
1930
2045
  `;
1931
2046
  }
1932
2047
  cards.forEach((c) => {
@@ -1985,7 +2100,7 @@ function renderProductRecommendationInline(ctx, target) {
1985
2100
  } else {
1986
2101
  grid.style.cssText = `
1987
2102
  display: flex; gap: 10px; overflow-x: auto;
1988
- scroll-snap-type: x proximity; padding: 0 0 6px;
2103
+ scroll-snap-type: x proximity; padding: 10px 8px 18px; margin: -8px -8px -6px;
1989
2104
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
1990
2105
  `;
1991
2106
  grid.style.msOverflowStyle = "none";
@@ -4921,6 +5036,7 @@ function svgNode(svg) {
4921
5036
  }
4922
5037
  const _AegisInAppManager = class _AegisInAppManager {
4923
5038
  constructor(config) {
5039
+ this.productUrl = (sku) => `/p/${encodeURIComponent(sku)}`;
4924
5040
  this.campaigns = [];
4925
5041
  this.displayedCampaigns = /* @__PURE__ */ new Set();
4926
5042
  this._displaySizeMul = 1;
@@ -4950,6 +5066,8 @@ const _AegisInAppManager = class _AegisInAppManager {
4950
5066
  this.debugMode = config.debugMode || false;
4951
5067
  this.enableSSE = config.enableSSE === true;
4952
5068
  this.onInteractiveCampaign = config.onInteractiveCampaign;
5069
+ this.onActionHandler = config.onAction;
5070
+ if (config.productUrl) this.productUrl = config.productUrl;
4953
5071
  this.getWorkspaceId = config.getWorkspaceId;
4954
5072
  this.getCartState = config.getCartState;
4955
5073
  }
@@ -6115,8 +6233,8 @@ const _AegisInAppManager = class _AegisInAppManager {
6115
6233
  void this.trackEvent(campaign.id, "clicked");
6116
6234
  if (actionUrl) {
6117
6235
  window.open(actionUrl, "_blank", "noopener");
6118
- } else if (typeof window !== "undefined") {
6119
- window.dispatchEvent(new CustomEvent("aegis:open-login", { detail: { campaign_id: campaign.id } }));
6236
+ } else {
6237
+ this.dispatchAction({ type: "open_login" }, campaign.id);
6120
6238
  }
6121
6239
  });
6122
6240
  row.appendChild(btn);
@@ -7790,6 +7908,7 @@ const _AegisInAppManager = class _AegisInAppManager {
7790
7908
  },
7791
7909
  sanitizeUrl: (url) => this.sanitizeUrl(url),
7792
7910
  navigate: (url, buttonId) => this.navigateToCampaignAction(campaign, url, buttonId),
7911
+ dispatchAction: (action) => this.dispatchAction(action, campaign.id),
7793
7912
  submitResponse: (responseType, payload) => this.submitWidgetResponse(campaign, responseType, payload),
7794
7913
  playReaction: (el, intent, lottieFile, scope) => this._playReaction(el, intent, lottieFile, campaign.interactive_config, scope),
7795
7914
  haptic: (pattern) => {
@@ -8565,13 +8684,79 @@ const _AegisInAppManager = class _AegisInAppManager {
8565
8684
  window.location.href = safeUrl;
8566
8685
  }
8567
8686
  }
8687
+ /** Register/replace the canonical CTA-action handler after construction (the
8688
+ * config `onAction` is the construct-time equivalent). Idempotent. */
8689
+ setOnAction(handler) {
8690
+ this.onActionHandler = handler;
8691
+ }
8692
+ /**
8693
+ * Canonical CTA-action dispatcher (CANONICAL_ACTION_MODEL_TRACKER.md). ONE path
8694
+ * for every host-facing CTA action:
8695
+ * 1. give the host's `onAction` handler first refusal (return `true` = handled);
8696
+ * 2. ALSO fire the legacy `aegis:*` DOM event (cancelable) so existing
8697
+ * listeners (ACS) keep working unchanged during the migration;
8698
+ * 3. if STILL unhandled → canonical fallback (navigate to the product/cart
8699
+ * page) so a CTA is never a dead no-op (the rail-"Add" 3rd-party gap).
8700
+ * Both add-to-cart paths (card "Add" + the `aegis://cart-add` verb) funnel here,
8701
+ * so they're one behaviour with one legacy event (`aegis:add-to-cart`).
8702
+ */
8703
+ dispatchAction(action, campaignId) {
8704
+ var _a;
8705
+ let handled = false;
8706
+ try {
8707
+ handled = ((_a = this.onActionHandler) == null ? void 0 : _a.call(this, action)) === true;
8708
+ } catch {
8709
+ }
8710
+ if (typeof window !== "undefined") {
8711
+ const legacy = this._legacyEventFor(action, campaignId);
8712
+ if (legacy) {
8713
+ const evt = new CustomEvent(legacy.name, { detail: legacy.detail, cancelable: true });
8714
+ const proceed = window.dispatchEvent(evt);
8715
+ if (!proceed || evt.defaultPrevented) handled = true;
8716
+ }
8717
+ }
8718
+ if (!handled) {
8719
+ const url = fallbackUrlForAction(action, this.productUrl);
8720
+ if (url && typeof window !== "undefined") window.location.href = url;
8721
+ }
8722
+ }
8723
+ /** Back-compat mapping: canonical action → the legacy DOM event hosts listen
8724
+ * for today. `add_to_cart` always emits `aegis:add-to-cart` (the one ACS
8725
+ * handles), collapsing the old `aegis:cart-add` divergence. */
8726
+ _legacyEventFor(a, campaignId) {
8727
+ const base = { campaign_id: campaignId };
8728
+ switch (a.type) {
8729
+ case "add_to_cart":
8730
+ return {
8731
+ name: "aegis:add-to-cart",
8732
+ detail: {
8733
+ ...base,
8734
+ product_id: a.productId,
8735
+ sku: a.sku,
8736
+ title: a.title,
8737
+ price: a.price,
8738
+ image_url: a.imageUrl,
8739
+ cta_url: a.url,
8740
+ qty: a.qty ?? 1
8741
+ }
8742
+ };
8743
+ case "open_product":
8744
+ return { name: "aegis:open-product", detail: { ...base, sku: a.sku, product_id: a.productId, url: a.url } };
8745
+ case "open_cart":
8746
+ return { name: "aegis:open-cart", detail: base };
8747
+ case "open_login":
8748
+ return { name: "aegis:open-login", detail: base };
8749
+ default:
8750
+ return null;
8751
+ }
8752
+ }
8568
8753
  /**
8569
8754
  * Phase-2 CTA action verbs (`aegis://<verb>?<params>`). Performs an in-app
8570
8755
  * behaviour instead of navigating. Verbs:
8571
8756
  * dismiss — close the message (tracked as dismissed)
8572
8757
  * request-push — prompt for web-push permission, then close
8573
- * cart-add?sku=X — dispatch `aegis:cart-add` for the storefront to handle;
8574
- * if unhandled, fall back to the product page
8758
+ * cart-add?sku=X — canonical `add_to_cart` action via dispatchAction
8759
+ * (cancelable + product-page fallback)
8575
8760
  */
8576
8761
  runCampaignActionVerb(campaign, url) {
8577
8762
  const rest = url.slice("aegis://".length);
@@ -8594,14 +8779,7 @@ const _AegisInAppManager = class _AegisInAppManager {
8594
8779
  case "cart-add": {
8595
8780
  const sku = params.get("sku") || "";
8596
8781
  void this.trackEvent(campaign.id, "clicked", { meta: { verb: "cart-add", sku } });
8597
- const evt = new CustomEvent("aegis:cart-add", {
8598
- detail: { sku, qty: 1, campaign_id: campaign.id },
8599
- cancelable: true
8600
- });
8601
- const proceed = window.dispatchEvent(evt);
8602
- if (proceed && !evt.defaultPrevented && sku) {
8603
- window.location.href = `/p/${encodeURIComponent(sku)}`;
8604
- }
8782
+ this.dispatchAction(parseAction(url, { sku }), campaign.id);
8605
8783
  break;
8606
8784
  }
8607
8785
  default:
@@ -9965,7 +10143,8 @@ const _AegisInAppManager = class _AegisInAppManager {
9965
10143
  this.addAnimationStyles();
9966
10144
  const reduce = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
9967
10145
  const viewport = scope === "viewport";
9968
- const anchor = viewport ? document.body : host || document.body;
10146
+ const cardAnchor = !viewport && (host == null ? void 0 : host.closest) ? host.closest("[data-aegis-reaction-anchor]") : null;
10147
+ const anchor = viewport ? document.body : cardAnchor || host || document.body;
9969
10148
  const prevPos = anchor.style.position;
9970
10149
  if (!viewport && (!prevPos || prevPos === "static")) anchor.style.position = "relative";
9971
10150
  const layer = document.createElement("div");
@@ -10004,7 +10183,7 @@ const _AegisInAppManager = class _AegisInAppManager {
10004
10183
  g.style.cssText = `position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: ${px}px; height: ${px}px; display: flex; align-items: center; justify-content: center; line-height: 0; filter: drop-shadow(0 8px 16px rgba(0,0,0,0.30)); ${motion}`;
10005
10184
  layer.appendChild(g);
10006
10185
  anchor.appendChild(layer);
10007
- if (festive && !reduce) this._fireConfetti(host);
10186
+ if (festive && !reduce) this._fireConfetti(cardAnchor || host);
10008
10187
  const ra = ic && typeof ic === "object" ? ic.reaction_animations : void 0;
10009
10188
  const custom = ra && typeof ra[intent] === "string" ? ra[intent] : void 0;
10010
10189
  const file = custom || lottieFile || spec.file;
@@ -11498,6 +11677,7 @@ class AegisWidgetManager {
11498
11677
  this.onEvent = config.onEvent;
11499
11678
  this.sourcePlatform = config.sourcePlatform;
11500
11679
  this.getWorkspaceId = config.getWorkspaceId;
11680
+ this.onCartDetected = config.onCartDetected;
11501
11681
  }
11502
11682
  updateCSSCustomization(customization) {
11503
11683
  this.cssCustomization = { ...this.cssCustomization, ...customization };
@@ -11609,24 +11789,18 @@ class AegisWidgetManager {
11609
11789
  this.log("AegisWidgets initialized successfully");
11610
11790
  }
11611
11791
  setCartData(cartData) {
11792
+ var _a;
11612
11793
  this.cartData = cartData;
11613
11794
  this.log(`Cart data updated: ${cartData.cart_items.length} items, total: ${cartData.cart_currency} ${cartData.cart_total}`);
11795
+ (_a = this.onCartDetected) == null ? void 0 : _a.call(this, cartData);
11614
11796
  }
11615
11797
  detectPlatformCart() {
11616
- const shopifyCart = this.normalizeShopifyCart();
11617
- if (shopifyCart) {
11618
- this.log("Detected Shopify cart via browser globals");
11619
- return shopifyCart;
11620
- }
11621
- const wooCart = this.normalizeWooCart();
11622
- if (wooCart) {
11623
- this.log("Detected WooCommerce cart via injected data");
11624
- return wooCart;
11625
- }
11626
- const magentoCart = this.normalizeMagentoCart();
11627
- if (magentoCart) {
11628
- this.log("Detected Magento cart via localStorage");
11629
- return magentoCart;
11798
+ var _a;
11799
+ const detected = this.normalizeShopifyCart() ?? this.normalizeWooCart() ?? this.normalizeMagentoCart();
11800
+ if (detected) {
11801
+ this.log("Detected platform cart (Shopify/Woo/Magento)");
11802
+ (_a = this.onCartDetected) == null ? void 0 : _a.call(this, detected);
11803
+ return detected;
11630
11804
  }
11631
11805
  if (this.cartData) {
11632
11806
  this.log("Using manually set cart data");
@@ -13611,7 +13785,16 @@ class AegisMessageRuntime {
13611
13785
  ownsTriggerEngine,
13612
13786
  enablePrefetch: config.enableWidgetPrefetch !== false,
13613
13787
  sourcePlatform: config.sourcePlatform,
13614
- getWorkspaceId: config.getWorkspaceId
13788
+ getWorkspaceId: config.getWorkspaceId,
13789
+ // Cart-store unification: when the widget subsystem detects a platform
13790
+ // cart (Shopify/Woo/Magento) or the host calls `widgets.setCartData`, flow
13791
+ // it UP into the ONE cart store so the Free-Delivery card / progress bar /
13792
+ // rule eval see the same cart. See CART_STORE_UNIFICATION_TRACKER.md.
13793
+ onCartDetected: (cart) => this.applyCart({
13794
+ total: cart.cart_total,
13795
+ itemCount: Array.isArray(cart.cart_items) ? cart.cart_items.reduce((s, it) => s + (Number(it.quantity) || 1), 0) : 0,
13796
+ currency: cart.cart_currency
13797
+ })
13615
13798
  });
13616
13799
  this.inApp = new AegisInAppManager({
13617
13800
  writeKey: config.writeKey,
@@ -13631,7 +13814,13 @@ class AegisMessageRuntime {
13631
13814
  getWorkspaceId: config.getWorkspaceId,
13632
13815
  // First-party cart reader — renderers (progress-bar) read live cart
13633
13816
  // state from here instead of `window.*` cart globals. See setCartState().
13634
- getCartState: () => this.deriveCartState()
13817
+ getCartState: () => this.deriveCartState(),
13818
+ // Canonical CTA-action handler + platform product-URL resolver
13819
+ // (CANONICAL_ACTION_MODEL_TRACKER.md). A host (ACS / Shopify-Woo plugin /
13820
+ // native) registers ONE `onAction` to handle add_to_cart/open_product/…;
13821
+ // legacy `aegis:*` DOM events still fire for back-compat.
13822
+ onAction: config.onAction,
13823
+ productUrl: config.productUrl
13635
13824
  });
13636
13825
  this.intentRuleEvaluator = new IntentRuleEvaluator();
13637
13826
  if (config.governance) {
@@ -13781,11 +13970,42 @@ class AegisMessageRuntime {
13781
13970
  * feed for headless storefronts; prefer `updateContext` for new code.
13782
13971
  */
13783
13972
  setCartState(state) {
13784
- this.updateContext("cart", {
13785
- total: state.total,
13786
- items: state.itemCount,
13787
- currency: state.currency
13788
- });
13973
+ this.applyCart(state);
13974
+ }
13975
+ /**
13976
+ * Register the canonical CTA-action handler — the CEP-standard single callback
13977
+ * that receives every in-app intent (add_to_cart / open_product / open_cart /
13978
+ * open_login / navigate / custom). Return `true` when handled; otherwise the SDK
13979
+ * applies the graceful fallback. The construct-time `onAction` config is the
13980
+ * equivalent; this lets a host register/replace it once it holds the runtime.
13981
+ * See CANONICAL_ACTION_MODEL_TRACKER.md.
13982
+ */
13983
+ onAction(handler) {
13984
+ this.inApp.setOnAction(handler);
13985
+ }
13986
+ /**
13987
+ * The ONE cart writer. Normalizes any cart-ish shape and funnels it into the
13988
+ * single `cart` context namespace (read by the Free-Delivery progress card,
13989
+ * the progress-bar widget, and rule eval via the `cart_value` signal). EVERY
13990
+ * cart feed routes through here: ecommerce events (`bindEcommerce`), platform
13991
+ * auto-detect + `widgets.setCartData` (`onCartDetected`), and the explicit
13992
+ * `setCartState` escape hatch. See CART_STORE_UNIFICATION_TRACKER.md.
13993
+ */
13994
+ applyCart(cart) {
13995
+ const total = Number(cart.total) || 0;
13996
+ const itemCount = Number(cart.itemCount ?? cart.items) || 0;
13997
+ const currency = typeof cart.currency === "string" && cart.currency ? cart.currency : "INR";
13998
+ this.updateContext("cart", { total, items: itemCount, currency });
13999
+ }
14000
+ /**
14001
+ * Wire the analytics ecommerce tracker into the cart store — the CEP-standard
14002
+ * path. After this, a host firing `aegis.ecommerce.addToCart(...)` /
14003
+ * `cartViewed(...)` automatically drives every cart-reactive surface; no
14004
+ * separate `setCartState` call is needed. Call once after constructing both
14005
+ * the analytics SDK and the runtime. Idempotent.
14006
+ */
14007
+ bindEcommerce(tracker) {
14008
+ tracker.onCartChange((c) => this.applyCart(c));
13789
14009
  }
13790
14010
  // --- Qualifying-event refresh facade (DLR Tracker 1 / P1, 2026-05-28) ---
13791
14011
  //
@@ -13970,9 +14190,11 @@ export {
13970
14190
  debounce,
13971
14191
  aegis as default,
13972
14192
  deriveDeviceFingerprint,
14193
+ fallbackUrlForAction,
13973
14194
  getCurrentLauncher,
13974
14195
  m as murmurhash3_x86_32,
13975
14196
  openChat,
14197
+ parseAction,
13976
14198
  readFirstPartyCookie,
13977
14199
  renderPreview,
13978
14200
  throttle,