@mparticle/web-sdk 2.79.3 → 2.80.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/mparticle.js CHANGED
@@ -204,7 +204,7 @@ var mParticle = (function () {
204
204
  Base64: Base64$1
205
205
  };
206
206
 
207
- var version = "2.79.3";
207
+ var version = "2.80.0";
208
208
 
209
209
  var Constants = {
210
210
  sdkVersion: version,
@@ -5605,90 +5605,101 @@ var mParticle = (function () {
5605
5605
  Base64CookieKeys = Constants.Base64CookieKeys,
5606
5606
  SDKv2NonMPIDCookieKeys = Constants.SDKv2NonMPIDCookieKeys,
5607
5607
  StorageNames = Constants.StorageNames;
5608
+ function getMParticleManager() {
5609
+ return window.mParticle;
5610
+ }
5608
5611
  function _Persistence(mpInstance) {
5609
5612
  var self = this;
5610
-
5611
5613
  // https://go.mparticle.com/work/SQDSDKS-5022
5612
5614
  this.useLocalStorage = function () {
5613
5615
  return !mpInstance._Store.SDKConfig.useCookieStorage && mpInstance._Store.isLocalStorageAvailable;
5614
5616
  };
5617
+ function setFirstRunFromExistingData(localStorageData, cookies) {
5618
+ if (!localStorageData && !cookies) {
5619
+ mpInstance._Store.isFirstRun = true;
5620
+ mpInstance._Store.mpid = 0;
5621
+ return;
5622
+ }
5623
+ mpInstance._Store.isFirstRun = false;
5624
+ }
5625
+ function mergeStorageSources(localStorageData, cookies) {
5626
+ if (localStorageData && cookies) {
5627
+ // https://go.mparticle.com/work/SQDSDKS-6047
5628
+ return extend(false, localStorageData, cookies);
5629
+ }
5630
+ return localStorageData || cookies;
5631
+ }
5632
+ // For migrating from localStorage to cookies -- If an instance switches from
5633
+ // localStorage to cookies, then no mParticle cookie exists yet and there is
5634
+ // localStorage. Get the localStorage, set them to cookies, then delete the
5635
+ // localStorage item.
5636
+ function migrateLocalStorageToCookies(storage, localStorageData, cookies) {
5637
+ var allData;
5638
+ if (localStorageData) {
5639
+ allData = mergeStorageSources(localStorageData, cookies);
5640
+ storage.removeItem(mpInstance._Store.storageName);
5641
+ } else if (cookies) {
5642
+ allData = cookies;
5643
+ }
5644
+ self.storeDataInMemory(allData);
5645
+ return allData;
5646
+ }
5647
+ // For migrating from cookie to localStorage -- If an instance is newly
5648
+ // switching from cookies to localStorage, then no mParticle localStorage
5649
+ // exists yet and there are cookies. Get the cookies, set them to
5650
+ // localStorage, then delete the cookies.
5651
+ function migrateCookiesToLocalStorage(localStorageData, cookies) {
5652
+ if (!cookies) {
5653
+ self.storeDataInMemory(localStorageData);
5654
+ return;
5655
+ }
5656
+ var allData = mergeStorageSources(localStorageData, cookies);
5657
+ self.storeDataInMemory(allData);
5658
+ self.expireCookies(mpInstance._Store.storageName);
5659
+ return allData;
5660
+ }
5661
+ function loadPersistenceIntoMemory(localStorageData, cookies) {
5662
+ if (!mpInstance._Store.isLocalStorageAvailable) {
5663
+ self.storeDataInMemory(cookies);
5664
+ return;
5665
+ }
5666
+ if (mpInstance._Store.SDKConfig.useCookieStorage) {
5667
+ return migrateLocalStorageToCookies(window.localStorage, localStorageData, cookies);
5668
+ }
5669
+ return migrateCookiesToLocalStorage(localStorageData, cookies);
5670
+ }
5671
+ function copyNonCurrentUserMpids(allData) {
5672
+ for (var key in allData) {
5673
+ if (allData.hasOwnProperty(key) && !SDKv2NonMPIDCookieKeys[key]) {
5674
+ mpInstance._Store.nonCurrentUserMPIDs[key] = allData[key];
5675
+ }
5676
+ }
5677
+ }
5678
+ function clearCorruptStorage() {
5679
+ if (self.useLocalStorage() && mpInstance._Store.isLocalStorageAvailable) {
5680
+ localStorage.removeItem(mpInstance._Store.storageName);
5681
+ return;
5682
+ }
5683
+ self.expireCookies(mpInstance._Store.storageName);
5684
+ }
5615
5685
  this.initializeStorage = function () {
5616
5686
  try {
5617
- var storage,
5618
- localStorageData = self.getLocalStorage(),
5619
- cookies = self.getCookie(),
5620
- allData;
5621
-
5687
+ var localStorageData = self.getLocalStorage();
5688
+ var cookies = self.getCookie();
5622
5689
  // https://go.mparticle.com/work/SQDSDKS-6045
5623
- // Determine if there is any data in cookies or localStorage to figure out if it is the first time the browser is loading mParticle
5624
- if (!localStorageData && !cookies) {
5625
- mpInstance._Store.isFirstRun = true;
5626
- mpInstance._Store.mpid = 0;
5627
- } else {
5628
- mpInstance._Store.isFirstRun = false;
5629
- }
5630
-
5690
+ setFirstRunFromExistingData(localStorageData, cookies);
5631
5691
  // https://go.mparticle.com/work/SQDSDKS-6045
5632
5692
  if (!mpInstance._Store.isLocalStorageAvailable) {
5633
5693
  mpInstance._Store.SDKConfig.useCookieStorage = true;
5634
5694
  }
5635
-
5636
- // https://go.mparticle.com/work/SQDSDKS-6046
5637
- if (mpInstance._Store.isLocalStorageAvailable) {
5638
- storage = window.localStorage;
5639
- if (mpInstance._Store.SDKConfig.useCookieStorage) {
5640
- // For migrating from localStorage to cookies -- If an instance switches from localStorage to cookies, then
5641
- // no mParticle cookie exists yet and there is localStorage. Get the localStorage, set them to cookies, then delete the localStorage item.
5642
- if (localStorageData) {
5643
- if (cookies) {
5644
- // https://go.mparticle.com/work/SQDSDKS-6047
5645
- allData = extend(false, localStorageData, cookies);
5646
- } else {
5647
- allData = localStorageData;
5648
- }
5649
- storage.removeItem(mpInstance._Store.storageName);
5650
- } else if (cookies) {
5651
- allData = cookies;
5652
- }
5653
- self.storeDataInMemory(allData);
5654
- } else {
5655
- // For migrating from cookie to localStorage -- If an instance is newly switching from cookies to localStorage, then
5656
- // no mParticle localStorage exists yet and there are cookies. Get the cookies, set them to localStorage, then delete the cookies.
5657
- if (cookies) {
5658
- if (localStorageData) {
5659
- // https://go.mparticle.com/work/SQDSDKS-6047
5660
- allData = extend(false, localStorageData, cookies);
5661
- } else {
5662
- allData = cookies;
5663
- }
5664
- self.storeDataInMemory(allData);
5665
- self.expireCookies(mpInstance._Store.storageName);
5666
- } else {
5667
- self.storeDataInMemory(localStorageData);
5668
- }
5669
- }
5670
- } else {
5671
- self.storeDataInMemory(cookies);
5672
- }
5673
-
5674
5695
  // https://go.mparticle.com/work/SQDSDKS-6046
5675
- // Stores all non-current user MPID information into the store
5676
- for (var key in allData) {
5677
- if (allData.hasOwnProperty(key)) {
5678
- if (!SDKv2NonMPIDCookieKeys[key]) {
5679
- mpInstance._Store.nonCurrentUserMPIDs[key] = allData[key];
5680
- }
5681
- }
5682
- }
5696
+ var allData = loadPersistenceIntoMemory(localStorageData, cookies);
5697
+ copyNonCurrentUserMpids(allData);
5683
5698
  self.update();
5684
5699
  } catch (e) {
5685
5700
  // If cookies or local storage is corrupt, we want to remove it
5686
5701
  // so that in the future, initializeStorage will work
5687
- if (self.useLocalStorage() && mpInstance._Store.isLocalStorageAvailable) {
5688
- localStorage.removeItem(mpInstance._Store.storageName);
5689
- } else {
5690
- self.expireCookies(mpInstance._Store.storageName);
5691
- }
5702
+ clearCorruptStorage();
5692
5703
  mpInstance.Logger.error('Error initializing storage: ' + e);
5693
5704
  }
5694
5705
  };
@@ -5700,86 +5711,83 @@ var mParticle = (function () {
5700
5711
  self.setLocalStorage();
5701
5712
  }
5702
5713
  };
5703
-
5714
+ function applyEmptyPersistenceDefaults() {
5715
+ mpInstance.Logger.verbose(Messages$4.InformationMessages.CookieNotFound);
5716
+ mpInstance._Store.clientId = mpInstance._Store.clientId || mpInstance._Helpers.generateUniqueId();
5717
+ mpInstance._Store.deviceId = mpInstance._Store.deviceId || mpInstance._Helpers.generateUniqueId();
5718
+ }
5719
+ function hydrateStoreFromPersistence(obj, currentMPID) {
5720
+ // Set MPID first, then change object to match MPID data
5721
+ if (currentMPID) {
5722
+ mpInstance._Store.mpid = currentMPID;
5723
+ } else {
5724
+ mpInstance._Store.mpid = obj.cu || 0;
5725
+ }
5726
+ obj.gs = obj.gs || {};
5727
+ mpInstance._Store.sessionId = obj.gs.sid || mpInstance._Store.sessionId;
5728
+ mpInstance._Store.isEnabled = typeof obj.gs.ie !== 'undefined' ? obj.gs.ie : mpInstance._Store.isEnabled;
5729
+ mpInstance._Store.sessionAttributes = obj.gs.sa || mpInstance._Store.sessionAttributes;
5730
+ mpInstance._Store.localSessionAttributes = obj.gs.lsa || mpInstance._Store.localSessionAttributes;
5731
+ mpInstance._Store.serverSettings = obj.gs.ss || mpInstance._Store.serverSettings;
5732
+ mpInstance._Store.devToken = mpInstance._Store.devToken || obj.gs.dt;
5733
+ mpInstance._Store.SDKConfig.appVersion = mpInstance._Store.SDKConfig.appVersion || obj.gs.av;
5734
+ mpInstance._Store.clientId = obj.gs.cgid || mpInstance._Store.clientId || mpInstance._Helpers.generateUniqueId();
5735
+ // For most persistence values, we prioritize localstorage/cookie values over
5736
+ // Store. However, we allow device ID to be overriden via a config value and
5737
+ // thus the priority of the deviceId value is
5738
+ // 1. value passed via config.deviceId
5739
+ // 2. previous value in persistence
5740
+ // 3. generate new guid
5741
+ mpInstance._Store.deviceId = mpInstance._Store.deviceId || obj.gs.das || mpInstance._Helpers.generateUniqueId();
5742
+ mpInstance._Store.integrationAttributes = obj.gs.ia || {};
5743
+ mpInstance._Store.context = obj.gs.c || mpInstance._Store.context;
5744
+ mpInstance._Store.currentSessionMPIDs = obj.gs.csm || mpInstance._Store.currentSessionMPIDs;
5745
+ mpInstance._Store.isLoggedIn = obj.l === true;
5746
+ if (obj.gs.les) {
5747
+ mpInstance._Store.dateLastEventSent = new Date(obj.gs.les);
5748
+ }
5749
+ mpInstance._Store.sessionStartDate = obj.gs.ssd ? new Date(obj.gs.ssd) : new Date();
5750
+ if (currentMPID) {
5751
+ obj = obj[currentMPID];
5752
+ } else {
5753
+ obj = obj[obj.cu];
5754
+ }
5755
+ }
5704
5756
  // https://go.mparticle.com/work/SQDSDKS-6045
5705
5757
  this.storeDataInMemory = function (obj, currentMPID) {
5706
5758
  try {
5707
5759
  if (!obj) {
5708
- mpInstance.Logger.verbose(Messages$4.InformationMessages.CookieNotFound);
5709
- mpInstance._Store.clientId = mpInstance._Store.clientId || mpInstance._Helpers.generateUniqueId();
5710
- mpInstance._Store.deviceId = mpInstance._Store.deviceId || mpInstance._Helpers.generateUniqueId();
5711
- } else {
5712
- // Set MPID first, then change object to match MPID data
5713
- if (currentMPID) {
5714
- mpInstance._Store.mpid = currentMPID;
5715
- } else {
5716
- mpInstance._Store.mpid = obj.cu || 0;
5717
- }
5718
- obj.gs = obj.gs || {};
5719
- mpInstance._Store.sessionId = obj.gs.sid || mpInstance._Store.sessionId;
5720
- mpInstance._Store.isEnabled = typeof obj.gs.ie !== 'undefined' ? obj.gs.ie : mpInstance._Store.isEnabled;
5721
- mpInstance._Store.sessionAttributes = obj.gs.sa || mpInstance._Store.sessionAttributes;
5722
- mpInstance._Store.localSessionAttributes = obj.gs.lsa || mpInstance._Store.localSessionAttributes;
5723
- mpInstance._Store.serverSettings = obj.gs.ss || mpInstance._Store.serverSettings;
5724
- mpInstance._Store.devToken = mpInstance._Store.devToken || obj.gs.dt;
5725
- mpInstance._Store.SDKConfig.appVersion = mpInstance._Store.SDKConfig.appVersion || obj.gs.av;
5726
- mpInstance._Store.clientId = obj.gs.cgid || mpInstance._Store.clientId || mpInstance._Helpers.generateUniqueId();
5727
-
5728
- // For most persistence values, we prioritize localstorage/cookie values over
5729
- // Store. However, we allow device ID to be overriden via a config value and
5730
- // thus the priority of the deviceId value is
5731
- // 1. value passed via config.deviceId
5732
- // 2. previous value in persistence
5733
- // 3. generate new guid
5734
- mpInstance._Store.deviceId = mpInstance._Store.deviceId || obj.gs.das || mpInstance._Helpers.generateUniqueId();
5735
- mpInstance._Store.integrationAttributes = obj.gs.ia || {};
5736
- mpInstance._Store.context = obj.gs.c || mpInstance._Store.context;
5737
- mpInstance._Store.currentSessionMPIDs = obj.gs.csm || mpInstance._Store.currentSessionMPIDs;
5738
- mpInstance._Store.isLoggedIn = obj.l === true;
5739
- if (obj.gs.les) {
5740
- mpInstance._Store.dateLastEventSent = new Date(obj.gs.les);
5741
- }
5742
- if (obj.gs.ssd) {
5743
- mpInstance._Store.sessionStartDate = new Date(obj.gs.ssd);
5744
- } else {
5745
- mpInstance._Store.sessionStartDate = new Date();
5746
- }
5747
- if (currentMPID) {
5748
- obj = obj[currentMPID];
5749
- } else {
5750
- obj = obj[obj.cu];
5751
- }
5760
+ applyEmptyPersistenceDefaults();
5761
+ return;
5752
5762
  }
5763
+ hydrateStoreFromPersistence(obj, currentMPID);
5753
5764
  } catch (e) {
5754
5765
  mpInstance.Logger.error(Messages$4.ErrorMessages.CookieParseError);
5755
5766
  }
5756
5767
  };
5757
-
5758
5768
  // https://go.mparticle.com/work/SQDSDKS-5022
5759
5769
  this.determineLocalStorageAvailability = function (storage) {
5760
- var result;
5761
- if (window.mParticle && window.mParticle._forceNoLocalStorage) {
5762
- storage = undefined;
5770
+ var mParticleManager = getMParticleManager();
5771
+ if (mParticleManager === null || mParticleManager === void 0 ? void 0 : mParticleManager._forceNoLocalStorage) {
5772
+ return false;
5763
5773
  }
5764
5774
  try {
5765
5775
  storage.setItem('mparticle', 'test');
5766
- result = storage.getItem('mparticle') === 'test';
5776
+ var result = storage.getItem('mparticle') === 'test';
5767
5777
  storage.removeItem('mparticle');
5768
- return result && storage;
5778
+ return Boolean(result && storage);
5769
5779
  } catch (e) {
5770
5780
  return false;
5771
5781
  }
5772
5782
  };
5773
-
5774
5783
  // https://go.mparticle.com/work/SQDSDKS-6021
5775
5784
  this.setLocalStorage = function () {
5776
- var _mpInstance$_CookieCo;
5785
+ var _a;
5777
5786
  if (!mpInstance._Store.isLocalStorageAvailable) {
5778
5787
  return;
5779
5788
  }
5780
-
5781
5789
  // Block mprtcl-v4 localStorage when noFunctional is true
5782
- if ((_mpInstance$_CookieCo = mpInstance._CookieConsentManager) !== null && _mpInstance$_CookieCo !== void 0 && _mpInstance$_CookieCo.getNoFunctional()) {
5790
+ if ((_a = mpInstance._CookieConsentManager) === null || _a === void 0 ? void 0 : _a.getNoFunctional()) {
5783
5791
  return;
5784
5792
  }
5785
5793
  var key = mpInstance._Store.storageName,
@@ -5829,16 +5837,16 @@ var mParticle = (function () {
5829
5837
  if (!mpInstance._Store.isLocalStorageAvailable) {
5830
5838
  return null;
5831
5839
  }
5832
- var key = mpInstance._Store.storageName,
5833
- localStorageData = self.decodePersistence(window.localStorage.getItem(key)),
5834
- obj = {},
5835
- j;
5836
- if (localStorageData) {
5837
- localStorageData = JSON.parse(localStorageData);
5838
- for (j in localStorageData) {
5839
- if (localStorageData.hasOwnProperty(j)) {
5840
- obj[j] = localStorageData[j];
5841
- }
5840
+ var key = mpInstance._Store.storageName;
5841
+ var decodedPersistence = self.decodePersistence(window.localStorage.getItem(key));
5842
+ if (!decodedPersistence) {
5843
+ return null;
5844
+ }
5845
+ var parsedPersistence = JSON.parse(decodedPersistence);
5846
+ var obj = {};
5847
+ for (var key_1 in parsedPersistence) {
5848
+ if (parsedPersistence.hasOwnProperty(key_1)) {
5849
+ obj[key_1] = parsedPersistence[key_1];
5842
5850
  }
5843
5851
  }
5844
5852
  if (Object.keys(obj).length) {
@@ -5846,9 +5854,6 @@ var mParticle = (function () {
5846
5854
  }
5847
5855
  return null;
5848
5856
  };
5849
- function removeLocalStorage(localStorageName) {
5850
- localStorage.removeItem(localStorageName);
5851
- }
5852
5857
  this.expireCookies = function (cookieName) {
5853
5858
  var date = new Date(),
5854
5859
  expires,
@@ -5903,13 +5908,12 @@ var mParticle = (function () {
5903
5908
  return null;
5904
5909
  }
5905
5910
  };
5906
-
5907
5911
  // https://go.mparticle.com/work/SQDSDKS-5022
5908
5912
  // https://go.mparticle.com/work/SQDSDKS-6021
5909
5913
  this.setCookie = function () {
5910
- var _mpInstance$_CookieCo2;
5914
+ var _a;
5911
5915
  // Block mprtcl-v4 cookies when noFunctional is true
5912
- if ((_mpInstance$_CookieCo2 = mpInstance._CookieConsentManager) !== null && _mpInstance$_CookieCo2 !== void 0 && _mpInstance$_CookieCo2.getNoFunctional()) {
5916
+ if ((_a = mpInstance._CookieConsentManager) === null || _a === void 0 ? void 0 : _a.getNoFunctional()) {
5913
5917
  return;
5914
5918
  }
5915
5919
  var mpid,
@@ -5920,7 +5924,7 @@ var mParticle = (function () {
5920
5924
  var date = new Date(),
5921
5925
  key = mpInstance._Store.storageName,
5922
5926
  cookies = self.getCookie() || {},
5923
- expires = new Date(date.getTime() + mpInstance._Store.SDKConfig.cookieExpiration * 24 * 60 * 60 * 1000).toGMTString(),
5927
+ expires = new Date(date.getTime() + mpInstance._Store.SDKConfig.cookieExpiration * 24 * 60 * 60 * 1000).toUTCString(),
5924
5928
  cookieDomain,
5925
5929
  domain,
5926
5930
  encodedCookiesWithExpirationAndPath;
@@ -5947,7 +5951,6 @@ var mParticle = (function () {
5947
5951
  mpInstance.Logger.verbose(Messages$4.InformationMessages.CookieSet);
5948
5952
  window.document.cookie = encodeURIComponent(key) + '=' + encodedCookiesWithExpirationAndPath;
5949
5953
  };
5950
-
5951
5954
  /* This function determines if a cookie is greater than the configured maxCookieSize.
5952
5955
  - If it is, we remove an MPID and its associated UI/UA/CSD from the cookie.
5953
5956
  - Once removed, check size, and repeat.
@@ -5959,171 +5962,258 @@ var mParticle = (function () {
5959
5962
  b. Then remove MPIDs based on order in currentSessionMPIDs array, which
5960
5963
  stores MPIDs based on earliest login.
5961
5964
  */
5962
- this.reduceAndEncodePersistence = function (persistence, expires, domain, maxCookieSize) {
5963
- var encodedCookiesWithExpirationAndPath,
5964
- currentSessionMPIDs = persistence.gs.csm ? persistence.gs.csm : [];
5965
- // Comment 1 above
5966
- if (!currentSessionMPIDs.length) {
5967
- for (var key in persistence) {
5968
- if (persistence.hasOwnProperty(key)) {
5969
- encodedCookiesWithExpirationAndPath = createFullEncodedCookie(persistence, expires, domain);
5970
- if (encodedCookiesWithExpirationAndPath.length > maxCookieSize) {
5971
- if (!SDKv2NonMPIDCookieKeys[key] && key !== persistence.cu) {
5972
- delete persistence[key];
5973
- }
5974
- }
5975
- }
5965
+ function isEncodedCookieTooLarge(encoded, maxCookieSize) {
5966
+ return encoded.length > maxCookieSize;
5967
+ }
5968
+ function removeUnsessionedMpidsWhenOversized(persistence, expires, domain, maxCookieSize) {
5969
+ var encodedCookiesWithExpirationAndPath;
5970
+ for (var key in persistence) {
5971
+ if (!persistence.hasOwnProperty(key)) {
5972
+ continue;
5976
5973
  }
5977
- } else {
5978
- // Comment 2 above - First create an object of all MPIDs on the cookie
5979
- var MPIDsOnCookie = {};
5980
- for (var potentialMPID in persistence) {
5981
- if (persistence.hasOwnProperty(potentialMPID)) {
5982
- if (!SDKv2NonMPIDCookieKeys[potentialMPID] && potentialMPID !== persistence.cu) {
5983
- MPIDsOnCookie[potentialMPID] = 1;
5984
- }
5985
- }
5974
+ encodedCookiesWithExpirationAndPath = createFullEncodedCookie(persistence, expires, domain);
5975
+ if (!isEncodedCookieTooLarge(encodedCookiesWithExpirationAndPath, maxCookieSize)) {
5976
+ continue;
5986
5977
  }
5987
- // Comment 2a above
5988
- if (Object.keys(MPIDsOnCookie).length) {
5989
- for (var mpid in MPIDsOnCookie) {
5990
- encodedCookiesWithExpirationAndPath = createFullEncodedCookie(persistence, expires, domain);
5991
- if (encodedCookiesWithExpirationAndPath.length > maxCookieSize) {
5992
- if (MPIDsOnCookie.hasOwnProperty(mpid)) {
5993
- if (currentSessionMPIDs.indexOf(mpid) === -1) {
5994
- delete persistence[mpid];
5995
- }
5996
- }
5997
- }
5998
- }
5978
+ if (SDKv2NonMPIDCookieKeys[key] || key === persistence.cu) {
5979
+ continue;
5999
5980
  }
6000
- // Comment 2b above
6001
- for (var i = 0; i < currentSessionMPIDs.length; i++) {
6002
- encodedCookiesWithExpirationAndPath = createFullEncodedCookie(persistence, expires, domain);
6003
- if (encodedCookiesWithExpirationAndPath.length > maxCookieSize) {
6004
- var MPIDtoRemove = currentSessionMPIDs[i];
6005
- if (persistence[MPIDtoRemove]) {
6006
- mpInstance.Logger.verbose('Size of new encoded cookie is larger than maxCookieSize setting of ' + maxCookieSize + '. Removing from cookie the earliest logged in MPID containing: ' + JSON.stringify(persistence[MPIDtoRemove], 0, 2));
6007
- delete persistence[MPIDtoRemove];
6008
- } else {
6009
- mpInstance.Logger.error('Unable to save MPID data to cookies because the resulting encoded cookie is larger than the maxCookieSize setting of ' + maxCookieSize + '. We recommend using a maxCookieSize of 1500.');
6010
- }
6011
- } else {
6012
- break;
6013
- }
5981
+ delete persistence[key];
5982
+ }
5983
+ return encodedCookiesWithExpirationAndPath;
5984
+ }
5985
+ function collectRemovableMpids(persistence) {
5986
+ var MPIDsOnCookie = {};
5987
+ for (var potentialMPID in persistence) {
5988
+ if (!persistence.hasOwnProperty(potentialMPID)) {
5989
+ continue;
5990
+ }
5991
+ if (SDKv2NonMPIDCookieKeys[potentialMPID] || potentialMPID === persistence.cu) {
5992
+ continue;
5993
+ }
5994
+ MPIDsOnCookie[potentialMPID] = 1;
5995
+ }
5996
+ return MPIDsOnCookie;
5997
+ }
5998
+ function removeMpidsNotInCurrentSession(persistence, MPIDsOnCookie, currentSessionMPIDs, expires, domain, maxCookieSize) {
5999
+ var encodedCookiesWithExpirationAndPath;
6000
+ for (var mpid in MPIDsOnCookie) {
6001
+ encodedCookiesWithExpirationAndPath = createFullEncodedCookie(persistence, expires, domain);
6002
+ if (!isEncodedCookieTooLarge(encodedCookiesWithExpirationAndPath, maxCookieSize)) {
6003
+ continue;
6004
+ }
6005
+ if (!MPIDsOnCookie.hasOwnProperty(mpid)) {
6006
+ continue;
6007
+ }
6008
+ if (currentSessionMPIDs.indexOf(mpid) === -1) {
6009
+ delete persistence[mpid];
6010
+ }
6011
+ }
6012
+ return encodedCookiesWithExpirationAndPath;
6013
+ }
6014
+ function logAndRemoveOversizedMpid(persistence, MPIDtoRemove, maxCookieSize) {
6015
+ if (persistence[MPIDtoRemove]) {
6016
+ mpInstance.Logger.verbose('Size of new encoded cookie is larger than maxCookieSize setting of ' + maxCookieSize + '. Removing from cookie the earliest logged in MPID containing: ' + JSON.stringify(persistence[MPIDtoRemove], null, 2));
6017
+ delete persistence[MPIDtoRemove];
6018
+ return;
6019
+ }
6020
+ mpInstance.Logger.error('Unable to save MPID data to cookies because the resulting encoded cookie is larger than the maxCookieSize setting of ' + maxCookieSize + '. We recommend using a maxCookieSize of 1500.');
6021
+ }
6022
+ function removeCurrentSessionMpidsByAge(persistence, currentSessionMPIDs, expires, domain, maxCookieSize) {
6023
+ var encodedCookiesWithExpirationAndPath;
6024
+ for (var i = 0; i < currentSessionMPIDs.length; i++) {
6025
+ encodedCookiesWithExpirationAndPath = createFullEncodedCookie(persistence, expires, domain);
6026
+ if (!isEncodedCookieTooLarge(encodedCookiesWithExpirationAndPath, maxCookieSize)) {
6027
+ break;
6014
6028
  }
6029
+ logAndRemoveOversizedMpid(persistence, currentSessionMPIDs[i], maxCookieSize);
6015
6030
  }
6016
6031
  return encodedCookiesWithExpirationAndPath;
6032
+ }
6033
+ this.reduceAndEncodePersistence = function (persistence, expires, domain, maxCookieSize) {
6034
+ var currentSessionMPIDs = persistence.gs.csm ? persistence.gs.csm : [];
6035
+ if (!currentSessionMPIDs.length) {
6036
+ return removeUnsessionedMpidsWhenOversized(persistence, expires, domain, maxCookieSize);
6037
+ }
6038
+ var MPIDsOnCookie = collectRemovableMpids(persistence);
6039
+ if (Object.keys(MPIDsOnCookie).length) {
6040
+ removeMpidsNotInCurrentSession(persistence, MPIDsOnCookie, currentSessionMPIDs, expires, domain, maxCookieSize);
6041
+ }
6042
+ return removeCurrentSessionMpidsByAge(persistence, currentSessionMPIDs, expires, domain, maxCookieSize);
6017
6043
  };
6018
6044
  function createFullEncodedCookie(persistence, expires, domain) {
6019
6045
  return self.encodePersistence(JSON.stringify(persistence)) + ';expires=' + expires + ';path=/' + domain;
6020
6046
  }
6021
- this.findPrevCookiesBasedOnUI = function (identityApiData) {
6022
- var persistence = mpInstance._Persistence.getPersistence();
6047
+ function cookieUiMatchesRequestedIdentity(cookieUIs, requestedIdentityType, requestedValue) {
6048
+ for (var cookieUIType in cookieUIs) {
6049
+ if (requestedIdentityType === cookieUIType && requestedValue === cookieUIs[cookieUIType]) {
6050
+ return true;
6051
+ }
6052
+ }
6053
+ return false;
6054
+ }
6055
+ function findMpidForRequestedIdentity(persistence, requestedIdentityType, requestedValue) {
6023
6056
  var matchedUser;
6024
- if (identityApiData) {
6025
- for (var requestedIdentityType in identityApiData.userIdentities) {
6026
- if (persistence && Object.keys(persistence).length) {
6027
- for (var key in persistence) {
6028
- // any value in persistence that has an MPID key will be an MPID to search through
6029
- // other keys on the cookie are currentSessionMPIDs and currentMPID which should not be searched
6030
- if (persistence[key].mpid) {
6031
- var cookieUIs = persistence[key].ui;
6032
- for (var cookieUIType in cookieUIs) {
6033
- if (requestedIdentityType === cookieUIType && identityApiData.userIdentities[requestedIdentityType] === cookieUIs[cookieUIType]) {
6034
- matchedUser = key;
6035
- break;
6036
- }
6037
- }
6038
- }
6039
- }
6040
- }
6057
+ for (var key in persistence) {
6058
+ // any value in persistence that has an MPID key will be an MPID to search through
6059
+ // other keys on the cookie are currentSessionMPIDs and currentMPID which should not be searched
6060
+ if (!persistence[key].mpid) {
6061
+ continue;
6062
+ }
6063
+ if (cookieUiMatchesRequestedIdentity(persistence[key].ui, requestedIdentityType, requestedValue)) {
6064
+ matchedUser = key;
6065
+ }
6066
+ }
6067
+ return matchedUser;
6068
+ }
6069
+ function findMpidMatchingIdentities(persistence, identityApiData) {
6070
+ var matchedUser;
6071
+ for (var requestedIdentityType in identityApiData.userIdentities) {
6072
+ if (!persistence || !Object.keys(persistence).length) {
6073
+ continue;
6074
+ }
6075
+ var match = findMpidForRequestedIdentity(persistence, requestedIdentityType, identityApiData.userIdentities[requestedIdentityType]);
6076
+ if (match) {
6077
+ matchedUser = match;
6041
6078
  }
6042
6079
  }
6080
+ return matchedUser;
6081
+ }
6082
+ this.findPrevCookiesBasedOnUI = function (identityApiData) {
6083
+ var persistence = mpInstance._Persistence.getPersistence();
6084
+ if (!identityApiData) {
6085
+ return;
6086
+ }
6087
+ var matchedUser = findMpidMatchingIdentities(persistence, identityApiData);
6043
6088
  if (matchedUser) {
6044
6089
  self.storeDataInMemory(persistence, matchedUser);
6045
6090
  }
6046
6091
  };
6047
- this.encodePersistence = function (persistence) {
6048
- persistence = JSON.parse(persistence);
6049
- for (var key in persistence.gs) {
6050
- if (persistence.gs.hasOwnProperty(key)) {
6051
- if (Base64CookieKeys[key]) {
6052
- if (persistence.gs[key]) {
6053
- // base64 encode any value that is an object or Array in globalSettings
6054
- if (Array.isArray(persistence.gs[key]) && persistence.gs[key].length || mpInstance._Helpers.isObject(persistence.gs[key]) && Object.keys(persistence.gs[key]).length) {
6055
- persistence.gs[key] = Base64.encode(JSON.stringify(persistence.gs[key]));
6056
- } else {
6057
- delete persistence.gs[key];
6058
- }
6059
- } else {
6060
- delete persistence.gs[key];
6061
- }
6062
- } else if (key === 'ie') {
6063
- persistence.gs[key] = persistence.gs[key] ? 1 : 0;
6064
- } else if (!persistence.gs[key]) {
6065
- delete persistence.gs[key];
6066
- }
6092
+ function isNonEmptyArrayOrObject(value) {
6093
+ if (Array.isArray(value)) {
6094
+ return value.length > 0;
6095
+ }
6096
+ if (!mpInstance._Helpers.isObject(value)) {
6097
+ return false;
6098
+ }
6099
+ return Object.keys(value).length > 0;
6100
+ }
6101
+ function encodeGsBase64Field(gs, key) {
6102
+ if (!gs[key] || !isNonEmptyArrayOrObject(gs[key])) {
6103
+ delete gs[key];
6104
+ return;
6105
+ }
6106
+ gs[key] = Base64.encode(JSON.stringify(gs[key]));
6107
+ }
6108
+ function encodeGlobalSettings(gs) {
6109
+ for (var key in gs) {
6110
+ if (!gs.hasOwnProperty(key)) {
6111
+ continue;
6112
+ }
6113
+ if (Base64CookieKeys[key]) {
6114
+ encodeGsBase64Field(gs, key);
6115
+ continue;
6116
+ }
6117
+ if (key === 'ie') {
6118
+ gs[key] = gs[key] ? 1 : 0;
6119
+ continue;
6120
+ }
6121
+ if (!gs[key]) {
6122
+ delete gs[key];
6123
+ }
6124
+ }
6125
+ }
6126
+ function encodeMpidBase64Field(container, key) {
6127
+ var value = container[key];
6128
+ if (mpInstance._Helpers.isObject(value) && Object.keys(value).length) {
6129
+ container[key] = Base64.encode(JSON.stringify(value));
6130
+ return;
6131
+ }
6132
+ delete container[key];
6133
+ }
6134
+ function encodeMpidRecord(record) {
6135
+ for (var key in record) {
6136
+ if (!record.hasOwnProperty(key)) {
6137
+ continue;
6138
+ }
6139
+ if (Base64CookieKeys[key]) {
6140
+ encodeMpidBase64Field(record, key);
6067
6141
  }
6068
6142
  }
6143
+ }
6144
+ function encodeMpidRecords(persistence) {
6069
6145
  for (var mpid in persistence) {
6070
- if (persistence.hasOwnProperty(mpid)) {
6071
- if (!SDKv2NonMPIDCookieKeys[mpid]) {
6072
- for (key in persistence[mpid]) {
6073
- if (persistence[mpid].hasOwnProperty(key)) {
6074
- if (Base64CookieKeys[key]) {
6075
- if (mpInstance._Helpers.isObject(persistence[mpid][key]) && Object.keys(persistence[mpid][key]).length) {
6076
- persistence[mpid][key] = Base64.encode(JSON.stringify(persistence[mpid][key]));
6077
- } else {
6078
- delete persistence[mpid][key];
6079
- }
6080
- }
6081
- }
6082
- }
6083
- }
6146
+ if (!persistence.hasOwnProperty(mpid)) {
6147
+ continue;
6148
+ }
6149
+ if (SDKv2NonMPIDCookieKeys[mpid]) {
6150
+ continue;
6084
6151
  }
6152
+ encodeMpidRecord(persistence[mpid]);
6085
6153
  }
6154
+ }
6155
+ this.encodePersistence = function (persistenceString) {
6156
+ var persistence = JSON.parse(persistenceString);
6157
+ encodeGlobalSettings(persistence.gs);
6158
+ encodeMpidRecords(persistence);
6086
6159
  return createCookieString(JSON.stringify(persistence));
6087
6160
  };
6088
-
6161
+ function decodeGlobalSettings(gs) {
6162
+ for (var key in gs) {
6163
+ if (!gs.hasOwnProperty(key)) {
6164
+ continue;
6165
+ }
6166
+ if (Base64CookieKeys[key]) {
6167
+ gs[key] = JSON.parse(Base64.decode(gs[key]));
6168
+ continue;
6169
+ }
6170
+ if (key === 'ie') {
6171
+ gs[key] = Boolean(gs[key]);
6172
+ }
6173
+ }
6174
+ }
6175
+ function decodeMpidRecord(record) {
6176
+ for (var key in record) {
6177
+ if (!record.hasOwnProperty(key)) {
6178
+ continue;
6179
+ }
6180
+ if (!Base64CookieKeys[key]) {
6181
+ continue;
6182
+ }
6183
+ if (record[key].length) {
6184
+ record[key] = JSON.parse(Base64.decode(record[key]));
6185
+ }
6186
+ }
6187
+ }
6188
+ function decodeMpidRecords(persistence) {
6189
+ for (var mpid in persistence) {
6190
+ if (!persistence.hasOwnProperty(mpid)) {
6191
+ continue;
6192
+ }
6193
+ if (!SDKv2NonMPIDCookieKeys[mpid]) {
6194
+ decodeMpidRecord(persistence[mpid]);
6195
+ continue;
6196
+ }
6197
+ if (mpid === 'l') {
6198
+ persistence[mpid] = Boolean(persistence[mpid]);
6199
+ }
6200
+ }
6201
+ }
6089
6202
  // TODO: This should actually be decodePersistenceString or
6090
6203
  // we should refactor this to take a string and return an object
6091
- this.decodePersistence = function (persistence) {
6204
+ this.decodePersistence = function (persistenceString) {
6092
6205
  try {
6093
- if (persistence) {
6094
- persistence = JSON.parse(revertCookieString(persistence));
6095
- if (mpInstance._Helpers.isObject(persistence) && Object.keys(persistence).length) {
6096
- for (var key in persistence.gs) {
6097
- if (persistence.gs.hasOwnProperty(key)) {
6098
- if (Base64CookieKeys[key]) {
6099
- persistence.gs[key] = JSON.parse(Base64.decode(persistence.gs[key]));
6100
- } else if (key === 'ie') {
6101
- persistence.gs[key] = Boolean(persistence.gs[key]);
6102
- }
6103
- }
6104
- }
6105
- for (var mpid in persistence) {
6106
- if (persistence.hasOwnProperty(mpid)) {
6107
- if (!SDKv2NonMPIDCookieKeys[mpid]) {
6108
- for (key in persistence[mpid]) {
6109
- if (persistence[mpid].hasOwnProperty(key)) {
6110
- if (Base64CookieKeys[key]) {
6111
- if (persistence[mpid][key].length) {
6112
- persistence[mpid][key] = JSON.parse(Base64.decode(persistence[mpid][key]));
6113
- }
6114
- }
6115
- }
6116
- }
6117
- } else if (mpid === 'l') {
6118
- persistence[mpid] = Boolean(persistence[mpid]);
6119
- }
6120
- }
6121
- }
6122
- }
6123
- return JSON.stringify(persistence);
6206
+ if (!persistenceString) {
6207
+ return;
6124
6208
  }
6209
+ var persistence = JSON.parse(revertCookieString(persistenceString));
6210
+ if (mpInstance._Helpers.isObject(persistence) && Object.keys(persistence).length) {
6211
+ decodeGlobalSettings(persistence.gs);
6212
+ decodeMpidRecords(persistence);
6213
+ }
6214
+ return JSON.stringify(persistence);
6125
6215
  } catch (e) {
6126
- mpInstance.Logger.error('Problem with decoding cookie', e);
6216
+ mpInstance.Logger.error('Problem with decoding cookie');
6127
6217
  }
6128
6218
  };
6129
6219
  this.getCookieDomain = function () {
@@ -6138,7 +6228,6 @@ var mParticle = (function () {
6138
6228
  }
6139
6229
  }
6140
6230
  };
6141
-
6142
6231
  // This function loops through the parts of a full hostname, attempting to set a cookie on that domain. It will set a cookie at the highest level possible.
6143
6232
  // For example subdomain.domain.co.uk would try the following combinations:
6144
6233
  // "co.uk" -> fail
@@ -6184,18 +6273,17 @@ var mParticle = (function () {
6184
6273
  }
6185
6274
  }
6186
6275
  };
6187
-
6188
6276
  // https://go.mparticle.com/work/SQDSDKS-6021
6189
6277
  this.savePersistence = function (persistence) {
6190
- var _mpInstance$_CookieCo3;
6278
+ var _a;
6191
6279
  // Block mprtcl-v4 persistence when noFunctional is true
6192
- if ((_mpInstance$_CookieCo3 = mpInstance._CookieConsentManager) !== null && _mpInstance$_CookieCo3 !== void 0 && _mpInstance$_CookieCo3.getNoFunctional()) {
6280
+ if ((_a = mpInstance._CookieConsentManager) === null || _a === void 0 ? void 0 : _a.getNoFunctional()) {
6193
6281
  return;
6194
6282
  }
6195
6283
  var encodedPersistence = self.encodePersistence(JSON.stringify(persistence)),
6196
6284
  date = new Date(),
6197
6285
  key = mpInstance._Store.storageName,
6198
- expires = new Date(date.getTime() + mpInstance._Store.SDKConfig.cookieExpiration * 24 * 60 * 60 * 1000).toGMTString(),
6286
+ expires = new Date(date.getTime() + mpInstance._Store.SDKConfig.cookieExpiration * 24 * 60 * 60 * 1000).toUTCString(),
6199
6287
  cookieDomain = self.getCookieDomain(),
6200
6288
  domain;
6201
6289
  if (cookieDomain === '') {
@@ -6231,7 +6319,6 @@ var mParticle = (function () {
6231
6319
  return null;
6232
6320
  }
6233
6321
  };
6234
-
6235
6322
  /**
6236
6323
  * set the "first seen" time for a user. the time will only be set once for a given
6237
6324
  * mpid after which subsequent calls will be ignored
@@ -6255,7 +6342,6 @@ var mParticle = (function () {
6255
6342
  }
6256
6343
  }
6257
6344
  };
6258
-
6259
6345
  /**
6260
6346
  * returns the "last seen" time for a user. If the mpid represents the current user, the
6261
6347
  * return value will always be the current time, otherwise it will be to stored "last seen"
@@ -6304,13 +6390,13 @@ var mParticle = (function () {
6304
6390
  self.expireCookies(StorageNames.cookieNameV3);
6305
6391
  self.expireCookies(StorageNames.cookieNameV4);
6306
6392
  self.expireCookies(mpInstance._Store.storageName);
6307
- if (mParticle._isTestEnv) {
6393
+ var mParticleManager = getMParticleManager();
6394
+ if (mParticleManager === null || mParticleManager === void 0 ? void 0 : mParticleManager._isTestEnv) {
6308
6395
  var testWorkspaceToken = 'abcdef';
6309
- removeLocalStorage(mpInstance._Helpers.createMainStorageName(testWorkspaceToken));
6396
+ localStorage.removeItem(mpInstance._Helpers.createMainStorageName(testWorkspaceToken));
6310
6397
  self.expireCookies(mpInstance._Helpers.createMainStorageName(testWorkspaceToken));
6311
6398
  }
6312
6399
  };
6313
-
6314
6400
  // https://go.mparticle.com/work/SQDSDKS-6045
6315
6401
  // Forwarder Batching Code
6316
6402
  this.forwardingStatsBatches = {
@@ -6319,266 +6405,689 @@ var mParticle = (function () {
6319
6405
  };
6320
6406
  }
6321
6407
 
6322
- var Messages$3 = Constants.Messages;
6323
- function Events(mpInstance) {
6324
- var self = this;
6325
- this.logEvent = function (event, options) {
6326
- mpInstance.Logger.verbose(Messages$3.InformationMessages.StartingLogEvent + ': ' + event.name);
6327
- if (mpInstance._Helpers.canLog()) {
6328
- var uploadObject = mpInstance._ServerModel.createEventObject(event);
6329
- mpInstance._APIClient.sendEventToServer(uploadObject, options);
6330
- } else {
6331
- mpInstance.Logger.verbose(Messages$3.InformationMessages.AbandonLogEvent);
6332
- }
6333
- };
6334
- this.startTracking = function (callback) {
6335
- if (!mpInstance._Store.isTracking) {
6336
- if ('geolocation' in navigator) {
6337
- mpInstance._Store.watchPositionId = navigator.geolocation.watchPosition(successTracking, errorTracking);
6338
- }
6339
- } else {
6340
- var position = {
6341
- coords: {
6342
- latitude: mpInstance._Store.currentPosition.lat,
6343
- longitude: mpInstance._Store.currentPosition.lng
6344
- }
6345
- };
6346
- triggerCallback(callback, position);
6347
- }
6348
- function successTracking(position) {
6349
- mpInstance._Store.currentPosition = {
6350
- lat: position.coords.latitude,
6351
- lng: position.coords.longitude
6352
- };
6353
- triggerCallback(callback, position);
6354
- // prevents callback from being fired multiple times
6355
- callback = null;
6356
- mpInstance._Store.isTracking = true;
6357
- }
6358
- function errorTracking() {
6359
- triggerCallback(callback);
6360
- // prevents callback from being fired multiple times
6361
- callback = null;
6362
- mpInstance._Store.isTracking = false;
6363
- }
6364
- function triggerCallback(callback, position) {
6365
- if (callback) {
6366
- try {
6367
- if (position) {
6368
- callback(position);
6369
- } else {
6370
- callback();
6371
- }
6372
- } catch (e) {
6373
- mpInstance.Logger.error('Error invoking the callback passed to startTrackingLocation.');
6374
- mpInstance.Logger.error(e);
6375
- }
6376
- }
6377
- }
6408
+ var HISTORY_METHODS = ['pushState', 'replaceState'];
6409
+ var WRAPPED_MARKER = '__mpApvWrapped__';
6410
+ // All APV state hangs off one window key, and it is the public debugging
6411
+ // contract: `window.__mpApv__` is what you inspect in a console to see whether
6412
+ // tracking is live.
6413
+ //
6414
+ // It lives on `window` rather than in module scope because Next.js re-executes
6415
+ // the SDK bundle on every SPA navigation: module-level state is reset each time,
6416
+ // but `window` persists for the lifetime of the tab.
6417
+ //
6418
+ // One object rather than a key per flag, so resetting is a single reassignment.
6419
+ // Nothing has to be deleted, and there is no ordering in which half the state
6420
+ // survives the reset.
6421
+ var WIN_APV_KEY = '__mpApv__';
6422
+ // The query params an auto page view may carry. An allowlist, not a denylist:
6423
+ // partner URLs routinely hold order ids, email addresses and session tokens, and
6424
+ // none of those should reach the event stream because someone forgot to exclude
6425
+ // them. Anything absent from this list is dropped.
6426
+ //
6427
+ // SECURITY: `code`, `state` and `nonce` are the OAuth 2.0 / OIDC authorization
6428
+ // code and the CSRF/replay tokens. They are credentials until redeemed, and
6429
+ // attaching them here persists them in the event store and forwards them to
6430
+ // every configured kit. They are on the list by explicit product decision —
6431
+ // deleting that line is the whole of the fix if that decision is revisited.
6432
+ var ALLOWED_QUERY_PARAMS = [
6433
+ // Campaign attribution
6434
+ 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id',
6435
+ // Ad-network click ids
6436
+ 'gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid', 'ttclid', 'twclid', 'li_fat_id', 'dclid',
6437
+ // OAuth / OIDC — see the SECURITY note above
6438
+ 'client_id', 'redirect_uri', 'response_type', 'scope', 'state', 'code', 'nonce',
6439
+ // Pagination and search
6440
+ 'page', 'limit', 'offset', 'cursor', 'per_page', 'q', 'search',
6441
+ // Referral
6442
+ 'ref', 'referrer'];
6443
+ // ---------------------------------------------------------------------------
6444
+ // Pure helpers. No `this`, no globals, no side effects — callable and assertable
6445
+ // on their own, which is where the interesting rules live.
6446
+ // ---------------------------------------------------------------------------
6447
+ // Pulls the allowlisted query params off a URL. Delegates to the SDK's own
6448
+ // parser, which lowercases keys (so `?UTM_Source=` and `?utm_source=` land on one
6449
+ // attribute), drops empty values, and carries the fallback for browsers without
6450
+ // URLSearchParams. Tolerates an empty href, so SSR yields no params rather than
6451
+ // throwing.
6452
+ var allowedQueryParams = function allowedQueryParams(href) {
6453
+ return queryStringParser(href, ALLOWED_QUERY_PARAMS);
6454
+ };
6455
+ // The captured params, in allowlist order. Ordering comes from the constant
6456
+ // rather than a sort: it is deterministic without needing a comparator, and it
6457
+ // does not depend on the object's insertion order, so reordering the query string
6458
+ // cannot produce a different key.
6459
+ var capturedNames = function capturedNames(params) {
6460
+ return ALLOWED_QUERY_PARAMS.filter(function (name) {
6461
+ return name in params;
6462
+ });
6463
+ };
6464
+ // The dedup key: pathname plus the allowlisted params in a fixed order, so that
6465
+ // reordering the query string is not a new page. Params outside the allowlist
6466
+ // never make it into `page.params` and so cannot key a view — nor can the hash.
6467
+ //
6468
+ // Values are re-encoded because queryStringParser hands them back DECODED. A
6469
+ // value holding the pair delimiters would otherwise serialize exactly like two
6470
+ // separate params — `{q: 'a&search=b'}` and `{q: 'a', search: 'b'}` both becoming
6471
+ // `q=a&search=b` — and dedup would treat a real navigation between them as the
6472
+ // same page and drop the view. `q`, `search` and `redirect_uri` carry `&` and `=`
6473
+ // routinely, so this is reachable rather than theoretical.
6474
+ var pageKey = function pageKey(page) {
6475
+ var query = capturedNames(page.params).map(function (name) {
6476
+ return "".concat(name, "=").concat(encodeURIComponent(page.params[name]));
6477
+ }).join('&');
6478
+ return query ? "".concat(page.path, "?").concat(query) : page.path;
6479
+ };
6480
+ // Dedup keys on the pageKey above: a change to any captured param is a new page
6481
+ // (`?page=2` is a distinct pagination view), while a hash-only change, or a
6482
+ // change confined to params we do not capture, is the same page.
6483
+ var isNewPage = function isNewPage(lastKey, candidateKey) {
6484
+ return candidateKey !== lastKey;
6485
+ };
6486
+ var supportsHistoryTracking = function supportsHistoryTracking(win) {
6487
+ return !!win && win.history !== undefined && typeof win.history.pushState === 'function' && typeof win.addEventListener === 'function';
6488
+ };
6489
+ // Mirrors the event shape of the public mParticle.logPageView(), but carries the
6490
+ // path and query params captured when the navigation was accepted rather than the
6491
+ // live location.
6492
+ var buildPageViewEvent = function buildPageViewEvent(_a) {
6493
+ var params = _a.params,
6494
+ hostname = _a.hostname,
6495
+ title = _a.title,
6496
+ path = _a.path;
6497
+ return {
6498
+ messageType: MessageType$1.PageView,
6499
+ name: 'PageView',
6500
+ // Params spread first, then the core fields by name, so a core field always
6501
+ // wins. No allowlist entry collides with hostname/title/path today; naming
6502
+ // them here is what keeps a later addition from silently overwriting one.
6503
+ data: __assign(__assign({}, params), {
6504
+ hostname: hostname,
6505
+ title: title,
6506
+ path: path
6507
+ }),
6508
+ eventType: EventType.Unknown
6378
6509
  };
6379
- this.stopTracking = function () {
6380
- if (mpInstance._Store.isTracking) {
6381
- navigator.geolocation.clearWatch(mpInstance._Store.watchPositionId);
6382
- mpInstance._Store.currentPosition = null;
6383
- mpInstance._Store.isTracking = false;
6384
- }
6510
+ };
6511
+ // ---------------------------------------------------------------------------
6512
+ // Window-scoped APV state. Every read and write goes through these helpers, so
6513
+ // neither the tracker nor mp-instance touches `window` directly and
6514
+ // `typeof window` is checked in exactly one place.
6515
+ // ---------------------------------------------------------------------------
6516
+ var apvWindow = function apvWindow() {
6517
+ return typeof window === 'undefined' ? null : window;
6518
+ };
6519
+ var freshState = function freshState() {
6520
+ return {
6521
+ initialPageViewFired: false
6385
6522
  };
6386
- this.logOptOut = function () {
6387
- mpInstance.Logger.verbose(Messages$3.InformationMessages.StartingLogOptOut);
6388
- var event = mpInstance._ServerModel.createEventObject({
6389
- messageType: Types.MessageType.OptOut,
6390
- eventType: Types.EventType.Other
6391
- });
6392
- mpInstance._APIClient.sendEventToServer(event);
6523
+ };
6524
+ // Reads tolerate absent state, so a read never mutates `window`. Only the
6525
+ // writers below create it.
6526
+ var readState = function readState() {
6527
+ var win = apvWindow();
6528
+ return win ? win[WIN_APV_KEY] : undefined;
6529
+ };
6530
+ var writeState = function writeState() {
6531
+ var win = apvWindow();
6532
+ if (!win) {
6533
+ return null;
6534
+ }
6535
+ if (!win[WIN_APV_KEY]) {
6536
+ win[WIN_APV_KEY] = freshState();
6537
+ }
6538
+ return win[WIN_APV_KEY];
6539
+ };
6540
+ var getActiveTracker = function getActiveTracker() {
6541
+ var state = readState();
6542
+ return state ? state.tracker : undefined;
6543
+ };
6544
+ var hasInitialPageViewFired = function hasInitialPageViewFired() {
6545
+ var state = readState();
6546
+ return !!(state && state.initialPageViewFired);
6547
+ };
6548
+ var markInitialPageViewFired = function markInitialPageViewFired() {
6549
+ var state = writeState();
6550
+ if (state) {
6551
+ state.initialPageViewFired = true;
6552
+ }
6553
+ };
6554
+ // Returns the page to its pre-APV state: stops the active tracker (restoring the
6555
+ // history methods it patched), then swaps in fresh state. The state object is the
6556
+ // authoritative handle, so this reaches trackers whose owning module is gone.
6557
+ var resetPageViewTracking = function resetPageViewTracking() {
6558
+ var win = apvWindow();
6559
+ if (!win) {
6560
+ return;
6561
+ }
6562
+ var active = getActiveTracker();
6563
+ if (active) {
6564
+ active.teardown();
6565
+ }
6566
+ // One reassignment replaces every flag at once, so nothing can be left behind
6567
+ // whatever shape the previous state was in.
6568
+ win[WIN_APV_KEY] = freshState();
6569
+ };
6570
+ var setActiveTracker = function setActiveTracker(tracker) {
6571
+ var state = writeState();
6572
+ if (state) {
6573
+ state.tracker = tracker;
6574
+ }
6575
+ };
6576
+ var clearActiveTracker = function clearActiveTracker(tracker) {
6577
+ var state = readState();
6578
+ if (state && state.tracker === tracker) {
6579
+ state.tracker = undefined;
6580
+ }
6581
+ };
6582
+ var currentPage = function currentPage() {
6583
+ return {
6584
+ path: window.location.pathname,
6585
+ params: allowedQueryParams(getHref())
6393
6586
  };
6394
- this.logAST = function () {
6395
- self.logEvent({
6396
- messageType: Types.MessageType.AppStateTransition
6587
+ };
6588
+ // Log-safe description of a page: the path, plus the NAMES of the captured
6589
+ // params. Values are deliberately omitted — verbose logging goes to the console,
6590
+ // and session-replay tooling ships console output off-domain, which is not a
6591
+ // place for an OAuth code or a consumer's search terms.
6592
+ // Only reachable before init() assigns lastPage, which is ahead of any navigation
6593
+ // handler being able to run, so this returns nothing rather than a sentinel.
6594
+ var describePage = function describePage(page) {
6595
+ if (!page) {
6596
+ return '';
6597
+ }
6598
+ var names = capturedNames(page.params);
6599
+ return names.length ? "".concat(page.path, " (params: ").concat(names.join(), ")") : page.path;
6600
+ };
6601
+ // ---------------------------------------------------------------------------
6602
+ // History patching
6603
+ // ---------------------------------------------------------------------------
6604
+ // Wraps pushState/replaceState so `onNavigate` runs after the real method, and
6605
+ // returns the single function that undoes it — or null when nothing was
6606
+ // installed (history is already wrapped, or a frozen/sealed History rejected the
6607
+ // assignment). Handing back one undo closure keeps the wrapper and original
6608
+ // references out of the tracker entirely: there is no half-patched state for a
6609
+ // caller to inspect, and no way to restore the wrong pair.
6610
+ var patchHistory = function patchHistory(onNavigate, log) {
6611
+ if (window.history.pushState[WRAPPED_MARKER]) {
6612
+ log('[patch] history already wrapped, skipping to avoid double-wrap — STACKED WRAPPER DETECTED');
6613
+ return null;
6614
+ }
6615
+ var originals = {};
6616
+ var wrappers = {};
6617
+ HISTORY_METHODS.forEach(function (name) {
6618
+ var original = window.history[name];
6619
+ originals[name] = original;
6620
+ var wrapper = function wrapper() {
6621
+ var args = [];
6622
+ for (var _i = 0; _i < arguments.length; _i++) {
6623
+ args[_i] = arguments[_i];
6624
+ }
6625
+ var result = original.apply(this, args);
6626
+ onNavigate(name);
6627
+ return result;
6628
+ };
6629
+ Object.defineProperty(wrapper, WRAPPED_MARKER, {
6630
+ value: true,
6631
+ enumerable: false
6397
6632
  });
6398
- };
6399
- this.logPageView = function () {
6400
- self.logEvent({
6401
- messageType: Types.MessageType.PageView,
6402
- name: 'PageView',
6403
- data: {
6404
- hostname: window.location.hostname,
6405
- title: window.document.title
6406
- },
6407
- eventType: Types.EventType.Unknown
6633
+ wrappers[name] = wrapper;
6634
+ });
6635
+ // Restore per method: a third party may have patched one of the two on top of
6636
+ // ours after we installed. Clobbering theirs would break their tracking, so
6637
+ // leave anything that is no longer ours in place.
6638
+ var restore = function restore() {
6639
+ return HISTORY_METHODS.forEach(function (name) {
6640
+ if (window.history[name] === wrappers[name]) {
6641
+ window.history[name] = originals[name];
6642
+ log("[teardown] restored original ".concat(name));
6643
+ } else {
6644
+ log("[teardown] ".concat(name, " no longer ours; leaving in place, gating callback to no-op"));
6645
+ }
6408
6646
  });
6409
6647
  };
6410
- this.logCheckoutEvent = function (step, option, attrs, customFlags) {
6411
- var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6412
- if (event) {
6413
- event.EventName += mpInstance._Ecommerce.getProductActionEventName(Types.ProductActionType.Checkout);
6414
- event.EventCategory = Types.CommerceEventType.ProductCheckout;
6415
- event.ProductAction = {
6416
- ProductActionType: Types.ProductActionType.Checkout,
6417
- CheckoutStep: step,
6418
- CheckoutOptions: option,
6419
- ProductList: []
6420
- };
6421
- self.logCommerceEvent(event, attrs);
6422
- }
6423
- };
6424
- this.logProductActionEvent = function (productActionType, product, customAttrs, customFlags, transactionAttributes, options) {
6425
- var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags, options);
6426
- var productList = Array.isArray(product) ? product : [product];
6427
- productList.forEach(function (product) {
6428
- if (product.TotalAmount) {
6429
- product.TotalAmount = mpInstance._Ecommerce.sanitizeAmount(product.TotalAmount, 'TotalAmount');
6430
- }
6431
- if (product.Position) {
6432
- product.Position = mpInstance._Ecommerce.sanitizeAmount(product.Position, 'Position');
6433
- }
6434
- if (product.Price) {
6435
- product.Price = mpInstance._Ecommerce.sanitizeAmount(product.Price, 'Price');
6436
- }
6437
- if (product.Quantity) {
6438
- product.Quantity = mpInstance._Ecommerce.sanitizeAmount(product.Quantity, 'Quantity');
6439
- }
6648
+ try {
6649
+ HISTORY_METHODS.forEach(function (name) {
6650
+ window.history[name] = wrappers[name];
6440
6651
  });
6441
- if (event) {
6442
- event.EventCategory = mpInstance._Ecommerce.convertProductActionToEventType(productActionType);
6443
- event.EventName += mpInstance._Ecommerce.getProductActionEventName(productActionType);
6444
- event.ProductAction = {
6445
- ProductActionType: productActionType,
6446
- ProductList: productList
6447
- };
6448
- if (Types.ProductActionType.isRoktCommerceType(productActionType)) {
6449
- event.CustomFlags = event.CustomFlags || {};
6450
- event.CustomFlags['Rokt.CommerceEventType'] = Types.ProductActionType.getExpansionName(productActionType);
6451
- }
6452
- if (mpInstance._Helpers.isObject(transactionAttributes)) {
6453
- mpInstance._Ecommerce.convertTransactionAttributesToProductAction(transactionAttributes, event.ProductAction);
6454
- }
6455
- self.logCommerceEvent(event, customAttrs, options);
6652
+ } catch (e) {
6653
+ log("[error] failed to patch history methods (frozen/sealed), rolling back: ".concat(e));
6654
+ try {
6655
+ restore();
6656
+ } catch (restoreError) {
6657
+ log("[error] failed to restore history methods after patch failure: ".concat(restoreError));
6456
6658
  }
6457
- };
6458
- this.logPurchaseEvent = function (transactionAttributes, product, attrs, customFlags) {
6459
- var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6460
- if (event) {
6461
- event.EventName += mpInstance._Ecommerce.getProductActionEventName(Types.ProductActionType.Purchase);
6462
- event.EventCategory = Types.CommerceEventType.ProductPurchase;
6463
- event.ProductAction = {
6464
- ProductActionType: Types.ProductActionType.Purchase
6465
- };
6466
- event.ProductAction.ProductList = mpInstance._Ecommerce.buildProductList(event, product);
6467
- mpInstance._Ecommerce.convertTransactionAttributesToProductAction(transactionAttributes, event.ProductAction);
6468
- self.logCommerceEvent(event, attrs);
6659
+ return null;
6660
+ }
6661
+ return restore;
6662
+ };
6663
+ // ---------------------------------------------------------------------------
6664
+ // Tracker
6665
+ // ---------------------------------------------------------------------------
6666
+ var PageViewTracker = /** @class */function () {
6667
+ function PageViewTracker(mpInstance) {
6668
+ this.lastPage = null;
6669
+ this.active = false;
6670
+ this.pendingNavigations = [];
6671
+ this.undoHistoryPatch = null;
6672
+ this.popStateListener = null;
6673
+ this.mpInstance = mpInstance;
6674
+ }
6675
+ Object.defineProperty(PageViewTracker.prototype, "isActive", {
6676
+ // True while this tracker owns the history patch and is listening for
6677
+ // navigations. Flips to false on teardown, including the teardown a
6678
+ // successor tracker performs during a handoff. Read-only, and the one thing
6679
+ // worth checking on `window.__mpApv__.tracker` from a console.
6680
+ get: function get() {
6681
+ return this.active;
6682
+ },
6683
+ enumerable: false,
6684
+ configurable: true
6685
+ });
6686
+ PageViewTracker.prototype.init = function () {
6687
+ var _this = this;
6688
+ this.log('[init] PageViewTracker Init');
6689
+ if (!supportsHistoryTracking(apvWindow())) {
6690
+ this.log('[init] unsupported environment (no History API), not starting');
6691
+ return;
6692
+ }
6693
+ // Take over from whichever tracker is currently active: one left behind by
6694
+ // a previous module load, or this same tracker on a repeat
6695
+ // mParticle.init(). Their pending navigations are transferred rather than
6696
+ // dropped — a route change may have queued a deferred fire that has not
6697
+ // flushed yet, and dropping it loses a page view entirely.
6698
+ var active = getActiveTracker();
6699
+ var inheritedPages = this.retire(active);
6700
+ if (this.active && active !== this) {
6701
+ inheritedPages = inheritedPages.concat(this.retire(this));
6469
6702
  }
6703
+ this.active = true;
6704
+ this.lastPage = currentPage();
6705
+ this.log("[init] seeded lastPage: ".concat(describePage(this.lastPage)));
6706
+ this.undoHistoryPatch = patchHistory(function (source) {
6707
+ return _this.safeHandleNavigation(source);
6708
+ }, function (message) {
6709
+ return _this.log(message);
6710
+ });
6711
+ this.popStateListener = function () {
6712
+ return _this.safeHandleNavigation('popstate');
6713
+ };
6714
+ window.addEventListener('popstate', this.popStateListener);
6715
+ setActiveTracker(this);
6716
+ this.log('[init] patched pushState/replaceState + listening for popstate');
6717
+ inheritedPages.forEach(function (page) {
6718
+ return _this.scheduleFire(page);
6719
+ });
6470
6720
  };
6471
- this.logRefundEvent = function (transactionAttributes, product, attrs, customFlags) {
6472
- if (!transactionAttributes) {
6473
- mpInstance.Logger.error(Messages$3.ErrorMessages.TransactionRequired);
6474
- return;
6721
+ PageViewTracker.prototype.teardown = function () {
6722
+ // Discard pending deferred fires (e.g. AutoLogPageView switched off on
6723
+ // re-init). A handoff calls takePendingNavigations() first, so by this
6724
+ // point those paths are already transferred rather than dropped.
6725
+ this.takePendingNavigations();
6726
+ if (this.popStateListener) {
6727
+ window.removeEventListener('popstate', this.popStateListener);
6728
+ this.popStateListener = null;
6475
6729
  }
6476
- var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6477
- if (event) {
6478
- event.EventName += mpInstance._Ecommerce.getProductActionEventName(Types.ProductActionType.Refund);
6479
- event.EventCategory = Types.CommerceEventType.ProductRefund;
6480
- event.ProductAction = {
6481
- ProductActionType: Types.ProductActionType.Refund
6482
- };
6483
- event.ProductAction.ProductList = mpInstance._Ecommerce.buildProductList(event, product);
6484
- mpInstance._Ecommerce.convertTransactionAttributesToProductAction(transactionAttributes, event.ProductAction);
6485
- self.logCommerceEvent(event, attrs);
6730
+ if (this.undoHistoryPatch) {
6731
+ this.undoHistoryPatch();
6732
+ this.undoHistoryPatch = null;
6486
6733
  }
6734
+ this.active = false;
6735
+ clearActiveTracker(this);
6487
6736
  };
6488
- this.logPromotionEvent = function (promotionType, promotion, attrs, customFlags, eventOptions) {
6489
- var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6490
- if (event) {
6491
- event.EventName += mpInstance._Ecommerce.getPromotionActionEventName(promotionType);
6492
- event.EventCategory = mpInstance._Ecommerce.convertPromotionActionToEventType(promotionType);
6493
- event.PromotionAction = {
6494
- PromotionActionType: promotionType,
6495
- PromotionList: Array.isArray(promotion) ? promotion : [promotion]
6496
- };
6497
- self.logCommerceEvent(event, attrs, eventOptions);
6737
+ // Stops the outgoing tracker and returns the pages it had queued so this
6738
+ // tracker can re-schedule them once it is active. Private, but reachable
6739
+ // across instances: the handoff protocol stays internal to the class.
6740
+ PageViewTracker.prototype.retire = function (outgoing) {
6741
+ if (!outgoing) {
6742
+ return [];
6498
6743
  }
6744
+ this.log(outgoing === this ? '[init] retiring this tracker for a repeat mParticle.init() — transferring pending navigations and tearing down' : '[init] retiring a tracker from a previous module load — transferring pending navigations and tearing down');
6745
+ var pages = outgoing.takePendingNavigations();
6746
+ outgoing.teardown();
6747
+ return pages;
6499
6748
  };
6500
- this.logImpressionEvent = function (impression, attrs, customFlags, options) {
6501
- var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6502
- if (event) {
6503
- event.EventName += 'Impression';
6504
- event.EventCategory = Types.CommerceEventType.ProductImpression;
6505
- if (!Array.isArray(impression)) {
6506
- impression = [impression];
6507
- }
6508
- event.ProductImpressions = [];
6509
- impression.forEach(function (impression) {
6510
- event.ProductImpressions.push({
6511
- ProductImpressionList: impression.Name,
6512
- ProductList: Array.isArray(impression.Product) ? impression.Product : [impression.Product]
6513
- });
6514
- });
6515
- self.logCommerceEvent(event, attrs, options);
6749
+ // Cancels this tracker's deferred fires and hands back their captured pages.
6750
+ PageViewTracker.prototype.takePendingNavigations = function () {
6751
+ var pending = this.pendingNavigations;
6752
+ this.pendingNavigations = [];
6753
+ pending.forEach(function (_a) {
6754
+ var timeoutId = _a.timeoutId;
6755
+ return clearTimeout(timeoutId);
6756
+ });
6757
+ return pending.map(function (_a) {
6758
+ var page = _a.page;
6759
+ return page;
6760
+ });
6761
+ };
6762
+ PageViewTracker.prototype.safeHandleNavigation = function (source) {
6763
+ // An orphaned wrapper still calls in: teardown can only restore a history
6764
+ // method that is still ours, so one a third party patched over stays
6765
+ // installed and keeps firing at a dead tracker. Bail before touching
6766
+ // lastPath or queueing a fire the flush would just abort. Guarding here
6767
+ // rather than at each wrapper also covers the popstate path.
6768
+ if (!this.active) {
6769
+ return;
6770
+ }
6771
+ try {
6772
+ this.handleNavigation(source);
6773
+ } catch (e) {
6774
+ this.log("[error] navigation handler threw (".concat(source, "), page view skipped: ").concat(e));
6516
6775
  }
6517
6776
  };
6518
- this.logCommerceEvent = function (commerceEvent, attrs, options) {
6519
- mpInstance.Logger.verbose(Messages$3.InformationMessages.StartingLogCommerceEvent);
6520
- // If a developer typos the ProductActionType, the event category will be
6521
- // null, resulting in kit forwarding errors on the server.
6522
- // The check for `ProductAction` is required to denote that these are
6523
- // ProductAction events, and not impression or promotions
6524
- if (commerceEvent.ProductAction && commerceEvent.EventCategory === null) {
6525
- mpInstance.Logger.error('Commerce event not sent. The mParticle.ProductActionType you passed was invalid. Re-check your code.');
6777
+ PageViewTracker.prototype.handleNavigation = function (source) {
6778
+ var candidate = currentPage();
6779
+ var lastKey = this.lastPage ? pageKey(this.lastPage) : null;
6780
+ this.log("[detect] navigation signal (source: ".concat(source, ", candidate: ").concat(describePage(candidate), ", last: ").concat(describePage(this.lastPage), ")"));
6781
+ if (!isNewPage(lastKey, pageKey(candidate))) {
6782
+ this.log("[dedupe] page unchanged, skipping (source: ".concat(source, ", page: ").concat(describePage(candidate), ")"));
6526
6783
  return;
6527
6784
  }
6528
- var sanitizedAttrs = mpInstance._Helpers.sanitizeAttributes(attrs, commerceEvent.EventName);
6529
- if (mpInstance._Helpers.canLog()) {
6530
- if (mpInstance._Store.webviewBridgeEnabled) {
6531
- // Don't send shopping cart to parent sdks
6532
- commerceEvent.ShoppingCart = {};
6533
- }
6534
- if (sanitizedAttrs) {
6535
- commerceEvent.EventAttributes = sanitizedAttrs;
6536
- }
6537
- // When no transaction total (Revenue) was provided, derive it from
6538
- // the products, shipping, and tax.
6539
- if (commerceEvent.ProductAction) {
6540
- mpInstance._Ecommerce.calculateProductActionTotalAmount(commerceEvent.ProductAction);
6785
+ this.log("[accept] page changed, scheduling fire (source: ".concat(source, ", from: ").concat(describePage(this.lastPage), ", to: ").concat(describePage(candidate), ")"));
6786
+ this.lastPage = candidate;
6787
+ this.scheduleFire(candidate);
6788
+ };
6789
+ // Deferred by a macrotask so the router's post-navigation render commit has a
6790
+ // chance to update document.title before the event is built. The path and
6791
+ // query params are snapshotted now because they are already settled, whereas a
6792
+ // same-tick navigation would overwrite window.location before the flush reads
6793
+ // it.
6794
+ PageViewTracker.prototype.scheduleFire = function (page) {
6795
+ var _this = this;
6796
+ var timeoutId = setTimeout(function () {
6797
+ _this.pendingNavigations = _this.pendingNavigations.filter(function (p) {
6798
+ return p.timeoutId !== timeoutId;
6799
+ });
6800
+ if (!_this.active) {
6801
+ _this.log('[defer] fire aborted, tracker inactive (torn down before flush)');
6802
+ return;
6541
6803
  }
6542
- mpInstance._APIClient.sendEventToServer(commerceEvent, options);
6543
- // https://go.mparticle.com/work/SQDSDKS-6038
6544
- mpInstance._Persistence.update();
6804
+ _this.mpInstance._SessionManager.resetSessionTimer();
6805
+ _this.firePageView(page);
6806
+ }, 0);
6807
+ this.pendingNavigations.push({
6808
+ page: page,
6809
+ timeoutId: timeoutId
6810
+ });
6811
+ };
6812
+ PageViewTracker.prototype.firePageView = function (page) {
6813
+ var title = window.document.title;
6814
+ var event = buildPageViewEvent(__assign(__assign({}, page), {
6815
+ hostname: window.location.hostname,
6816
+ title: title
6817
+ }));
6818
+ this.log("[fire] deferred flush -> _Events.logEvent(PageView) (page: ".concat(describePage(page), ", title: ").concat(title, ")"));
6819
+ this.mpInstance._Events.logEvent(event);
6820
+ };
6821
+ PageViewTracker.prototype.log = function (message) {
6822
+ this.mpInstance.Logger.verbose("mParticle APV: ".concat(message));
6823
+ };
6824
+ return PageViewTracker;
6825
+ }();
6826
+
6827
+ var Messages$3 = Constants.Messages;
6828
+ function Events(mpInstance) {
6829
+ var self = this;
6830
+ this.logEvent = function (event, options) {
6831
+ mpInstance.Logger.verbose(Messages$3.InformationMessages.StartingLogEvent + ': ' + event.name);
6832
+ if (mpInstance._Helpers.canLog()) {
6833
+ var uploadObject = mpInstance._ServerModel.createEventObject(event);
6834
+ mpInstance._APIClient.sendEventToServer(uploadObject, options);
6545
6835
  } else {
6546
6836
  mpInstance.Logger.verbose(Messages$3.InformationMessages.AbandonLogEvent);
6547
6837
  }
6548
6838
  };
6549
- this.addEventHandler = function (domEvent, selector, eventName, data, eventType) {
6550
- var elements = [],
6551
- handler = function handler(e) {
6552
- var timeoutHandler = function timeoutHandler() {
6553
- if (element.href) {
6554
- window.location.href = element.href;
6555
- } else if (element.submit) {
6556
- element.submit();
6557
- }
6558
- };
6559
- mpInstance.Logger.verbose('DOM event triggered, handling event');
6560
- self.logEvent({
6561
- messageType: Types.MessageType.PageEvent,
6562
- name: typeof eventName === 'function' ? eventName(element) : eventName,
6563
- data: typeof data === 'function' ? data(element) : data,
6564
- eventType: eventType || Types.EventType.Other
6565
- });
6566
- // TODO: Handle middle-clicks and special keys (ctrl, alt, etc)
6567
- if (element.href && element.target !== '_blank' || element.submit) {
6568
- // Give xmlhttprequest enough time to execute before navigating a link or submitting form
6569
- if (e.preventDefault) {
6570
- e.preventDefault();
6571
- } else {
6572
- e.returnValue = false;
6573
- }
6574
- setTimeout(timeoutHandler, mpInstance._Store.SDKConfig.timeout);
6839
+ this.startTracking = function (callback) {
6840
+ if (!mpInstance._Store.isTracking) {
6841
+ if ('geolocation' in navigator) {
6842
+ mpInstance._Store.watchPositionId = navigator.geolocation.watchPosition(successTracking, errorTracking);
6843
+ }
6844
+ } else {
6845
+ var position = {
6846
+ coords: {
6847
+ latitude: mpInstance._Store.currentPosition.lat,
6848
+ longitude: mpInstance._Store.currentPosition.lng
6575
6849
  }
6576
- },
6577
- element,
6578
- i;
6579
- if (!selector) {
6580
- mpInstance.Logger.error("Can't bind event, selector is required");
6581
- return;
6850
+ };
6851
+ triggerCallback(callback, position);
6852
+ }
6853
+ function successTracking(position) {
6854
+ mpInstance._Store.currentPosition = {
6855
+ lat: position.coords.latitude,
6856
+ lng: position.coords.longitude
6857
+ };
6858
+ triggerCallback(callback, position);
6859
+ // prevents callback from being fired multiple times
6860
+ callback = null;
6861
+ mpInstance._Store.isTracking = true;
6862
+ }
6863
+ function errorTracking() {
6864
+ triggerCallback(callback);
6865
+ // prevents callback from being fired multiple times
6866
+ callback = null;
6867
+ mpInstance._Store.isTracking = false;
6868
+ }
6869
+ function triggerCallback(callback, position) {
6870
+ if (callback) {
6871
+ try {
6872
+ if (position) {
6873
+ callback(position);
6874
+ } else {
6875
+ callback();
6876
+ }
6877
+ } catch (e) {
6878
+ mpInstance.Logger.error('Error invoking the callback passed to startTrackingLocation.');
6879
+ mpInstance.Logger.error(e);
6880
+ }
6881
+ }
6882
+ }
6883
+ };
6884
+ this.stopTracking = function () {
6885
+ if (mpInstance._Store.isTracking) {
6886
+ navigator.geolocation.clearWatch(mpInstance._Store.watchPositionId);
6887
+ mpInstance._Store.currentPosition = null;
6888
+ mpInstance._Store.isTracking = false;
6889
+ }
6890
+ };
6891
+ this.logOptOut = function () {
6892
+ mpInstance.Logger.verbose(Messages$3.InformationMessages.StartingLogOptOut);
6893
+ var event = mpInstance._ServerModel.createEventObject({
6894
+ messageType: Types.MessageType.OptOut,
6895
+ eventType: Types.EventType.Other
6896
+ });
6897
+ mpInstance._APIClient.sendEventToServer(event);
6898
+ };
6899
+ this.logAST = function () {
6900
+ self.logEvent({
6901
+ messageType: Types.MessageType.AppStateTransition
6902
+ });
6903
+ };
6904
+ // The auto page view for the landing page. The SPA navigations that follow it
6905
+ // come from PageViewTracker instead, so both emitters attach the same
6906
+ // allowlisted query params — and this is the one that matters for campaign
6907
+ // attribution, since utm_*/gclid live on the entry URL.
6908
+ this.logPageView = function () {
6909
+ self.logEvent({
6910
+ messageType: Types.MessageType.PageView,
6911
+ name: 'PageView',
6912
+ data: __assign(__assign({}, allowedQueryParams(getHref())), {
6913
+ hostname: window.location.hostname,
6914
+ title: window.document.title
6915
+ }),
6916
+ eventType: Types.EventType.Unknown
6917
+ });
6918
+ };
6919
+ this.logCheckoutEvent = function (step, option, attrs, customFlags) {
6920
+ var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6921
+ if (event) {
6922
+ event.EventName += mpInstance._Ecommerce.getProductActionEventName(Types.ProductActionType.Checkout);
6923
+ event.EventCategory = Types.CommerceEventType.ProductCheckout;
6924
+ event.ProductAction = {
6925
+ ProductActionType: Types.ProductActionType.Checkout,
6926
+ CheckoutStep: step,
6927
+ CheckoutOptions: option,
6928
+ ProductList: []
6929
+ };
6930
+ self.logCommerceEvent(event, attrs);
6931
+ }
6932
+ };
6933
+ this.logProductActionEvent = function (productActionType, product, customAttrs, customFlags, transactionAttributes, options) {
6934
+ var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags, options);
6935
+ var productList = Array.isArray(product) ? product : [product];
6936
+ productList.forEach(function (product) {
6937
+ if (product.TotalAmount) {
6938
+ product.TotalAmount = mpInstance._Ecommerce.sanitizeAmount(product.TotalAmount, 'TotalAmount');
6939
+ }
6940
+ if (product.Position) {
6941
+ product.Position = mpInstance._Ecommerce.sanitizeAmount(product.Position, 'Position');
6942
+ }
6943
+ if (product.Price) {
6944
+ product.Price = mpInstance._Ecommerce.sanitizeAmount(product.Price, 'Price');
6945
+ }
6946
+ if (product.Quantity) {
6947
+ product.Quantity = mpInstance._Ecommerce.sanitizeAmount(product.Quantity, 'Quantity');
6948
+ }
6949
+ });
6950
+ if (event) {
6951
+ event.EventCategory = mpInstance._Ecommerce.convertProductActionToEventType(productActionType);
6952
+ event.EventName += mpInstance._Ecommerce.getProductActionEventName(productActionType);
6953
+ event.ProductAction = {
6954
+ ProductActionType: productActionType,
6955
+ ProductList: productList
6956
+ };
6957
+ if (Types.ProductActionType.isRoktCommerceType(productActionType)) {
6958
+ event.CustomFlags = event.CustomFlags || {};
6959
+ event.CustomFlags['Rokt.CommerceEventType'] = Types.ProductActionType.getExpansionName(productActionType);
6960
+ }
6961
+ if (mpInstance._Helpers.isObject(transactionAttributes)) {
6962
+ mpInstance._Ecommerce.convertTransactionAttributesToProductAction(transactionAttributes, event.ProductAction);
6963
+ }
6964
+ self.logCommerceEvent(event, customAttrs, options);
6965
+ }
6966
+ };
6967
+ this.logPurchaseEvent = function (transactionAttributes, product, attrs, customFlags) {
6968
+ var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6969
+ if (event) {
6970
+ event.EventName += mpInstance._Ecommerce.getProductActionEventName(Types.ProductActionType.Purchase);
6971
+ event.EventCategory = Types.CommerceEventType.ProductPurchase;
6972
+ event.ProductAction = {
6973
+ ProductActionType: Types.ProductActionType.Purchase
6974
+ };
6975
+ event.ProductAction.ProductList = mpInstance._Ecommerce.buildProductList(event, product);
6976
+ mpInstance._Ecommerce.convertTransactionAttributesToProductAction(transactionAttributes, event.ProductAction);
6977
+ self.logCommerceEvent(event, attrs);
6978
+ }
6979
+ };
6980
+ this.logRefundEvent = function (transactionAttributes, product, attrs, customFlags) {
6981
+ if (!transactionAttributes) {
6982
+ mpInstance.Logger.error(Messages$3.ErrorMessages.TransactionRequired);
6983
+ return;
6984
+ }
6985
+ var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6986
+ if (event) {
6987
+ event.EventName += mpInstance._Ecommerce.getProductActionEventName(Types.ProductActionType.Refund);
6988
+ event.EventCategory = Types.CommerceEventType.ProductRefund;
6989
+ event.ProductAction = {
6990
+ ProductActionType: Types.ProductActionType.Refund
6991
+ };
6992
+ event.ProductAction.ProductList = mpInstance._Ecommerce.buildProductList(event, product);
6993
+ mpInstance._Ecommerce.convertTransactionAttributesToProductAction(transactionAttributes, event.ProductAction);
6994
+ self.logCommerceEvent(event, attrs);
6995
+ }
6996
+ };
6997
+ this.logPromotionEvent = function (promotionType, promotion, attrs, customFlags, eventOptions) {
6998
+ var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
6999
+ if (event) {
7000
+ event.EventName += mpInstance._Ecommerce.getPromotionActionEventName(promotionType);
7001
+ event.EventCategory = mpInstance._Ecommerce.convertPromotionActionToEventType(promotionType);
7002
+ event.PromotionAction = {
7003
+ PromotionActionType: promotionType,
7004
+ PromotionList: Array.isArray(promotion) ? promotion : [promotion]
7005
+ };
7006
+ self.logCommerceEvent(event, attrs, eventOptions);
7007
+ }
7008
+ };
7009
+ this.logImpressionEvent = function (impression, attrs, customFlags, options) {
7010
+ var event = mpInstance._Ecommerce.createCommerceEventObject(customFlags);
7011
+ if (event) {
7012
+ event.EventName += 'Impression';
7013
+ event.EventCategory = Types.CommerceEventType.ProductImpression;
7014
+ if (!Array.isArray(impression)) {
7015
+ impression = [impression];
7016
+ }
7017
+ event.ProductImpressions = [];
7018
+ impression.forEach(function (impression) {
7019
+ event.ProductImpressions.push({
7020
+ ProductImpressionList: impression.Name,
7021
+ ProductList: Array.isArray(impression.Product) ? impression.Product : [impression.Product]
7022
+ });
7023
+ });
7024
+ self.logCommerceEvent(event, attrs, options);
7025
+ }
7026
+ };
7027
+ this.logCommerceEvent = function (commerceEvent, attrs, options) {
7028
+ mpInstance.Logger.verbose(Messages$3.InformationMessages.StartingLogCommerceEvent);
7029
+ // If a developer typos the ProductActionType, the event category will be
7030
+ // null, resulting in kit forwarding errors on the server.
7031
+ // The check for `ProductAction` is required to denote that these are
7032
+ // ProductAction events, and not impression or promotions
7033
+ if (commerceEvent.ProductAction && commerceEvent.EventCategory === null) {
7034
+ mpInstance.Logger.error('Commerce event not sent. The mParticle.ProductActionType you passed was invalid. Re-check your code.');
7035
+ return;
7036
+ }
7037
+ var sanitizedAttrs = mpInstance._Helpers.sanitizeAttributes(attrs, commerceEvent.EventName);
7038
+ if (mpInstance._Helpers.canLog()) {
7039
+ if (mpInstance._Store.webviewBridgeEnabled) {
7040
+ // Don't send shopping cart to parent sdks
7041
+ commerceEvent.ShoppingCart = {};
7042
+ }
7043
+ if (sanitizedAttrs) {
7044
+ commerceEvent.EventAttributes = sanitizedAttrs;
7045
+ }
7046
+ // When no transaction total (Revenue) was provided, derive it from
7047
+ // the products, shipping, and tax.
7048
+ if (commerceEvent.ProductAction) {
7049
+ mpInstance._Ecommerce.calculateProductActionTotalAmount(commerceEvent.ProductAction);
7050
+ }
7051
+ mpInstance._APIClient.sendEventToServer(commerceEvent, options);
7052
+ // https://go.mparticle.com/work/SQDSDKS-6038
7053
+ mpInstance._Persistence.update();
7054
+ } else {
7055
+ mpInstance.Logger.verbose(Messages$3.InformationMessages.AbandonLogEvent);
7056
+ }
7057
+ };
7058
+ this.addEventHandler = function (domEvent, selector, eventName, data, eventType) {
7059
+ var elements = [],
7060
+ handler = function handler(e) {
7061
+ var timeoutHandler = function timeoutHandler() {
7062
+ if (element.href) {
7063
+ window.location.href = element.href;
7064
+ } else if (element.submit) {
7065
+ element.submit();
7066
+ }
7067
+ };
7068
+ mpInstance.Logger.verbose('DOM event triggered, handling event');
7069
+ self.logEvent({
7070
+ messageType: Types.MessageType.PageEvent,
7071
+ name: typeof eventName === 'function' ? eventName(element) : eventName,
7072
+ data: typeof data === 'function' ? data(element) : data,
7073
+ eventType: eventType || Types.EventType.Other
7074
+ });
7075
+ // TODO: Handle middle-clicks and special keys (ctrl, alt, etc)
7076
+ if (element.href && element.target !== '_blank' || element.submit) {
7077
+ // Give xmlhttprequest enough time to execute before navigating a link or submitting form
7078
+ if (e.preventDefault) {
7079
+ e.preventDefault();
7080
+ } else {
7081
+ e.returnValue = false;
7082
+ }
7083
+ setTimeout(timeoutHandler, mpInstance._Store.SDKConfig.timeout);
7084
+ }
7085
+ },
7086
+ element,
7087
+ i;
7088
+ if (!selector) {
7089
+ mpInstance.Logger.error("Can't bind event, selector is required");
7090
+ return;
6582
7091
  }
6583
7092
  // Handle a css selector string or a dom element
6584
7093
  if (typeof selector === 'string') {
@@ -10764,766 +11273,433 @@ var mParticle = (function () {
10764
11273
  enrichedAttributes.email = finalUserIdentities.email;
10765
11274
  }
10766
11275
  // Propagate emailsha256 from current user identities if not already in attributes
10767
- if (isValidHashedEmailIdentityType) {
10768
- hashedEmail = finalUserIdentities[this.mappedEmailShaIdentityType];
10769
- if (hashedEmail && !enrichedAttributes.emailsha256 && !enrichedAttributes[this.mappedEmailShaIdentityType]) {
10770
- enrichedAttributes.emailsha256 = hashedEmail;
10771
- }
10772
- }
10773
- if (!enrichedAttributes[PASSBACK_CONVERSION_TRACKING_ID]) {
10774
- (_p = this.integrationCapture) === null || _p === void 0 ? void 0 : _p.capture();
10775
- capturedAttrs = (_q = this.integrationCapture) === null || _q === void 0 ? void 0 : _q.getClickIdsAsIntegrationAttributes();
10776
- passbackId = (_r = capturedAttrs === null || capturedAttrs === void 0 ? void 0 : capturedAttrs[PARTNER_MODULE_IDS.Rokt]) === null || _r === void 0 ? void 0 : _r[PASSBACK_CONVERSION_TRACKING_ID];
10777
- if (passbackId) {
10778
- enrichedAttributes[PASSBACK_CONVERSION_TRACKING_ID] = passbackId;
10779
- }
10780
- }
10781
- this.filters.filteredUser = this.currentUser || this.filters.filteredUser || null;
10782
- enrichedOptions = __assign(__assign({}, options), {
10783
- attributes: enrichedAttributes
10784
- });
10785
- return [2 /*return*/, this.kit.selectPlacements(enrichedOptions)];
10786
- } catch (error) {
10787
- return [2 /*return*/, Promise.reject(error instanceof Error ? error : new Error('Unknown error occurred'))];
10788
- }
10789
- return [2 /*return*/];
10790
- });
10791
- });
10792
- };
10793
- /**
10794
- * Hashes attributes and returns both original and hashed versions
10795
- * with Rokt-compatible key names (like emailsha256, mobilesha256)
10796
- *
10797
- *
10798
- * @param {RoktAttributes} attributes - Attributes to hash
10799
- * @returns {Promise<RoktAttributes>} Object with both original and hashed attributes
10800
- *
10801
- */
10802
- RoktManager.prototype.hashAttributes = function (attributes) {
10803
- return __awaiter(this, void 0, void 0, function () {
10804
- var keys, hashPromises, results, hashedAttributes, _i, results_1, _a, key, attributeValue, hashedValue, error_1;
10805
- var _this = this;
10806
- return __generator(this, function (_b) {
10807
- switch (_b.label) {
10808
- case 0:
10809
- _b.trys.push([0, 2,, 3]);
10810
- if (!attributes || _typeof$1(attributes) !== 'object') {
10811
- return [2 /*return*/, {}];
10812
- }
10813
- keys = Object.keys(attributes);
10814
- if (keys.length === 0) {
10815
- return [2 /*return*/, {}];
10816
- }
10817
- hashPromises = keys.map(function (key) {
10818
- return __awaiter(_this, void 0, void 0, function () {
10819
- var attributeValue, hashedValue;
10820
- return __generator(this, function (_a) {
10821
- switch (_a.label) {
10822
- case 0:
10823
- attributeValue = attributes[key];
10824
- return [4 /*yield*/, this.hashSha256(attributeValue)];
10825
- case 1:
10826
- hashedValue = _a.sent();
10827
- return [2 /*return*/, {
10828
- key: key,
10829
- attributeValue: attributeValue,
10830
- hashedValue: hashedValue
10831
- }];
10832
- }
10833
- });
10834
- });
10835
- });
10836
- return [4 /*yield*/, Promise.all(hashPromises)];
10837
- case 1:
10838
- results = _b.sent();
10839
- hashedAttributes = {};
10840
- for (_i = 0, results_1 = results; _i < results_1.length; _i++) {
10841
- _a = results_1[_i], key = _a.key, attributeValue = _a.attributeValue, hashedValue = _a.hashedValue;
10842
- hashedAttributes[key] = attributeValue;
10843
- if (hashedValue) {
10844
- hashedAttributes["".concat(key, "sha256")] = hashedValue;
10845
- }
10846
- }
10847
- return [2 /*return*/, hashedAttributes];
10848
- case 2:
10849
- error_1 = _b.sent();
10850
- this.logger.error("Failed to hashAttributes, returning an empty object: ".concat(getErrorMessage(error_1)));
10851
- return [2 /*return*/, {}];
10852
- case 3:
10853
- return [2 /*return*/];
10854
- }
10855
- });
10856
- });
10857
- };
10858
-
10859
- RoktManager.prototype.setExtensionData = function (extensionData) {
10860
- if (!this.isReady()) {
10861
- this.deferredCall('setExtensionData', extensionData);
10862
- return;
10863
- }
10864
- try {
10865
- this.kit.setExtensionData(extensionData);
10866
- } catch (error) {
10867
- throw new Error('Error setting extension data: ' + getErrorMessage(error));
10868
- }
10869
- };
10870
- RoktManager.prototype.use = function (name) {
10871
- if (!this.isReady()) {
10872
- return this.deferredCall('use', name);
10873
- }
10874
- try {
10875
- return this.kit.use(name);
10876
- } catch (error) {
10877
- return Promise.reject(error instanceof Error ? error : new Error('Error using extension: ' + name));
10878
- }
10879
- };
10880
- RoktManager.prototype.onShoppableAdsReady = function (callback) {
10881
- if (!this.kit || !this.isShoppableAdsLoaded) {
10882
- this.deferredCall('onShoppableAdsReady', callback);
10883
- return;
10884
- }
10885
- try {
10886
- this.kit.onShoppableAdsReady(callback);
10887
- } catch (error) {
10888
- this.logger.error("Failed to register onShoppableAdsReady callback: ".concat(getErrorMessage(error)));
10889
- }
10890
- };
10891
- RoktManager.prototype.flushOnShoppableAdsReadyMessageQueue = function (kit) {
10892
- var _this = this;
10893
- this.kit = kit;
10894
- this.messageQueue.forEach(function (message, key) {
10895
- var _a;
10896
- if (message.methodName === ON_SHOPPABLE_ADS_READY_METHOD) {
10897
- try {
10898
- kit.onShoppableAdsReady(message.payload);
10899
- } catch (e) {
10900
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('RoktManager: Error flushing onShoppableAdsReady: ' + e);
10901
- }
10902
- _this.messageQueue["delete"](key);
10903
- }
10904
- });
10905
- this.isShoppableAdsLoaded = true;
10906
- };
10907
- /**
10908
- * Hashes an attribute using SHA-256
10909
- *
10910
- * @param {string | number | boolean | undefined | null} attribute - The value to hash
10911
- * @returns {Promise<string | undefined | null>} SHA-256 hashed value or undefined/null
10912
- *
10913
- */
10914
- RoktManager.prototype.hashSha256 = function (attribute) {
10915
- return __awaiter(this, void 0, void 0, function () {
10916
- var normalizedValue, error_2;
10917
- return __generator(this, function (_a) {
10918
- switch (_a.label) {
10919
- case 0:
10920
- if (attribute === null || attribute === undefined) {
10921
- this.logger.warning("hashSha256 received null/undefined as input");
10922
- return [2 /*return*/, attribute];
10923
- }
10924
- _a.label = 1;
10925
- case 1:
10926
- _a.trys.push([1, 3,, 4]);
10927
- normalizedValue = String(attribute).trim().toLocaleLowerCase();
10928
- return [4 /*yield*/, this.sha256Hex(normalizedValue)];
10929
- case 2:
10930
- return [2 /*return*/, _a.sent()];
10931
- case 3:
10932
- error_2 = _a.sent();
10933
- this.logger.error("Failed to hashSha256, returning undefined: ".concat(getErrorMessage(error_2)));
10934
- return [2 /*return*/, undefined];
10935
- case 4:
10936
- return [2 /*return*/];
10937
- }
10938
- });
10939
- });
10940
- };
10941
-
10942
- RoktManager.prototype.getLocalSessionAttributes = function () {
10943
- return this.store.getLocalSessionAttributes();
10944
- };
10945
- RoktManager.prototype.setLocalSessionAttribute = function (key, value) {
10946
- this.store.setLocalSessionAttribute(key, value);
10947
- };
10948
- RoktManager.prototype.isReady = function () {
10949
- // The Rokt Manager is ready when a kit is attached and has a launcher
10950
- return Boolean(this.kit && this.kit.launcher);
10951
- };
10952
- RoktManager.prototype.setUserAttributes = function (attributes) {
10953
- var reservedAttributes = ['sandbox'];
10954
- var filteredAttributes = {};
10955
- for (var key in attributes) {
10956
- if (attributes.hasOwnProperty(key) && reservedAttributes.indexOf(key) === -1) {
10957
- var value = attributes[key];
10958
- filteredAttributes[key] = Array.isArray(value) ? JSON.stringify(value) : value;
10959
- }
10960
- }
10961
- try {
10962
- this.currentUser.setUserAttributes(filteredAttributes);
10963
- } catch (error) {
10964
- this.logger.error('Error setting user attributes: ' + error);
10965
- }
10966
- };
10967
- RoktManager.prototype.mapPlacementAttributes = function (attributes, placementAttributesMapping) {
10968
- var mappingLookup = {};
10969
- for (var _i = 0, placementAttributesMapping_1 = placementAttributesMapping; _i < placementAttributesMapping_1.length; _i++) {
10970
- var mapping = placementAttributesMapping_1[_i];
10971
- mappingLookup[mapping.map] = mapping.value;
10972
- }
10973
- var mappedAttributes = {};
10974
- for (var key in attributes) {
10975
- if (attributes.hasOwnProperty(key)) {
10976
- var newKey = mappingLookup[key] || key;
10977
- mappedAttributes[newKey] = attributes[key];
10978
- }
10979
- }
10980
- return mappedAttributes;
10981
- };
10982
- RoktManager.prototype.onIdentityComplete = function () {
10983
- if (this.isReady()) {
10984
- this.currentUser = this.identityService.getCurrentUser();
10985
- // Process any queued selectPlacements calls that were waiting for identity
10986
- this.processMessageQueue();
10987
- }
10988
- };
10989
- RoktManager.prototype.processMessageQueue = function () {
10990
- var _this = this;
10991
- var _a;
10992
- if (!this.isReady() || this.messageQueue.size === 0) {
10993
- return;
10994
- }
10995
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose("RoktManager: Processing ".concat(this.messageQueue.size, " queued messages"));
10996
- var messagesToProcess = Array.from(this.messageQueue.values());
10997
- // Clear the queue immediately to prevent re-processing
10998
- this.messageQueue.clear();
10999
- messagesToProcess.forEach(function (message) {
11000
- var _a, _b, _c, _d;
11001
- if (!(message.methodName in _this) || !isFunction(_this[message.methodName])) {
11002
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error("RoktManager: Method ".concat(message.methodName, " not found"));
11003
- return;
11004
- }
11005
- if ((_b = _this.logger) === null || _b === void 0 ? void 0 : _b.isVerbose()) {
11006
- var payloadToLog = obfuscateDevData(message.payload, (_d = (_c = _this.store) === null || _c === void 0 ? void 0 : _c.SDKConfig) === null || _d === void 0 ? void 0 : _d.isDevelopmentMode);
11007
- _this.logger.verbose("RoktManager: Processing queued message: ".concat(message.methodName, " with payload: ").concat(JSON.stringify(payloadToLog)));
11008
- }
11009
- // Capture resolve/reject functions before async processing
11010
- var resolve = message.resolve;
11011
- var reject = message.reject;
11012
- var handleError = function handleError(error) {
11013
- var _a;
11014
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error("RoktManager: Error processing message '".concat(message.methodName, "': ").concat(getErrorMessage(error)));
11015
- if (reject) {
11016
- reject(error);
11017
- }
11018
- };
11019
- try {
11020
- var result = _this[message.methodName](message.payload);
11021
- // Handle both sync and async methods
11022
- Promise.resolve(result).then(function (resolvedResult) {
11023
- if (resolve) {
11024
- resolve(resolvedResult);
11025
- }
11026
- })["catch"](handleError);
11027
- } catch (error) {
11028
- handleError(error);
11029
- }
11030
- });
11031
- };
11032
- RoktManager.prototype.queueMessage = function (message) {
11033
- this.messageQueue.set(message.messageId, message);
11034
- };
11035
- RoktManager.prototype.deferredCall = function (methodName, payload) {
11036
- var _this = this;
11037
- return new Promise(function (resolve, reject) {
11038
- var messageId = "".concat(methodName, "_").concat(generateUniqueId());
11039
- _this.queueMessage({
11040
- messageId: messageId,
11041
- methodName: methodName,
11042
- payload: payload,
11043
- resolve: resolve,
11044
- reject: reject
11045
- });
11046
- });
11047
- };
11048
- /**
11049
- * Hashes a string input using SHA-256 and returns the hex digest
11050
- * Uses the Web Crypto API for secure hashing
11051
- *
11052
- * @param {string} input - The string to hash
11053
- * @returns {Promise<string>} The SHA-256 hash as a hexadecimal string
11054
- */
11055
- RoktManager.prototype.sha256Hex = function (input) {
11056
- return __awaiter(this, void 0, void 0, function () {
11057
- var encoder, encodedInput, digest;
11058
- return __generator(this, function (_a) {
11059
- switch (_a.label) {
11060
- case 0:
11061
- encoder = new TextEncoder();
11062
- encodedInput = encoder.encode(input);
11063
- return [4 /*yield*/, crypto.subtle.digest('SHA-256', encodedInput)];
11064
- case 1:
11065
- digest = _a.sent();
11066
- return [2 /*return*/, this.arrayBufferToHex(digest)];
11067
- }
11068
- });
11069
- });
11070
- };
11071
- /**
11072
- * Converts an ArrayBuffer to a hexadecimal string representation
11073
- * Each byte is converted to a 2-character hex string with leading zeros
11074
- *
11075
- * @param {ArrayBuffer} buffer - The buffer to convert
11076
- * @returns {string} The hexadecimal string representation
11077
- */
11078
- RoktManager.prototype.arrayBufferToHex = function (buffer) {
11079
- var bytes = new Uint8Array(buffer);
11080
- var hexString = '';
11081
- for (var i = 0; i < bytes.length; i++) {
11082
- var hexByte = bytes[i].toString(16).padStart(2, '0');
11083
- hexString += hexByte;
11084
- }
11085
- return hexString;
11086
- };
11087
- /**
11088
- * Checks if an identity value has changed by comparing current and new values
11089
- *
11090
- * @param {string | undefined} currentValue - The current identity value
11091
- * @param {string | undefined} newValue - The new identity value to compare against
11092
- * @returns {boolean} True if the identity has changed (new value exists and differs from current), false otherwise
11093
- */
11094
- RoktManager.prototype.hasIdentityChanged = function (currentValue, newValue) {
11095
- if (!newValue) {
11096
- return false;
11097
- }
11098
- if (!currentValue) {
11099
- return true; // New value exists but no current value
11100
- }
11101
-
11102
- if (currentValue !== newValue) {
11103
- return true; // Values are different
11104
- }
11105
-
11106
- return false; // Values are the same
11107
- };
11108
-
11109
- return RoktManager;
11110
- }();
11111
-
11112
- /**
11113
- * CookieConsentManager handles storage and access of consent flags (noFunctional, noTargeting)
11114
- * that are passed via launcherOptions during SDK initialization.
11115
- *
11116
- * These flags allow Rokt integration to respect user privacy choices.
11117
- *
11118
- * Default behavior: Both flags default to false, meaning all features are allowed
11119
- * unless explicitly opted out by the user.
11120
- */
11121
- var CookieConsentManager = /** @class */function () {
11122
- function CookieConsentManager(flags) {
11123
- this.flags = flags;
11124
- this.flags = {
11125
- noFunctional: flags.noFunctional === true,
11126
- noTargeting: flags.noTargeting === true
11127
- };
11128
- }
11129
- /**
11130
- * Returns true if third-party targeting is disabled.
11131
- * Targeting is allowed when noTargeting is false (default).
11132
- */
11133
- CookieConsentManager.prototype.getNoTargeting = function () {
11134
- return this.flags.noTargeting;
11135
- };
11136
- /**
11137
- * Returns true if functional tracking is disabled.
11138
- * Functional tracking is allowed when noFunctional is false (default).
11139
- */
11140
- CookieConsentManager.prototype.getNoFunctional = function () {
11141
- return this.flags.noFunctional;
11142
- };
11143
- CookieConsentManager.prototype.syncNoTargetingAttribute = function (user) {
11144
- if (!user) return;
11145
- if (this.flags.noTargeting) {
11146
- user.setUserAttribute(NO_TARGETING_ATTRIBUTE, true);
11147
- } else if (user.getAllUserAttributes().hasOwnProperty(NO_TARGETING_ATTRIBUTE)) {
11148
- user.removeUserAttribute(NO_TARGETING_ATTRIBUTE);
11149
- }
11150
- };
11151
- return CookieConsentManager;
11152
- }();
11153
-
11154
- var ErrorReportingDispatcher = /** @class */function () {
11155
- function ErrorReportingDispatcher() {
11156
- this.services = [];
11157
- }
11158
- ErrorReportingDispatcher.prototype.register = function (service) {
11159
- this.services.push(service);
11160
- };
11161
- ErrorReportingDispatcher.prototype.report = function (error) {
11162
- var _this = this;
11163
- this.services.forEach(function (s) {
11164
- var _a;
11165
- try {
11166
- s.report(error);
11167
- } catch (e) {
11168
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('Error in ErrorReportingService: ' + e);
11169
- }
11170
- });
11171
- };
11172
- return ErrorReportingDispatcher;
11173
- }();
11174
-
11175
- var LoggingDispatcher = /** @class */function () {
11176
- function LoggingDispatcher() {
11177
- this.services = [];
11178
- }
11179
- LoggingDispatcher.prototype.register = function (service) {
11180
- this.services.push(service);
11181
- };
11182
- LoggingDispatcher.prototype.log = function (entry) {
11183
- var _this = this;
11184
- this.services.forEach(function (s) {
11185
- var _a;
11186
- try {
11187
- s.log(entry);
11188
- } catch (e) {
11189
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('Error in LoggingService: ' + e);
11190
- }
11191
- });
11192
- };
11193
- return LoggingDispatcher;
11194
- }();
11195
-
11196
- var HISTORY_METHODS = ['pushState', 'replaceState'];
11197
- var WRAPPED_MARKER = '__mpApvWrapped__';
11198
- // All APV state hangs off one window key, and it is the public debugging
11199
- // contract: `window.__mpApv__` is what you inspect in a console to see whether
11200
- // tracking is live.
11201
- //
11202
- // It lives on `window` rather than in module scope because Next.js re-executes
11203
- // the SDK bundle on every SPA navigation: module-level state is reset each time,
11204
- // but `window` persists for the lifetime of the tab.
11205
- //
11206
- // One object rather than a key per flag, so resetting is a single reassignment.
11207
- // Nothing has to be deleted, and there is no ordering in which half the state
11208
- // survives the reset.
11209
- var WIN_APV_KEY = '__mpApv__';
11210
- // ---------------------------------------------------------------------------
11211
- // Pure helpers. No `this`, no globals, no side effects — callable and assertable
11212
- // on their own, which is where the interesting rules live.
11213
- // ---------------------------------------------------------------------------
11214
- // Dedup keys on pathname only: a query-string- or hash-only change is the same
11215
- // page and must not fire a view.
11216
- var isNewPage = function isNewPage(lastPath, candidatePath) {
11217
- return candidatePath !== lastPath;
11218
- };
11219
- var supportsHistoryTracking = function supportsHistoryTracking(win) {
11220
- return !!win && win.history !== undefined && typeof win.history.pushState === 'function' && typeof win.addEventListener === 'function';
11221
- };
11222
- // Mirrors the event shape of the public mParticle.logPageView(), but carries the
11223
- // path captured when the navigation was accepted rather than the live location.
11224
- var buildPageViewEvent = function buildPageViewEvent(data) {
11225
- return {
11226
- messageType: MessageType$1.PageView,
11227
- name: 'PageView',
11228
- data: __assign({}, data),
11229
- eventType: EventType.Unknown
11230
- };
11231
- };
11232
- // ---------------------------------------------------------------------------
11233
- // Window-scoped APV state. Every read and write goes through these helpers, so
11234
- // neither the tracker nor mp-instance touches `window` directly and
11235
- // `typeof window` is checked in exactly one place.
11236
- // ---------------------------------------------------------------------------
11237
- var apvWindow = function apvWindow() {
11238
- return typeof window === 'undefined' ? null : window;
11239
- };
11240
- var freshState = function freshState() {
11241
- return {
11242
- initialPageViewFired: false
11243
- };
11244
- };
11245
- // Reads tolerate absent state, so a read never mutates `window`. Only the
11246
- // writers below create it.
11247
- var readState = function readState() {
11248
- var win = apvWindow();
11249
- return win ? win[WIN_APV_KEY] : undefined;
11250
- };
11251
- var writeState = function writeState() {
11252
- var win = apvWindow();
11253
- if (!win) {
11254
- return null;
11255
- }
11256
- if (!win[WIN_APV_KEY]) {
11257
- win[WIN_APV_KEY] = freshState();
11258
- }
11259
- return win[WIN_APV_KEY];
11260
- };
11261
- var getActiveTracker = function getActiveTracker() {
11262
- var state = readState();
11263
- return state ? state.tracker : undefined;
11264
- };
11265
- var hasInitialPageViewFired = function hasInitialPageViewFired() {
11266
- var state = readState();
11267
- return !!(state && state.initialPageViewFired);
11268
- };
11269
- var markInitialPageViewFired = function markInitialPageViewFired() {
11270
- var state = writeState();
11271
- if (state) {
11272
- state.initialPageViewFired = true;
11273
- }
11274
- };
11275
- // Returns the page to its pre-APV state: stops the active tracker (restoring the
11276
- // history methods it patched), then swaps in fresh state. The state object is the
11277
- // authoritative handle, so this reaches trackers whose owning module is gone.
11278
- var resetPageViewTracking = function resetPageViewTracking() {
11279
- var win = apvWindow();
11280
- if (!win) {
11281
- return;
11282
- }
11283
- var active = getActiveTracker();
11284
- if (active) {
11285
- active.teardown();
11286
- }
11287
- // One reassignment replaces every flag at once, so nothing can be left behind
11288
- // whatever shape the previous state was in.
11289
- win[WIN_APV_KEY] = freshState();
11290
- };
11291
- var setActiveTracker = function setActiveTracker(tracker) {
11292
- var state = writeState();
11293
- if (state) {
11294
- state.tracker = tracker;
11295
- }
11296
- };
11297
- var clearActiveTracker = function clearActiveTracker(tracker) {
11298
- var state = readState();
11299
- if (state && state.tracker === tracker) {
11300
- state.tracker = undefined;
11301
- }
11302
- };
11303
- var currentPathname = function currentPathname() {
11304
- return window.location.pathname;
11305
- };
11306
- // ---------------------------------------------------------------------------
11307
- // History patching
11308
- // ---------------------------------------------------------------------------
11309
- // Wraps pushState/replaceState so `onNavigate` runs after the real method, and
11310
- // returns the single function that undoes it — or null when nothing was
11311
- // installed (history is already wrapped, or a frozen/sealed History rejected the
11312
- // assignment). Handing back one undo closure keeps the wrapper and original
11313
- // references out of the tracker entirely: there is no half-patched state for a
11314
- // caller to inspect, and no way to restore the wrong pair.
11315
- var patchHistory = function patchHistory(onNavigate, log) {
11316
- if (window.history.pushState[WRAPPED_MARKER]) {
11317
- log('[patch] history already wrapped, skipping to avoid double-wrap — STACKED WRAPPER DETECTED');
11318
- return null;
11319
- }
11320
- var originals = {};
11321
- var wrappers = {};
11322
- HISTORY_METHODS.forEach(function (name) {
11323
- var original = window.history[name];
11324
- originals[name] = original;
11325
- var wrapper = function wrapper() {
11326
- var args = [];
11327
- for (var _i = 0; _i < arguments.length; _i++) {
11328
- args[_i] = arguments[_i];
11329
- }
11330
- var result = original.apply(this, args);
11331
- onNavigate(name);
11332
- return result;
11333
- };
11334
- Object.defineProperty(wrapper, WRAPPED_MARKER, {
11335
- value: true,
11336
- enumerable: false
11337
- });
11338
- wrappers[name] = wrapper;
11339
- });
11340
- // Restore per method: a third party may have patched one of the two on top of
11341
- // ours after we installed. Clobbering theirs would break their tracking, so
11342
- // leave anything that is no longer ours in place.
11343
- var restore = function restore() {
11344
- return HISTORY_METHODS.forEach(function (name) {
11345
- if (window.history[name] === wrappers[name]) {
11346
- window.history[name] = originals[name];
11347
- log("[teardown] restored original ".concat(name));
11348
- } else {
11349
- log("[teardown] ".concat(name, " no longer ours; leaving in place, gating callback to no-op"));
11350
- }
11276
+ if (isValidHashedEmailIdentityType) {
11277
+ hashedEmail = finalUserIdentities[this.mappedEmailShaIdentityType];
11278
+ if (hashedEmail && !enrichedAttributes.emailsha256 && !enrichedAttributes[this.mappedEmailShaIdentityType]) {
11279
+ enrichedAttributes.emailsha256 = hashedEmail;
11280
+ }
11281
+ }
11282
+ if (!enrichedAttributes[PASSBACK_CONVERSION_TRACKING_ID]) {
11283
+ (_p = this.integrationCapture) === null || _p === void 0 ? void 0 : _p.capture();
11284
+ capturedAttrs = (_q = this.integrationCapture) === null || _q === void 0 ? void 0 : _q.getClickIdsAsIntegrationAttributes();
11285
+ passbackId = (_r = capturedAttrs === null || capturedAttrs === void 0 ? void 0 : capturedAttrs[PARTNER_MODULE_IDS.Rokt]) === null || _r === void 0 ? void 0 : _r[PASSBACK_CONVERSION_TRACKING_ID];
11286
+ if (passbackId) {
11287
+ enrichedAttributes[PASSBACK_CONVERSION_TRACKING_ID] = passbackId;
11288
+ }
11289
+ }
11290
+ this.filters.filteredUser = this.currentUser || this.filters.filteredUser || null;
11291
+ enrichedOptions = __assign(__assign({}, options), {
11292
+ attributes: enrichedAttributes
11293
+ });
11294
+ return [2 /*return*/, this.kit.selectPlacements(enrichedOptions)];
11295
+ } catch (error) {
11296
+ return [2 /*return*/, Promise.reject(error instanceof Error ? error : new Error('Unknown error occurred'))];
11297
+ }
11298
+ return [2 /*return*/];
11299
+ });
11351
11300
  });
11352
11301
  };
11353
- try {
11354
- HISTORY_METHODS.forEach(function (name) {
11355
- window.history[name] = wrappers[name];
11302
+ /**
11303
+ * Hashes attributes and returns both original and hashed versions
11304
+ * with Rokt-compatible key names (like emailsha256, mobilesha256)
11305
+ *
11306
+ *
11307
+ * @param {RoktAttributes} attributes - Attributes to hash
11308
+ * @returns {Promise<RoktAttributes>} Object with both original and hashed attributes
11309
+ *
11310
+ */
11311
+ RoktManager.prototype.hashAttributes = function (attributes) {
11312
+ return __awaiter(this, void 0, void 0, function () {
11313
+ var keys, hashPromises, results, hashedAttributes, _i, results_1, _a, key, attributeValue, hashedValue, error_1;
11314
+ var _this = this;
11315
+ return __generator(this, function (_b) {
11316
+ switch (_b.label) {
11317
+ case 0:
11318
+ _b.trys.push([0, 2,, 3]);
11319
+ if (!attributes || _typeof$1(attributes) !== 'object') {
11320
+ return [2 /*return*/, {}];
11321
+ }
11322
+ keys = Object.keys(attributes);
11323
+ if (keys.length === 0) {
11324
+ return [2 /*return*/, {}];
11325
+ }
11326
+ hashPromises = keys.map(function (key) {
11327
+ return __awaiter(_this, void 0, void 0, function () {
11328
+ var attributeValue, hashedValue;
11329
+ return __generator(this, function (_a) {
11330
+ switch (_a.label) {
11331
+ case 0:
11332
+ attributeValue = attributes[key];
11333
+ return [4 /*yield*/, this.hashSha256(attributeValue)];
11334
+ case 1:
11335
+ hashedValue = _a.sent();
11336
+ return [2 /*return*/, {
11337
+ key: key,
11338
+ attributeValue: attributeValue,
11339
+ hashedValue: hashedValue
11340
+ }];
11341
+ }
11342
+ });
11343
+ });
11344
+ });
11345
+ return [4 /*yield*/, Promise.all(hashPromises)];
11346
+ case 1:
11347
+ results = _b.sent();
11348
+ hashedAttributes = {};
11349
+ for (_i = 0, results_1 = results; _i < results_1.length; _i++) {
11350
+ _a = results_1[_i], key = _a.key, attributeValue = _a.attributeValue, hashedValue = _a.hashedValue;
11351
+ hashedAttributes[key] = attributeValue;
11352
+ if (hashedValue) {
11353
+ hashedAttributes["".concat(key, "sha256")] = hashedValue;
11354
+ }
11355
+ }
11356
+ return [2 /*return*/, hashedAttributes];
11357
+ case 2:
11358
+ error_1 = _b.sent();
11359
+ this.logger.error("Failed to hashAttributes, returning an empty object: ".concat(getErrorMessage(error_1)));
11360
+ return [2 /*return*/, {}];
11361
+ case 3:
11362
+ return [2 /*return*/];
11363
+ }
11364
+ });
11356
11365
  });
11357
- } catch (e) {
11358
- log("[error] failed to patch history methods (frozen/sealed), rolling back: ".concat(e));
11366
+ };
11367
+
11368
+ RoktManager.prototype.setExtensionData = function (extensionData) {
11369
+ if (!this.isReady()) {
11370
+ this.deferredCall('setExtensionData', extensionData);
11371
+ return;
11372
+ }
11359
11373
  try {
11360
- restore();
11361
- } catch (restoreError) {
11362
- log("[error] failed to restore history methods after patch failure: ".concat(restoreError));
11374
+ this.kit.setExtensionData(extensionData);
11375
+ } catch (error) {
11376
+ throw new Error('Error setting extension data: ' + getErrorMessage(error));
11363
11377
  }
11364
- return null;
11365
- }
11366
- return restore;
11367
- };
11368
- // ---------------------------------------------------------------------------
11369
- // Tracker
11370
- // ---------------------------------------------------------------------------
11371
- var PageViewTracker = /** @class */function () {
11372
- function PageViewTracker(mpInstance) {
11373
- this.lastPath = null;
11374
- this.active = false;
11375
- this.pendingNavigations = [];
11376
- this.undoHistoryPatch = null;
11377
- this.popStateListener = null;
11378
- this.mpInstance = mpInstance;
11379
- }
11380
- Object.defineProperty(PageViewTracker.prototype, "isActive", {
11381
- // True while this tracker owns the history patch and is listening for
11382
- // navigations. Flips to false on teardown, including the teardown a
11383
- // successor tracker performs during a handoff. Read-only, and the one thing
11384
- // worth checking on `window.__mpApv__.tracker` from a console.
11385
- get: function get() {
11386
- return this.active;
11387
- },
11388
- enumerable: false,
11389
- configurable: true
11390
- });
11391
- PageViewTracker.prototype.init = function () {
11392
- var _this = this;
11393
- this.log('[init] PageViewTracker Init');
11394
- if (!supportsHistoryTracking(apvWindow())) {
11395
- this.log('[init] unsupported environment (no History API), not starting');
11378
+ };
11379
+ RoktManager.prototype.use = function (name) {
11380
+ if (!this.isReady()) {
11381
+ return this.deferredCall('use', name);
11382
+ }
11383
+ try {
11384
+ return this.kit.use(name);
11385
+ } catch (error) {
11386
+ return Promise.reject(error instanceof Error ? error : new Error('Error using extension: ' + name));
11387
+ }
11388
+ };
11389
+ RoktManager.prototype.onShoppableAdsReady = function (callback) {
11390
+ if (!this.kit || !this.isShoppableAdsLoaded) {
11391
+ this.deferredCall('onShoppableAdsReady', callback);
11396
11392
  return;
11397
11393
  }
11398
- // Take over from whichever tracker is currently active: one left behind by
11399
- // a previous module load, or this same tracker on a repeat
11400
- // mParticle.init(). Their pending navigations are transferred rather than
11401
- // dropped — a route change may have queued a deferred fire that has not
11402
- // flushed yet, and dropping it loses a page view entirely.
11403
- var active = getActiveTracker();
11404
- var inheritedPaths = this.retire(active);
11405
- if (this.active && active !== this) {
11406
- inheritedPaths = inheritedPaths.concat(this.retire(this));
11394
+ try {
11395
+ this.kit.onShoppableAdsReady(callback);
11396
+ } catch (error) {
11397
+ this.logger.error("Failed to register onShoppableAdsReady callback: ".concat(getErrorMessage(error)));
11407
11398
  }
11408
- this.active = true;
11409
- this.lastPath = currentPathname();
11410
- this.log("[init] seeded lastPath: ".concat(this.lastPath));
11411
- this.undoHistoryPatch = patchHistory(function (source) {
11412
- return _this.safeHandleNavigation(source);
11413
- }, function (message) {
11414
- return _this.log(message);
11399
+ };
11400
+ RoktManager.prototype.flushOnShoppableAdsReadyMessageQueue = function (kit) {
11401
+ var _this = this;
11402
+ this.kit = kit;
11403
+ this.messageQueue.forEach(function (message, key) {
11404
+ var _a;
11405
+ if (message.methodName === ON_SHOPPABLE_ADS_READY_METHOD) {
11406
+ try {
11407
+ kit.onShoppableAdsReady(message.payload);
11408
+ } catch (e) {
11409
+ (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('RoktManager: Error flushing onShoppableAdsReady: ' + e);
11410
+ }
11411
+ _this.messageQueue["delete"](key);
11412
+ }
11415
11413
  });
11416
- this.popStateListener = function () {
11417
- return _this.safeHandleNavigation('popstate');
11418
- };
11419
- window.addEventListener('popstate', this.popStateListener);
11420
- setActiveTracker(this);
11421
- this.log('[init] patched pushState/replaceState + listening for popstate');
11422
- inheritedPaths.forEach(function (path) {
11423
- return _this.scheduleFire(path);
11414
+ this.isShoppableAdsLoaded = true;
11415
+ };
11416
+ /**
11417
+ * Hashes an attribute using SHA-256
11418
+ *
11419
+ * @param {string | number | boolean | undefined | null} attribute - The value to hash
11420
+ * @returns {Promise<string | undefined | null>} SHA-256 hashed value or undefined/null
11421
+ *
11422
+ */
11423
+ RoktManager.prototype.hashSha256 = function (attribute) {
11424
+ return __awaiter(this, void 0, void 0, function () {
11425
+ var normalizedValue, error_2;
11426
+ return __generator(this, function (_a) {
11427
+ switch (_a.label) {
11428
+ case 0:
11429
+ if (attribute === null || attribute === undefined) {
11430
+ this.logger.warning("hashSha256 received null/undefined as input");
11431
+ return [2 /*return*/, attribute];
11432
+ }
11433
+ _a.label = 1;
11434
+ case 1:
11435
+ _a.trys.push([1, 3,, 4]);
11436
+ normalizedValue = String(attribute).trim().toLocaleLowerCase();
11437
+ return [4 /*yield*/, this.sha256Hex(normalizedValue)];
11438
+ case 2:
11439
+ return [2 /*return*/, _a.sent()];
11440
+ case 3:
11441
+ error_2 = _a.sent();
11442
+ this.logger.error("Failed to hashSha256, returning undefined: ".concat(getErrorMessage(error_2)));
11443
+ return [2 /*return*/, undefined];
11444
+ case 4:
11445
+ return [2 /*return*/];
11446
+ }
11447
+ });
11424
11448
  });
11425
11449
  };
11426
- PageViewTracker.prototype.teardown = function () {
11427
- // Discard pending deferred fires (e.g. AutoLogPageView switched off on
11428
- // re-init). A handoff calls takePendingNavigations() first, so by this
11429
- // point those paths are already transferred rather than dropped.
11430
- this.takePendingNavigations();
11431
- if (this.popStateListener) {
11432
- window.removeEventListener('popstate', this.popStateListener);
11433
- this.popStateListener = null;
11450
+
11451
+ RoktManager.prototype.getLocalSessionAttributes = function () {
11452
+ return this.store.getLocalSessionAttributes();
11453
+ };
11454
+ RoktManager.prototype.setLocalSessionAttribute = function (key, value) {
11455
+ this.store.setLocalSessionAttribute(key, value);
11456
+ };
11457
+ RoktManager.prototype.isReady = function () {
11458
+ // The Rokt Manager is ready when a kit is attached and has a launcher
11459
+ return Boolean(this.kit && this.kit.launcher);
11460
+ };
11461
+ RoktManager.prototype.setUserAttributes = function (attributes) {
11462
+ var reservedAttributes = ['sandbox'];
11463
+ var filteredAttributes = {};
11464
+ for (var key in attributes) {
11465
+ if (attributes.hasOwnProperty(key) && reservedAttributes.indexOf(key) === -1) {
11466
+ var value = attributes[key];
11467
+ filteredAttributes[key] = Array.isArray(value) ? JSON.stringify(value) : value;
11468
+ }
11469
+ }
11470
+ try {
11471
+ this.currentUser.setUserAttributes(filteredAttributes);
11472
+ } catch (error) {
11473
+ this.logger.error('Error setting user attributes: ' + error);
11474
+ }
11475
+ };
11476
+ RoktManager.prototype.mapPlacementAttributes = function (attributes, placementAttributesMapping) {
11477
+ var mappingLookup = {};
11478
+ for (var _i = 0, placementAttributesMapping_1 = placementAttributesMapping; _i < placementAttributesMapping_1.length; _i++) {
11479
+ var mapping = placementAttributesMapping_1[_i];
11480
+ mappingLookup[mapping.map] = mapping.value;
11434
11481
  }
11435
- if (this.undoHistoryPatch) {
11436
- this.undoHistoryPatch();
11437
- this.undoHistoryPatch = null;
11482
+ var mappedAttributes = {};
11483
+ for (var key in attributes) {
11484
+ if (attributes.hasOwnProperty(key)) {
11485
+ var newKey = mappingLookup[key] || key;
11486
+ mappedAttributes[newKey] = attributes[key];
11487
+ }
11438
11488
  }
11439
- this.active = false;
11440
- clearActiveTracker(this);
11489
+ return mappedAttributes;
11441
11490
  };
11442
- // Stops the outgoing tracker and returns the paths it had queued so this
11443
- // tracker can re-schedule them once it is active. Private, but reachable
11444
- // across instances: the handoff protocol stays internal to the class.
11445
- PageViewTracker.prototype.retire = function (outgoing) {
11446
- if (!outgoing) {
11447
- return [];
11491
+ RoktManager.prototype.onIdentityComplete = function () {
11492
+ if (this.isReady()) {
11493
+ this.currentUser = this.identityService.getCurrentUser();
11494
+ // Process any queued selectPlacements calls that were waiting for identity
11495
+ this.processMessageQueue();
11448
11496
  }
11449
- this.log(outgoing === this ? '[init] retiring this tracker for a repeat mParticle.init() — transferring pending navigations and tearing down' : '[init] retiring a tracker from a previous module load — transferring pending navigations and tearing down');
11450
- var paths = outgoing.takePendingNavigations();
11451
- outgoing.teardown();
11452
- return paths;
11453
11497
  };
11454
- // Cancels this tracker's deferred fires and hands back their captured paths.
11455
- PageViewTracker.prototype.takePendingNavigations = function () {
11456
- var pending = this.pendingNavigations;
11457
- this.pendingNavigations = [];
11458
- pending.forEach(function (p) {
11459
- return clearTimeout(p.timeoutId);
11498
+ RoktManager.prototype.processMessageQueue = function () {
11499
+ var _this = this;
11500
+ var _a;
11501
+ if (!this.isReady() || this.messageQueue.size === 0) {
11502
+ return;
11503
+ }
11504
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose("RoktManager: Processing ".concat(this.messageQueue.size, " queued messages"));
11505
+ var messagesToProcess = Array.from(this.messageQueue.values());
11506
+ // Clear the queue immediately to prevent re-processing
11507
+ this.messageQueue.clear();
11508
+ messagesToProcess.forEach(function (message) {
11509
+ var _a, _b, _c, _d;
11510
+ if (!(message.methodName in _this) || !isFunction(_this[message.methodName])) {
11511
+ (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error("RoktManager: Method ".concat(message.methodName, " not found"));
11512
+ return;
11513
+ }
11514
+ if ((_b = _this.logger) === null || _b === void 0 ? void 0 : _b.isVerbose()) {
11515
+ var payloadToLog = obfuscateDevData(message.payload, (_d = (_c = _this.store) === null || _c === void 0 ? void 0 : _c.SDKConfig) === null || _d === void 0 ? void 0 : _d.isDevelopmentMode);
11516
+ _this.logger.verbose("RoktManager: Processing queued message: ".concat(message.methodName, " with payload: ").concat(JSON.stringify(payloadToLog)));
11517
+ }
11518
+ // Capture resolve/reject functions before async processing
11519
+ var resolve = message.resolve;
11520
+ var reject = message.reject;
11521
+ var handleError = function handleError(error) {
11522
+ var _a;
11523
+ (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error("RoktManager: Error processing message '".concat(message.methodName, "': ").concat(getErrorMessage(error)));
11524
+ if (reject) {
11525
+ reject(error);
11526
+ }
11527
+ };
11528
+ try {
11529
+ var result = _this[message.methodName](message.payload);
11530
+ // Handle both sync and async methods
11531
+ Promise.resolve(result).then(function (resolvedResult) {
11532
+ if (resolve) {
11533
+ resolve(resolvedResult);
11534
+ }
11535
+ })["catch"](handleError);
11536
+ } catch (error) {
11537
+ handleError(error);
11538
+ }
11460
11539
  });
11461
- return pending.map(function (p) {
11462
- return p.path;
11540
+ };
11541
+ RoktManager.prototype.queueMessage = function (message) {
11542
+ this.messageQueue.set(message.messageId, message);
11543
+ };
11544
+ RoktManager.prototype.deferredCall = function (methodName, payload) {
11545
+ var _this = this;
11546
+ return new Promise(function (resolve, reject) {
11547
+ var messageId = "".concat(methodName, "_").concat(generateUniqueId());
11548
+ _this.queueMessage({
11549
+ messageId: messageId,
11550
+ methodName: methodName,
11551
+ payload: payload,
11552
+ resolve: resolve,
11553
+ reject: reject
11554
+ });
11463
11555
  });
11464
11556
  };
11465
- PageViewTracker.prototype.safeHandleNavigation = function (source) {
11466
- // An orphaned wrapper still calls in: teardown can only restore a history
11467
- // method that is still ours, so one a third party patched over stays
11468
- // installed and keeps firing at a dead tracker. Bail before touching
11469
- // lastPath or queueing a fire the flush would just abort. Guarding here
11470
- // rather than at each wrapper also covers the popstate path.
11471
- if (!this.active) {
11472
- return;
11557
+ /**
11558
+ * Hashes a string input using SHA-256 and returns the hex digest
11559
+ * Uses the Web Crypto API for secure hashing
11560
+ *
11561
+ * @param {string} input - The string to hash
11562
+ * @returns {Promise<string>} The SHA-256 hash as a hexadecimal string
11563
+ */
11564
+ RoktManager.prototype.sha256Hex = function (input) {
11565
+ return __awaiter(this, void 0, void 0, function () {
11566
+ var encoder, encodedInput, digest;
11567
+ return __generator(this, function (_a) {
11568
+ switch (_a.label) {
11569
+ case 0:
11570
+ encoder = new TextEncoder();
11571
+ encodedInput = encoder.encode(input);
11572
+ return [4 /*yield*/, crypto.subtle.digest('SHA-256', encodedInput)];
11573
+ case 1:
11574
+ digest = _a.sent();
11575
+ return [2 /*return*/, this.arrayBufferToHex(digest)];
11576
+ }
11577
+ });
11578
+ });
11579
+ };
11580
+ /**
11581
+ * Converts an ArrayBuffer to a hexadecimal string representation
11582
+ * Each byte is converted to a 2-character hex string with leading zeros
11583
+ *
11584
+ * @param {ArrayBuffer} buffer - The buffer to convert
11585
+ * @returns {string} The hexadecimal string representation
11586
+ */
11587
+ RoktManager.prototype.arrayBufferToHex = function (buffer) {
11588
+ var bytes = new Uint8Array(buffer);
11589
+ var hexString = '';
11590
+ for (var i = 0; i < bytes.length; i++) {
11591
+ var hexByte = bytes[i].toString(16).padStart(2, '0');
11592
+ hexString += hexByte;
11473
11593
  }
11474
- try {
11475
- this.handleNavigation(source);
11476
- } catch (e) {
11477
- this.log("[error] navigation handler threw (".concat(source, "), page view skipped: ").concat(e));
11594
+ return hexString;
11595
+ };
11596
+ /**
11597
+ * Checks if an identity value has changed by comparing current and new values
11598
+ *
11599
+ * @param {string | undefined} currentValue - The current identity value
11600
+ * @param {string | undefined} newValue - The new identity value to compare against
11601
+ * @returns {boolean} True if the identity has changed (new value exists and differs from current), false otherwise
11602
+ */
11603
+ RoktManager.prototype.hasIdentityChanged = function (currentValue, newValue) {
11604
+ if (!newValue) {
11605
+ return false;
11606
+ }
11607
+ if (!currentValue) {
11608
+ return true; // New value exists but no current value
11609
+ }
11610
+
11611
+ if (currentValue !== newValue) {
11612
+ return true; // Values are different
11478
11613
  }
11614
+
11615
+ return false; // Values are the same
11479
11616
  };
11480
- PageViewTracker.prototype.handleNavigation = function (source) {
11481
- var candidatePath = currentPathname();
11482
- this.log("[detect] navigation signal (source: ".concat(source, ", candidatePath: ").concat(candidatePath, ", lastPath: ").concat(this.lastPath, ")"));
11483
- if (!isNewPage(this.lastPath, candidatePath)) {
11484
- this.log("[dedupe] pathname unchanged, skipping (source: ".concat(source, ", path: ").concat(candidatePath, ")"));
11485
- return;
11617
+
11618
+ return RoktManager;
11619
+ }();
11620
+
11621
+ /**
11622
+ * CookieConsentManager handles storage and access of consent flags (noFunctional, noTargeting)
11623
+ * that are passed via launcherOptions during SDK initialization.
11624
+ *
11625
+ * These flags allow Rokt integration to respect user privacy choices.
11626
+ *
11627
+ * Default behavior: Both flags default to false, meaning all features are allowed
11628
+ * unless explicitly opted out by the user.
11629
+ */
11630
+ var CookieConsentManager = /** @class */function () {
11631
+ function CookieConsentManager(flags) {
11632
+ this.flags = flags;
11633
+ this.flags = {
11634
+ noFunctional: flags.noFunctional === true,
11635
+ noTargeting: flags.noTargeting === true
11636
+ };
11637
+ }
11638
+ /**
11639
+ * Returns true if third-party targeting is disabled.
11640
+ * Targeting is allowed when noTargeting is false (default).
11641
+ */
11642
+ CookieConsentManager.prototype.getNoTargeting = function () {
11643
+ return this.flags.noTargeting;
11644
+ };
11645
+ /**
11646
+ * Returns true if functional tracking is disabled.
11647
+ * Functional tracking is allowed when noFunctional is false (default).
11648
+ */
11649
+ CookieConsentManager.prototype.getNoFunctional = function () {
11650
+ return this.flags.noFunctional;
11651
+ };
11652
+ CookieConsentManager.prototype.syncNoTargetingAttribute = function (user) {
11653
+ if (!user) return;
11654
+ if (this.flags.noTargeting) {
11655
+ user.setUserAttribute(NO_TARGETING_ATTRIBUTE, true);
11656
+ } else if (user.getAllUserAttributes().hasOwnProperty(NO_TARGETING_ATTRIBUTE)) {
11657
+ user.removeUserAttribute(NO_TARGETING_ATTRIBUTE);
11486
11658
  }
11487
- this.log("[accept] pathname changed, scheduling fire (source: ".concat(source, ", from: ").concat(this.lastPath, ", to: ").concat(candidatePath, ")"));
11488
- this.lastPath = candidatePath;
11489
- this.scheduleFire(candidatePath);
11490
11659
  };
11491
- // Deferred by a macrotask so the router's post-navigation render commit has a
11492
- // chance to update document.title before the event is built. The path is
11493
- // snapshotted now because it is already settled, whereas a same-tick
11494
- // navigation would overwrite window.location before the flush reads it.
11495
- PageViewTracker.prototype.scheduleFire = function (path) {
11660
+ return CookieConsentManager;
11661
+ }();
11662
+
11663
+ var ErrorReportingDispatcher = /** @class */function () {
11664
+ function ErrorReportingDispatcher() {
11665
+ this.services = [];
11666
+ }
11667
+ ErrorReportingDispatcher.prototype.register = function (service) {
11668
+ this.services.push(service);
11669
+ };
11670
+ ErrorReportingDispatcher.prototype.report = function (error) {
11496
11671
  var _this = this;
11497
- var timeoutId = setTimeout(function () {
11498
- _this.pendingNavigations = _this.pendingNavigations.filter(function (p) {
11499
- return p.timeoutId !== timeoutId;
11500
- });
11501
- if (!_this.active) {
11502
- _this.log('[defer] fire aborted, tracker inactive (torn down before flush)');
11503
- return;
11672
+ this.services.forEach(function (s) {
11673
+ var _a;
11674
+ try {
11675
+ s.report(error);
11676
+ } catch (e) {
11677
+ (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('Error in ErrorReportingService: ' + e);
11504
11678
  }
11505
- _this.mpInstance._SessionManager.resetSessionTimer();
11506
- _this.firePageView(path);
11507
- }, 0);
11508
- this.pendingNavigations.push({
11509
- path: path,
11510
- timeoutId: timeoutId
11511
11679
  });
11512
11680
  };
11513
- PageViewTracker.prototype.firePageView = function (path) {
11514
- var title = window.document.title;
11515
- var event = buildPageViewEvent({
11516
- hostname: window.location.hostname,
11517
- title: title,
11518
- path: path
11519
- });
11520
- this.log("[fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(path, ", title: ").concat(title, ")"));
11521
- this.mpInstance._Events.logEvent(event);
11681
+ return ErrorReportingDispatcher;
11682
+ }();
11683
+
11684
+ var LoggingDispatcher = /** @class */function () {
11685
+ function LoggingDispatcher() {
11686
+ this.services = [];
11687
+ }
11688
+ LoggingDispatcher.prototype.register = function (service) {
11689
+ this.services.push(service);
11522
11690
  };
11523
- PageViewTracker.prototype.log = function (message) {
11524
- this.mpInstance.Logger.verbose("mParticle APV: ".concat(message));
11691
+ LoggingDispatcher.prototype.log = function (entry) {
11692
+ var _this = this;
11693
+ this.services.forEach(function (s) {
11694
+ var _a;
11695
+ try {
11696
+ s.log(entry);
11697
+ } catch (e) {
11698
+ (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('Error in LoggingService: ' + e);
11699
+ }
11700
+ });
11525
11701
  };
11526
- return PageViewTracker;
11702
+ return LoggingDispatcher;
11527
11703
  }();
11528
11704
 
11529
11705
  var Messages = Constants.Messages,