@mparticle/web-sdk 2.76.0 → 2.77.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.
@@ -15,7 +15,7 @@ map:function map(a,b){var c,d,e;if(null===this)throw new TypeError(" this is nul
15
15
  filter:function filter(a/*, thisArg*/){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f<c;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g);}return d},// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
16
16
  isArray:function isArray(a){return "[object Array]"===Object.prototype.toString.call(a)},Base64:Base64$1};
17
17
 
18
- var version = "2.76.0";
18
+ var version = "2.77.0";
19
19
 
20
20
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{DeprecationMessages:{MethodHasBeenDeprecated:"has been deprecated.",MethodMarkedForDeprecationPostfix:"is a deprecated method and will be removed in future releases.",AlternativeMethodPrefix:"Please use the alternate method:"},ErrorMessages:{NoToken:"A token must be specified.",EventNameInvalidType:"Event name must be a valid string value.",EventDataInvalidType:"Event data must be a valid object hash.",LoggingDisabled:"Event logging is currently disabled.",CookieParseError:"Could not parse cookie",EventEmpty:"Event object is null or undefined, cancelling send",APIRequestEmpty:"APIRequest is null or undefined, cancelling send",NoEventType:"Event type must be specified.",TransactionIdRequired:"Transaction ID is required",TransactionRequired:"A transaction attributes object is required",PromotionIdRequired:"Promotion ID is required",BadAttribute:"Attribute value cannot be object or array",BadKey:"Key value cannot be object or array",BadLogPurchase:"Transaction attributes and a product are both required to log a purchase, https://docs.mparticle.com/?javascript#measuring-transactions",AudienceAPINotEnabled:"Your workspace is not enabled to retrieve user audiences."},InformationMessages:{CookieSearch:"Searching for cookie",CookieFound:"Cookie found, parsing values",CookieNotFound:"Cookies not found",CookieSet:"Setting cookie",CookieSync:"Performing cookie sync",SendBegin:"Starting to send event",SendIdentityBegin:"Starting to send event to identity server",SendWindowsPhone:"Sending event to Windows Phone container",SendIOS:"Calling iOS path: ",SendAndroid:"Calling Android JS interface method: ",SendHttp:"Sending event to mParticle HTTP service",SendAliasHttp:"Sending alias request to mParticle HTTP service",SendIdentityHttp:"Sending event to mParticle HTTP service",StartingNewSession:"Starting new Session",StartingLogEvent:"Starting to log event",StartingLogOptOut:"Starting to log user opt in/out",StartingEndSession:"Starting to end session",StartingInitialization:"Starting to initialize",StartingLogCommerceEvent:"Starting to log commerce event",StartingAliasRequest:"Starting to Alias MPIDs",LoadingConfig:"Loading configuration options",AbandonLogEvent:"Cannot log event, logging disabled or developer token not set",AbandonAliasUsers:"Cannot Alias Users, logging disabled or developer token not set",AbandonStartSession:"Cannot start session, logging disabled or developer token not set",AbandonEndSession:"Cannot end session, logging disabled or developer token not set",NoSessionToEnd:"Cannot end session, no active session found"},ValidationMessages:{ModifyIdentityRequestUserIdentitiesPresent:"identityRequests to modify require userIdentities to be present. Request not sent to server. Please fix and try again",IdentityRequesetInvalidKey:"There is an invalid key on your identityRequest object. It can only contain a `userIdentities` object and a `onUserAlias` function. Request not sent to server. Please fix and try again.",OnUserAliasType:"The onUserAlias value must be a function.",UserIdentities:"The userIdentities key must be an object with keys of identityTypes and values of strings. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidKey:"There is an invalid identity key on your `userIdentities` object within the identityRequest. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidValues:"All user identity values must be strings or null. Request not sent to server. Please fix and try again.",AliasMissingMpid:"Alias Request must contain both a destinationMpid and a sourceMpid",AliasNonUniqueMpid:"Alias Request's destinationMpid and sourceMpid must be unique",AliasMissingTime:"Alias Request must have both a startTime and an endTime",AliasStartBeforeEndTime:"Alias Request's endTime must be later than its startTime"}},NativeSdkPaths:{LogEvent:"logEvent",SetUserTag:"setUserTag",RemoveUserTag:"removeUserTag",SetUserAttribute:"setUserAttribute",RemoveUserAttribute:"removeUserAttribute",SetSessionAttribute:"setSessionAttribute",AddToCart:"addToCart",RemoveFromCart:"removeFromCart",ClearCart:"clearCart",LogOut:"logOut",SetUserAttributeList:"setUserAttributeList",RemoveAllUserAttributes:"removeAllUserAttributes",GetUserAttributesLists:"getUserAttributesLists",GetAllUserAttributes:"getAllUserAttributes",Identify:"identify",Logout:"logout",Login:"login",Modify:"modify",Alias:"aliasUsers",Upload:"upload"},StorageNames:{localStorageName:"mprtcl-api",localStorageNameV3:"mprtcl-v3",cookieName:"mprtcl-api",cookieNameV2:"mprtcl-v2",cookieNameV3:"mprtcl-v3",localStorageNameV4:"mprtcl-v4",localStorageProductsV4:"mprtcl-prodv4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4",currentStorageProductsName:"mprtcl-prodv4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,maxProducts:20,forwarderStatsTimeout:5e3,integrationDelayTimeout:5e3,maxCookieSize:3e3,aliasMaxWindow:90,uploadInterval:0// Maximum milliseconds in between batch uploads, below 500 will mean immediate upload. The server returns this as a string, but we are using it as a number internally
21
21
  },DefaultBaseUrls:{v1SecureServiceUrl:"jssdks.mparticle.com/v1/JS/",v2SecureServiceUrl:"jssdks.mparticle.com/v2/JS/",v3SecureServiceUrl:"jssdks.mparticle.com/v3/JS/",configUrl:"jssdkcdns.mparticle.com/JS/v2/",identityUrl:"identity.mparticle.com/v1/",aliasUrl:"jssdks.mparticle.com/v1/identity/",userAudienceUrl:"nativesdks.mparticle.com/v1/"},// These are the paths that are used to construct the CNAME urls
