@active-reach/web-sdk 1.36.9 → 1.38.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 (38) hide show
  1. package/dist/aegis.min.js +1 -1
  2. package/dist/aegis.min.js.map +1 -1
  3. package/dist/{analytics-Qyzk8JaB.mjs → analytics-Cz1ZBOxH.mjs} +244 -110
  4. package/dist/analytics-Cz1ZBOxH.mjs.map +1 -0
  5. package/dist/core/analytics.d.ts +10 -0
  6. package/dist/core/analytics.d.ts.map +1 -1
  7. package/dist/core/prefetch-bundle-client.d.ts +1 -0
  8. package/dist/core/prefetch-bundle-client.d.ts.map +1 -1
  9. package/dist/core/queue.d.ts.map +1 -1
  10. package/dist/core/session.d.ts +12 -1
  11. package/dist/core/session.d.ts.map +1 -1
  12. package/dist/ecommerce/index.d.ts.map +1 -1
  13. package/dist/inapp/AegisInAppManager.d.ts +18 -1
  14. package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
  15. package/dist/inapp/actions.d.ts +7 -0
  16. package/dist/inapp/actions.d.ts.map +1 -1
  17. package/dist/inapp/index.d.ts +1 -1
  18. package/dist/inapp/index.d.ts.map +1 -1
  19. package/dist/inapp/renderers/card-template.d.ts.map +1 -1
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +217 -30
  23. package/dist/index.js.map +1 -1
  24. package/dist/react.js +1 -1
  25. package/dist/triggers/TriggerEngine.d.ts +5 -0
  26. package/dist/triggers/TriggerEngine.d.ts.map +1 -1
  27. package/dist/utils/device.d.ts.map +1 -1
  28. package/dist/utils/meta-cookies.d.ts +7 -0
  29. package/dist/utils/meta-cookies.d.ts.map +1 -1
  30. package/dist/utils/redact-url.d.ts +56 -0
  31. package/dist/utils/redact-url.d.ts.map +1 -0
  32. package/dist/utils/url-parser.d.ts +38 -0
  33. package/dist/utils/url-parser.d.ts.map +1 -1
  34. package/dist/utils/visitor.d.ts +43 -0
  35. package/dist/utils/visitor.d.ts.map +1 -0
  36. package/dist/widgets/AegisWidgetManager.d.ts.map +1 -1
  37. package/package.json +1 -1
  38. package/dist/analytics-Qyzk8JaB.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Storage, l as logger, A as Aegis } from "./analytics-Qyzk8JaB.mjs";
2
- import { B, E, N, R, U, m } from "./analytics-Qyzk8JaB.mjs";
1
+ import { S as Storage, r as redactUrl, l as logger, A as Aegis } from "./analytics-Cz1ZBOxH.mjs";
2
+ import { B, E, N, R, U, m } from "./analytics-Cz1ZBOxH.mjs";
3
3
  import { AegisWebPush } from "./push/AegisWebPush.js";
4
4
  function debounce(func, wait) {
5
5
  let timeoutId = null;
@@ -26,6 +26,13 @@ function throttle(func, limit) {
26
26
  };
27
27
  }
