@active-reach/web-sdk 1.37.0 → 1.39.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 (54) hide show
  1. package/dist/aegis-push.min.js +1 -1
  2. package/dist/aegis-push.min.js.map +1 -1
  3. package/dist/aegis.min.js +1 -1
  4. package/dist/aegis.min.js.map +1 -1
  5. package/dist/{analytics-Qyzk8JaB.mjs → analytics-DoHdxjjh.mjs} +255 -110
  6. package/dist/analytics-DoHdxjjh.mjs.map +1 -0
  7. package/dist/core/analytics.d.ts +10 -0
  8. package/dist/core/analytics.d.ts.map +1 -1
  9. package/dist/core/prefetch-bundle-client.d.ts +1 -0
  10. package/dist/core/prefetch-bundle-client.d.ts.map +1 -1
  11. package/dist/core/queue.d.ts.map +1 -1
  12. package/dist/core/session.d.ts +12 -1
  13. package/dist/core/session.d.ts.map +1 -1
  14. package/dist/ecommerce/index.d.ts.map +1 -1
  15. package/dist/inapp/AegisInAppManager.d.ts +22 -2
  16. package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
  17. package/dist/inapp/actions.d.ts +7 -0
  18. package/dist/inapp/actions.d.ts.map +1 -1
  19. package/dist/inapp/index.d.ts +1 -1
  20. package/dist/inapp/index.d.ts.map +1 -1
  21. package/dist/inapp/renderers/card-template.d.ts.map +1 -1
  22. package/dist/inapp/renderers/games.d.ts.map +1 -1
  23. package/dist/inapp/renderers/types.d.ts +13 -0
  24. package/dist/inapp/renderers/types.d.ts.map +1 -1
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +266 -40
  28. package/dist/index.js.map +1 -1
  29. package/dist/push/AegisWebPush.d.ts +19 -0
  30. package/dist/push/AegisWebPush.d.ts.map +1 -1
  31. package/dist/push/AegisWebPush.js +31 -2
  32. package/dist/push/AegisWebPush.js.map +1 -1
  33. package/dist/react.js +1 -1
  34. package/dist/runtime/AegisMessageRuntime.d.ts +1 -1
  35. package/dist/runtime/AegisMessageRuntime.d.ts.map +1 -1
  36. package/dist/triggers/ContactScoresFetcher.d.ts +2 -1
  37. package/dist/triggers/ContactScoresFetcher.d.ts.map +1 -1
  38. package/dist/triggers/IntentRuleEvaluator.d.ts +1 -1
  39. package/dist/triggers/IntentRuleEvaluator.d.ts.map +1 -1
  40. package/dist/triggers/TriggerEngine.d.ts +5 -0
  41. package/dist/triggers/TriggerEngine.d.ts.map +1 -1
  42. package/dist/utils/device.d.ts.map +1 -1
  43. package/dist/utils/meta-cookies.d.ts +7 -0
  44. package/dist/utils/meta-cookies.d.ts.map +1 -1
  45. package/dist/utils/redact-url.d.ts +56 -0
  46. package/dist/utils/redact-url.d.ts.map +1 -0
  47. package/dist/utils/url-parser.d.ts +38 -0
  48. package/dist/utils/url-parser.d.ts.map +1 -1
  49. package/dist/utils/visitor.d.ts +43 -0
  50. package/dist/utils/visitor.d.ts.map +1 -0
  51. package/dist/widgets/AegisWidgetManager.d.ts +17 -0
  52. package/dist/widgets/AegisWidgetManager.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/dist/analytics-Qyzk8JaB.mjs.map +0 -1
@@ -164,6 +164,7 @@ class Identity {
164
164
  };
165
165
  }
166
166
  }
