@mparticle/web-sdk 3.6.2 → 3.7.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 = "3.6.2";
207
+ var version = "3.7.0";
208
208
 
209
209
  var Constants = {
210
210
  sdkVersion: version,
@@ -547,7 +547,7 @@ var mParticle = (function () {
547
547
  var findKeyInObject = function findKeyInObject(obj, key) {
548
548
  if (key && obj) {
549
549
  for (var prop in obj) {
550
- if (obj.hasOwnProperty(prop) && prop.toLowerCase() === key.toLowerCase()) {
550
+ if (hasOwnProp(obj, prop) && prop.toLowerCase() === key.toLowerCase()) {
551
551
  return prop;
552
552
  }
553
553
  }
@@ -704,10 +704,14 @@ var mParticle = (function () {
704
704
  '/': '_',
705
705
  '=': ''
706
706
  };
707
+ // btoa is defined only over Latin-1 and throws InvalidCharacterError above U+00FF
708
+ var isLatin1 = function isLatin1(value) {
709
+ return !/[^\u0000-\u00ff]/.test(value);
710
+ };
707
711
  var toWebSafeBase64 = function toWebSafeBase64(value) {
708
- return btoa(value).replace(/[+/=]/g, function (c) {
712
+ return isLatin1(value) ? btoa(value).replace(/[+/=]/g, function (c) {
709
713
  return WEB_SAFE_BASE64_REPLACEMENTS[c];
710
- });
714
+ }) : undefined;
711
715
  };
712
716
  // FIXME: REFACTOR for V3
713
717
  // only used in store.js to sanitize server-side formatting of
@@ -773,6 +777,23 @@ var mParticle = (function () {
773
777
  var hasOwnProp = function hasOwnProp(obj, key) {
774
778
  return Object.prototype.hasOwnProperty.call(obj, key);
775
779
  };
780
+ // Assigning `__proto__` replaces the target's prototype instead of adding an own
781
+ // property; `constructor` is reserved alongside it by the stored-record contract.
782
+ // `prototype` is deliberately absent: isValidKeyValue accepts it, so setUserAttribute
783
+ // persists it and the getters must hand it back.
784
+ var isReservedStoredPropertyName = function isReservedStoredPropertyName(name) {
785
+ return name === '__proto__' || name === 'constructor';
786
+ };
787
+ // extend's historical set, which additionally refuses `prototype` when merging.
788
+ var isUncopyableMergeName = function isUncopyableMergeName(name) {
789
+ return isReservedStoredPropertyName(name) || name === 'prototype';
790
+ };
791
+ // Kit-facing copies stay compatible with conventional own-property iteration, such as
792
+ // attributes.hasOwnProperty(key), which is the one read pattern an attribute of that name
793
+ // interferes with. The public getters keep the name; only this filter drops it.
794
+ var isReservedKitPropertyName = function isReservedKitPropertyName(name) {
795
+ return isReservedStoredPropertyName(name) || name === 'hasOwnProperty';
796
+ };
776
797
  var queryStringParser = function queryStringParser(url, keys) {
777
798
  if (keys === void 0) {
778
799
  keys = [];
@@ -877,7 +898,7 @@ var mParticle = (function () {
877
898
  var filtered = {};
878
899
  if (!isEmpty(dictionary)) {
879
900
  for (var key in dictionary) {
880
- if (dictionary.hasOwnProperty(key)) {
901
+ if (hasOwnProp(dictionary, key) && !isReservedKitPropertyName(key)) {
881
902
  var hashedKey = hashFn(key);
882
903
  if (!inArray(filterList, hashedKey)) {
883
904
  filtered[key] = dictionary[key];
@@ -1013,7 +1034,7 @@ var mParticle = (function () {
1013
1034
  for (; i < length; i++) {
1014
1035
  if ((options = args[i]) != null) {
1015
1036
  for (name in options) {
1016
- if (name === '__proto__' || name === 'constructor' || name === 'prototype') {
1037
+ if (isUncopyableMergeName(name)) {
1017
1038
  continue;
1018
1039
  }
1019
1040
  if (!Object.prototype.hasOwnProperty.call(options, name)) {
@@ -1484,6 +1505,17 @@ var mParticle = (function () {
1484
1505
  JointSdkSelectPlacements: 'mp:jointSdkSelectPlacements',
1485
1506
  JointSdkRoktKitInit: 'mp:jointSdkRoktKitInit'
1486
1507
  };
1508
+ // A stored `ui` key is the decimal string of an IdentityType; anything else was
1509
+ // not written by this SDK. The round-trip comparison is load bearing: parseNumber
1510
+ // returns 0 for any non-numeric key and IdentityType.Other is 0, so after parsing
1511
+ // alone a stored "hasOwnProperty" is indistinguishable from a genuine Other entry.
1512
+ function getIdentityTypeFromStoredKey(key) {
1513
+ var identityType = parseNumber(key);
1514
+ if (String(identityType) !== key || !IdentityType.isValid(identityType)) {
1515
+ return null;
1516
+ }
1517
+ return identityType;
1518
+ }
1487
1519
  var Types = {
1488
1520
  MessageType: MessageType$1,
1489
1521
  EventType: EventType,
@@ -3462,8 +3494,7 @@ var mParticle = (function () {
3462
3494
  }
3463
3495
  var cacheKey = concatenateIdentities(method, proposedUserIdentities);
3464
3496
  var hashedKey = generateHash(cacheKey);
3465
- // if cache doesn't have the cacheKey, there is no valid cached identity
3466
- if (!cache.hasOwnProperty(hashedKey)) {
3497
+ if (!hasOwnProp(cache, hashedKey)) {
3467
3498
  return false;
3468
3499
  }
3469
3500
  // If there is a valid cache key, compare the expireTimestamp to the current time.
@@ -4223,57 +4254,65 @@ var mParticle = (function () {
4223
4254
  }
4224
4255
  pixelConfigurations.forEach(function (pixelSettings) {
4225
4256
  var _a, _b, _c;
4226
- // set requiresConsent to false to start each additional pixel configuration
4227
- // set to true only if filteringConsenRuleValues.values.length exists
4228
- var requiresConsent = false;
4229
- // Filtering rules as defined in UI
4230
- var filteringConsentRuleValues = pixelSettings.filteringConsentRuleValues,
4231
- pixelUrl = pixelSettings.pixelUrl,
4232
- redirectUrl = pixelSettings.redirectUrl,
4233
- moduleId = pixelSettings.moduleId,
4234
- settings = pixelSettings.settings,
4235
- // Tells you how often we should do a cookie sync (in days)
4236
- frequencyCap = pixelSettings.frequencyCap;
4237
- var values = (filteringConsentRuleValues || {}).values;
4238
- if (isEmpty(pixelUrl)) {
4239
- return;
4240
- }
4241
- if (!isEmpty(values)) {
4242
- requiresConsent = true;
4243
- }
4244
- // If MPID is new to cookies, we should not try to perform the cookie sync
4245
- // because a cookie sync can only occur once a user either consents or doesn't.
4246
- // we should not check if it's enabled if the user has a blank consent
4247
- if (requiresConsent && mpidIsNotInCookies) {
4248
- return;
4249
- }
4250
- // For Rokt, block cookie sync when noTargeting privacy flag is true
4251
- if (moduleId === PARTNER_MODULE_IDS.Rokt && mpInstance._CookieConsentManager.getNoTargeting()) {
4252
- return;
4253
- }
4254
- var isEnabledForUserConsent = mpInstance._Consent.isEnabledForUserConsent;
4255
- if (!isEnabledForUserConsent(filteringConsentRuleValues, mpInstance.Identity.getCurrentUser())) {
4256
- return;
4257
- }
4258
- var cookieSyncDates = (_b = (_a = persistence[mpid]) === null || _a === void 0 ? void 0 : _a.csd) !== null && _b !== void 0 ? _b : {};
4259
- var lastSyncDateForModule = cookieSyncDates[moduleId] || null;
4260
- if (!isLastSyncDateExpired(frequencyCap, lastSyncDateForModule)) {
4261
- return;
4257
+ try {
4258
+ // set requiresConsent to false to start each additional pixel configuration
4259
+ // set to true only if filteringConsenRuleValues.values.length exists
4260
+ var requiresConsent = false;
4261
+ // Filtering rules as defined in UI
4262
+ var filteringConsentRuleValues = pixelSettings.filteringConsentRuleValues,
4263
+ pixelUrl = pixelSettings.pixelUrl,
4264
+ redirectUrl = pixelSettings.redirectUrl,
4265
+ moduleId = pixelSettings.moduleId,
4266
+ settings = pixelSettings.settings,
4267
+ // Tells you how often we should do a cookie sync (in days)
4268
+ frequencyCap = pixelSettings.frequencyCap;
4269
+ var values = (filteringConsentRuleValues || {}).values;
4270
+ if (isEmpty(pixelUrl)) {
4271
+ return;
4272
+ }
4273
+ if (!isEmpty(values)) {
4274
+ requiresConsent = true;
4275
+ }
4276
+ // If MPID is new to cookies, we should not try to perform the cookie sync
4277
+ // because a cookie sync can only occur once a user either consents or doesn't.
4278
+ // we should not check if it's enabled if the user has a blank consent
4279
+ if (requiresConsent && mpidIsNotInCookies) {
4280
+ return;
4281
+ }
4282
+ // For Rokt, block cookie sync when noTargeting privacy flag is true
4283
+ if (moduleId === PARTNER_MODULE_IDS.Rokt && mpInstance._CookieConsentManager.getNoTargeting()) {
4284
+ return;
4285
+ }
4286
+ var isEnabledForUserConsent = mpInstance._Consent.isEnabledForUserConsent;
4287
+ if (!isEnabledForUserConsent(filteringConsentRuleValues, mpInstance.Identity.getCurrentUser())) {
4288
+ return;
4289
+ }
4290
+ var cookieSyncDates = (_b = (_a = persistence[mpid]) === null || _a === void 0 ? void 0 : _a.csd) !== null && _b !== void 0 ? _b : {};
4291
+ var lastSyncDateForModule = cookieSyncDates[moduleId] || null;
4292
+ if (!isLastSyncDateExpired(frequencyCap, lastSyncDateForModule)) {
4293
+ return;
4294
+ }
4295
+ // The Trade Desk requires a URL parameter for GDPR enabled users.
4296
+ // It is optional but to simplify the code, we add it for all Trade
4297
+ // // Desk cookie syncs.
4298
+ var domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
4299
+ // Google Marketing Platform accepts a web-safe base64-encoded MPID via the
4300
+ // `google_hm` query parameter, but only for accounts provisioned for Google
4301
+ // Hosted Matching — Google returns errors otherwise. It is therefore gated
4302
+ // behind the opt-in `enableHmTag` setting (off by default), which the server
4303
+ // delivers as the string 'True' when the UI toggle is enabled.
4304
+ var enableHmTag = ((_c = settings === null || settings === void 0 ? void 0 : settings.enableHmTag) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'true';
4305
+ var isDoubleClickModule = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP;
4306
+ var usesHostedMatching = isDoubleClickModule && enableHmTag;
4307
+ var base64Mpid = usesHostedMatching ? toWebSafeBase64(mpid) : undefined;
4308
+ if (usesHostedMatching && !base64Mpid) {
4309
+ mpInstance.Logger.warning('Unable to base64 encode the current MPID; omitting the google_hm parameter from this cookie sync');
4310
+ }
4311
+ var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid);
4312
+ self.performCookieSync(fullUrl, moduleId.toString(), mpid, cookieSyncDates);
4313
+ } catch (e) {
4314
+ mpInstance.Logger.error('Error performing cookie sync: ' + e);
4262
4315
  }
4263
- // The Trade Desk requires a URL parameter for GDPR enabled users.
4264
- // It is optional but to simplify the code, we add it for all Trade
4265
- // // Desk cookie syncs.
4266
- var domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
4267
- // Google Marketing Platform accepts a web-safe base64-encoded MPID via the
4268
- // `google_hm` query parameter, but only for accounts provisioned for Google
4269
- // Hosted Matching — Google returns errors otherwise. It is therefore gated
4270
- // behind the opt-in `enableHmTag` setting (off by default), which the server
4271
- // delivers as the string 'True' when the UI toggle is enabled.
4272
- var enableHmTag = ((_c = settings === null || settings === void 0 ? void 0 : settings.enableHmTag) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'true';
4273
- var isDoubleClickModule = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP;
4274
- var base64Mpid = isDoubleClickModule && enableHmTag ? toWebSafeBase64(mpid) : undefined;
4275
- var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid);
4276
- self.performCookieSync(fullUrl, moduleId.toString(), mpid, cookieSyncDates);
4277
4316
  });
4278
4317
  };
4279
4318
  // Private
@@ -5259,7 +5298,7 @@ var mParticle = (function () {
5259
5298
  }
5260
5299
  _this.syncPersistenceData();
5261
5300
  if (_this.persistenceData) {
5262
- if (_this.persistenceData[mpid]) {
5301
+ if (isObject(_this.persistenceData[mpid])) {
5263
5302
  _this.persistenceData[mpid][key] = value;
5264
5303
  } else {
5265
5304
  _this.persistenceData[mpid] = (_a = {}, _a[key] = value, _a);
@@ -6237,7 +6276,7 @@ var mParticle = (function () {
6237
6276
  if (csd) {
6238
6277
  var persistence = self.getPersistence();
6239
6278
  if (persistence) {
6240
- if (persistence[mpid]) {
6279
+ if (mpInstance._Helpers.isObject(persistence[mpid])) {
6241
6280
  persistence[mpid].csd = csd;
6242
6281
  } else {
6243
6282
  persistence[mpid] = {
@@ -6318,7 +6357,7 @@ var mParticle = (function () {
6318
6357
  }
6319
6358
  var persistence = self.getPersistence();
6320
6359
  if (persistence) {
6321
- if (!persistence[mpid]) {
6360
+ if (!mpInstance._Helpers.isObject(persistence[mpid])) {
6322
6361
  persistence[mpid] = {};
6323
6362
  }
6324
6363
  if (!persistence[mpid].fst) {
@@ -6356,7 +6395,7 @@ var mParticle = (function () {
6356
6395
  time = new Date().getTime();
6357
6396
  }
6358
6397
  var persistence = self.getPersistence();
6359
- if (persistence && persistence[mpid]) {
6398
+ if (persistence && mpInstance._Helpers.isObject(persistence[mpid])) {
6360
6399
  persistence[mpid].lst = time;
6361
6400
  self.savePersistence(persistence);
6362
6401
  }
@@ -6445,6 +6484,14 @@ var mParticle = (function () {
6445
6484
  var allowedQueryParams = function allowedQueryParams(href) {
6446
6485
  return queryStringParser(href, ALLOWED_QUERY_PARAMS);
6447
6486
  };
6487
+ var AUTO_PAGE_VIEW_ATTRIBUTE = 'is_auto_page_view';
6488
+ // Absent rather than false on a manual page view, so the attribute is only ever
6489
+ // added by the automatic emitters. Shared by both of them so the key and that
6490
+ // rule have one definition instead of two that can drift.
6491
+ var autoPageViewAttribute = function autoPageViewAttribute(isAutoPageView) {
6492
+ var _a;
6493
+ return isAutoPageView ? (_a = {}, _a[AUTO_PAGE_VIEW_ATTRIBUTE] = true, _a) : {};
6494
+ };
6448
6495
  // The captured params, in allowlist order. Ordering comes from the constant
6449
6496
  // rather than a sort: it is deterministic without needing a comparator, and it
6450
6497
  // does not depend on the object's insertion order, so reordering the query string
@@ -6492,18 +6539,19 @@ var mParticle = (function () {
6492
6539
  var params = _a.params,
6493
6540
  hostname = _a.hostname,
6494
6541
  title = _a.title,
6495
- path = _a.path;
6542
+ path = _a.path,
6543
+ isAutoPageView = _a.isAutoPageView;
6496
6544
  return {
6497
6545
  messageType: MessageType$1.PageView,
6498
6546
  name: 'PageView',
6499
6547
  // Params spread first, then the core fields by name, so a core field always
6500
6548
  // wins. No allowlist entry collides with hostname/title/path today; naming
6501
6549
  // them here is what keeps a later addition from silently overwriting one.
6502
- data: __assign(__assign({}, params), {
6550
+ data: __assign(__assign(__assign({}, params), {
6503
6551
  hostname: hostname,
6504
6552
  title: title,
6505
6553
  path: path
6506
- }),
6554
+ }), autoPageViewAttribute(isAutoPageView)),
6507
6555
  eventType: EventType.Unknown
6508
6556
  };
6509
6557
  };
@@ -6663,13 +6711,14 @@ var mParticle = (function () {
6663
6711
  // Tracker
6664
6712
  // ---------------------------------------------------------------------------
6665
6713
  var PageViewTracker = /** @class */function () {
6666
- function PageViewTracker(mpInstance) {
6714
+ function PageViewTracker(mpInstance, options) {
6667
6715
  this.lastPage = null;
6668
6716
  this.active = false;
6669
6717
  this.pendingNavigations = [];
6670
6718
  this.undoHistoryPatch = null;
6671
6719
  this.popStateListener = null;
6672
6720
  this.mpInstance = mpInstance;
6721
+ this.isAutoPageView = !!(options && options.isAutoPageView);
6673
6722
  }
6674
6723
  Object.defineProperty(PageViewTracker.prototype, "isActive", {
6675
6724
  // True while this tracker owns the history patch and is listening for
@@ -6812,7 +6861,8 @@ var mParticle = (function () {
6812
6861
  var title = window.document.title;
6813
6862
  var event = buildPageViewEvent(__assign(__assign({}, page), {
6814
6863
  hostname: window.location.hostname,
6815
- title: title
6864
+ title: title,
6865
+ isAutoPageView: this.isAutoPageView
6816
6866
  }));
6817
6867
  this.log("[fire] deferred flush -> _Events.logEvent(PageView) (page: ".concat(describePage(page), ", title: ").concat(title, ")"));
6818
6868
  this.mpInstance._Events.logEvent(event);
@@ -6904,14 +6954,14 @@ var mParticle = (function () {
6904
6954
  // come from PageViewTracker instead, so both emitters attach the same
6905
6955
  // allowlisted query params — and this is the one that matters for campaign
6906
6956
  // attribution, since utm_*/gclid live on the entry URL.
6907
- this.logPageView = function () {
6957
+ this.logPageView = function (options) {
6908
6958
  self.logEvent({
6909
6959
  messageType: Types.MessageType.PageView,
6910
6960
  name: 'PageView',
6911
- data: __assign(__assign({}, allowedQueryParams(getHref())), {
6961
+ data: __assign(__assign(__assign({}, allowedQueryParams(getHref())), {
6912
6962
  hostname: window.location.hostname,
6913
6963
  title: window.document.title
6914
- }),
6964
+ }), autoPageViewAttribute(options && options.isAutoPageView)),
6915
6965
  eventType: Types.EventType.Unknown
6916
6966
  });
6917
6967
  };
@@ -7095,7 +7145,7 @@ var mParticle = (function () {
7095
7145
  return userAttributesCopy;
7096
7146
  }
7097
7147
  for (var prop in userAttributes) {
7098
- if (!userAttributes.hasOwnProperty(prop) || !isAttributeKeyAllowed(kitBlocker, prop)) {
7148
+ if (!hasOwnProp(userAttributes, prop) || isReservedStoredPropertyName(prop) || !isAttributeKeyAllowed(kitBlocker, prop)) {
7099
7149
  continue;
7100
7150
  }
7101
7151
  userAttributesCopy[prop] = copyUserAttributeValue(userAttributes[prop]);
@@ -7105,21 +7155,27 @@ var mParticle = (function () {
7105
7155
  function buildUserAttributeLists(userAttributes, kitBlocker) {
7106
7156
  var userAttributesLists = {};
7107
7157
  for (var key in userAttributes) {
7108
- if (!userAttributes.hasOwnProperty(key) || !Array.isArray(userAttributes[key]) || !isAttributeKeyAllowed(kitBlocker, key)) {
7158
+ if (!hasOwnProp(userAttributes, key) || isReservedStoredPropertyName(key) || !Array.isArray(userAttributes[key]) || !isAttributeKeyAllowed(kitBlocker, key)) {
7109
7159
  continue;
7110
7160
  }
7111
7161
  userAttributesLists[key] = userAttributes[key].slice();
7112
7162
  }
7113
7163
  return userAttributesLists;
7114
7164
  }
7115
- function buildFilteredUserIdentities(identities, kitBlocker, parseNumber) {
7165
+ function buildFilteredUserIdentities(identities, kitBlocker) {
7116
7166
  var currentUserIdentities = {};
7117
7167
  var identitiesByType = identities;
7118
7168
  for (var identityType in identitiesByType) {
7119
- if (!identitiesByType.hasOwnProperty(identityType)) {
7169
+ if (!hasOwnProp(identitiesByType, identityType)) {
7120
7170
  continue;
7121
7171
  }
7122
- var identityName = Types.IdentityType.getIdentityName(parseNumber(identityType));
7172
+ var storedIdentityType = getIdentityTypeFromStoredKey(identityType);
7173
+ // Must be `=== null`: IdentityType.Other is 0, so a falsy check would
7174
+ // drop a valid stored Other identity.
7175
+ if (storedIdentityType === null) {
7176
+ continue;
7177
+ }
7178
+ var identityName = Types.IdentityType.getIdentityName(storedIdentityType);
7123
7179
  if (!isIdentityAllowed(kitBlocker, identityName)) {
7124
7180
  continue;
7125
7181
  }
@@ -7134,7 +7190,7 @@ var mParticle = (function () {
7134
7190
  }
7135
7191
  return {
7136
7192
  getUserIdentities: function getUserIdentities() {
7137
- var currentUserIdentities = buildFilteredUserIdentities(mpInstance._Store.getUserIdentities(mpid), kitBlocker, mpInstance._Helpers.parseNumber);
7193
+ var currentUserIdentities = buildFilteredUserIdentities(mpInstance._Store.getUserIdentities(mpid), kitBlocker);
7138
7194
  currentUserIdentities = mpInstance._Helpers.filterUserIdentitiesForForwarders(currentUserIdentities, forwarder.userIdentityFilters);
7139
7195
  return {
7140
7196
  userIdentities: currentUserIdentities
@@ -7245,7 +7301,7 @@ var mParticle = (function () {
7245
7301
  }
7246
7302
  function userAttributesMatchFilter(userAttributes, filterObject) {
7247
7303
  for (var attrName in userAttributes) {
7248
- if (!userAttributes.hasOwnProperty(attrName)) {
7304
+ if (!hasOwnProp(userAttributes, attrName)) {
7249
7305
  continue;
7250
7306
  }
7251
7307
  var attrHash = KitFilterHelper.hashAttributeConditionalForwarding(attrName);
@@ -8596,9 +8652,16 @@ var mParticle = (function () {
8596
8652
  var currentUserIdentities = {};
8597
8653
  var identities = mpInstance._Store.getUserIdentities(mpid);
8598
8654
  for (var identityType in identities) {
8599
- if (identities.hasOwnProperty(identityType)) {
8600
- currentUserIdentities[Types.IdentityType.getIdentityName(mpInstance._Helpers.parseNumber(identityType))] = identities[identityType];
8655
+ if (!hasOwnProp(identities, identityType)) {
8656
+ continue;
8601
8657
  }
8658
+ var storedIdentityType = getIdentityTypeFromStoredKey(identityType);
8659
+ // Must be `=== null`: IdentityType.Other is 0, so a falsy
8660
+ // check would drop a valid stored Other identity.
8661
+ if (storedIdentityType === null) {
8662
+ continue;
8663
+ }
8664
+ currentUserIdentities[Types.IdentityType.getIdentityName(storedIdentityType)] = identities[identityType];
8602
8665
  }
8603
8666
  return {
8604
8667
  userIdentities: currentUserIdentities
@@ -8727,7 +8790,7 @@ var mParticle = (function () {
8727
8790
  }
8728
8791
  var deletedUAKeyCopy = userAttributes[key] ? userAttributes[key].toString() : null;
8729
8792
  delete userAttributes[key];
8730
- if (cookies && cookies[mpid]) {
8793
+ if (cookies && isObject(cookies[mpid])) {
8731
8794
  cookies[mpid].ua = userAttributes;
8732
8795
  mpInstance._Persistence.savePersistence(cookies);
8733
8796
  }
@@ -8810,7 +8873,7 @@ var mParticle = (function () {
8810
8873
  mpInstance._Forwarders.initForwarders(self.IdentityAPI.getCurrentUser().getUserIdentities().userIdentities, mpInstance._APIClient.prepareForwardingStats);
8811
8874
  if (userAttributes) {
8812
8875
  for (var prop in userAttributes) {
8813
- if (userAttributes.hasOwnProperty(prop)) {
8876
+ if (hasOwnProp(userAttributes, prop)) {
8814
8877
  mpInstance._Forwarders.handleForwarderUserAttributes('removeUserAttribute', prop, null);
8815
8878
  }
8816
8879
  this.removeUserAttribute(prop);
@@ -8828,7 +8891,7 @@ var mParticle = (function () {
8828
8891
  var userAttributesLists = {};
8829
8892
  userAttributes = this.getAllUserAttributes();
8830
8893
  for (var key in userAttributes) {
8831
- if (userAttributes.hasOwnProperty(key) && Array.isArray(userAttributes[key])) {
8894
+ if (hasOwnProp(userAttributes, key) && !isReservedStoredPropertyName(key) && Array.isArray(userAttributes[key])) {
8832
8895
  userAttributesLists[key] = userAttributes[key].slice();
8833
8896
  }
8834
8897
  }
@@ -8845,7 +8908,7 @@ var mParticle = (function () {
8845
8908
  var userAttributes = getUserAttributes(mpid);
8846
8909
  if (userAttributes) {
8847
8910
  for (var prop in userAttributes) {
8848
- if (userAttributes.hasOwnProperty(prop)) {
8911
+ if (hasOwnProp(userAttributes, prop) && !isReservedStoredPropertyName(prop)) {
8849
8912
  var attrValue = userAttributes[prop];
8850
8913
  if (Array.isArray(attrValue)) {
8851
8914
  userAttributesCopy[prop] = attrValue.slice();
@@ -9236,14 +9299,14 @@ var mParticle = (function () {
9236
9299
  var state = self.createConsentState();
9237
9300
  if (json.gdpr) {
9238
9301
  for (var purpose in json.gdpr) {
9239
- if (json.gdpr.hasOwnProperty(purpose)) {
9302
+ if (hasOwnProp(json.gdpr, purpose)) {
9240
9303
  var gdprConsent = self.createPrivacyConsent(json.gdpr[purpose].c, json.gdpr[purpose].ts, json.gdpr[purpose].d, json.gdpr[purpose].l, json.gdpr[purpose].h);
9241
9304
  state.addGDPRConsentState(purpose, gdprConsent);
9242
9305
  }
9243
9306
  }
9244
9307
  }
9245
9308
  if (json.ccpa) {
9246
- if (json.ccpa.hasOwnProperty(CCPAPurpose)) {
9309
+ if (hasOwnProp(json.ccpa, CCPAPurpose)) {
9247
9310
  var ccpaConsent = self.createPrivacyConsent(json.ccpa[CCPAPurpose].c, json.ccpa[CCPAPurpose].ts, json.ccpa[CCPAPurpose].d, json.ccpa[CCPAPurpose].l, json.ccpa[CCPAPurpose].h);
9248
9311
  state.setCCPAConsentState(ccpaConsent);
9249
9312
  }
@@ -10268,6 +10331,17 @@ var mParticle = (function () {
10268
10331
  mappedKey: 'SnapchatConversions.ClickId',
10269
10332
  output: IntegrationOutputs.CUSTOM_FLAGS
10270
10333
  },
10334
+ // Pinterest
10335
+ // https://developers.pinterest.com/docs/track-conversions/track-conversions-in-the-api/
10336
+ // https://help.pinterest.com/en/business/article/pinterest-tag-parameters-and-cookies
10337
+ epik: {
10338
+ mappedKey: 'Pinterest.click_id',
10339
+ output: IntegrationOutputs.CUSTOM_FLAGS
10340
+ },
10341
+ _epik: {
10342
+ mappedKey: 'Pinterest.click_id',
10343
+ output: IntegrationOutputs.CUSTOM_FLAGS
10344
+ },
10271
10345
  // Snapchat
10272
10346
  // https://developers.snap.com/api/marketing-api/Conversions-API/UsingTheAPI#sending-click-id
10273
10347
  _scid: {
@@ -10311,6 +10385,7 @@ var mParticle = (function () {
10311
10385
  var queryParams = this.captureQueryParams() || {};
10312
10386
  var cookies = this.captureCookies() || {};
10313
10387
  var localStorage = this.captureLocalStorage() || {};
10388
+ this.applyPinterestRules(queryParams, localStorage, cookies);
10314
10389
  // Facebook Rules
10315
10390
  // Exclude _fbc if fbclid is present
10316
10391
  // https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc#retrieve-from-fbclid-url-query-parameter
@@ -10438,6 +10513,39 @@ var mParticle = (function () {
10438
10513
  }
10439
10514
  return mappedClickIds;
10440
10515
  };
10516
+ IntegrationCapture.prototype.normalizePinterestClickId = function (clickIds) {
10517
+ // Deterministic tie-breaker when both aliases are present in the same source:
10518
+ // keep _epik and drop epik.
10519
+ if (!isEmpty(clickIds === null || clickIds === void 0 ? void 0 : clickIds['_epik']) && !isEmpty(clickIds === null || clickIds === void 0 ? void 0 : clickIds['epik'])) {
10520
+ delete clickIds['epik'];
10521
+ }
10522
+ };
10523
+ IntegrationCapture.prototype.hasPinterestAlias = function (clickIds) {
10524
+ return !isEmpty(clickIds === null || clickIds === void 0 ? void 0 : clickIds['epik']) || !isEmpty(clickIds === null || clickIds === void 0 ? void 0 : clickIds['_epik']);
10525
+ };
10526
+ IntegrationCapture.prototype.applyPinterestRules = function (queryParams, localStorage, cookies) {
10527
+ var _a, _b;
10528
+ this.normalizePinterestClickId(queryParams);
10529
+ this.normalizePinterestClickId(localStorage);
10530
+ this.normalizePinterestClickId(cookies);
10531
+ // Cross-source precedence: query params > localStorage > cookies.
10532
+ // Within the same source, prefer _epik when both aliases are present.
10533
+ if (this.hasPinterestAlias(queryParams) || this.hasPinterestAlias(localStorage) || this.hasPinterestAlias(cookies)) {
10534
+ (_a = this.clickIds) === null || _a === void 0 ? true : delete _a['epik'];
10535
+ (_b = this.clickIds) === null || _b === void 0 ? true : delete _b['_epik'];
10536
+ }
10537
+ if (this.hasPinterestAlias(queryParams)) {
10538
+ delete cookies['epik'];
10539
+ delete cookies['_epik'];
10540
+ delete localStorage['epik'];
10541
+ delete localStorage['_epik'];
10542
+ return;
10543
+ }
10544
+ if (this.hasPinterestAlias(localStorage)) {
10545
+ delete cookies['epik'];
10546
+ delete cookies['_epik'];
10547
+ }
10548
+ };
10441
10549
  IntegrationCapture.prototype.applyProcessors = function (clickIds, url, timestamp) {
10442
10550
  var _a;
10443
10551
  var processedClickIds = {};
@@ -11134,7 +11242,7 @@ var mParticle = (function () {
11134
11242
  if (!user) return;
11135
11243
  if (this.flags.noTargeting) {
11136
11244
  user.setUserAttribute(NO_TARGETING_ATTRIBUTE, true);
11137
- } else if (user.getAllUserAttributes().hasOwnProperty(NO_TARGETING_ATTRIBUTE)) {
11245
+ } else if (hasOwnProp(user.getAllUserAttributes(), NO_TARGETING_ATTRIBUTE)) {
11138
11246
  user.removeUserAttribute(NO_TARGETING_ATTRIBUTE);
11139
11247
  }
11140
11248
  };
@@ -12175,7 +12283,9 @@ var mParticle = (function () {
12175
12283
  if (getFeatureFlag(AutoLogPageView)) {
12176
12284
  if (!mpInstance._PageViewTracker) {
12177
12285
  mpInstance.Logger.verbose('mParticle APV: [sdk-init] creating new PageViewTracker for this instance');
12178
- mpInstance._PageViewTracker = new PageViewTracker(mpInstance);
12286
+ mpInstance._PageViewTracker = new PageViewTracker(mpInstance, {
12287
+ isAutoPageView: true
12288
+ });
12179
12289
  }
12180
12290
  // The initial page view fires once per hard page load, not once per
12181
12291
  // init(): the window flag survives the module re-evaluation Next.js
@@ -12185,7 +12295,9 @@ var mParticle = (function () {
12185
12295
  mpInstance.Logger.verbose('mParticle APV: [sdk-init] initial page view already logged this page load, skipping');
12186
12296
  } else {
12187
12297
  markInitialPageViewFired();
12188
- mpInstance._Events.logPageView();
12298
+ mpInstance._Events.logPageView({
12299
+ isAutoPageView: true
12300
+ });
12189
12301
  }
12190
12302
  mpInstance._PageViewTracker.init();
12191
12303
  } else if (mpInstance._PageViewTracker) {
@@ -1,5 +1,6 @@
1
1
  import { Callback, SDKEventAttrs, SDKEventOptions, TransactionAttributes } from '@mparticle/web-sdk';
2
2
  import { BaseEvent, SDKEvent, SDKEventCustomFlags, SDKImpression, SDKProduct, SDKPromotion } from './sdkRuntimeModels';
3
+ import { IPageViewOptions } from './pageViewTracker';
3
4
  import { valueof } from './utils';
4
5
  import { EventType, ProductActionType, PromotionActionType } from './types';
5
6
  type EventHandlerFunction<T> = (element: HTMLLinkElement | HTMLFormElement) => T;
@@ -10,7 +11,7 @@ export interface IEvents {
10
11
  logEvent(event: BaseEvent, eventOptions?: SDKEventOptions): void;
11
12
  logImpressionEvent(impression: SDKImpression | SDKImpression[], attrs?: SDKEventAttrs, customFlags?: SDKEventCustomFlags, eventOptions?: SDKEventOptions): void;
12
13
  logOptOut(): void;
13
- logPageView(): void;
14
+ logPageView(options?: IPageViewOptions): void;
14
15
  logProductActionEvent(productActionType: valueof<typeof ProductActionType>, product: SDKProduct | SDKProduct[], attrs?: SDKEventAttrs, customFlags?: SDKEventCustomFlags, transactionAttributes?: TransactionAttributes, eventOptions?: SDKEventOptions): void;
15
16
  logPromotionEvent(promotionType: valueof<typeof PromotionActionType>, promotion: SDKPromotion | SDKPromotion[], attrs?: SDKEventAttrs, customFlags?: SDKEventCustomFlags, eventOptions?: SDKEventOptions): void;
16
17
  logPurchaseEvent(transactionAttributes: TransactionAttributes, product: SDKProduct | SDKProduct[], attrs?: SDKEventAttrs, customFlags?: SDKEventCustomFlags): void;
@@ -65,6 +65,9 @@ export default class IntegrationCapture {
65
65
  */
66
66
  getClickIdsAsIntegrationAttributes(): IntegrationAttributes;
67
67
  private getClickIds;
68
+ private normalizePinterestClickId;
69
+ private hasPinterestAlias;
70
+ private applyPinterestRules;
68
71
  private applyProcessors;
69
72
  private filterMappings;
70
73
  /**
@@ -12,12 +12,18 @@ interface IPageSnapshot {
12
12
  interface IPageViewData extends IPageSnapshot {
13
13
  hostname: string;
14
14
  title: string;
15
+ isAutoPageView?: boolean;
16
+ }
17
+ export interface IPageViewOptions {
18
+ isAutoPageView?: boolean;
15
19
  }
16
20
  export declare const allowedQueryParams: (href: string) => Dictionary<string>;
21
+ export declare const AUTO_PAGE_VIEW_ATTRIBUTE = "is_auto_page_view";
22
+ export declare const autoPageViewAttribute: (isAutoPageView?: boolean) => Dictionary<boolean>;
17
23
  export declare const pageKey: (page: IPageSnapshot) => string;
18
24
  export declare const isNewPage: (lastKey: string | null, candidateKey: string) => boolean;
19
25
  export declare const supportsHistoryTracking: (win: Window | null) => boolean;
20
- export declare const buildPageViewEvent: ({ params, hostname, title, path, }: IPageViewData) => BaseEvent;
26
+ export declare const buildPageViewEvent: ({ params, hostname, title, path, isAutoPageView, }: IPageViewData) => BaseEvent;
21
27
  export declare const getActiveTracker: () => PageViewTracker | undefined;
22
28
  export declare const hasInitialPageViewFired: () => boolean;
23
29
  export declare const markInitialPageViewFired: () => void;
@@ -30,7 +36,8 @@ export declare class PageViewTracker {
30
36
  private pendingNavigations;
31
37
  private undoHistoryPatch;
32
38
  private popStateListener;
33
- constructor(mpInstance: IMParticleWebSDKInstance);
39
+ private readonly isAutoPageView;
40
+ constructor(mpInstance: IMParticleWebSDKInstance, options?: IPageViewOptions);
34
41
  get isActive(): boolean;
35
42
  init(): void;
36
43
  teardown(): void;
@@ -99,6 +99,7 @@ export declare const PerformanceMarkType: {
99
99
  };
100
100
  export declare function getMessageTypeFromEventType(eventType: string): number;
101
101
  export declare function getEventCategoryFromCustomEventType(customEventType: string): number;
102
+ export declare function getIdentityTypeFromStoredKey(key: string): number | null;
102
103
  export declare function getIdentityTypeFromBatchKey(key: string): number;
103
104
  declare const _default: {
104
105
  readonly MessageType: {
@@ -28,7 +28,7 @@ declare const replaceQuotesWithApostrophes: (value: string) => string;
28
28
  declare const replaceMPID: (value: string, mpid: MPID) => string;
29
29
  declare const replaceAmpWithAmpersand: (value: string) => string;
30
30
  declare const createCookieSyncUrl: (mpid: MPID, pixelUrl: string, redirectUrl?: string, domain?: string, base64Mpid?: string) => string;
31
- declare const toWebSafeBase64: (value: string) => string;
31
+ declare const toWebSafeBase64: (value: string) => string | undefined;
32
32
  declare const returnConvertedBoolean: (data: string | boolean | number) => boolean;
33
33
  declare const decoded: (s: string) => string;
34
34
  declare const converted: (s: string) => string;
@@ -43,7 +43,8 @@ declare const isStringOrNumber: (value: any) => boolean;
43
43
  declare const isEmpty: (value: Dictionary<any> | string | null | undefined) => boolean;
44
44
  declare const mergeObjects: <T extends object>(...objects: T[]) => T;
45
45
  declare const moveElementToEnd: <T>(array: T[], index: number) => T[];
46
- declare const hasOwnProp: (obj: object, key: string) => boolean;
46
+ declare const hasOwnProp: (obj: object, key: PropertyKey) => boolean;
47
+ declare const isReservedStoredPropertyName: (name: string) => boolean;
47
48
  declare const queryStringParser: (url: string, keys?: string[]) => Dictionary<string>;
48
49
  declare const getCookies: (keys?: string[]) => Dictionary<string>;
49
50
  declare const getHref: () => string;
@@ -83,4 +84,4 @@ declare const obfuscateData: (value: any) => any;
83
84
  declare const obfuscateDevData: (data: any, isDevelopmentMode?: boolean) => any;
84
85
  declare function extend(...args: any[]): any;
85
86
  declare const getErrorMessage: (e: unknown, fallback?: string) => string;
86
- export { createCookieString, revertCookieString, createCookieSyncUrl, toWebSafeBase64, valueof, AttributeValue, converted, decoded, obfuscateDevData, filterDictionaryWithHash, findKeyInObject, generateDeprecationMessage, generateHash, generateUniqueId, getErrorMessage, getRampNumber, inArray, isObject, isStringOrNumber, obfuscateData, parseConfig, parseNumber, parseSettingsString, parseStringOrNumber, replaceCommasWithPipes, replacePipesWithCommas, replaceApostrophesWithQuotes, replaceQuotesWithApostrophes, returnConvertedBoolean, toDataPlanSlug, isString, isNumber, isFunction, isDataPlanSlug, isEmpty, isValidAttributeValue, isValidCustomFlagProperty, mergeObjects, moveElementToEnd, queryStringParser, hasOwnProp, getCookies, getHref, replaceMPID, replaceAmpWithAmpersand, extend, };
87
+ export { createCookieString, revertCookieString, createCookieSyncUrl, toWebSafeBase64, valueof, AttributeValue, converted, decoded, obfuscateDevData, filterDictionaryWithHash, findKeyInObject, generateDeprecationMessage, generateHash, generateUniqueId, getErrorMessage, getRampNumber, inArray, isObject, isStringOrNumber, obfuscateData, parseConfig, parseNumber, parseSettingsString, parseStringOrNumber, replaceCommasWithPipes, replacePipesWithCommas, replaceApostrophesWithQuotes, replaceQuotesWithApostrophes, returnConvertedBoolean, toDataPlanSlug, isString, isNumber, isFunction, isDataPlanSlug, isEmpty, isValidAttributeValue, isValidCustomFlagProperty, mergeObjects, moveElementToEnd, queryStringParser, hasOwnProp, isReservedStoredPropertyName, getCookies, getHref, replaceMPID, replaceAmpWithAmpersand, extend, };