28
28
  const PDP_SKU_RE = /\/p\/([^/?#]+)/;
29
+ function productPath(sku) {
30
+ const encoded = encodeURIComponent(sku).replace(
31
+ /[!'()*]/g,
32
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
33
+ );
34
+ return `/p/${encoded}`;
35
+ }
29
36
  function parseAction(actionUrl, meta) {
30
37
  const url = (actionUrl ?? "").trim();
31
38
  const m2 = meta ?? {};
@@ -61,7 +68,7 @@ function parseAction(actionUrl, meta) {
61
68
  case "open-product":
62
69
  case "product": {
63
70
  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 };
71
+ return { type: "open_product", sku, productId: m2.product_id, url: sku ? productPath(sku) : void 0 };
65
72
  }
66
73
  default:
67
74
  return { type: "custom", name: verb || "unknown", payload: Object.fromEntries(params) };
@@ -96,7 +103,7 @@ function parseAction(actionUrl, meta) {
96
103
  }
97
104
  return { type: "dismiss" };
98
105
  }
99
- function fallbackUrlForAction(a, productUrl = (sku) => `/p/${encodeURIComponent(sku)}`) {
106
+ function fallbackUrlForAction(a, productUrl = productPath) {
100
107
  switch (a.type) {
101
108
  case "navigate":
102
109
  return a.url;
@@ -652,9 +659,11 @@ function appendRatingThanks(wrap, el2, ctx, fg, accent, isHigh) {
652
659
  cta.textContent = el2.rating_high_cta || "Leave a review";
653
660
  cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
654
661
  cta.addEventListener("click", () => {
662
+ var _a;
655
663
  ctx.trackEvent(ctx.campaign.id, "clicked", {
656
664
  meta: { element: "rating_input", cta: "high_review" }
657
665
  });
666
+ void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "review_redirect", { cta: "high_review" }));
658
667
  if (ctx.navigate) ctx.navigate(url, "cta");
659
668
  else window.location.href = url;
660
669
  });
@@ -821,6 +830,7 @@ function buildProductRatingListElement(el2, ctx, fg) {
821
830
  const orderId = slot.getAttribute("data-order-id");
822
831
  const billCode = slot.getAttribute("data-bill-code");
823
832
  const token = slot.getAttribute("data-token");
833
+ const reviewsViaPage = Boolean(slot.getAttribute("data-reviews-url"));
824
834
  const reviewsUrl = slot.getAttribute("data-reviews-url") || el2.rating_high_url || null;
825
835
  const scale = typeof el2.rating_scale === "number" && el2.rating_scale >= 2 && el2.rating_scale <= 10 ? Math.floor(el2.rating_scale) : 5;
826
836
  const showThumb = el2.show_thumbnail !== false && el2.rating_layout !== "compact";
@@ -1071,6 +1081,12 @@ function buildProductRatingListElement(el2, ctx, fg) {
1071
1081
  a.href = ctx.sanitizeUrl(reviewsUrl) || "#";
1072
1082
  a.textContent = el2.rating_high_cta || "Share a review";
1073
1083
  a.style.cssText = `display:inline-block;margin-top:6px;font-size:12px;font-weight:700;color:${accent};text-decoration:none;`;
1084
+ if (!reviewsViaPage) {
1085
+ a.addEventListener("click", () => {
1086
+ var _a2;
1087
+ void ((_a2 = ctx.submitResponse) == null ? void 0 : _a2.call(ctx, "review_redirect", { cta: "item_rating_review" }));
1088
+ });
1089
+ }
1074
1090
  ack.appendChild(a);
1075
1091
  }
1076
1092
  wrap.appendChild(ack);
@@ -1247,9 +1263,11 @@ function appendNpsThanks(wrap, el2, ctx, fg, accent, isPromoter) {
1247
1263
  cta.textContent = el2.nps_promoter_cta || "Leave a review";
1248
1264
  cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
1249
1265
  cta.addEventListener("click", () => {
1266
+ var _a;
1250
1267
  ctx.trackEvent(ctx.campaign.id, "clicked", {
1251
1268
  meta: { element: "nps", cta: "promoter_review" }
1252
1269
  });
1270
+ void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "review_redirect", { cta: "promoter_review" }));
1253
1271
  if (ctx.navigate) ctx.navigate(url, "cta");
1254
1272
  else window.location.href = url;
1255
1273
  });
@@ -4937,7 +4955,7 @@ class TriggerEngine {
4937
4955
  this.exitIntentFired = true;
4938
4956
  this.emit("exit_intent", {
4939
4957
  client_y: event.clientY,
4940
- page_url: window.location.href,
4958
+ page_url: redactUrl(window.location.href),
4941
4959
  time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1e3 : 0
4942
4960
  });
4943
4961
  }
@@ -4958,7 +4976,7 @@ class TriggerEngine {
4958
4976
  scroll_velocity: velocity,
4959
4977
  scroll_distance: distance,
4960
4978
  current_position: currentY,
4961
- page_url: window.location.href,
4979
+ page_url: redactUrl(window.location.href),
4962
4980
  time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1e3 : 0
4963
4981
  });
4964
4982
  this.scrollVelocityCooldownTimer = window.setTimeout(() => {
@@ -4972,12 +4990,12 @@ class TriggerEngine {
4972
4990
  this.handleVisibilityChange = () => {
4973
4991
  if (document.hidden) {
4974
4992
  this.emit("visibility_hidden", {
4975
- page_url: window.location.href,
4993
+ page_url: redactUrl(window.location.href),
4976
4994
  time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1e3 : 0
4977
4995
  });
4978
4996
  } else {
4979
4997
  this.emit("visibility_visible", {
4980
- page_url: window.location.href
4998
+ page_url: redactUrl(window.location.href)
4981
4999
  });
4982
5000
  }
4983
5001
  };
@@ -4988,7 +5006,7 @@ class TriggerEngine {
4988
5006
  this.backButtonFired = true;
4989
5007
  window.history.pushState(null, "", window.location.href);
4990
5008
  this.emit("back_button", {
4991
- page_url: window.location.href,
5009
+ page_url: redactUrl(window.location.href),
4992
5010
  time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1e3 : 0
4993
5011
  });
4994
5012
  };
@@ -5016,7 +5034,7 @@ class TriggerEngine {
5016
5034
  const timerId = window.setTimeout(() => {
5017
5035
  this.emit(`time_on_page_${seconds}`, {
5018
5036
  seconds,
5019
- page_url: window.location.href
5037
+ page_url: redactUrl(window.location.href)
5020
5038
  });
5021
5039
  this.timeOnPageTargets.delete(seconds);
5022
5040
  }, seconds * 1e3);
@@ -5936,6 +5954,54 @@ function renderReviewsOverlay(ctx, creds) {
5936
5954
  document.body.appendChild(overlay);
5937
5955
  renderReviewsInline(ctx, body, creds);
5938
5956
  }
5957
+ const RETURNING_MARKER_KEY = "aegis_returning_user";
5958
+ const SESSION_VERDICT_KEY = "aegis_new_visitor_session";
5959
+ let cachedVerdict = null;
5960
+ function readSessionVerdict() {
5961
+ try {
5962
+ if (typeof sessionStorage === "undefined") return null;
5963
+ const stored = sessionStorage.getItem(SESSION_VERDICT_KEY);
5964
+ if (stored === "1") return true;
5965
+ if (stored === "0") return false;
5966
+ return null;
5967
+ } catch {
5968
+ return null;
5969
+ }
5970
+ }
5971
+ function writeSessionVerdict(verdict) {
5972
+ try {
5973
+ if (typeof sessionStorage === "undefined") return;
5974
+ sessionStorage.setItem(SESSION_VERDICT_KEY, verdict ? "1" : "0");
5975
+ } catch {
5976
+ }
5977
+ }
5978
+ function readMarkerVerdict() {
5979
+ try {
5980
+ if (typeof localStorage === "undefined") return false;
5981
+ return localStorage.getItem(RETURNING_MARKER_KEY) === null;
5982
+ } catch {
5983
+ return false;
5984
+ }
5985
+ }
5986
+ function isNewVisitor() {
5987
+ if (cachedVerdict !== null) return cachedVerdict;
5988
+ const sessionVerdict = readSessionVerdict();
5989
+ if (sessionVerdict !== null) {
5990
+ cachedVerdict = sessionVerdict;
5991
+ return cachedVerdict;
5992
+ }
5993
+ cachedVerdict = readMarkerVerdict();
5994
+ writeSessionVerdict(cachedVerdict);
5995
+ return cachedVerdict;
5996
+ }
5997
+ function markReturningVisitor() {
5998
+ isNewVisitor();
5999
+ try {
6000
+ if (typeof localStorage === "undefined") return;
6001
+ localStorage.setItem(RETURNING_MARKER_KEY, "1");
6002
+ } catch {
6003
+ }
6004
+ }
5939
6005
  function readAnonIdFromStorage$1() {
5940
6006
  if (typeof document === "undefined") return void 0;
5941
6007
  try {
@@ -5945,6 +6011,11 @@ function readAnonIdFromStorage$1() {
5945
6011
  return void 0;
5946
6012
  }
5947
6013
  }
6014
+ const JOURNEY_ACK_ACTION = {
6015
+ impression: "viewed",
6016
+ clicked: "clicked",
6017
+ dismissed: "dismissed"
6018
+ };
5948
6019
  function svgNode(svg) {
5949
6020
  try {
5950
6021
  const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
@@ -5955,9 +6026,41 @@ function svgNode(svg) {
5955
6026
  return null;
5956
6027
  }
5957
6028
  }
6029
+ const productIdentityCache = /* @__PURE__ */ new WeakMap();
6030
+ const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6031
+ function productIdentitiesFor(campaign) {
6032
+ const ic = campaign == null ? void 0 : campaign.interactive_config;
6033
+ const cards = ic && Array.isArray(ic.cards) ? ic.cards : [];
6034
+ if (cards.length === 0) return void 0;
6035
+ const cached = productIdentityCache.get(cards);
6036
+ if (cached !== void 0) return cached || void 0;
6037
+ const items = [];
6038
+ const seen = /* @__PURE__ */ new Set();
6039
+ for (const card of cards.slice(0, 24)) {
6040
+ const meta = (card == null ? void 0 : card.metadata) || {};
6041
+ const rawId = String(meta.product_id || "").trim();
6042
+ const rawSku = String(meta.sku || "").trim();
6043
+ const platformId = String(meta.platform_product_id || "").trim();
6044
+ const productId = UUID_SHAPE.test(rawId) ? rawId : "";
6045
+ const sku = rawSku && !UUID_SHAPE.test(rawSku) ? rawSku : rawId && !productId ? rawId : "";
6046
+ const identity = productId || sku || platformId;
6047
+ if (!identity || seen.has(identity)) continue;
6048
+ seen.add(identity);
6049
+ items.push({
6050
+ ...productId ? { product_id: productId } : {},
6051
+ ...sku ? { sku } : {},
6052
+ ...!productId && !sku && platformId ? { platform_product_id: platformId } : {},
6053
+ name: String((card == null ? void 0 : card.title) || identity),
6054
+ quantity: 1
6055
+ });
6056
+ }
6057
+ const encoded = items.length > 0 ? JSON.stringify(items) : "";
6058
+ productIdentityCache.set(cards, encoded);
6059
+ return encoded || void 0;
6060
+ }
5958
6061
  const _AegisInAppManager = class _AegisInAppManager {
5959
6062
  constructor(config) {
5960
- this.productUrl = (sku) => `/p/${encodeURIComponent(sku)}`;
6063
+ this.productUrl = productPath;
5961
6064
  this.campaigns = [];
5962
6065
  this.displayedCampaigns = /* @__PURE__ */ new Set();
5963
6066
  this._displaySizeMul = 1;
@@ -5983,6 +6086,8 @@ const _AegisInAppManager = class _AegisInAppManager {
5983
6086
  this.slotUnfilledBeaconed = /* @__PURE__ */ new Set();
5984
6087
  this.slotUnfilledSweepTimer = null;
5985
6088
  this._pushOptInLoading = false;
6089
+ this.journeyStepByCampaign = /* @__PURE__ */ new Map();
6090
+ this.journeyAcks = /* @__PURE__ */ new Set();
5986
6091
  this.writeKey = config.writeKey;
5987
6092
  this.apiHost = config.apiHost || "https://api.aegis.ai";
5988
6093
  this.userId = config.userId ?? readAnonIdFromStorage$1();
@@ -6088,13 +6193,13 @@ const _AegisInAppManager = class _AegisInAppManager {
6088
6193
  this.log("AegisInApp already initialized");
6089
6194
  return;
6090
6195
  }
6091
- if (typeof localStorage !== "undefined") {
6092
- localStorage.setItem("aegis_returning_user", "1");
6093
- try {
6196
+ markReturningVisitor();
6197
+ try {
6198
+ if (typeof localStorage !== "undefined") {
6094
6199
  const pv = parseInt(localStorage.getItem("aegis_pv_count") || "0", 10);
6095
6200
  localStorage.setItem("aegis_pv_count", String(pv + 1));
6096
- } catch {
6097
6201
  }
6202
+ } catch {
6098
6203
  }
6099
6204
  ensureInAppFont();
6100
6205
  await this.refreshCampaigns();
@@ -6471,9 +6576,11 @@ const _AegisInAppManager = class _AegisInAppManager {
6471
6576
  try {
6472
6577
  const context = new URLSearchParams({
6473
6578
  device_type: this.detectDeviceType(),
6474
- page_url: typeof window !== "undefined" ? window.location.pathname + window.location.search : "/"
6579
+ // Credential params keep their key and lose their value, so a
6580
+ // `*placed=1*` / `*?step=cart*` rule still matches.
6581
+ page_url: typeof window !== "undefined" ? redactUrl(window.location.pathname + window.location.search) : "/"
6475
6582
  });
6476
- context.set("is_new_user", this.isNewUser() ? "true" : "false");
6583
+ context.set("is_new_user", isNewVisitor() ? "true" : "false");
6477
6584
  if (this.currentSurface) {
6478
6585
  context.set("current_surface", this.currentSurface);
6479
6586
  }
@@ -6555,28 +6662,29 @@ const _AegisInAppManager = class _AegisInAppManager {
6555
6662
  if (/iPad|Tablet/i.test(ua)) return "tablet";
6556
6663
  return "desktop";
6557
6664
  }
6558
- isNewUser() {
6559
- if (typeof localStorage === "undefined") return true;
6560
- return !localStorage.getItem("aegis_returning_user");
6561
- }
6562
6665
  // --- A/B Assignment Persistence (Critical Fix C) ---
6666
+ // Both A/B helpers sit on the campaign-fetch path, so blocked storage
6667
+ // (where even `typeof localStorage` throws) must not abort the fetch.
6563
6668
  getABAssignments() {
6564
- if (typeof localStorage === "undefined") return {};
6565
6669
  try {
6670
+ if (typeof localStorage === "undefined") return {};
6566
6671
  return JSON.parse(localStorage.getItem("aegis_ab_assignments") || "{}");
6567
6672
  } catch {
6568
6673
  return {};
6569
6674
  }
6570
6675
  }
6571
6676
  processABAssignments(campaigns) {
6572
- if (typeof localStorage === "undefined") return;
6573
6677
  const stored = this.getABAssignments();
6574
6678
  for (const campaign of campaigns) {
6575
6679
  if (campaign.assigned_variant_id) {
6576
6680
  stored[campaign.id] = campaign.assigned_variant_id;
6577
6681
  }
6578
6682
  }
6579
- localStorage.setItem("aegis_ab_assignments", JSON.stringify(stored));
6683
+ try {
6684
+ if (typeof localStorage === "undefined") return;
6685
+ localStorage.setItem("aegis_ab_assignments", JSON.stringify(stored));
6686
+ } catch {
6687
+ }
6580
6688
  }
6581
6689
  getVariantId(campaignId) {
6582
6690
  const assignments = this.getABAssignments();
@@ -11788,6 +11896,20 @@ const _AegisInAppManager = class _AegisInAppManager {
11788
11896
  // to in_app_events.surface so the property By-screen breakdown can
11789
11897
  // show real per-screen impressions/CTR.
11790
11898
  current_surface: this.currentSurface ?? void 0,
11899
+ // The products this card actually rendered. Lands on the canonical
11900
+ // event's `properties.items` (gateway → switchboard), which is the
11901
+ // same array shape the bill page and the purchase event use, so ONE
11902
+ // materialized view turns every surface into per-product impressions.
11903
+ // See `productIdentitiesFor` for why this exists.
11904
+ //
11905
+ // ONLY on `impression` and `clicked`. `served` means "the SDK received
11906
+ // the payload", not "a customer saw it" — on ST there are 3,179 served
11907
+ // against 1,077 impressions, so counting both would treble the
11908
+ // impression denominator with renders that never reached a screen, and
11909
+ // put ~2KB of product JSON on three times as many requests for the
11910
+ // privilege. dismissed / anchor_unresolved / slot_unfilled describe the
11911
+ // widget, not the products.
11912
+ items: eventType === "impression" || eventType === "clicked" ? productIdentitiesFor(trackedCampaign) : void 0,
11791
11913
  // P3 — optional step_id surfaces multi-step granularity
11792
11914
  // (WEB_SDK_DLR_ENHANCEMENT_TRACKER P3). Server validator + CH
11793
11915
  // schema accept null/empty as the single-step default.
@@ -11826,6 +11948,67 @@ const _AegisInAppManager = class _AegisInAppManager {
11826
11948
  } catch (error) {
11827
11949
  this.log(`Error tracking event: ${error}`, "error");
11828
11950
  }
11951
+ const step = (trackedCampaign == null ? void 0 : trackedCampaign.journey_step_execution_id) || this.journeyStepByCampaign.get(campaignId);
11952
+ const ackAction = JOURNEY_ACK_ACTION[eventType];
11953
+ if (step) {
11954
+ this.journeyStepByCampaign.set(campaignId, step);
11955
+ if (ackAction) void this.ackJourneyQueueItem(step, ackAction);
11956
+ }
11957
+ }
11958
+ /**
11959
+ * POST /v1/in-app/widgets/{step}/ack — the canonical writer of a queue row's
11960
+ * seen-state (the same endpoint the Android and iOS SDKs call). Fail-soft by
11961
+ * construction: it runs after the render, is never awaited by it, and never
11962
+ * throws. A network error or 5xx is retried once; if that also fails the key
11963
+ * is released, so the next render of the (still re-served) message tries
11964
+ * again. A 4xx is the server's answer and is not retried. The server side is
11965
+ * idempotent — a repeated action is answered `late_arrival` and writes
11966
+ * nothing — so a duplicate ack is harmless.
11967
+ */
11968
+ async ackJourneyQueueItem(step, action, attempt = 0) {
11969
+ const key = `${step}:${action}`;
11970
+ if (attempt === 0) {
11971
+ if (this.journeyAcks.has(key)) return;
11972
+ this.journeyAcks.add(key);
11973
+ }
11974
+ const headers = {
11975
+ "Content-Type": "application/json",
11976
+ "X-Aegis-Write-Key": this.writeKey
11977
+ };
11978
+ if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
11979
+ if (this.userId) headers["X-User-ID"] = this.userId;
11980
+ if (this.contactId) headers["X-Contact-ID"] = this.contactId;
11981
+ let retryable = true;
11982
+ try {
11983
+ const response = await fetch(
11984
+ `${this.apiHost}/v1/in-app/widgets/${encodeURIComponent(step)}/ack`,
11985
+ {
11986
+ method: "POST",
11987
+ headers,
11988
+ body: JSON.stringify({ action }),
11989
+ credentials: "include",
11990
+ // A click usually navigates away at once; keepalive lets the ack
11991
+ // outlive the page instead of being cancelled with it.
11992
+ keepalive: true
11993
+ }
11994
+ );
11995
+ if (response.ok) {
11996
+ this.log(`Acknowledged queued message ${step}: ${action}`);
11997
+ return;
11998
+ }
11999
+ retryable = response.status >= 500 || response.status === 408 || response.status === 429;
12000
+ this.log(`Queue ack ${action} for ${step} failed: HTTP ${response.status}`, "warn");
12001
+ } catch (error) {
12002
+ this.log(`Queue ack ${action} for ${step} failed: ${error}`, "warn");
12003
+ }
12004
+ if (!retryable) return;
12005
+ if (attempt === 0) {
12006
+ setTimeout(() => {
12007
+ void this.ackJourneyQueueItem(step, action, 1);
12008
+ }, _AegisInAppManager.JOURNEY_ACK_RETRY_MS);
12009
+ return;
12010
+ }
12011
+ this.journeyAcks.delete(key);
11829
12012
  }
11830
12013
  log(message, level = "log") {
11831
12014
  if (this.debugMode) {
@@ -11876,6 +12059,7 @@ _AegisInAppManager.KNOWN_SURFACES = /* @__PURE__ */ new Set([
11876
12059
  "external_mobile_screen"
11877
12060
  ]);
11878
12061
  _AegisInAppManager.SERVED_DEDUP_PREFIX = "aegis_served_fired:";
12062
+ _AegisInAppManager.JOURNEY_ACK_RETRY_MS = 2e3;
11879
12063
  let AegisInAppManager = _AegisInAppManager;
11880
12064
  function renderPreview(config, opts) {
11881
12065
  document.querySelectorAll(
@@ -12594,9 +12778,10 @@ class PrefetchBundleClient {
12594
12778
  const ctx = this.contextProvider();
12595
12779
  const qs = new URLSearchParams();
12596
12780
  if (ctx.device_type) qs.set("device_type", ctx.device_type);
12597
- if (ctx.page_url) qs.set("page_url", ctx.page_url);
12781
+ if (ctx.page_url) qs.set("page_url", redactUrl(ctx.page_url));
12598
12782
  if (ctx.geo) qs.set("geo", ctx.geo);
12599
- qs.set("is_new_user", ctx.is_new_user ? "true" : "false");
12783
+ const isNewUser = ctx.is_new_user ?? isNewVisitor();
12784
+ qs.set("is_new_user", isNewUser ? "true" : "false");
12600
12785
  const url = `${this.apiHost}/v1/sdk/prefetch-bundle?${qs.toString()}`;
12601
12786
  const headers = {
12602
12787
  "X-Aegis-Write-Key": this.writeKey,
@@ -13478,8 +13663,8 @@ class AegisWidgetManager {
13478
13663
  cart_items: this.cartData.cart_items,
13479
13664
  user_email: this.userId,
13480
13665
  abandoned_at: (/* @__PURE__ */ new Date()).toISOString(),
13481
- page_url: window.location.href,
13482
- referrer: document.referrer
13666
+ page_url: redactUrl(window.location.href),
13667
+ referrer: redactUrl(document.referrer)
13483
13668
  };
13484
13669
  const beaconUrl = `${this.apiHost}/v1/widgets/beacon/cart-abandoned`;
13485
13670
  if (navigator.sendBeacon) {
@@ -13983,7 +14168,9 @@ class AegisWidgetManager {
13983
14168
  cart_total: ((_c = data.cart) == null ? void 0 : _c.cart_total) || 0,
13984
14169
  cart_currency: ((_d = data.cart) == null ? void 0 : _d.cart_currency) || "USD",
13985
14170
  cart_items: ((_e = data.cart) == null ? void 0 : _e.cart_items) || [],
13986
- cart_url: window.location.href,
14171
+ // Redacted: a stored cart URL is re-sent later as a recovery link, and a
14172
+ // credential in it (`?t=` / `?k=`) must not outlive this page view.
14173
+ cart_url: redactUrl(window.location.href),
13987
14174
  platform: "web",
13988
14175
  geo_region: geoRegion,
13989
14176
  device_type: deviceType,
@@ -15099,7 +15286,7 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
15099
15286
  var _a;
15100
15287
  this.initialized = false;
15101
15288
  {
15102
- const v = "1.36.9";
15289
+ const v = "1.38.0";
15103
15290
  try {
15104
15291
  globalThis.__AEGIS_SDK_VERSION__ = v;
15105
15292
  } catch {