@@ -1650,6 +1650,13 @@ var ErrorReportingDispatcher=/** @class */function(){function a(){this.services=
1650
1650
 
1651
1651
  var LoggingDispatcher=/** @class */function(){function a(){this.services=[];}return a.prototype.register=function(a){this.services.push(a);},a.prototype.log=function(a){var b=this;this.services.forEach(function(c){var d;try{c.log(a);}catch(a){null===(d=b.logger)||void 0===d?void 0:d.error("Error in LoggingService: "+a);}});},a}();
1652
1652
 
1653
+ var WRAPPED_MARKER="__mpApvWrapped__",PageViewTracker=/** @class */function(){function a(a){this.lastPath=null,this.isActive=!1,this.originalPushState=null,this.originalReplaceState=null,this.pushStateWrapper=null,this.replaceStateWrapper=null,this.popStateListener=null,this.mpInstance=a;}return a.prototype.isSupportedEnvironment=function(){return "undefined"!=typeof window&&"undefined"!=typeof window.history&&"function"==typeof window.history.pushState&&"function"==typeof window.addEventListener},a.prototype.init=function(){return this.mpInstance.Logger.verbose("mParticle APV: [init] PageViewTracker Init"),this.isSupportedEnvironment()?void(this.isActive&&(this.mpInstance.Logger.verbose("mParticle APV: [init] starting (teardown-first for idempotency)"),this.teardown()),this.isActive=!0,this.lastPath=this.getCurrentKey(),this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath)),this.patchHistoryMethods(),this.addNavigationListeners(),this.mpInstance.Logger.verbose("mParticle APV: [init] patched pushState/replaceState + listening for popstate")):void this.mpInstance.Logger.verbose("mParticle APV: [init] unsupported environment (no History API), not starting")},a.prototype.patchHistoryMethods=function(){var a=this,b=window.history.pushState;if(b[WRAPPED_MARKER])return void this.mpInstance.Logger.verbose("mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap");var c=window.history.pushState,d=window.history.replaceState;this.originalPushState=c,this.originalReplaceState=d;var e=function(){for(var b=[],d=0;d<arguments.length;d++)b[d]=arguments[d];var e=c.apply(this,b);return a.safeHandleNavigation("pushState"),e},f=function(){for(var b=[],c=0;c<arguments.length;c++)b[c]=arguments[c];var e=d.apply(this,b);return a.safeHandleNavigation("replaceState"),e};Object.defineProperty(e,WRAPPED_MARKER,{value:!0,enumerable:!1}),Object.defineProperty(f,WRAPPED_MARKER,{value:!0,enumerable:!1}),this.pushStateWrapper=e,this.replaceStateWrapper=f;try{window.history.pushState=e,window.history.replaceState=f;}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(a));try{window.history.pushState===e&&(window.history.pushState=c),window.history.replaceState===f&&(window.history.replaceState=d);}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(a));}this.pushStateWrapper=null,this.replaceStateWrapper=null,this.originalPushState=null,this.originalReplaceState=null;}},a.prototype.addNavigationListeners=function(){var a=this;this.popStateListener=function(){return a.safeHandleNavigation("popstate")},window.addEventListener("popstate",this.popStateListener);},a.prototype.safeHandleNavigation=function(a){try{this.handleNavigation(a);}catch(b){this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(a,"), page view skipped: ").concat(b));}},a.prototype.getCurrentKey=function(){return window.location.pathname},a.prototype.handleNavigation=function(a){var b=this,c=this.getCurrentKey();if(this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(a,", candidatePath: ").concat(c,", lastPath: ").concat(this.lastPath,")")),c===this.lastPath)return void this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(a,", path: ").concat(c,")"));this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(a,", from: ").concat(this.lastPath,", to: ").concat(c,")")),this.lastPath=c;// Snapshot the path now: it is already settled at this point, and a
1654
+ // same-tick navigation would otherwise overwrite window.location
1655
+ // before the deferred flush reads it. The title is intentionally read
1656
+ // later (in the deferred flush) so the router's post-navigation render
1657
+ // commit has a chance to update document.title first.
1658
+ setTimeout(function(){return b.isActive?void(b.mpInstance._SessionManager.resetSessionTimer(),b.firePageView(c)):void b.mpInstance.Logger.verbose("mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)")},0);},a.prototype.firePageView=function(a){var b=window.document.title;this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(a,", title: ").concat(b,")")),this.mpInstance._Events.logEvent({messageType:MessageType$1.PageView,name:"PageView",data:{hostname:window.location.hostname,title:b,path:a},eventType:EventType.Unknown});},a.prototype.teardown=function(){this.popStateListener&&(window.removeEventListener("popstate",this.popStateListener),this.popStateListener=null);var a=null!==this.pushStateWrapper&&window.history.pushState===this.pushStateWrapper;this.originalPushState&&(a?(window.history.pushState=this.originalPushState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original pushState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op"),this.originalPushState=null,this.pushStateWrapper=null);var b=null!==this.replaceStateWrapper&&window.history.replaceState===this.replaceStateWrapper;this.originalReplaceState&&(b?(window.history.replaceState=this.originalReplaceState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original replaceState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op"),this.originalReplaceState=null,this.replaceStateWrapper=null),this.isActive=!1;},a}();
1659
+
1653
1660
  var Messages=Constants.Messages,HTTPCodes=Constants.HTTPCodes,FeatureFlags=Constants.FeatureFlags,CaptureIntegrationSpecificIdsV2Modes=Constants.CaptureIntegrationSpecificIdsV2Modes,ReportBatching=FeatureFlags.ReportBatching,CaptureIntegrationSpecificIds=FeatureFlags.CaptureIntegrationSpecificIds,CaptureIntegrationSpecificIdsV2=FeatureFlags.CaptureIntegrationSpecificIdsV2,StartingInitialization=Messages.InformationMessages.StartingInitialization;/**
1654
1661
  * <p>All of the following methods can be called on the primary mParticle class. In version 2.10.0, we introduced <a href="https://docs.mparticle.com/developers/sdk/web/multiple-instances/">multiple instances</a>. If you are using multiple instances (self hosted environments only), you should call these methods on each instance.</p>
1655
1662
  * <p>In current versions of mParticle, if your site has one instance, that instance name is 'default_instance'. Any methods called on mParticle on a site with one instance will be mapped to the `default_instance`.</p>
@@ -1827,7 +1834,7 @@ if((null===(d=c._APIClient)||void 0===d?void 0:d.uploader)&&c._APIClient.uploade
1827
1834
  var k=c.Identity.getCurrentUser(),l=k?k.getMPID():null,m=k?k.getUserIdentities().userIdentities:{};c._Store.SDKConfig.identifyRequest=c._Store.hasInvalidIdentifyRequest()?{userIdentities:m}:c._Store.SDKConfig.identifyRequest,i(ReportBatching)&&c._ForwardingStatsUploader.startForwardingStatsTimer();// https://go.mparticle.com/work/SQDSDKS-7639
1828
1835
  var n=i(CaptureIntegrationSpecificIds),o=i(CaptureIntegrationSpecificIdsV2),p=o&&o!==CaptureIntegrationSpecificIdsV2Modes.None||!0===n;if(p){var q;n||o===CaptureIntegrationSpecificIdsV2Modes.All?q="all":o===CaptureIntegrationSpecificIdsV2Modes.RoktOnly&&(q="roktonly"),c._IntegrationCapture=new IntegrationCapture(q),c._IntegrationCapture.capture();}// Configure Rokt Manager with user and filtered user
1829
1836
  var r=parseConfig(b,"Rokt",181);if(r){var s=null===(e=r.settings)||void 0===e?void 0:e.accountId;c._Store.setRoktAccountId(s);var t=r.userAttributeFilters,u=filteredMparticleUser(l,{userAttributeFilters:t},c),v={sandbox:null===b||void 0===b?void 0:b.isDevelopmentMode,launcherOptions:null===b||void 0===b?void 0:b.launcherOptions,domain:null===b||void 0===b?void 0:b.domain};// https://go.mparticle.com/work/SQDSDKS-7339
1830
- c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)&&c._Events.logPageView(),processIdentityCallback(c,k,l,m);}// We will continue to clear out the ready queue as part of the initial init flow
1837
+ c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)?(c._Events.logPageView(),!c._PageViewTracker&&(c._PageViewTracker=new PageViewTracker(c)),c._PageViewTracker.init()):c._PageViewTracker&&c._PageViewTracker.teardown(),processIdentityCallback(c,k,l,m);}// We will continue to clear out the ready queue as part of the initial init flow
1831
1838
  // if an identify request is unnecessary, such as if there is an existing session
1832
1839
  (c._Store.mpid&&!c._Store.identifyCalled||c._Store.webviewBridgeEnabled)&&(c._Store.isInitialized=!0,null===(f=c._CookieConsentManager)||void 0===f?void 0:f.syncNoTargetingAttribute(c.Identity.getCurrentUser()),c._preInit.readyQueue=processReadyQueue(c._preInit.readyQueue,c.Logger)),null===(g=c.processQueueOnNoFunctional)||void 0===g?void 0:g.call(c),c._Store.isFirstRun&&(c._Store.isFirstRun=!1);}// https://go.mparticle.com/work/SQDSDKS-7061
1833
1840
  function createKitBlocker(a,b){var c,d,e,f;/* There are three ways a data plan object for blocking can be passed to the SDK:
@@ -15,7 +15,7 @@ map:function map(a,b){var c,d,e;if(null===this)throw new TypeError(" this is nul
15
15
  filter:function filter(a/*, thisArg*/){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f<c;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g);}return d},// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
