@cross-deck/web 1.11.2 → 1.12.1

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/react.cjs CHANGED
@@ -98,7 +98,7 @@ function typeMapForStatus(status) {
98
98
  }
99
99
 
100
100
  // src/_version.ts
101
- var SDK_VERSION = "1.11.2";
101
+ var SDK_VERSION = "1.12.1";
102
102
  var SDK_NAME = "@cross-deck/web";
103
103
 
104
104
  // src/http.ts
@@ -1588,6 +1588,7 @@ var AutoTracker = class {
1588
1588
  this.pageviewId = null;
1589
1589
  this.storage = opts?.storage ?? null;
1590
1590
  this.sessionKey = opts?.storageKey ?? SESSION_STORAGE_KEY;
1591
+ this.cookieStorage = opts?.cookieStorage ?? null;
1591
1592
  }
1592
1593
  install() {
1593
1594
  if (!isBrowserSafe()) return;
@@ -1739,9 +1740,74 @@ var AutoTracker = class {
1739
1740
  lastActivityAt: now,
1740
1741
  hiddenAt: null,
1741
1742
  endedSent: false,
1742
- acquisition: captureAcquisition()
1743
+ acquisition: this.resolveAcquisition()
1743
1744
  };
1744
1745
  }
1746
+ /**
1747
+ * Session acquisition, with FIRST-TOUCH survival across sessions.
1748
+ *
1749
+ * WHY: `captureAcquisition()` reads the CURRENT page. That is correct for a
1750
+ * fresh arrival, but a session boundary (30-min idle, or a return visit next
1751
+ * week) starts a new session — and a visitor returning directly has no utm_*
1752
+ * and no click id, so the acquisition that actually WON them was silently
1753
+ * replaced with empty. Someone who arrives from LinkedIn on Monday, returns
1754
+ * direct on Tuesday and signs up on Wednesday converted with no source at all.
1755
+ *
1756
+ * That is exactly the question the product exists to answer — "where did this
1757
+ * PAYING CUSTOMER come from" — so origin has to outlive the session that
1758
+ * captured it. We persist the first touch that carried a real signal and,
1759
+ * whenever a later session arrives with nothing, fall back to it.
1760
+ *
1761
+ * Deliberately NOT last-touch-wins: a real new campaign click overwrites
1762
+ * nothing, it simply records a newer touch for that session while the stored
1763
+ * first touch stays put. Write-once by design.
1764
+ */
1765
+ resolveAcquisition() {
1766
+ const current = captureAcquisition();
1767
+ if (!this.storage && !this.cookieStorage) return current;
1768
+ const key = `${this.sessionKey}_origin_first`;
1769
+ const readFirstTouch = () => {
1770
+ try {
1771
+ const v = this.storage?.getItem(key);
1772
+ if (v) return v;
1773
+ } catch {
1774
+ }
1775
+ try {
1776
+ return this.cookieStorage?.getItem(key) ?? null;
1777
+ } catch {
1778
+ return null;
1779
+ }
1780
+ };
1781
+ const writeFirstTouch = (v) => {
1782
+ try {
1783
+ this.storage?.setItem(key, v);
1784
+ } catch {
1785
+ }
1786
+ try {
1787
+ this.cookieStorage?.setItem(key, v);
1788
+ } catch {
1789
+ }
1790
+ };
1791
+ const hasSignal = (a) => Boolean(a.utm_source || a.utm_medium || a.utm_campaign || a.utm_content || a.utm_term || a.gclid || a.fbclid || a.msclkid || a.ttclid || a.li_fat_id || a.twclid);
1792
+ try {
1793
+ if (hasSignal(current)) {
1794
+ if (readFirstTouch() === null) writeFirstTouch(JSON.stringify(current));
1795
+ return current;
1796
+ }
1797
+ const raw = readFirstTouch();
1798
+ if (raw) {
1799
+ const stored = JSON.parse(raw);
1800
+ return {
1801
+ ...EMPTY_ACQUISITION,
1802
+ ...stored,
1803
+ // Referrer always describes THIS visit, never the remembered one.
1804
+ referrer: current.referrer
1805
+ };
1806
+ }
1807
+ } catch {
1808
+ }
1809
+ return current;
1810
+ }
1745
1811
  /**
1746
1812
  * Read the persisted session continuity record. Returns null on no
1747
1813
  * storage, no record, malformed JSON, or a record missing its required
@@ -4788,7 +4854,15 @@ var CrossdeckClient = class {
4788
4854
  // a visit survives full-page navigations (multi-page sites
4789
4855
  // re-install the SDK on every page) and honours the same consent
4790
4856
  // posture — MemoryStorage when identity persistence is off.
4791
- { storage: effectiveStorage, storageKey: opts.storagePrefix + "session" }
4857
+ // cookieStorage is the SAME registrable-domain cookie identity uses.
4858
+ // First-touch origin rides it so the campaign that won a visitor on the
4859
+ // marketing site is still known when they land on the app subdomain —
4860
+ // localStorage alone is per-origin and would lose it at exactly that hop.
4861
+ {
4862
+ storage: effectiveStorage,
4863
+ storageKey: opts.storagePrefix + "session",
4864
+ cookieStorage: cookieStore ?? void 0
4865
+ }
4792
4866
  );
4793
4867
  this.state.autoTracker = tracker;
4794
4868
  tracker.install();