167
+ const LINK_ATTRIBUTION_DURABLE_DAYS = 30;
167
168
  class SessionManager {
168
169
  constructor(storage, sessionTimeoutMinutes = 30) {
169
170
  this.eventCount = 0;
@@ -171,6 +172,7 @@ class SessionManager {
171
172
  this.activityThrottle = 1e3;
172
173
  this.lastActivityUpdate = 0;
173
174
  this.adClickIDs = {};
175
+ this.linkAttribution = {};
174
176
  this.storage = storage;
175
177
  this.sessionTimeout = sessionTimeoutMinutes * 60 * 1e3;
176
178
  this.lastActivityTime = Date.now();
@@ -183,6 +185,7 @@ class SessionManager {
183
185
  this.eventCount = session.eventCount;
184
186
  this.adClickIDs = session.adClickIDs || {};
185
187
  this.landingPage = session.landingPage;
188
+ this.linkAttribution = session.linkAttribution || {};
186
189
  logger.debug("Session loaded:", session);
187
190
  } else {
188
191
  this.sessionId = this.createNewSession();
@@ -228,7 +231,8 @@ class SessionManager {
228
231
  lastActivityTime: this.lastActivityTime,
229
232
  eventCount: this.eventCount,
230
233
  adClickIDs: this.adClickIDs,
231
- landingPage: this.landingPage
234
+ landingPage: this.landingPage,
235
+ linkAttribution: this.linkAttribution
232
236
  };
233
237
  this.storage.set("session", JSON.stringify(sessionData));
234
238
  }
@@ -293,7 +297,8 @@ class SessionManager {
293
297
  lastActivityTime: this.lastActivityTime,
294
298
  eventCount: this.eventCount,
295
299
  adClickIDs: this.adClickIDs,
296
- landingPage: this.landingPage
300
+ landingPage: this.landingPage,
301
+ linkAttribution: this.linkAttribution
297
302
  };
298
303
  }
299
304
  setAdClickIDs(adClickIDs, landingPage) {
@@ -334,6 +339,41 @@ class SessionManager {
334
339
  getLandingPage() {
335
340
  return this.landingPage;
336
341
  }
342
+ /**
343
+ * Record the landing's Aegis link attribution. LAST touch: a new `aegis_code`
344
+ * replaces the previous set whole — never merged, because the fields are the
345
+ * attributes of ONE link and a mix of two links describes neither.
346
+ * Session + durable (30d, the server's short-link attribution window) so a
347
+ * checkout in a later session can still name the send that brought them.
348
+ */
349
+ setLinkAttribution(attribution) {
350
+ if (!attribution.aegis_code) return;
351
+ this.linkAttribution = { ...attribution };
352
+ this.persistSession();
353
+ try {
354
+ this.storage.set(
355
+ "link_attribution",
356
+ JSON.stringify(this.linkAttribution),
357
+ LINK_ATTRIBUTION_DURABLE_DAYS
358
+ );
359
+ } catch {
360
+ }
361
+ }
362
+ getLinkAttribution() {
363
+ if (!this.linkAttribution.aegis_code) {
364
+ try {
365
+ const durable = this.storage.get("link_attribution");
366
+ if (durable) {
367
+ const parsed = JSON.parse(durable);
368
+ if (parsed && parsed.aegis_code) {
369
+ this.linkAttribution = parsed;
370
+ }
371
+ }
372
+ } catch {
373
+ }
374
+ }
375
+ return this.linkAttribution;
376
+ }
337
377
  destroy() {
338
378
  if (this.checkInterval) {
339
379
  clearInterval(this.checkInterval);
@@ -341,6 +381,97 @@ class SessionManager {
341
381
  }
342
382
  }
343
383
  }
384
+ const FBP_COOKIE = "_fbp";
385
+ const FBC_COOKIE = "_fbc";
386
+ const COOKIE_LIFETIME_DAYS = 90;
387
+ function readCookie(name) {
388
+ if (typeof document === "undefined") return null;
389
+ try {
390
+ const target = `${name}=`;
391
+ const parts = document.cookie.split(";");
392
+ for (let i = 0; i < parts.length; i++) {
393
+ let c = parts[i];
394
+ while (c.charAt(0) === " ") c = c.substring(1);
395
+ if (c.indexOf(target) === 0) {
396
+ return decodeURIComponent(c.substring(target.length));
397
+ }
398
+ }
399
+ } catch {
400
+ }
401
+ return null;
402
+ }
403
+ function writeCookie(name, value, days) {
404
+ if (typeof document === "undefined" || typeof window === "undefined") return;
405
+ try {
406
+ const expiry = new Date(Date.now() + days * 24 * 60 * 60 * 1e3).toUTCString();
407
+ const domain = getRootDomain();
408
+ const isHttps = window.location.protocol === "https:";
409
+ let cookieStr = `${name}=${encodeURIComponent(value)};expires=${expiry};path=/;SameSite=Lax`;
410
+ if (isHttps) cookieStr += ";Secure";
411
+ if (domain) cookieStr += `;domain=${domain}`;
412
+ document.cookie = cookieStr;
413
+ } catch {
414
+ }
415
+ }
416
+ function getRootDomain() {
417
+ if (typeof window === "undefined") return null;
418
+ const host = window.location.hostname;
419
+ if (!host || host === "localhost") return null;
420
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(host)) return null;
421
+ const parts = host.split(".");
422
+ if (parts.length < 2) return null;
423
+ return "." + parts.slice(-2).join(".");
424
+ }
425
+ function subdomainIndex() {
426
+ if (typeof window === "undefined") return 1;
427
+ const host = window.location.hostname;
428
+ if (!host || host === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(host)) return 1;
429
+ const parts = host.split(".");
430
+ return Math.max(1, parts.length - 1);
431
+ }
432
+ function randomToken() {
433
+ const n = Math.floor(Math.random() * 1e10);
434
+ return n.toString().padStart(10, "0");
435
+ }
436
+ function ensureFbp() {
437
+ const existing = readCookie(FBP_COOKIE);
438
+ if (existing) return existing;
439
+ const fbp = `fb.${subdomainIndex()}.${Date.now()}.${randomToken()}`;
440
+ writeCookie(FBP_COOKIE, fbp, COOKIE_LIFETIME_DAYS);
441
+ return fbp;
442
+ }
443
+ function ensureFbc() {
444
+ const existing = readCookie(FBC_COOKIE);
445
+ if (existing) return existing;
446
+ const fbclid = readFbclidFromUrl();
447
+ if (!fbclid) return null;
448
+ const fbc = `fb.${subdomainIndex()}.${Date.now()}.${fbclid}`;
449
+ writeCookie(FBC_COOKIE, fbc, COOKIE_LIFETIME_DAYS);
450
+ return fbc;
451
+ }
452
+ function readFbclidFromUrl() {
453
+ if (typeof window === "undefined") return null;
454
+ try {
455
+ return new URLSearchParams(window.location.search).get("fbclid");
456
+ } catch {
457
+ return null;
458
+ }
459
+ }
460
+ function captureMetaCookies() {
461
+ return {
462
+ fbp: ensureFbp(),
463
+ fbc: ensureFbc(),
464
+ fbclid: readFbclidFromUrl()
465
+ };
466
+ }
467
+ function redactMetaCookies(event) {
468
+ const props = event == null ? void 0 : event.properties;
469
+ if (!props || props.fbc === void 0 && props.fbp === void 0) return event;
470
+ const masked = { ...props };
471
+ if (masked.fbc !== void 0) masked.fbc = "[redacted]";
472
+ if (masked.fbp !== void 0) masked.fbp = "[redacted]";
473
+ return { ...event, properties: masked };
474
+ }
344
475
  class EventQueue {
345
476
  constructor(config, transport, storage) {
346
477
  this.buffer = [];
@@ -428,7 +559,7 @@ class EventQueue {
428
559
  return;
429
560
  }
430
561
  this.buffer.push(event);
431
- logger.debug("Event queued:", event.type, event);
562
+ logger.debug("Event queued:", event.type, redactMetaCookies(event));
432
563
  if (this.buffer.length >= this.config.batchSize) {
433
564
  this.flush();
434
565
  }
@@ -980,6 +1111,31 @@ class Storage {
980
1111
  }
981
1112
  }
982
1113
  }
1114
+ const CREDENTIAL_URL_PARAMS = [
1115
+ "t",
1116
+ "k",
1117
+ "code",
1118
+ "session_code",
1119
+ "aegis_pair",
1120
+ "u",
1121
+ // The Shopify CarrierService callback token (2026-09-18). Server-to-server
1122
+ // today, so the SDK never sees one — it is here because the two lists are
1123
+ // ONE rule, and a list that is only correct for today's callers is not.
1124
+ "carrier_token"
1125
+ ];
1126
+ const REDACTED = "REDACTED";
1127
+ const CREDENTIAL_PARAM_PATTERN = new RegExp(
1128
+ `([?&])(${CREDENTIAL_URL_PARAMS.join("|")})=([^&#\\s"'<>]+)`,
1129
+ "g"
1130
+ );
1131
+ function redactUrl(value) {
1132
+ if (typeof value !== "string" || value.length === 0) return value;
1133
+ try {
1134
+ return value.replace(CREDENTIAL_PARAM_PATTERN, `$1$2=${REDACTED}`);
1135
+ } catch {
1136
+ return value;
1137
+ }
1138
+ }
983
1139
  function parseUTMParameters(url) {
984
1140
  try {
985
1141
  const searchParams = url ? new URL(url).searchParams : new URLSearchParams(window.location.search);
@@ -1010,6 +1166,8 @@ function parseAdClickIDs(url) {
1010
1166
  const adClickIDs = {};
1011
1167
  const clickIDKeys = [
1012
1168
  "gclid",
1169
+ "gbraid",
1170
+ "wbraid",
1013
1171
  "fbclid",
1014
1172
  "msclkid",
1015
1173
  "ctwa_clid",
@@ -1031,6 +1189,30 @@ function parseAdClickIDs(url) {
1031
1189
  function hasAdClickIDs(adClickIDs) {
1032
1190
  return Object.keys(adClickIDs).length > 0;
1033
1191
  }
1192
+ const LINK_ATTRIBUTION_KEYS = [
1193
+ "aegis_code",
1194
+ "aegis_campaign",
1195
+ "aegis_journey",
1196
+ "aegis_playbook",
1197
+ "aegis_button"
1198
+ ];
1199
+ function parseLinkAttribution(url) {
1200
+ try {
1201
+ const searchParams = url ? new URL(url).searchParams : new URLSearchParams(window.location.search);
1202
+ if (!searchParams.get("aegis_code")) return {};
1203
+ const attribution = {};
1204
+ LINK_ATTRIBUTION_KEYS.forEach((key) => {
1205
+ const value = searchParams.get(key);
1206
+ if (value) {
1207
+ attribution[key] = value;
1208
+ }
1209
+ });
1210
+ return attribution;
1211
+ } catch (error) {
1212
+ console.warn("[Active Reach URL Parser] parseLinkAttribution failed:", error);
1213
+ return {};
1214
+ }
1215
+ }
1034
1216
  function buildContext(config, session) {
1035
1217
  const utm = config.capture_utm ? parseUTMParameters() : void 0;
1036
1218
  const device = detectDevice();
@@ -1038,18 +1220,22 @@ function buildContext(config, session) {
1038
1220
  const browser = detectBrowser();
1039
1221
  const network = detectNetworkInfo();
1040
1222
  const adClickIDs = session == null ? void 0 : session.getAdClickIDs();
1041
- const landingPage = session == null ? void 0 : session.getLandingPage();
1223
+ const storedLandingPage = session == null ? void 0 : session.getLandingPage();
1224
+ const landingPage = storedLandingPage ? redactUrl(storedLandingPage) : void 0;
1042
1225
  return {
1043
1226
  library: {
1044
1227
  name: "@active-reach/web-sdk",
1045
1228
  version: "1.0.0"
1046
1229
  },
1230
+ // Credential params (`k`, `t`, `code`, ...) keep their key and lose their
1231
+ // value here, where the URL enters the page object. The referrer is
1232
+ // redacted too: the previous page can be our own order/landing link.
1047
1233
  page: {
1048
1234
  path: window.location.pathname,
1049
- referrer: config.capture_referrer ? document.referrer : "",
1050
- search: window.location.search,
1235
+ referrer: config.capture_referrer ? redactUrl(document.referrer) : "",
1236
+ search: redactUrl(window.location.search),
1051
1237
  title: document.title,
1052
- url: window.location.href,
1238
+ url: redactUrl(window.location.href),
1053
1239
  hash: window.location.hash
1054
1240
  },
1055
1241
  userAgent: navigator.userAgent,
@@ -1525,96 +1711,13 @@ class ConsentManager {
1525
1711
  updateGoogleConsent();
1526
1712
  }
1527
1713
  }
1528
- const FBP_COOKIE = "_fbp";
1529
- const FBC_COOKIE = "_fbc";
1530
- const COOKIE_LIFETIME_DAYS = 90;
1531
- function readCookie(name) {
1532
- if (typeof document === "undefined") return null;
1533
- try {
1534
- const target = `${name}=`;
1535
- const parts = document.cookie.split(";");
1536
- for (let i = 0; i < parts.length; i++) {
1537
- let c = parts[i];
1538
- while (c.charAt(0) === " ") c = c.substring(1);
1539
- if (c.indexOf(target) === 0) {
1540
- return decodeURIComponent(c.substring(target.length));
1541
- }
1542
- }
1543
- } catch {
1544
- }
1545
- return null;
1546
- }
1547
- function writeCookie(name, value, days) {
1548
- if (typeof document === "undefined" || typeof window === "undefined") return;
1549
- try {
1550
- const expiry = new Date(Date.now() + days * 24 * 60 * 60 * 1e3).toUTCString();
1551
- const domain = getRootDomain();
1552
- const isHttps = window.location.protocol === "https:";
1553
- let cookieStr = `${name}=${encodeURIComponent(value)};expires=${expiry};path=/;SameSite=Lax`;
1554
- if (isHttps) cookieStr += ";Secure";
1555
- if (domain) cookieStr += `;domain=${domain}`;
1556
- document.cookie = cookieStr;
1557
- } catch {
1558
- }
1559
- }
1560
- function getRootDomain() {
1561
- if (typeof window === "undefined") return null;
1562
- const host = window.location.hostname;
1563
- if (!host || host === "localhost") return null;
1564
- if (/^\d+\.\d+\.\d+\.\d+$/.test(host)) return null;
1565
- const parts = host.split(".");
1566
- if (parts.length < 2) return null;
1567
- return "." + parts.slice(-2).join(".");
1568
- }
1569
- function subdomainIndex() {
1570
- if (typeof window === "undefined") return 1;
1571
- const host = window.location.hostname;
1572
- if (!host || host === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(host)) return 1;
1573
- const parts = host.split(".");
1574
- return Math.max(1, parts.length - 1);
1575
- }
1576
- function randomToken() {
1577
- const n = Math.floor(Math.random() * 1e10);
1578
- return n.toString().padStart(10, "0");
1579
- }
1580
- function ensureFbp() {
1581
- const existing = readCookie(FBP_COOKIE);
1582
- if (existing) return existing;
1583
- const fbp = `fb.${subdomainIndex()}.${Date.now()}.${randomToken()}`;
1584
- writeCookie(FBP_COOKIE, fbp, COOKIE_LIFETIME_DAYS);
1585
- return fbp;
1586
- }
1587
- function ensureFbc() {
1588
- const existing = readCookie(FBC_COOKIE);
1589
- if (existing) return existing;
1590
- const fbclid = readFbclidFromUrl();
1591
- if (!fbclid) return null;
1592
- const fbc = `fb.${subdomainIndex()}.${Date.now()}.${fbclid}`;
1593
- writeCookie(FBC_COOKIE, fbc, COOKIE_LIFETIME_DAYS);
1594
- return fbc;
1595
- }
1596
- function readFbclidFromUrl() {
1597
- if (typeof window === "undefined") return null;
1598
- try {
1599
- return new URLSearchParams(window.location.search).get("fbclid");
1600
- } catch {
1601
- return null;
1602
- }
1603
- }
1604
- function captureMetaCookies() {
1605
- return {
1606
- fbp: ensureFbp(),
1607
- fbc: ensureFbc(),
1608
- fbclid: readFbclidFromUrl()
1609
- };
1610
- }
1611
1714
  function sumItems(products) {
1612
1715
  return products.reduce((sum, p) => sum + (p.quantity ?? 1), 0);
1613
1716
  }
1614
1717
  function mapProducts(products) {
1615
1718
  return products.map((p) => ({
1616
1719
  product_id: p.product_id,
1617
- sku: p.sku ?? p.product_id,
1720
+ sku: p.sku,
1618
1721
  name: p.name,
1619
1722
  price: p.price,
1620
1723
  quantity: p.quantity ?? 1,
@@ -1653,7 +1756,7 @@ class EcommerceTracker {
1653
1756
  productViewed(product) {
1654
1757
  this.aegis.track("product_viewed", {
1655
1758
  product_id: product.product_id,
1656
- sku: product.sku ?? product.product_id,
1759
+ sku: product.sku,
1657
1760
  name: product.name,
1658
1761
  price: product.price,
1659
1762
  currency: product.currency ?? "INR",
@@ -1666,17 +1769,29 @@ class EcommerceTracker {
1666
1769
  });
1667
1770
  }
1668
1771
  productListViewed(list) {
1772
+ const products = mapProducts(list.products);
1669
1773
  this.aegis.track("product_list_viewed", {
1670
1774
  list_id: list.list_id,
1671
1775
  list_name: list.list_name,
1672
1776
  category: list.category,
1673
- products: mapProducts(list.products)
1777
+ products,
1778
+ // `items` as well as `products` — a THIRD spelling of the same fact was
1779
+ // one spelling too many. Every product reader in the platform looks for
1780
+ // `items` (or `line_items`, the Shopify shape); this event alone said
1781
+ // `products`, so a list view showed N products and contributed nothing to
1782
+ // any of them. On ST that was 4 views recorded as `not measured`, and it
1783
+ // is the same defect that made every Shopify order invisible.
1784
+ //
1785
+ // `products` is kept so nothing already reading it breaks. mapProducts
1786
+ // already emits product_id / sku / name / price / quantity, which is
1787
+ // exactly the canonical item shape — no second mapping to drift.
1788
+ items: products
1674
1789
  });
1675
1790
  }
1676
1791
  productClicked(product, source) {
1677
1792
  this.aegis.track("product_clicked", {
1678
1793
  product_id: product.product_id,
1679
- sku: product.sku ?? product.product_id,
1794
+ sku: product.sku,
1680
1795
  name: product.name,
1681
1796
  price: product.price,
1682
1797
  currency: product.currency ?? "INR",
@@ -1691,7 +1806,7 @@ class EcommerceTracker {
1691
1806
  productImpressed(product, source) {
1692
1807
  this.aegis.track("product_impression", {
1693
1808
  product_id: product.product_id,
1694
- sku: product.sku ?? product.product_id,
1809
+ sku: product.sku,
1695
1810
  name: product.name,
1696
1811
  price: product.price,
1697
1812
  currency: product.currency ?? "INR",
@@ -1720,7 +1835,7 @@ class EcommerceTracker {
1720
1835
  const qty = product.quantity ?? 1;
1721
1836
  this.aegis.track("cart_item_added", {
1722
1837
  product_id: product.product_id,
1723
- sku: product.sku ?? product.product_id,
1838
+ sku: product.sku,
1724
1839
  name: product.name,
1725
1840
  price: product.price,
1726
1841
  quantity: qty,
@@ -1739,7 +1854,7 @@ class EcommerceTracker {
1739
1854
  const qty = product.quantity ?? 1;
1740
1855
  this.aegis.track("cart_item_removed", {
1741
1856
  product_id: product.product_id,
1742
- sku: product.sku ?? product.product_id,
1857
+ sku: product.sku,
1743
1858
  name: product.name,
1744
1859
  price: product.price,
1745
1860
  quantity: qty,
@@ -1834,7 +1949,7 @@ class EcommerceTracker {
1834
1949
  wishlist_id: wishlist.wishlist_id,
1835
1950
  wishlist_name: wishlist.wishlist_name,
1836
1951
  product_id: wishlist.product.product_id,
1837
- sku: wishlist.product.sku ?? wishlist.product.product_id,
1952
+ sku: wishlist.product.sku,
1838
1953
  name: wishlist.product.name,
1839
1954
  price: wishlist.product.price,
1840
1955
  variant_id: wishlist.product.variant_id
@@ -1845,7 +1960,7 @@ class EcommerceTracker {
1845
1960
  wishlist_id: wishlist.wishlist_id,
1846
1961
  wishlist_name: wishlist.wishlist_name,
1847
1962
  product_id: wishlist.product.product_id,
1848
- sku: wishlist.product.sku ?? wishlist.product.product_id,
1963
+ sku: wishlist.product.sku,
1849
1964
  name: wishlist.product.name,
1850
1965
  price: wishlist.product.price,
1851
1966
  variant_id: wishlist.product.variant_id
@@ -1862,7 +1977,7 @@ class EcommerceTracker {
1862
1977
  productWaitlisted(waitlist) {
1863
1978
  this.aegis.track("product_waitlisted", {
1864
1979
  product_id: waitlist.product.product_id,
1865
- sku: waitlist.product.sku ?? waitlist.product.product_id,
1980
+ sku: waitlist.product.sku,
1866
1981
  variant_id: waitlist.product.variant_id,
1867
1982
  name: waitlist.product.name,
1868
1983
  price: waitlist.product.price,
@@ -2569,6 +2684,7 @@ class Aegis {
2569
2684
  if (currentUrl === this._lastPageUrl) return;
2570
2685
  this._lastPageUrl = currentUrl;
2571
2686
  setTimeout(() => {
2687
+ if (!this._popstateHandler) return;
2572
2688
  this.page();
2573
2689
  }, 50);
2574
2690
  };
@@ -2617,9 +2733,13 @@ class Aegis {
2617
2733
  if (!this.session) return;
2618
2734
  const adClickIDs = parseAdClickIDs();
2619
2735
  if (hasAdClickIDs(adClickIDs)) {
2620
- this.session.setAdClickIDs(adClickIDs, window.location.href);
2736
+ this.session.setAdClickIDs(adClickIDs, redactUrl(window.location.href));
2621
2737
  logger.info("Ad click IDs captured on page load:", adClickIDs);
2622
2738
  }
2739
+ const linkAttribution = parseLinkAttribution();
2740
+ if (linkAttribution.aegis_code) {
2741
+ this.session.setLinkAttribution(linkAttribution);
2742
+ }
2623
2743
  }
2624
2744
  async initializePlugins() {
2625
2745
  if (!this.config) return;
@@ -3028,26 +3148,38 @@ class Aegis {
3028
3148
  }
3029
3149
  const ctx = event.context;
3030
3150
  const props = event.properties;
3031
- if ((ctx == null ? void 0 : ctx.campaign) && props && !props.campaign_id) {
3151
+ if ((ctx == null ? void 0 : ctx.campaign) && props) {
3032
3152
  const utm = ctx.campaign;
3033
- if (utm.campaign) props.campaign_id = utm.campaign;
3034
- if (utm.source) props.campaign_source = utm.source;
3035
- if (utm.medium) props.campaign_medium = utm.medium;
3036
- if (utm.content) props.campaign_content = utm.content;
3037
- if (utm.term) props.campaign_term = utm.term;
3153
+ if (utm.source && !props.utm_source) props.utm_source = utm.source;
3154
+ if (utm.medium && !props.utm_medium) props.utm_medium = utm.medium;
3155
+ if (utm.campaign && !props.utm_campaign) props.utm_campaign = utm.campaign;
3156
+ if (utm.content && !props.utm_content) props.utm_content = utm.content;
3157
+ if (utm.term && !props.utm_term) props.utm_term = utm.term;
3038
3158
  }
3039
3159
  if ((ctx == null ? void 0 : ctx.adClickIDs) && props) {
3040
3160
  const clicks = ctx.adClickIDs;
3161
+ if (clicks.gclid && !props.gclid) props.gclid = clicks.gclid;
3162
+ if (clicks.gbraid && !props.gbraid) props.gbraid = clicks.gbraid;
3163
+ if (clicks.wbraid && !props.wbraid) props.wbraid = clicks.wbraid;
3164
+ if (clicks.fbclid && !props.fbclid) props.fbclid = clicks.fbclid;
3041
3165
  const clickId = clicks.gclid || clicks.fbclid || clicks.ctwa_clid || clicks.msclkid || clicks.ttclid;
3042
3166
  if (clickId && !props.campaign_click_id) props.campaign_click_id = clickId;
3043
3167
  }
3168
+ if (props && this.session) {
3169
+ const link = this.session.getLinkAttribution();
3170
+ if (link.aegis_code) {
3171
+ for (const [key, value] of Object.entries(link)) {
3172
+ if (value && !props[key]) props[key] = value;
3173
+ }
3174
+ }
3175
+ }
3044
3176
  if (props) {
3045
3177
  const meta = captureMetaCookies();
3046
3178
  if (meta.fbp && !props.fbp) props.fbp = meta.fbp;
3047
3179
  if (meta.fbc && !props.fbc) props.fbc = meta.fbc;
3048
3180
  if (meta.fbclid && !props.fbclid) props.fbclid = meta.fbclid;
3049
3181
  }
3050
- logger.debug("Capturing event:", event);
3182
+ logger.debug("Capturing event:", redactMetaCookies(event));
3051
3183
  const transformedEvent = await this.plugins.executeHookChain(
3052
3184
  "beforeEventCapture",
3053
3185
  event
@@ -3154,6 +3286,18 @@ class Aegis {
3154
3286
  if (!this.identity) return null;
3155
3287
  return this.identity.getUserId();
3156
3288
  }
3289
+ /**
3290
+ * The Aegis short-link attribution captured from this visitor's landing URL
3291
+ * (`aegis_code` + the link row's `aegis_campaign` / `aegis_journey` /
3292
+ * `aegis_playbook` / `aegis_button`), or `{}` when they did not arrive
3293
+ * through one. Hosts forward `aegis_code` with an order so the order can be
3294
+ * joined to the send that brought the shopper. Never contains `utm_*`, `k`,
3295
+ * `t` or `u`.
3296
+ */
3297
+ getLinkAttribution() {
3298
+ if (!this.session) return {};
3299
+ return { ...this.session.getLinkAttribution() };
3300
+ }
3157
3301
  /**
3158
3302
  * Fetch the identified contact's REDEEMABLE rewards — coupon codes the
3159
3303
  * storefront can auto-apply at checkout + loyalty points balance/tier.
@@ -3345,6 +3489,7 @@ export {
3345
3489
  Storage as S,
3346
3490
  UserNamespace as U,
3347
3491
  logger as l,
3348
- murmurhash3_x86_32 as m
3492
+ murmurhash3_x86_32 as m,
3493
+ redactUrl as r
3349
3494
  };
3350
- //# sourceMappingURL=analytics-Qyzk8JaB.mjs.map
3495
+ //# sourceMappingURL=analytics-DoHdxjjh.mjs.map