16
16
  isArray:function isArray(a){return "[object Array]"===Object.prototype.toString.call(a)},Base64:Base64$1};
17
17
 
18
- var version = "2.76.0";
18
+ var version = "2.77.0";
19
19
 
20
20
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{DeprecationMessages:{MethodHasBeenDeprecated:"has been deprecated.",MethodMarkedForDeprecationPostfix:"is a deprecated method and will be removed in future releases.",AlternativeMethodPrefix:"Please use the alternate method:"},ErrorMessages:{NoToken:"A token must be specified.",EventNameInvalidType:"Event name must be a valid string value.",EventDataInvalidType:"Event data must be a valid object hash.",LoggingDisabled:"Event logging is currently disabled.",CookieParseError:"Could not parse cookie",EventEmpty:"Event object is null or undefined, cancelling send",APIRequestEmpty:"APIRequest is null or undefined, cancelling send",NoEventType:"Event type must be specified.",TransactionIdRequired:"Transaction ID is required",TransactionRequired:"A transaction attributes object is required",PromotionIdRequired:"Promotion ID is required",BadAttribute:"Attribute value cannot be object or array",BadKey:"Key value cannot be object or array",BadLogPurchase:"Transaction attributes and a product are both required to log a purchase, https://docs.mparticle.com/?javascript#measuring-transactions",AudienceAPINotEnabled:"Your workspace is not enabled to retrieve user audiences."},InformationMessages:{CookieSearch:"Searching for cookie",CookieFound:"Cookie found, parsing values",CookieNotFound:"Cookies not found",CookieSet:"Setting cookie",CookieSync:"Performing cookie sync",SendBegin:"Starting to send event",SendIdentityBegin:"Starting to send event to identity server",SendWindowsPhone:"Sending event to Windows Phone container",SendIOS:"Calling iOS path: ",SendAndroid:"Calling Android JS interface method: ",SendHttp:"Sending event to mParticle HTTP service",SendAliasHttp:"Sending alias request to mParticle HTTP service",SendIdentityHttp:"Sending event to mParticle HTTP service",StartingNewSession:"Starting new Session",StartingLogEvent:"Starting to log event",StartingLogOptOut:"Starting to log user opt in/out",StartingEndSession:"Starting to end session",StartingInitialization:"Starting to initialize",StartingLogCommerceEvent:"Starting to log commerce event",StartingAliasRequest:"Starting to Alias MPIDs",LoadingConfig:"Loading configuration options",AbandonLogEvent:"Cannot log event, logging disabled or developer token not set",AbandonAliasUsers:"Cannot Alias Users, logging disabled or developer token not set",AbandonStartSession:"Cannot start session, logging disabled or developer token not set",AbandonEndSession:"Cannot end session, logging disabled or developer token not set",NoSessionToEnd:"Cannot end session, no active session found"},ValidationMessages:{ModifyIdentityRequestUserIdentitiesPresent:"identityRequests to modify require userIdentities to be present. Request not sent to server. Please fix and try again",IdentityRequesetInvalidKey:"There is an invalid key on your identityRequest object. It can only contain a `userIdentities` object and a `onUserAlias` function. Request not sent to server. Please fix and try again.",OnUserAliasType:"The onUserAlias value must be a function.",UserIdentities:"The userIdentities key must be an object with keys of identityTypes and values of strings. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidKey:"There is an invalid identity key on your `userIdentities` object within the identityRequest. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidValues:"All user identity values must be strings or null. Request not sent to server. Please fix and try again.",AliasMissingMpid:"Alias Request must contain both a destinationMpid and a sourceMpid",AliasNonUniqueMpid:"Alias Request's destinationMpid and sourceMpid must be unique",AliasMissingTime:"Alias Request must have both a startTime and an endTime",AliasStartBeforeEndTime:"Alias Request's endTime must be later than its startTime"}},NativeSdkPaths:{LogEvent:"logEvent",SetUserTag:"setUserTag",RemoveUserTag:"removeUserTag",SetUserAttribute:"setUserAttribute",RemoveUserAttribute:"removeUserAttribute",SetSessionAttribute:"setSessionAttribute",AddToCart:"addToCart",RemoveFromCart:"removeFromCart",ClearCart:"clearCart",LogOut:"logOut",SetUserAttributeList:"setUserAttributeList",RemoveAllUserAttributes:"removeAllUserAttributes",GetUserAttributesLists:"getUserAttributesLists",GetAllUserAttributes:"getAllUserAttributes",Identify:"identify",Logout:"logout",Login:"login",Modify:"modify",Alias:"aliasUsers",Upload:"upload"},StorageNames:{localStorageName:"mprtcl-api",localStorageNameV3:"mprtcl-v3",cookieName:"mprtcl-api",cookieNameV2:"mprtcl-v2",cookieNameV3:"mprtcl-v3",localStorageNameV4:"mprtcl-v4",localStorageProductsV4:"mprtcl-prodv4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4",currentStorageProductsName:"mprtcl-prodv4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,maxProducts:20,forwarderStatsTimeout:5e3,integrationDelayTimeout:5e3,maxCookieSize:3e3,aliasMaxWindow:90,uploadInterval:0// Maximum milliseconds in between batch uploads, below 500 will mean immediate upload. The server returns this as a string, but we are using it as a number internally
21
21
  },DefaultBaseUrls:{v1SecureServiceUrl:"jssdks.mparticle.com/v1/JS/",v2SecureServiceUrl:"jssdks.mparticle.com/v2/JS/",v3SecureServiceUrl:"jssdks.mparticle.com/v3/JS/",configUrl:"jssdkcdns.mparticle.com/JS/v2/",identityUrl:"identity.mparticle.com/v1/",aliasUrl:"jssdks.mparticle.com/v1/identity/",userAudienceUrl:"nativesdks.mparticle.com/v1/"},// These are the paths that are used to construct the CNAME urls
