@mparticle/web-sdk 2.79.1 → 2.79.3

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.1";
207
+ var version = "2.79.3";
208
208
 
209
209
  var Constants = {
210
210
  sdkVersion: version,
@@ -3866,7 +3866,7 @@ var mParticle = (function () {
3866
3866
  filteredUserIdentities.push(identity);
3867
3867
  }
3868
3868
  }
3869
- function buildFilteredUserIdentities(userIdentitiesObject, filterList, inArray) {
3869
+ function buildFilteredUserIdentities$1(userIdentitiesObject, filterList, inArray) {
3870
3870
  var filteredUserIdentities = [];
3871
3871
  if (!userIdentitiesObject || !Object.keys(userIdentitiesObject).length) {
3872
3872
  return filteredUserIdentities;
@@ -3988,7 +3988,7 @@ var mParticle = (function () {
3988
3988
  return xhr;
3989
3989
  };
3990
3990
  this.filterUserIdentities = function (userIdentitiesObject, filterList) {
3991
- return buildFilteredUserIdentities(userIdentitiesObject, filterList, self.inArray);
3991
+ return buildFilteredUserIdentities$1(userIdentitiesObject, filterList, self.inArray);
3992
3992
  };
3993
3993
  this.filterUserIdentitiesForForwarders = KitFilterHelper.filterUserIdentities;
3994
3994
  this.filterUserAttributes = KitFilterHelper.filterUserAttributes;
@@ -4056,6 +4056,15 @@ var mParticle = (function () {
4056
4056
  var Messages$8 = Constants.Messages;
4057
4057
  var androidBridgeNameBase = 'mParticleAndroid';
4058
4058
  var iosBridgeNameBase = 'mParticle';
4059
+ function getAndroidBridge(name) {
4060
+ if (!Object.prototype.hasOwnProperty.call(window, name)) {
4061
+ return undefined;
4062
+ }
4063
+ return window[name];
4064
+ }
4065
+ function getInstanceNamedValue(instance, name) {
4066
+ return instance[name];
4067
+ }
4059
4068
  function NativeSdkHelpers(mpInstance) {
4060
4069
  var self = this;
4061
4070
  this.initializeSessionAttributes = function (apiKey) {
@@ -4074,14 +4083,14 @@ var mParticle = (function () {
4074
4083
  }
4075
4084
  };
4076
4085
  this.isBridgeV2Available = function (bridgeName) {
4086
+ var _a, _b;
4077
4087
  if (!bridgeName) {
4078
4088
  return false;
4079
4089
  }
4080
4090
  var androidBridgeName = androidBridgeNameBase + '_' + bridgeName + '_v2';
4081
4091
  var iosBridgeName = iosBridgeNameBase + '_' + bridgeName + '_v2';
4082
-
4083
4092
  // iOS v2 bridge
4084
- if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.hasOwnProperty(iosBridgeName)) {
4093
+ if ((_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.hasOwnProperty(iosBridgeName)) {
4085
4094
  return true;
4086
4095
  }
4087
4096
  // other iOS v2 bridge
@@ -4101,7 +4110,6 @@ var mParticle = (function () {
4101
4110
  if (minWebviewBridgeVersion === 2) {
4102
4111
  return mpInstance._Store.bridgeV2Available;
4103
4112
  }
4104
-
4105
4113
  // iOS BridgeV1 can be available via mParticle.isIOS, but return false if uiwebviewBridgeName doesn't match requiredWebviewBridgeName
4106
4114
  if (window.mParticle) {
4107
4115
  if (window.mParticle.uiwebviewBridgeName && window.mParticle.uiwebviewBridgeName !== iosBridgeNameBase + '_' + requiredWebviewBridgeName + '_v2') {
@@ -4150,19 +4158,19 @@ var mParticle = (function () {
4150
4158
  iframe.parentNode.removeChild(iframe);
4151
4159
  };
4152
4160
  this.sendViaBridgeV2 = function (path, value, requiredWebviewBridgeName) {
4161
+ var _a, _b;
4153
4162
  if (!requiredWebviewBridgeName) {
4154
4163
  return;
4155
4164
  }
4156
4165
  var androidBridgeName = androidBridgeNameBase + '_' + requiredWebviewBridgeName + '_v2',
4157
- androidBridge = window[androidBridgeName],
4158
- iosBridgeName = iosBridgeNameBase + '_' + requiredWebviewBridgeName + '_v2',
4159
- iOSBridgeMessageHandler,
4160
- iOSBridgeNonMessageHandler;
4161
- if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers[iosBridgeName]) {
4166
+ androidBridge = getAndroidBridge(androidBridgeName),
4167
+ iosBridgeName = iosBridgeNameBase + '_' + requiredWebviewBridgeName + '_v2';
4168
+ var iOSBridgeMessageHandler, iOSBridgeNonMessageHandler;
4169
+ if ((_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b[iosBridgeName]) {
4162
4170
  iOSBridgeMessageHandler = window.webkit.messageHandlers[iosBridgeName];
4163
4171
  }
4164
4172
  if (mpInstance.uiwebviewBridgeName === iosBridgeName) {
4165
- iOSBridgeNonMessageHandler = mpInstance[iosBridgeName];
4173
+ iOSBridgeNonMessageHandler = getInstanceNamedValue(mpInstance, iosBridgeName);
4166
4174
  }
4167
4175
  if (androidBridge && androidBridge.hasOwnProperty(path)) {
4168
4176
  mpInstance.Logger.verbose(Messages$8.InformationMessages.SendAndroid + path);
@@ -6979,20 +6987,61 @@ var mParticle = (function () {
6979
6987
 
6980
6988
  var _regeneratorRuntime = /*@__PURE__*/getDefaultExportFromCjs(regenerator);
6981
6989
 
6990
+ function isAttributeKeyAllowed(kitBlocker, key) {
6991
+ return !(kitBlocker === null || kitBlocker === void 0 ? void 0 : kitBlocker.isAttributeKeyBlocked(key));
6992
+ }
6993
+ function isIdentityAllowed(kitBlocker, identityName) {
6994
+ return !(kitBlocker === null || kitBlocker === void 0 ? void 0 : kitBlocker.isIdentityBlocked(identityName));
6995
+ }
6996
+ function copyUserAttributeValue(value) {
6997
+ return Array.isArray(value) ? value.slice() : value;
6998
+ }
6999
+ function buildUserAttributesCopy(userAttributes, kitBlocker) {
7000
+ var userAttributesCopy = {};
7001
+ if (!userAttributes) {
7002
+ return userAttributesCopy;
7003
+ }
7004
+ for (var prop in userAttributes) {
7005
+ if (!userAttributes.hasOwnProperty(prop) || !isAttributeKeyAllowed(kitBlocker, prop)) {
7006
+ continue;
7007
+ }
7008
+ userAttributesCopy[prop] = copyUserAttributeValue(userAttributes[prop]);
7009
+ }
7010
+ return userAttributesCopy;
7011
+ }
7012
+ function buildUserAttributeLists(userAttributes, kitBlocker) {
7013
+ var userAttributesLists = {};
7014
+ for (var key in userAttributes) {
7015
+ if (!userAttributes.hasOwnProperty(key) || !Array.isArray(userAttributes[key]) || !isAttributeKeyAllowed(kitBlocker, key)) {
7016
+ continue;
7017
+ }
7018
+ userAttributesLists[key] = userAttributes[key].slice();
7019
+ }
7020
+ return userAttributesLists;
7021
+ }
7022
+ function buildFilteredUserIdentities(identities, kitBlocker, parseNumber) {
7023
+ var currentUserIdentities = {};
7024
+ var identitiesByType = identities;
7025
+ for (var identityType in identitiesByType) {
7026
+ if (!identitiesByType.hasOwnProperty(identityType)) {
7027
+ continue;
7028
+ }
7029
+ var identityName = Types.IdentityType.getIdentityName(parseNumber(identityType));
7030
+ if (!isIdentityAllowed(kitBlocker, identityName)) {
7031
+ continue;
7032
+ }
7033
+ currentUserIdentities[identityName] = identitiesByType[identityType];
7034
+ }
7035
+ return currentUserIdentities;
7036
+ }
6982
7037
  function filteredMparticleUser(mpid, forwarder, mpInstance, kitBlocker) {
6983
- var self = this;
7038
+ function getAllUserAttributes() {
7039
+ var userAttributesCopy = buildUserAttributesCopy(mpInstance._Store.getUserAttributes(mpid), kitBlocker);
7040
+ return mpInstance._Helpers.filterUserAttributes(userAttributesCopy, forwarder.userAttributeFilters);
7041
+ }
6984
7042
  return {
6985
7043
  getUserIdentities: function getUserIdentities() {
6986
- var currentUserIdentities = {};
6987
- var identities = mpInstance._Store.getUserIdentities(mpid);
6988
- for (var identityType in identities) {
6989
- if (identities.hasOwnProperty(identityType)) {
6990
- var identityName = Types.IdentityType.getIdentityName(mpInstance._Helpers.parseNumber(identityType));
6991
- if (!kitBlocker || kitBlocker && !kitBlocker.isIdentityBlocked(identityName))
6992
- //if identity type is not blocked
6993
- currentUserIdentities[identityName] = identities[identityType];
6994
- }
6995
- }
7044
+ var currentUserIdentities = buildFilteredUserIdentities(mpInstance._Store.getUserIdentities(mpid), kitBlocker, mpInstance._Helpers.parseNumber);
6996
7045
  currentUserIdentities = mpInstance._Helpers.filterUserIdentitiesForForwarders(currentUserIdentities, forwarder.userIdentityFilters);
6997
7046
  return {
6998
7047
  userIdentities: currentUserIdentities
@@ -7002,38 +7051,10 @@ var mParticle = (function () {
7002
7051
  return mpid;
7003
7052
  },
7004
7053
  getUserAttributesLists: function getUserAttributesLists(forwarder) {
7005
- var userAttributes,
7006
- userAttributesLists = {};
7007
- userAttributes = self.getAllUserAttributes();
7008
- for (var key in userAttributes) {
7009
- if (userAttributes.hasOwnProperty(key) && Array.isArray(userAttributes[key])) {
7010
- if (!kitBlocker || kitBlocker && !kitBlocker.isAttributeKeyBlocked(key)) {
7011
- userAttributesLists[key] = userAttributes[key].slice();
7012
- }
7013
- }
7014
- }
7015
- userAttributesLists = mpInstance._Helpers.filterUserAttributes(userAttributesLists, forwarder.userAttributeFilters);
7016
- return userAttributesLists;
7054
+ var userAttributesLists = buildUserAttributeLists(getAllUserAttributes(), kitBlocker);
7055
+ return mpInstance._Helpers.filterUserAttributes(userAttributesLists, forwarder.userAttributeFilters);
7017
7056
  },
7018
- getAllUserAttributes: function getAllUserAttributes() {
7019
- var userAttributesCopy = {};
7020
- var userAttributes = mpInstance._Store.getUserAttributes(mpid);
7021
- if (userAttributes) {
7022
- for (var prop in userAttributes) {
7023
- if (userAttributes.hasOwnProperty(prop)) {
7024
- if (!kitBlocker || kitBlocker && !kitBlocker.isAttributeKeyBlocked(prop)) {
7025
- if (Array.isArray(userAttributes[prop])) {
7026
- userAttributesCopy[prop] = userAttributes[prop].slice();
7027
- } else {
7028
- userAttributesCopy[prop] = userAttributes[prop];
7029
- }
7030
- }
7031
- }
7032
- }
7033
- }
7034
- userAttributesCopy = mpInstance._Helpers.filterUserAttributes(userAttributesCopy, forwarder.userAttributeFilters);
7035
- return userAttributesCopy;
7036
- }
7057
+ getAllUserAttributes: getAllUserAttributes
7037
7058
  };
7038
7059
  }
7039
7060
 
@@ -7839,7 +7860,7 @@ var mParticle = (function () {
7839
7860
 
7840
7861
  function forwardingStatsUploader(mpInstance) {
7841
7862
  this.startForwardingStatsTimer = function () {
7842
- mParticle._forwardingStatsTimer = setInterval(function () {
7863
+ window.mParticle._forwardingStatsTimer = setInterval(function () {
7843
7864
  prepareAndSendForwardingStatsBatch();
7844
7865
  }, mpInstance._Store.SDKConfig.forwarderStatsTimeout);
7845
7866
  };
@@ -7859,12 +7880,12 @@ var mParticle = (function () {
7859
7880
  if (uploadsTable.hasOwnProperty(date)) {
7860
7881
  if (uploadsTable[date].uploading === false) {
7861
7882
  var xhrCallback = function xhrCallback() {
7862
- if (xhr.readyState === 4) {
7863
- if (xhr.status === 200 || xhr.status === 202) {
7864
- mpInstance.Logger.verbose('Successfully sent ' + xhr.statusText + ' from server');
7883
+ if (xhr_1.readyState === 4) {
7884
+ if (xhr_1.status === 200 || xhr_1.status === 202) {
7885
+ mpInstance.Logger.verbose('Successfully sent ' + xhr_1.statusText + ' from server');
7865
7886
  delete uploadsTable[date];
7866
- } else if (xhr.status.toString()[0] === '4') {
7867
- if (xhr.status !== 429) {
7887
+ } else if (xhr_1.status.toString()[0] === '4') {
7888
+ if (xhr_1.status !== 429) {
7868
7889
  delete uploadsTable[date];
7869
7890
  }
7870
7891
  } else {
@@ -7872,10 +7893,10 @@ var mParticle = (function () {
7872
7893
  }
7873
7894
  }
7874
7895
  };
7875
- var xhr = mpInstance._Helpers.createXHR(xhrCallback);
7896
+ var xhr_1 = mpInstance._Helpers.createXHR(xhrCallback);
7876
7897
  var forwardingStatsData = uploadsTable[date].data;
7877
7898
  uploadsTable[date].uploading = true;
7878
- mpInstance._APIClient.sendBatchForwardingStatsToServer(forwardingStatsData, xhr);
7899
+ mpInstance._APIClient.sendBatchForwardingStatsToServer(forwardingStatsData, xhr_1);
7879
7900
  }
7880
7901
  }
7881
7902
  })(date);
@@ -11172,184 +11193,335 @@ var mParticle = (function () {
11172
11193
  return LoggingDispatcher;
11173
11194
  }();
11174
11195
 
11196
+ var HISTORY_METHODS = ['pushState', 'replaceState'];
11175
11197
  var WRAPPED_MARKER = '__mpApvWrapped__';
11176
- var PageViewTracker = /** @class */function () {
11177
- function PageViewTracker(mpInstance) {
11178
- this.lastPath = null;
11179
- this.isActive = false;
11180
- this.originalPushState = null;
11181
- this.originalReplaceState = null;
11182
- this.pushStateWrapper = null;
11183
- this.replaceStateWrapper = null;
11184
- this.popStateListener = null;
11185
- this.mpInstance = mpInstance;
11186
- }
11187
- PageViewTracker.prototype.isSupportedEnvironment = function () {
11188
- return typeof window !== 'undefined' && typeof window.history !== 'undefined' && typeof window.history.pushState === 'function' && typeof window.addEventListener === 'function';
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
11189
11230
  };
11190
- PageViewTracker.prototype.init = function () {
11191
- this.mpInstance.Logger.verbose('mParticle APV: [init] PageViewTracker Init');
11192
- if (!this.isSupportedEnvironment()) {
11193
- this.mpInstance.Logger.verbose('mParticle APV: [init] unsupported environment (no History API), not starting');
11194
- return;
11195
- }
11196
- if (this.isActive) {
11197
- this.mpInstance.Logger.verbose('mParticle APV: [init] starting (teardown-first for idempotency)');
11198
- this.teardown();
11199
- }
11200
- this.isActive = true;
11201
- this.lastPath = this.getCurrentKey();
11202
- this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath));
11203
- this.patchHistoryMethods();
11204
- this.addNavigationListeners();
11205
- this.mpInstance.Logger.verbose('mParticle APV: [init] patched pushState/replaceState + listening for popstate');
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
11206
11243
  };
11207
- PageViewTracker.prototype.patchHistoryMethods = function () {
11208
- var self = this;
11209
- var installed = window.history.pushState;
11210
- if (installed[WRAPPED_MARKER]) {
11211
- this.mpInstance.Logger.verbose('mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap');
11212
- return;
11213
- }
11214
- var originalPushState = window.history.pushState;
11215
- var originalReplaceState = window.history.replaceState;
11216
- this.originalPushState = originalPushState;
11217
- this.originalReplaceState = originalReplaceState;
11218
- var pushStateWrapper = function pushStateWrapper() {
11219
- var args = [];
11220
- for (var _i = 0; _i < arguments.length; _i++) {
11221
- args[_i] = arguments[_i];
11222
- }
11223
- var result = originalPushState.apply(this, args);
11224
- self.safeHandleNavigation('pushState');
11225
- return result;
11226
- };
11227
- var replaceStateWrapper = function replaceStateWrapper() {
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() {
11228
11326
  var args = [];
11229
11327
  for (var _i = 0; _i < arguments.length; _i++) {
11230
11328
  args[_i] = arguments[_i];
11231
11329
  }
11232
- var result = originalReplaceState.apply(this, args);
11233
- self.safeHandleNavigation('replaceState');
11330
+ var result = original.apply(this, args);
11331
+ onNavigate(name);
11234
11332
  return result;
11235
11333
  };
11236
- Object.defineProperty(pushStateWrapper, WRAPPED_MARKER, {
11334
+ Object.defineProperty(wrapper, WRAPPED_MARKER, {
11237
11335
  value: true,
11238
11336
  enumerable: false
11239
11337
  });
11240
- Object.defineProperty(replaceStateWrapper, WRAPPED_MARKER, {
11241
- value: true,
11242
- enumerable: false
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
+ }
11351
+ });
11352
+ };
11353
+ try {
11354
+ HISTORY_METHODS.forEach(function (name) {
11355
+ window.history[name] = wrappers[name];
11243
11356
  });
11244
- this.pushStateWrapper = pushStateWrapper;
11245
- this.replaceStateWrapper = replaceStateWrapper;
11357
+ } catch (e) {
11358
+ log("[error] failed to patch history methods (frozen/sealed), rolling back: ".concat(e));
11246
11359
  try {
11247
- window.history.pushState = pushStateWrapper;
11248
- window.history.replaceState = replaceStateWrapper;
11249
- } catch (e) {
11250
- this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(e));
11251
- try {
11252
- if (window.history.pushState === pushStateWrapper) {
11253
- window.history.pushState = originalPushState;
11254
- }
11255
- if (window.history.replaceState === replaceStateWrapper) {
11256
- window.history.replaceState = originalReplaceState;
11257
- }
11258
- } catch (restoreError) {
11259
- this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(restoreError));
11260
- }
11261
- this.pushStateWrapper = null;
11262
- this.replaceStateWrapper = null;
11263
- this.originalPushState = null;
11264
- this.originalReplaceState = null;
11360
+ restore();
11361
+ } catch (restoreError) {
11362
+ log("[error] failed to restore history methods after patch failure: ".concat(restoreError));
11265
11363
  }
11266
- };
11267
- PageViewTracker.prototype.addNavigationListeners = function () {
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 () {
11268
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');
11396
+ return;
11397
+ }
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));
11407
+ }
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);
11415
+ });
11269
11416
  this.popStateListener = function () {
11270
11417
  return _this.safeHandleNavigation('popstate');
11271
11418
  };
11272
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);
11424
+ });
11425
+ };
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;
11434
+ }
11435
+ if (this.undoHistoryPatch) {
11436
+ this.undoHistoryPatch();
11437
+ this.undoHistoryPatch = null;
11438
+ }
11439
+ this.active = false;
11440
+ clearActiveTracker(this);
11441
+ };
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 [];
11448
+ }
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
+ };
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);
11460
+ });
11461
+ return pending.map(function (p) {
11462
+ return p.path;
11463
+ });
11273
11464
  };
11274
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;
11473
+ }
11275
11474
  try {
11276
11475
  this.handleNavigation(source);
11277
11476
  } catch (e) {
11278
- this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(source, "), page view skipped: ").concat(e));
11477
+ this.log("[error] navigation handler threw (".concat(source, "), page view skipped: ").concat(e));
11279
11478
  }
11280
11479
  };
11281
- PageViewTracker.prototype.getCurrentKey = function () {
11282
- return window.location.pathname;
11283
- };
11284
11480
  PageViewTracker.prototype.handleNavigation = function (source) {
11285
- var _this = this;
11286
- var candidatePath = this.getCurrentKey();
11287
- this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(source, ", candidatePath: ").concat(candidatePath, ", lastPath: ").concat(this.lastPath, ")"));
11288
- if (candidatePath === this.lastPath) {
11289
- this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(source, ", path: ").concat(candidatePath, ")"));
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, ")"));
11290
11485
  return;
11291
11486
  }
11292
- this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(source, ", from: ").concat(this.lastPath, ", to: ").concat(candidatePath, ")"));
11487
+ this.log("[accept] pathname changed, scheduling fire (source: ".concat(source, ", from: ").concat(this.lastPath, ", to: ").concat(candidatePath, ")"));
11293
11488
  this.lastPath = candidatePath;
11294
- // Snapshot the path now: it is already settled at this point, and a
11295
- // same-tick navigation would otherwise overwrite window.location
11296
- // before the deferred flush reads it. The title is intentionally read
11297
- // later (in the deferred flush) so the router's post-navigation render
11298
- // commit has a chance to update document.title first.
11299
- var capturedPath = candidatePath;
11300
- setTimeout(function () {
11301
- if (!_this.isActive) {
11302
- _this.mpInstance.Logger.verbose('mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)');
11489
+ this.scheduleFire(candidatePath);
11490
+ };
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) {
11496
+ 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)');
11303
11503
  return;
11304
11504
  }
11305
11505
  _this.mpInstance._SessionManager.resetSessionTimer();
11306
- _this.firePageView(capturedPath);
11506
+ _this.firePageView(path);
11307
11507
  }, 0);
11508
+ this.pendingNavigations.push({
11509
+ path: path,
11510
+ timeoutId: timeoutId
11511
+ });
11308
11512
  };
11309
- // Mirrors the event shape of the public mParticle.logPageView(), but carries
11310
- // the captured SPA path rather than reading the live location.
11311
11513
  PageViewTracker.prototype.firePageView = function (path) {
11312
11514
  var title = window.document.title;
11313
- this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(path, ", title: ").concat(title, ")"));
11314
- this.mpInstance._Events.logEvent({
11315
- messageType: MessageType$1.PageView,
11316
- name: 'PageView',
11317
- data: {
11318
- hostname: window.location.hostname,
11319
- title: title,
11320
- path: path
11321
- },
11322
- eventType: EventType.Unknown
11515
+ var event = buildPageViewEvent({
11516
+ hostname: window.location.hostname,
11517
+ title: title,
11518
+ path: path
11323
11519
  });
11520
+ this.log("[fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(path, ", title: ").concat(title, ")"));
11521
+ this.mpInstance._Events.logEvent(event);
11324
11522
  };
11325
- PageViewTracker.prototype.teardown = function () {
11326
- if (this.popStateListener) {
11327
- window.removeEventListener('popstate', this.popStateListener);
11328
- this.popStateListener = null;
11329
- }
11330
- var pushStateStillOurs = this.pushStateWrapper !== null && window.history.pushState === this.pushStateWrapper;
11331
- if (this.originalPushState) {
11332
- if (pushStateStillOurs) {
11333
- window.history.pushState = this.originalPushState;
11334
- this.mpInstance.Logger.verbose('mParticle APV: [teardown] restored original pushState');
11335
- } else {
11336
- this.mpInstance.Logger.verbose('mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op');
11337
- }
11338
- this.originalPushState = null;
11339
- this.pushStateWrapper = null;
11340
- }
11341
- var replaceStateStillOurs = this.replaceStateWrapper !== null && window.history.replaceState === this.replaceStateWrapper;
11342
- if (this.originalReplaceState) {
11343
- if (replaceStateStillOurs) {
11344
- window.history.replaceState = this.originalReplaceState;
11345
- this.mpInstance.Logger.verbose('mParticle APV: [teardown] restored original replaceState');
11346
- } else {
11347
- this.mpInstance.Logger.verbose('mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op');
11348
- }
11349
- this.originalReplaceState = null;
11350
- this.replaceStateWrapper = null;
11351
- }
11352
- this.isActive = false;
11523
+ PageViewTracker.prototype.log = function (message) {
11524
+ this.mpInstance.Logger.verbose("mParticle APV: ".concat(message));
11353
11525
  };
11354
11526
  return PageViewTracker;
11355
11527
  }();
@@ -11506,6 +11678,8 @@ var mParticle = (function () {
11506
11678
  instance._Store = new Store(config, instance);
11507
11679
  instance._Store.isLocalStorageAvailable = instance._Persistence.determineLocalStorageAvailability(window.localStorage);
11508
11680
  instance._Events.stopTracking();
11681
+ resetPageViewTracking();
11682
+ instance._PageViewTracker = undefined;
11509
11683
  if (!keepPersistence) {
11510
11684
  instance._Persistence.resetPersistence();
11511
11685
  }
@@ -12432,13 +12606,28 @@ var mParticle = (function () {
12432
12606
  // Logs a session start or session end event accordingly
12433
12607
  mpInstance._SessionManager.initialize();
12434
12608
  mpInstance._Events.logAST();
12609
+ // Note: APV state (the active tracker and the initial-page-view flag) is
12610
+ // window-scoped and assumes a single active SDK instance. Multiple-instance
12611
+ // support is out of scope.
12435
12612
  if (getFeatureFlag(AutoLogPageView)) {
12436
- mpInstance._Events.logPageView();
12437
12613
  if (!mpInstance._PageViewTracker) {
12614
+ mpInstance.Logger.verbose('mParticle APV: [sdk-init] creating new PageViewTracker for this instance');
12438
12615
  mpInstance._PageViewTracker = new PageViewTracker(mpInstance);
12439
12616
  }
12617
+ // The initial page view fires once per hard page load, not once per
12618
+ // init(): the window flag survives the module re-evaluation Next.js
12619
+ // performs on every SPA navigation, so repeated init() calls from SPA
12620
+ // re-renders don't fire duplicate logPageView() events.
12621
+ if (hasInitialPageViewFired()) {
12622
+ mpInstance.Logger.verbose('mParticle APV: [sdk-init] initial page view already logged this page load, skipping');
12623
+ } else {
12624
+ markInitialPageViewFired();
12625
+ mpInstance._Events.logPageView();
12626
+ }
12440
12627
  mpInstance._PageViewTracker.init();
12441
12628
  } else if (mpInstance._PageViewTracker) {
12629
+ // AutoLogPageView was disabled on re-init: tear down the active tracker
12630
+ // so it stops listening. Re-created if the flag is re-enabled later.
12442
12631
  mpInstance._PageViewTracker.teardown();
12443
12632
  }
12444
12633
  processIdentityCallback(mpInstance, currentUser, currentUserMPID, currentUserIdentities);