@@ -1650,6 +1650,13 @@ var ErrorReportingDispatcher=/** @class */function(){function a(){this.services=
1650
1650
 
1651
1651
  var LoggingDispatcher=/** @class */function(){function a(){this.services=[];}return a.prototype.register=function(a){this.services.push(a);},a.prototype.log=function(a){var b=this;this.services.forEach(function(c){var d;try{c.log(a);}catch(a){null===(d=b.logger)||void 0===d?void 0:d.error("Error in LoggingService: "+a);}});},a}();
1652
1652
 
1653
+ var WRAPPED_MARKER="__mpApvWrapped__",PageViewTracker=/** @class */function(){function a(a){this.lastPath=null,this.isActive=!1,this.originalPushState=null,this.originalReplaceState=null,this.pushStateWrapper=null,this.replaceStateWrapper=null,this.popStateListener=null,this.mpInstance=a;}return a.prototype.isSupportedEnvironment=function(){return "undefined"!=typeof window&&"undefined"!=typeof window.history&&"function"==typeof window.history.pushState&&"function"==typeof window.addEventListener},a.prototype.init=function(){return this.mpInstance.Logger.verbose("mParticle APV: [init] PageViewTracker Init"),this.isSupportedEnvironment()?void(this.isActive&&(this.mpInstance.Logger.verbose("mParticle APV: [init] starting (teardown-first for idempotency)"),this.teardown()),this.isActive=!0,this.lastPath=this.getCurrentKey(),this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath)),this.patchHistoryMethods(),this.addNavigationListeners(),this.mpInstance.Logger.verbose("mParticle APV: [init] patched pushState/replaceState + listening for popstate")):void this.mpInstance.Logger.verbose("mParticle APV: [init] unsupported environment (no History API), not starting")},a.prototype.patchHistoryMethods=function(){var a=this,b=window.history.pushState;if(b[WRAPPED_MARKER])return void this.mpInstance.Logger.verbose("mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap");var c=window.history.pushState,d=window.history.replaceState;this.originalPushState=c,this.originalReplaceState=d;var e=function(){for(var b=[],d=0;d<arguments.length;d++)b[d]=arguments[d];var e=c.apply(this,b);return a.safeHandleNavigation("pushState"),e},f=function(){for(var b=[],c=0;c<arguments.length;c++)b[c]=arguments[c];var e=d.apply(this,b);return a.safeHandleNavigation("replaceState"),e};Object.defineProperty(e,WRAPPED_MARKER,{value:!0,enumerable:!1}),Object.defineProperty(f,WRAPPED_MARKER,{value:!0,enumerable:!1}),this.pushStateWrapper=e,this.replaceStateWrapper=f;try{window.history.pushState=e,window.history.replaceState=f;}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(a));try{window.history.pushState===e&&(window.history.pushState=c),window.history.replaceState===f&&(window.history.replaceState=d);}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(a));}this.pushStateWrapper=null,this.replaceStateWrapper=null,this.originalPushState=null,this.originalReplaceState=null;}},a.prototype.addNavigationListeners=function(){var a=this;this.popStateListener=function(){return a.safeHandleNavigation("popstate")},window.addEventListener("popstate",this.popStateListener);},a.prototype.safeHandleNavigation=function(a){try{this.handleNavigation(a);}catch(b){this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(a,"), page view skipped: ").concat(b));}},a.prototype.getCurrentKey=function(){return window.location.pathname},a.prototype.handleNavigation=function(a){var b=this,c=this.getCurrentKey();if(this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(a,", candidatePath: ").concat(c,", lastPath: ").concat(this.lastPath,")")),c===this.lastPath)return void this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(a,", path: ").concat(c,")"));this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(a,", from: ").concat(this.lastPath,", to: ").concat(c,")")),this.lastPath=c;// Snapshot the path now: it is already settled at this point, and a
1654
+ // same-tick navigation would otherwise overwrite window.location
1655
+ // before the deferred flush reads it. The title is intentionally read
1656
+ // later (in the deferred flush) so the router's post-navigation render
1657
+ // commit has a chance to update document.title first.
1658
+ setTimeout(function(){return b.isActive?void(b.mpInstance._SessionManager.resetSessionTimer(),b.firePageView(c)):void b.mpInstance.Logger.verbose("mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)")},0);},a.prototype.firePageView=function(a){var b=window.document.title;this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(a,", title: ").concat(b,")")),this.mpInstance._Events.logEvent({messageType:MessageType$1.PageView,name:"PageView",data:{hostname:window.location.hostname,title:b,path:a},eventType:EventType.Unknown});},a.prototype.teardown=function(){this.popStateListener&&(window.removeEventListener("popstate",this.popStateListener),this.popStateListener=null);var a=null!==this.pushStateWrapper&&window.history.pushState===this.pushStateWrapper;this.originalPushState&&(a?(window.history.pushState=this.originalPushState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original pushState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op"),this.originalPushState=null,this.pushStateWrapper=null);var b=null!==this.replaceStateWrapper&&window.history.replaceState===this.replaceStateWrapper;this.originalReplaceState&&(b?(window.history.replaceState=this.originalReplaceState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original replaceState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op"),this.originalReplaceState=null,this.replaceStateWrapper=null),this.isActive=!1;},a}();
1659
+
1653
1660
  var Messages=Constants.Messages,HTTPCodes=Constants.HTTPCodes,FeatureFlags=Constants.FeatureFlags,CaptureIntegrationSpecificIdsV2Modes=Constants.CaptureIntegrationSpecificIdsV2Modes,ReportBatching=FeatureFlags.ReportBatching,CaptureIntegrationSpecificIds=FeatureFlags.CaptureIntegrationSpecificIds,CaptureIntegrationSpecificIdsV2=FeatureFlags.CaptureIntegrationSpecificIdsV2,StartingInitialization=Messages.InformationMessages.StartingInitialization;/**
1654
1661
  * <p>All of the following methods can be called on the primary mParticle class. In version 2.10.0, we introduced <a href="https://docs.mparticle.com/developers/sdk/web/multiple-instances/">multiple instances</a>. If you are using multiple instances (self hosted environments only), you should call these methods on each instance.</p>
1655
1662
  * <p>In current versions of mParticle, if your site has one instance, that instance name is 'default_instance'. Any methods called on mParticle on a site with one instance will be mapped to the `default_instance`.</p>
@@ -1827,7 +1834,7 @@ if((null===(d=c._APIClient)||void 0===d?void 0:d.uploader)&&c._APIClient.uploade
1827
1834
  var k=c.Identity.getCurrentUser(),l=k?k.getMPID():null,m=k?k.getUserIdentities().userIdentities:{};c._Store.SDKConfig.identifyRequest=c._Store.hasInvalidIdentifyRequest()?{userIdentities:m}:c._Store.SDKConfig.identifyRequest,i(ReportBatching)&&c._ForwardingStatsUploader.startForwardingStatsTimer();// https://go.mparticle.com/work/SQDSDKS-7639
1828
1835
  var n=i(CaptureIntegrationSpecificIds),o=i(CaptureIntegrationSpecificIdsV2),p=o&&o!==CaptureIntegrationSpecificIdsV2Modes.None||!0===n;if(p){var q;n||o===CaptureIntegrationSpecificIdsV2Modes.All?q="all":o===CaptureIntegrationSpecificIdsV2Modes.RoktOnly&&(q="roktonly"),c._IntegrationCapture=new IntegrationCapture(q),c._IntegrationCapture.capture();}// Configure Rokt Manager with user and filtered user
1829
1836
  var r=parseConfig(b,"Rokt",181);if(r){var s=null===(e=r.settings)||void 0===e?void 0:e.accountId;c._Store.setRoktAccountId(s);var t=r.userAttributeFilters,u=filteredMparticleUser(l,{userAttributeFilters:t},c),v={sandbox:null===b||void 0===b?void 0:b.isDevelopmentMode,launcherOptions:null===b||void 0===b?void 0:b.launcherOptions,domain:null===b||void 0===b?void 0:b.domain};// https://go.mparticle.com/work/SQDSDKS-7339
1830
- c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)&&c._Events.logPageView(),processIdentityCallback(c,k,l,m);}// We will continue to clear out the ready queue as part of the initial init flow
1837
+ c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)?(c._Events.logPageView(),!c._PageViewTracker&&(c._PageViewTracker=new PageViewTracker(c)),c._PageViewTracker.init()):c._PageViewTracker&&c._PageViewTracker.teardown(),processIdentityCallback(c,k,l,m);}// We will continue to clear out the ready queue as part of the initial init flow
1831
1838
  // if an identify request is unnecessary, such as if there is an existing session
1832
1839
  (c._Store.mpid&&!c._Store.identifyCalled||c._Store.webviewBridgeEnabled)&&(c._Store.isInitialized=!0,null===(f=c._CookieConsentManager)||void 0===f?void 0:f.syncNoTargetingAttribute(c.Identity.getCurrentUser()),c._preInit.readyQueue=processReadyQueue(c._preInit.readyQueue,c.Logger)),null===(g=c.processQueueOnNoFunctional)||void 0===g?void 0:g.call(c),c._Store.isFirstRun&&(c._Store.isFirstRun=!1);}// https://go.mparticle.com/work/SQDSDKS-7061
1833
1840
  function createKitBlocker(a,b){var c,d,e,f;/* There are three ways a data plan object for blocking can be passed to the SDK:
package/dist/mparticle.js CHANGED
@@ -203,7 +203,7 @@ var mParticle = (function () {
203
203
  Base64: Base64$1
204
204
  };
205
205
 
206
- var version = "2.76.0";
206
+ var version = "2.77.0";
207
207
 
208
208
  var Constants = {
209
209
  sdkVersion: version,
@@ -11145,6 +11145,188 @@ var mParticle = (function () {
11145
11145
  return LoggingDispatcher;
11146
11146
  }();
11147
11147
 
11148
+ var WRAPPED_MARKER = '__mpApvWrapped__';
11149
+ var PageViewTracker = /** @class */function () {
11150
+ function PageViewTracker(mpInstance) {
11151
+ this.lastPath = null;
11152
+ this.isActive = false;
11153
+ this.originalPushState = null;
11154
+ this.originalReplaceState = null;
11155
+ this.pushStateWrapper = null;
11156
+ this.replaceStateWrapper = null;
11157
+ this.popStateListener = null;
11158
+ this.mpInstance = mpInstance;
11159
+ }
11160
+ PageViewTracker.prototype.isSupportedEnvironment = function () {
11161
+ return typeof window !== 'undefined' && typeof window.history !== 'undefined' && typeof window.history.pushState === 'function' && typeof window.addEventListener === 'function';
11162
+ };
11163
+ PageViewTracker.prototype.init = function () {
11164
+ this.mpInstance.Logger.verbose('mParticle APV: [init] PageViewTracker Init');
11165
+ if (!this.isSupportedEnvironment()) {
11166
+ this.mpInstance.Logger.verbose('mParticle APV: [init] unsupported environment (no History API), not starting');
11167
+ return;
11168
+ }
11169
+ if (this.isActive) {
11170
+ this.mpInstance.Logger.verbose('mParticle APV: [init] starting (teardown-first for idempotency)');
11171
+ this.teardown();
11172
+ }
11173
+ this.isActive = true;
11174
+ this.lastPath = this.getCurrentKey();
11175
+ this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath));
11176
+ this.patchHistoryMethods();
11177
+ this.addNavigationListeners();
11178
+ this.mpInstance.Logger.verbose('mParticle APV: [init] patched pushState/replaceState + listening for popstate');
11179
+ };
11180
+ PageViewTracker.prototype.patchHistoryMethods = function () {
11181
+ var self = this;
11182
+ var installed = window.history.pushState;
11183
+ if (installed[WRAPPED_MARKER]) {
11184
+ this.mpInstance.Logger.verbose('mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap');
11185
+ return;
11186
+ }
11187
+ var originalPushState = window.history.pushState;
11188
+ var originalReplaceState = window.history.replaceState;
11189
+ this.originalPushState = originalPushState;
11190
+ this.originalReplaceState = originalReplaceState;
11191
+ var pushStateWrapper = function pushStateWrapper() {
11192
+ var args = [];
11193
+ for (var _i = 0; _i < arguments.length; _i++) {
11194
+ args[_i] = arguments[_i];
11195
+ }
11196
+ var result = originalPushState.apply(this, args);
11197
+ self.safeHandleNavigation('pushState');
11198
+ return result;
11199
+ };
11200
+ var replaceStateWrapper = function replaceStateWrapper() {
11201
+ var args = [];
11202
+ for (var _i = 0; _i < arguments.length; _i++) {
11203
+ args[_i] = arguments[_i];
11204
+ }
11205
+ var result = originalReplaceState.apply(this, args);
11206
+ self.safeHandleNavigation('replaceState');
11207
+ return result;
11208
+ };
11209
+ Object.defineProperty(pushStateWrapper, WRAPPED_MARKER, {
11210
+ value: true,
11211
+ enumerable: false
11212
+ });
11213
+ Object.defineProperty(replaceStateWrapper, WRAPPED_MARKER, {
11214
+ value: true,
11215
+ enumerable: false
11216
+ });
11217
+ this.pushStateWrapper = pushStateWrapper;
11218
+ this.replaceStateWrapper = replaceStateWrapper;
11219
+ try {
11220
+ window.history.pushState = pushStateWrapper;
11221
+ window.history.replaceState = replaceStateWrapper;
11222
+ } catch (e) {
11223
+ this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(e));
11224
+ try {
11225
+ if (window.history.pushState === pushStateWrapper) {
11226
+ window.history.pushState = originalPushState;
11227
+ }
11228
+ if (window.history.replaceState === replaceStateWrapper) {
11229
+ window.history.replaceState = originalReplaceState;
11230
+ }
11231
+ } catch (restoreError) {
11232
+ this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(restoreError));
11233
+ }
11234
+ this.pushStateWrapper = null;
11235
+ this.replaceStateWrapper = null;
11236
+ this.originalPushState = null;
11237
+ this.originalReplaceState = null;
11238
+ }
11239
+ };
11240
+ PageViewTracker.prototype.addNavigationListeners = function () {
11241
+ var _this = this;
11242
+ this.popStateListener = function () {
11243
+ return _this.safeHandleNavigation('popstate');
11244
+ };
11245
+ window.addEventListener('popstate', this.popStateListener);
11246
+ };
11247
+ PageViewTracker.prototype.safeHandleNavigation = function (source) {
11248
+ try {
11249
+ this.handleNavigation(source);
11250
+ } catch (e) {
11251
+ this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(source, "), page view skipped: ").concat(e));
11252
+ }
11253
+ };
11254
+ PageViewTracker.prototype.getCurrentKey = function () {
11255
+ return window.location.pathname;
11256
+ };
11257
+ PageViewTracker.prototype.handleNavigation = function (source) {
11258
+ var _this = this;
11259
+ var candidatePath = this.getCurrentKey();
11260
+ this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(source, ", candidatePath: ").concat(candidatePath, ", lastPath: ").concat(this.lastPath, ")"));
11261
+ if (candidatePath === this.lastPath) {
11262
+ this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(source, ", path: ").concat(candidatePath, ")"));
11263
+ return;
11264
+ }
11265
+ this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(source, ", from: ").concat(this.lastPath, ", to: ").concat(candidatePath, ")"));
11266
+ this.lastPath = candidatePath;
11267
+ // Snapshot the path now: it is already settled at this point, and a
11268
+ // same-tick navigation would otherwise overwrite window.location
11269
+ // before the deferred flush reads it. The title is intentionally read
11270
+ // later (in the deferred flush) so the router's post-navigation render
11271
+ // commit has a chance to update document.title first.
11272
+ var capturedPath = candidatePath;
11273
+ setTimeout(function () {
11274
+ if (!_this.isActive) {
11275
+ _this.mpInstance.Logger.verbose('mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)');
11276
+ return;
11277
+ }
11278
+ _this.mpInstance._SessionManager.resetSessionTimer();
11279
+ _this.firePageView(capturedPath);
11280
+ }, 0);
11281
+ };
11282
+ // Mirrors the event shape of the public mParticle.logPageView(), but carries
11283
+ // the captured SPA path rather than reading the live location.
11284
+ PageViewTracker.prototype.firePageView = function (path) {
11285
+ var title = window.document.title;
11286
+ this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(path, ", title: ").concat(title, ")"));
11287
+ this.mpInstance._Events.logEvent({
11288
+ messageType: MessageType$1.PageView,
11289
+ name: 'PageView',
11290
+ data: {
11291
+ hostname: window.location.hostname,
11292
+ title: title,
11293
+ path: path
11294
+ },
11295
+ eventType: EventType.Unknown
11296
+ });
11297
+ };
11298
+ PageViewTracker.prototype.teardown = function () {
11299
+ if (this.popStateListener) {
11300
+ window.removeEventListener('popstate', this.popStateListener);
11301
+ this.popStateListener = null;
11302
+ }
11303
+ var pushStateStillOurs = this.pushStateWrapper !== null && window.history.pushState === this.pushStateWrapper;
11304
+ if (this.originalPushState) {
11305
+ if (pushStateStillOurs) {
11306
+ window.history.pushState = this.originalPushState;
11307
+ this.mpInstance.Logger.verbose('mParticle APV: [teardown] restored original pushState');
11308
+ } else {
11309
+ this.mpInstance.Logger.verbose('mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op');
11310
+ }
11311
+ this.originalPushState = null;
11312
+ this.pushStateWrapper = null;
11313
+ }
11314
+ var replaceStateStillOurs = this.replaceStateWrapper !== null && window.history.replaceState === this.replaceStateWrapper;
11315
+ if (this.originalReplaceState) {
11316
+ if (replaceStateStillOurs) {
11317
+ window.history.replaceState = this.originalReplaceState;
11318
+ this.mpInstance.Logger.verbose('mParticle APV: [teardown] restored original replaceState');
11319
+ } else {
11320
+ this.mpInstance.Logger.verbose('mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op');
11321
+ }
11322
+ this.originalReplaceState = null;
11323
+ this.replaceStateWrapper = null;
11324
+ }
11325
+ this.isActive = false;
11326
+ };
11327
+ return PageViewTracker;
11328
+ }();
11329
+
11148
11330
  var Messages = Constants.Messages,
11149
11331
  HTTPCodes = Constants.HTTPCodes,
11150
11332
  FeatureFlags = Constants.FeatureFlags,
@@ -12225,6 +12407,12 @@ var mParticle = (function () {
12225
12407
  mpInstance._Events.logAST();
12226
12408
  if (getFeatureFlag(AutoLogPageView)) {
12227
12409
  mpInstance._Events.logPageView();
12410
+ if (!mpInstance._PageViewTracker) {
12411
+ mpInstance._PageViewTracker = new PageViewTracker(mpInstance);
12412
+ }
12413
+ mpInstance._PageViewTracker.init();
12414
+ } else if (mpInstance._PageViewTracker) {
12415
+ mpInstance._PageViewTracker.teardown();
12228
12416
  }
12229
12417
  processIdentityCallback(mpInstance, currentUser, currentUserMPID, currentUserIdentities);
12230
12418
  }
@@ -20,6 +20,7 @@ import RoktManager from './roktManager';
20
20
  import { ICookieConsentManager } from './cookieConsentManager';
21
21
  import { ErrorReportingDispatcher } from './reporting/errorReportingDispatcher';
22
22
  import { LoggingDispatcher } from './reporting/loggingDispatcher';
23
+ import { PageViewTracker } from './pageViewTracker';
23
24
  export interface IErrorLogMessage {
24
25
  message?: string;
25
26
  name?: string;
@@ -44,6 +45,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
44
45
  _IdentityAPIClient: typeof IdentityAPIClient;
45
46
  _IntegrationCapture: IntegrationCapture;
46
47
  _NativeSdkHelpers: INativeSdkHelpers;
48
+ _PageViewTracker?: PageViewTracker;
47
49
  _Persistence: IPersistence;
48
50
  _CookieConsentManager: ICookieConsentManager;
49
51
  _ErrorReportingDispatcher: ErrorReportingDispatcher;
@@ -0,0 +1,21 @@
1
+ import { IMParticleWebSDKInstance } from './mp-instance';
2
+ export declare class PageViewTracker {
3
+ mpInstance: IMParticleWebSDKInstance;
4
+ private lastPath;
5
+ private isActive;
6
+ private originalPushState;
7
+ private originalReplaceState;
8
+ private pushStateWrapper;
9
+ private replaceStateWrapper;
10
+ private popStateListener;
11
+ constructor(mpInstance: IMParticleWebSDKInstance);
12
+ private isSupportedEnvironment;
13
+ init(): void;
14
+ private patchHistoryMethods;
15
+ private addNavigationListeners;
16
+ private safeHandleNavigation;
17
+ private getCurrentKey;
18
+ private handleNavigation;
19
+ private firePageView;
20
+ teardown(): void;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.76.0",
3
+ "version": "2.77.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -57,6 +57,7 @@ import { LoggingDispatcher } from './reporting/loggingDispatcher';
57
57
  import { IErrorReportingService, ILoggingService } from './reporting/types';
58
58
  import { logDeprecatedMethodUsage } from './reporting/deprecatedMethodLogger';
59
59
  import { normalizeRoktLauncherOptions } from './roktLauncherOptions';
60
+ import { PageViewTracker } from './pageViewTracker';
60
61
 
61
62
  export interface IErrorLogMessage {
62
63
  message?: string;
@@ -87,6 +88,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
87
88
  _IdentityAPIClient: typeof IdentityAPIClient;
88
89
  _IntegrationCapture: IntegrationCapture;
89
90
  _NativeSdkHelpers: INativeSdkHelpers;
91
+ _PageViewTracker?: PageViewTracker;
90
92
  _Persistence: IPersistence;
91
93
  _CookieConsentManager: ICookieConsentManager;
92
94
  _ErrorReportingDispatcher: ErrorReportingDispatcher;
@@ -1585,6 +1587,13 @@ function completeSDKInitialization(apiKey, config, mpInstance) {
1585
1587
 
1586
1588
  if (getFeatureFlag(AutoLogPageView)) {
1587
1589
  mpInstance._Events.logPageView();
1590
+
1591
+ if (!mpInstance._PageViewTracker) {
1592
+ mpInstance._PageViewTracker = new PageViewTracker(mpInstance);
1593
+ }
1594
+ mpInstance._PageViewTracker.init();
1595
+ } else if (mpInstance._PageViewTracker) {
1596
+ mpInstance._PageViewTracker.teardown();
1588
1597
  }
1589
1598
 
1590
1599
  processIdentityCallback(
@@ -0,0 +1,267 @@
1
+ import { IMParticleWebSDKInstance } from './mp-instance';
2
+ import { EventType, MessageType } from './types';
3
+
4
+ type HistoryStateMethod = History['pushState'];
5
+
6
+ const WRAPPED_MARKER = '__mpApvWrapped__';
7
+
8
+ type MarkedHistoryMethod = HistoryStateMethod & {
9
+ [WRAPPED_MARKER]?: boolean;
10
+ };
11
+
12
+ export class PageViewTracker {
13
+ mpInstance: IMParticleWebSDKInstance;
14
+
15
+ private lastPath: string | null = null;
16
+ private isActive = false;
17
+
18
+ private originalPushState: HistoryStateMethod | null = null;
19
+ private originalReplaceState: HistoryStateMethod | null = null;
20
+
21
+ private pushStateWrapper: HistoryStateMethod | null = null;
22
+ private replaceStateWrapper: HistoryStateMethod | null = null;
23
+
24
+ private popStateListener: (() => void) | null = null;
25
+
26
+ constructor(mpInstance: IMParticleWebSDKInstance) {
27
+ this.mpInstance = mpInstance;
28
+ }
29
+
30
+ private isSupportedEnvironment(): boolean {
31
+ return (
32
+ typeof window !== 'undefined' &&
33
+ typeof window.history !== 'undefined' &&
34
+ typeof window.history.pushState === 'function' &&
35
+ typeof window.addEventListener === 'function'
36
+ );
37
+ }
38
+
39
+ public init(): void {
40
+ this.mpInstance.Logger.verbose(
41
+ 'mParticle APV: [init] PageViewTracker Init'
42
+ );
43
+ if (!this.isSupportedEnvironment()) {
44
+ this.mpInstance.Logger.verbose(
45
+ 'mParticle APV: [init] unsupported environment (no History API), not starting'
46
+ );
47
+ return;
48
+ }
49
+
50
+ if (this.isActive) {
51
+ this.mpInstance.Logger.verbose(
52
+ 'mParticle APV: [init] starting (teardown-first for idempotency)'
53
+ );
54
+ this.teardown();
55
+ }
56
+
57
+ this.isActive = true;
58
+
59
+ this.lastPath = this.getCurrentKey();
60
+
61
+ this.mpInstance.Logger.verbose(
62
+ `mParticle APV: [init] seeded lastPath: ${this.lastPath}`
63
+ );
64
+
65
+ this.patchHistoryMethods();
66
+ this.addNavigationListeners();
67
+
68
+ this.mpInstance.Logger.verbose(
69
+ 'mParticle APV: [init] patched pushState/replaceState + listening for popstate'
70
+ );
71
+ }
72
+
73
+ private patchHistoryMethods(): void {
74
+ const self = this;
75
+
76
+ const installed = window.history.pushState as MarkedHistoryMethod;
77
+ if (installed[WRAPPED_MARKER]) {
78
+ this.mpInstance.Logger.verbose(
79
+ 'mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap'
80
+ );
81
+ return;
82
+ }
83
+
84
+ const originalPushState = window.history.pushState;
85
+ const originalReplaceState = window.history.replaceState;
86
+ this.originalPushState = originalPushState;
87
+ this.originalReplaceState = originalReplaceState;
88
+
89
+ const pushStateWrapper = function(
90
+ this: History,
91
+ ...args: Parameters<HistoryStateMethod>
92
+ ): void {
93
+ const result = originalPushState.apply(this, args);
94
+ self.safeHandleNavigation('pushState');
95
+ return result;
96
+ };
97
+
98
+ const replaceStateWrapper = function(
99
+ this: History,
100
+ ...args: Parameters<HistoryStateMethod>
101
+ ): void {
102
+ const result = originalReplaceState.apply(this, args);
103
+ self.safeHandleNavigation('replaceState');
104
+ return result;
105
+ };
106
+
107
+ Object.defineProperty(pushStateWrapper, WRAPPED_MARKER, {
108
+ value: true,
109
+ enumerable: false,
110
+ });
111
+ Object.defineProperty(replaceStateWrapper, WRAPPED_MARKER, {
112
+ value: true,
113
+ enumerable: false,
114
+ });
115
+
116
+ this.pushStateWrapper = pushStateWrapper;
117
+ this.replaceStateWrapper = replaceStateWrapper;
118
+
119
+ try {
120
+ window.history.pushState = pushStateWrapper;
121
+ window.history.replaceState = replaceStateWrapper;
122
+ } catch (e) {
123
+ this.mpInstance.Logger.verbose(
124
+ `mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ${e}`
125
+ );
126
+ try {
127
+ if (window.history.pushState === pushStateWrapper) {
128
+ window.history.pushState = originalPushState;
129
+ }
130
+ if (window.history.replaceState === replaceStateWrapper) {
131
+ window.history.replaceState = originalReplaceState;
132
+ }
133
+ } catch (restoreError) {
134
+ this.mpInstance.Logger.verbose(
135
+ `mParticle APV: [error] failed to restore history methods after patch failure: ${restoreError}`
136
+ );
137
+ }
138
+ this.pushStateWrapper = null;
139
+ this.replaceStateWrapper = null;
140
+ this.originalPushState = null;
141
+ this.originalReplaceState = null;
142
+ }
143
+ }
144
+
145
+ private addNavigationListeners(): void {
146
+ this.popStateListener = () => this.safeHandleNavigation('popstate');
147
+ window.addEventListener('popstate', this.popStateListener);
148
+ }
149
+
150
+ private safeHandleNavigation(source: string): void {
151
+ try {
152
+ this.handleNavigation(source);
153
+ } catch (e) {
154
+ this.mpInstance.Logger.verbose(
155
+ `mParticle APV: [error] navigation handler threw (${source}), page view skipped: ${e}`
156
+ );
157
+ }
158
+ }
159
+
160
+ private getCurrentKey(): string {
161
+ return window.location.pathname;
162
+ }
163
+
164
+ private handleNavigation(source: string): void {
165
+ const candidatePath = this.getCurrentKey();
166
+
167
+ this.mpInstance.Logger.verbose(
168
+ `mParticle APV: [detect] navigation signal (source: ${source}, candidatePath: ${candidatePath}, lastPath: ${this.lastPath})`
169
+ );
170
+
171
+ if (candidatePath === this.lastPath) {
172
+ this.mpInstance.Logger.verbose(
173
+ `mParticle APV: [dedupe] pathname unchanged, skipping (source: ${source}, path: ${candidatePath})`
174
+ );
175
+ return;
176
+ }
177
+
178
+ this.mpInstance.Logger.verbose(
179
+ `mParticle APV: [accept] pathname changed, scheduling fire (source: ${source}, from: ${this.lastPath}, to: ${candidatePath})`
180
+ );
181
+ this.lastPath = candidatePath;
182
+
183
+ // Snapshot the path now: it is already settled at this point, and a
184
+ // same-tick navigation would otherwise overwrite window.location
185
+ // before the deferred flush reads it. The title is intentionally read
186
+ // later (in the deferred flush) so the router's post-navigation render
187
+ // commit has a chance to update document.title first.
188
+ const capturedPath = candidatePath;
189
+
190
+ setTimeout(() => {
191
+ if (!this.isActive) {
192
+ this.mpInstance.Logger.verbose(
193
+ 'mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)'
194
+ );
195
+ return;
196
+ }
197
+
198
+ this.mpInstance._SessionManager.resetSessionTimer();
199
+ this.firePageView(capturedPath);
200
+ }, 0);
201
+ }
202
+
203
+ // Mirrors the event shape of the public mParticle.logPageView(), but carries
204
+ // the captured SPA path rather than reading the live location.
205
+ private firePageView(path: string): void {
206
+ const title = window.document.title;
207
+
208
+ this.mpInstance.Logger.verbose(
209
+ `mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ${path}, title: ${title})`
210
+ );
211
+
212
+ this.mpInstance._Events.logEvent({
213
+ messageType: MessageType.PageView,
214
+ name: 'PageView',
215
+ data: {
216
+ hostname: window.location.hostname,
217
+ title,
218
+ path,
219
+ },
220
+ eventType: EventType.Unknown,
221
+ });
222
+ }
223
+
224
+ public teardown(): void {
225
+ if (this.popStateListener) {
226
+ window.removeEventListener('popstate', this.popStateListener);
227
+ this.popStateListener = null;
228
+ }
229
+ const pushStateStillOurs =
230
+ this.pushStateWrapper !== null &&
231
+ window.history.pushState === this.pushStateWrapper;
232
+ if (this.originalPushState) {
233
+ if (pushStateStillOurs) {
234
+ window.history.pushState = this.originalPushState;
235
+ this.mpInstance.Logger.verbose(
236
+ 'mParticle APV: [teardown] restored original pushState'
237
+ );
238
+ } else {
239
+ this.mpInstance.Logger.verbose(
240
+ 'mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op'
241
+ );
242
+ }
243
+ this.originalPushState = null;
244
+ this.pushStateWrapper = null;
245
+ }
246
+
247
+ const replaceStateStillOurs =
248
+ this.replaceStateWrapper !== null &&
249
+ window.history.replaceState === this.replaceStateWrapper;
250
+ if (this.originalReplaceState) {
251
+ if (replaceStateStillOurs) {
252
+ window.history.replaceState = this.originalReplaceState;
253
+ this.mpInstance.Logger.verbose(
254
+ 'mParticle APV: [teardown] restored original replaceState'
255
+ );
256
+ } else {
257
+ this.mpInstance.Logger.verbose(
258
+ 'mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op'
259
+ );
260
+ }
261
+ this.originalReplaceState = null;
262
+ this.replaceStateWrapper = null;
263
+ }
264
+
265
+ this.isActive = false;
266
+ }
267
+ }