@mparticle/web-sdk 2.18.3 → 2.19.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.
@@ -32,7 +32,7 @@ function _defineProperty(obj, key, value) {
32
32
 
33
33
  var _TriggerUploadType,MessageType$1={SessionStart:1,SessionEnd:2,PageView:3,PageEvent:4,CrashReport:5,OptOut:6,AppStateTransition:10,Profile:14,Commerce:16,Media:20,UserAttributeChange:17,UserIdentityChange:18},TriggerUploadType=(_TriggerUploadType={},_defineProperty(_TriggerUploadType,MessageType$1.Commerce,1),_defineProperty(_TriggerUploadType,MessageType$1.UserAttributeChange,1),_defineProperty(_TriggerUploadType,MessageType$1.UserIdentityChange,1),_TriggerUploadType),EventType={Unknown:0,Navigation:1,Location:2,Search:3,Transaction:4,UserContent:5,UserPreference:6,Social:7,Other:8,Media:9,getName:function getName(a){return a===EventType.Unknown?"Unknown":a===EventType.Navigation?"Navigation":a===EventType.Location?"Location":a===EventType.Search?"Search":a===EventType.Transaction?"Transaction":a===EventType.UserContent?"User Content":a===EventType.UserPreference?"User Preference":a===EventType.Social?"Social":a===CommerceEventType.ProductAddToCart?"Product Added to Cart":a===CommerceEventType.ProductAddToWishlist?"Product Added to Wishlist":a===CommerceEventType.ProductCheckout?"Product Checkout":a===CommerceEventType.ProductCheckoutOption?"Product Checkout Options":a===CommerceEventType.ProductClick?"Product Click":a===CommerceEventType.ProductImpression?"Product Impression":a===CommerceEventType.ProductPurchase?"Product Purchased":a===CommerceEventType.ProductRefund?"Product Refunded":a===CommerceEventType.ProductRemoveFromCart?"Product Removed From Cart":a===CommerceEventType.ProductRemoveFromWishlist?"Product Removed from Wishlist":a===CommerceEventType.ProductViewDetail?"Product View Details":a===CommerceEventType.PromotionClick?"Promotion Click":a===CommerceEventType.PromotionView?"Promotion View":"Other"}},CommerceEventType={ProductAddToCart:10,ProductRemoveFromCart:11,ProductCheckout:12,ProductCheckoutOption:13,ProductClick:14,ProductViewDetail:15,ProductPurchase:16,ProductRefund:17,PromotionView:18,PromotionClick:19,ProductAddToWishlist:20,ProductRemoveFromWishlist:21,ProductImpression:22},IdentityType={Other:0,CustomerId:1,Facebook:2,Twitter:3,Google:4,Microsoft:5,Yahoo:6,Email:7,FacebookCustomAudienceId:9,Other2:10,Other3:11,Other4:12,Other5:13,Other6:14,Other7:15,Other8:16,Other9:17,Other10:18,MobileNumber:19,PhoneNumber2:20,PhoneNumber3:21};IdentityType.isValid=function(a){if("number"==typeof a)for(var b in IdentityType)if(IdentityType.hasOwnProperty(b)&&IdentityType[b]===a)return !0;return !1},IdentityType.getName=function(a){return a===window.mParticle.IdentityType.CustomerId?"Customer ID":a===window.mParticle.IdentityType.Facebook?"Facebook ID":a===window.mParticle.IdentityType.Twitter?"Twitter ID":a===window.mParticle.IdentityType.Google?"Google ID":a===window.mParticle.IdentityType.Microsoft?"Microsoft ID":a===window.mParticle.IdentityType.Yahoo?"Yahoo ID":a===window.mParticle.IdentityType.Email?"Email":a===window.mParticle.IdentityType.FacebookCustomAudienceId?"Facebook App User ID":"Other ID"},IdentityType.getIdentityType=function(a){return "other"===a?IdentityType.Other:"customerid"===a?IdentityType.CustomerId:"facebook"===a?IdentityType.Facebook:"twitter"===a?IdentityType.Twitter:"google"===a?IdentityType.Google:"microsoft"===a?IdentityType.Microsoft:"yahoo"===a?IdentityType.Yahoo:"email"===a?IdentityType.Email:"facebookcustomaudienceid"===a?IdentityType.FacebookCustomAudienceId:"other2"===a?IdentityType.Other2:"other3"===a?IdentityType.Other3:"other4"===a?IdentityType.Other4:"other5"===a?IdentityType.Other5:"other6"===a?IdentityType.Other6:"other7"===a?IdentityType.Other7:"other8"===a?IdentityType.Other8:"other9"===a?IdentityType.Other9:"other10"===a?IdentityType.Other10:"mobile_number"===a?IdentityType.MobileNumber:"phone_number_2"===a?IdentityType.PhoneNumber2:!("phone_number_3"!=a)&&IdentityType.PhoneNumber3},IdentityType.getIdentityName=function(a){return a===IdentityType.Other?"other":a===IdentityType.CustomerId?"customerid":a===IdentityType.Facebook?"facebook":a===IdentityType.Twitter?"twitter":a===IdentityType.Google?"google":a===IdentityType.Microsoft?"microsoft":a===IdentityType.Yahoo?"yahoo":a===IdentityType.Email?"email":a===IdentityType.FacebookCustomAudienceId?"facebookcustomaudienceid":a===IdentityType.Other2?"other2":a===IdentityType.Other3?"other3":a===IdentityType.Other4?"other4":a===IdentityType.Other5?"other5":a===IdentityType.Other6?"other6":a===IdentityType.Other7?"other7":a===IdentityType.Other8?"other8":a===IdentityType.Other9?"other9":a===IdentityType.Other10?"other10":a===IdentityType.MobileNumber?"mobile_number":a===IdentityType.PhoneNumber2?"phone_number_2":a===IdentityType.PhoneNumber3?"phone_number_3":void 0};var ProductActionType={Unknown:0,AddToCart:1,RemoveFromCart:2,Checkout:3,CheckoutOption:4,Click:5,ViewDetail:6,Purchase:7,Refund:8,AddToWishlist:9,RemoveFromWishlist:10};ProductActionType.getName=function(a){return a===ProductActionType.AddToCart?"Add to Cart":a===ProductActionType.RemoveFromCart?"Remove from Cart":a===ProductActionType.Checkout?"Checkout":a===ProductActionType.CheckoutOption?"Checkout Option":a===ProductActionType.Click?"Click":a===ProductActionType.ViewDetail?"View Detail":a===ProductActionType.Purchase?"Purchase":a===ProductActionType.Refund?"Refund":a===ProductActionType.AddToWishlist?"Add to Wishlist":a===ProductActionType.RemoveFromWishlist?"Remove from Wishlist":"Unknown"},ProductActionType.getExpansionName=function(a){return a===ProductActionType.AddToCart?"add_to_cart":a===ProductActionType.RemoveFromCart?"remove_from_cart":a===ProductActionType.Checkout?"checkout":a===ProductActionType.CheckoutOption?"checkout_option":a===ProductActionType.Click?"click":a===ProductActionType.ViewDetail?"view_detail":a===ProductActionType.Purchase?"purchase":a===ProductActionType.Refund?"refund":a===ProductActionType.AddToWishlist?"add_to_wishlist":a===ProductActionType.RemoveFromWishlist?"remove_from_wishlist":"unknown"};var PromotionActionType={Unknown:0,PromotionView:1,PromotionClick:2};PromotionActionType.getName=function(a){return a===PromotionActionType.PromotionView?"view":a===PromotionActionType.PromotionClick?"click":"unknown"},PromotionActionType.getExpansionName=function(a){return a===PromotionActionType.PromotionView?"view":a===PromotionActionType.PromotionClick?"click":"unknown"};var ProfileMessageType={Logout:3},ApplicationTransitionType$1={AppInit:1},Environment={Production:"production",Development:"development"};var Types = {MessageType:MessageType$1,EventType:EventType,CommerceEventType:CommerceEventType,IdentityType:IdentityType,ProfileMessageType:ProfileMessageType,ApplicationTransitionType:ApplicationTransitionType$1,ProductActionType:ProductActionType,PromotionActionType:PromotionActionType,TriggerUploadType:TriggerUploadType,Environment:Environment};
34
34
 
35
- var version = "2.18.3";
35
+ var version = "2.19.0";
36
36
 
37
37
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{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"},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
38
38
  },DefaultUrls:{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/"},Base64CookieKeys:{csm:1,sa:1,ss:1,ua:1,ui:1,csd:1,ia:1,con:1},SDKv2NonMPIDCookieKeys:{gs:1,cu:1,l:1,globalSettings:1,currentUserMPID:1},HTTPCodes:{noHttpCoverage:-1,activeIdentityRequest:-2,activeSession:-3,validationIssue:-4,nativeIdentityRequest:-5,loggingDisabledOrMissingAPIKey:-6,tooManyRequests:429},FeatureFlags:{ReportBatching:"reportBatching",EventsV3:"eventsV3",EventBatchingIntervalMillis:"eventBatchingIntervalMillis"},DefaultInstance:"default_instance"};
@@ -438,11 +438,28 @@ return null;case Types.MessageType.SessionEnd:return convertSessionEndEvent(a);c
438
438
  //spanning_mpids: sdkEvent.SessionMpids
439
439
  };return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.sessionEnd,data:c}}function convertSessionStartEvent(a){var b=convertBaseEventData(a),c={};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.sessionStart,data:c}}function convertPageViewEvent(a){var b=convertBaseEventData(a),c={custom_flags:a.CustomFlags,screen_name:a.EventName};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.screenView,data:c}}function convertOptOutEvent(a){var b=convertBaseEventData(a),c={is_opted_out:a.OptOut};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.optOut,data:c}}function convertCustomEvent(a){var b=convertBaseEventData(a),c={custom_event_type:convertSdkEventType(a.EventCategory),custom_flags:a.CustomFlags,event_name:a.EventName};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.customEvent,data:c}}function convertSdkEventType(a){return a===Types.EventType.Other?dist.CustomEventDataCustomEventTypeEnum.other:a===Types.EventType.Location?dist.CustomEventDataCustomEventTypeEnum.location:a===Types.EventType.Navigation?dist.CustomEventDataCustomEventTypeEnum.navigation:a===Types.EventType.Search?dist.CustomEventDataCustomEventTypeEnum.search:a===Types.EventType.Social?dist.CustomEventDataCustomEventTypeEnum.social:a===Types.EventType.Transaction?dist.CustomEventDataCustomEventTypeEnum.transaction:a===Types.EventType.UserContent?dist.CustomEventDataCustomEventTypeEnum.userContent:a===Types.EventType.UserPreference?dist.CustomEventDataCustomEventTypeEnum.userPreference:a===Types.EventType.Media?dist.CustomEventDataCustomEventTypeEnum.media:a===Types.CommerceEventType.ProductAddToCart?dist.CommerceEventDataCustomEventTypeEnum.addToCart:a===Types.CommerceEventType.ProductAddToWishlist?dist.CommerceEventDataCustomEventTypeEnum.addToWishlist:a===Types.CommerceEventType.ProductCheckout?dist.CommerceEventDataCustomEventTypeEnum.checkout:a===Types.CommerceEventType.ProductCheckoutOption?dist.CommerceEventDataCustomEventTypeEnum.checkoutOption:a===Types.CommerceEventType.ProductClick?dist.CommerceEventDataCustomEventTypeEnum.click:a===Types.CommerceEventType.ProductImpression?dist.CommerceEventDataCustomEventTypeEnum.impression:a===Types.CommerceEventType.ProductPurchase?dist.CommerceEventDataCustomEventTypeEnum.purchase:a===Types.CommerceEventType.ProductRefund?dist.CommerceEventDataCustomEventTypeEnum.refund:a===Types.CommerceEventType.ProductRemoveFromCart?dist.CommerceEventDataCustomEventTypeEnum.removeFromCart:a===Types.CommerceEventType.ProductRemoveFromWishlist?dist.CommerceEventDataCustomEventTypeEnum.removeFromWishlist:a===Types.CommerceEventType.ProductViewDetail?dist.CommerceEventDataCustomEventTypeEnum.viewDetail:a===Types.CommerceEventType.PromotionClick?dist.CommerceEventDataCustomEventTypeEnum.promotionClick:a===Types.CommerceEventType.PromotionView?dist.CommerceEventDataCustomEventTypeEnum.promotionView:dist.CustomEventDataCustomEventTypeEnum.unknown}function convertBaseEventData(a){var b={timestamp_unixtime_ms:a.Timestamp,session_uuid:a.SessionId,session_start_unixtime_ms:a.SessionStartDate,custom_attributes:a.EventAttributes,location:convertSDKLocation(a.Location),source_message_id:a.SourceMessageId};return b}function convertSDKLocation(a){return a&&Object.keys(a).length?{latitude:a.lat,longitude:a.lng}:null}function convertUserAttributeChangeEvent(a){var b=convertBaseEventData(a),c={user_attribute_name:a.UserAttributeChanges.UserAttributeName,new:a.UserAttributeChanges.New,old:a.UserAttributeChanges.Old,deleted:a.UserAttributeChanges.Deleted,is_new_attribute:a.UserAttributeChanges.IsNewAttribute};return c=__assign(__assign({},c),b),{event_type:dist.EventTypeEnum.userAttributeChange,data:c}}function convertUserIdentityChangeEvent(a){var b=convertBaseEventData(a),c={new:{identity_type:convertUserIdentityTypeToServerIdentityType(a.UserIdentityChanges.New.IdentityType),identity:a.UserIdentityChanges.New.Identity||null,timestamp_unixtime_ms:a.Timestamp,created_this_batch:a.UserIdentityChanges.New.CreatedThisBatch},old:{identity_type:convertUserIdentityTypeToServerIdentityType(a.UserIdentityChanges.Old.IdentityType),identity:a.UserIdentityChanges.Old.Identity||null,timestamp_unixtime_ms:a.Timestamp,created_this_batch:a.UserIdentityChanges.Old.CreatedThisBatch}};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.userIdentityChange,data:c}}function convertUserIdentityTypeToServerIdentityType(a){return a===SDKIdentityTypeEnum.other?dist.IdentityTypeEnum.other:a===SDKIdentityTypeEnum.customerId?dist.IdentityTypeEnum.customerId:a===SDKIdentityTypeEnum.facebook?dist.IdentityTypeEnum.facebook:a===SDKIdentityTypeEnum.twitter?dist.IdentityTypeEnum.twitter:a===SDKIdentityTypeEnum.google?dist.IdentityTypeEnum.google:a===SDKIdentityTypeEnum.microsoft?dist.IdentityTypeEnum.microsoft:a===SDKIdentityTypeEnum.yahoo?dist.IdentityTypeEnum.yahoo:a===SDKIdentityTypeEnum.email?dist.IdentityTypeEnum.email:a===SDKIdentityTypeEnum.alias?dist.IdentityTypeEnum.alias:a===SDKIdentityTypeEnum.facebookCustomAudienceId?dist.IdentityTypeEnum.facebookCustomAudienceId:a===SDKIdentityTypeEnum.otherId2?dist.IdentityTypeEnum.otherId2:a===SDKIdentityTypeEnum.otherId3?dist.IdentityTypeEnum.otherId3:a===SDKIdentityTypeEnum.otherId4?dist.IdentityTypeEnum.otherId4:a===SDKIdentityTypeEnum.otherId5?dist.IdentityTypeEnum.otherId5:a===SDKIdentityTypeEnum.otherId6?dist.IdentityTypeEnum.otherId6:a===SDKIdentityTypeEnum.otherId7?dist.IdentityTypeEnum.otherId7:a===SDKIdentityTypeEnum.otherId8?dist.IdentityTypeEnum.otherId8:a===SDKIdentityTypeEnum.otherId9?dist.IdentityTypeEnum.otherId9:a===SDKIdentityTypeEnum.otherId10?dist.IdentityTypeEnum.otherId10:a===SDKIdentityTypeEnum.mobileNumber?dist.IdentityTypeEnum.mobileNumber:a===SDKIdentityTypeEnum.phoneNumber2?dist.IdentityTypeEnum.phoneNumber2:a===SDKIdentityTypeEnum.phoneNumber3?dist.IdentityTypeEnum.phoneNumber3:void 0}
440
440
 
441
- var BatchUploader=/** @class */function(){function a(b,c){var d=this;this.mpInstance=b,this.uploadIntervalMillis=c,this.batchingEnabled=c>=a.MINIMUM_INTERVAL_MILLIS,this.uploadIntervalMillis<a.MINIMUM_INTERVAL_MILLIS&&(this.uploadIntervalMillis=a.MINIMUM_INTERVAL_MILLIS),this.pendingEvents=[],this.pendingUploads=[];var e=this.mpInstance._Store,f=e.SDKConfig,g=e.devToken,h=this.mpInstance._Helpers.createServiceUrl(f.v3SecureServiceUrl,g);this.uploadUrl="".concat(h,"/events"),setTimeout(function(){d.prepareAndUpload(!0,!1);},this.uploadIntervalMillis),this.addEventListeners();}return a.prototype.addEventListeners=function(){var a=this;document.addEventListener("visibilitychange",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("beforeunload",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("pagehide",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());});},a.prototype.isBeaconAvailable=function(){return !!navigator.sendBeacon},a.prototype.queueEvent=function(a){isEmpty(a)||(this.pendingEvents.push(a),this.mpInstance.Logger.verbose("Queuing event: ".concat(JSON.stringify(a))),this.mpInstance.Logger.verbose("Queued event count: ".concat(this.pendingEvents.length)),(!this.batchingEnabled||Types.TriggerUploadType[a.EventDataType])&&this.prepareAndUpload(!1,!1));},a.createNewUploads=function(a,b,c){if(!b||!a||!a.length)return null;//bucket by MPID, and then by session, ordered by timestamp
441
+ /**
442
+ * BatchUploader contains all the logic to upload batches to mParticle.
443
+ * It queues events as they come in and at set intervals turns them into batches.
444
+ * It then attempts to upload them to mParticle.
445
+ *
446
+ * These uploads happen on an interval basis using window.fetch or XHR
447
+ * requests, depending on what is available in the browser.
448
+ *
449
+ * Uploads can also be triggered on browser visibility/focus changes via an
450
+ * event listener, which then uploads to mPartice via the browser's Beacon API.
451
+ */var BatchUploader=/** @class */function(){/**
452
+ * Creates an instance of a BatchUploader
453
+ * @param {MParticleWebSDK} mpInstance - the mParticle SDK instance
454
+ * @param {number} uploadInterval - the desired upload interval in milliseconds
455
+ */function a(b,c){this.mpInstance=b,this.uploadIntervalMillis=c,this.batchingEnabled=c>=a.MINIMUM_INTERVAL_MILLIS,this.uploadIntervalMillis<a.MINIMUM_INTERVAL_MILLIS&&(this.uploadIntervalMillis=a.MINIMUM_INTERVAL_MILLIS),this.eventsQueuedForProcessing=[],this.batchesQueuedForProcessing=[];var d=this.mpInstance._Store,e=d.SDKConfig,f=d.devToken,g=this.mpInstance._Helpers.createServiceUrl(e.v3SecureServiceUrl,f);this.uploadUrl="".concat(g,"/events"),this.uploader=window.fetch?new FetchUploader(this.uploadUrl):new XHRUploader(this.uploadUrl),this.triggerUploadInterval(!0,!1),this.addEventListeners();}// Adds listeners to be used trigger Navigator.sendBeacon if the browser
456
+ // loses focus for any reason, such as closing browser tab or minimizing window
457
+ return a.prototype.addEventListeners=function(){var a=this;// visibility change is a document property, not window
458
+ document.addEventListener("visibilitychange",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("beforeunload",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("pagehide",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());});},a.prototype.isBeaconAvailable=function(){return !!navigator.sendBeacon},a.prototype.triggerUploadInterval=function(a,b){var c=this;void 0===a&&(a=!1),void 0===b&&(b=!1),setTimeout(function(){c.prepareAndUpload(a,b);},this.uploadIntervalMillis);},a.prototype.queueEvent=function(a){isEmpty(a)||(this.eventsQueuedForProcessing.push(a),this.mpInstance.Logger.verbose("Queuing event: ".concat(JSON.stringify(a))),this.mpInstance.Logger.verbose("Queued event count: ".concat(this.eventsQueuedForProcessing.length)),(!this.batchingEnabled||Types.TriggerUploadType[a.EventDataType])&&this.prepareAndUpload(!1,!1));},a.createNewBatches=function(a,b,c){if(!b||!a||!a.length)return null;//bucket by MPID, and then by session, ordered by timestamp
442
459
  for(var d,e=[],f=new Map,g=0,h=a;g<h.length;g++){//on initial startup, there may be events logged without an mpid.
443
- if(d=h[g],!d.MPID){var i=b.getMPID();d.MPID=i;}var j=f.get(d.MPID);j||(j=[]),j.push(d),f.set(d.MPID,j);}for(var k=0,l=Array.from(f.entries());k<l.length;k++){for(var m=l[k],i=m[0],n=m[1],o=new Map,p=0,q=n;p<q.length;p++){var d=q[p],j=o.get(d.SessionId);j||(j=[]),j.push(d),o.set(d.SessionId,j);}for(var r=0,s=Array.from(o.entries());r<s.length;r++){var t=s[r],u=convertEvents(i,t[1],c),v=c._Store.SDKConfig.onCreateBatch;v&&(u=v(u),u?u.modified=!0:c.Logger.warning("Skiping batch upload because no batch was returned from onCreateBatch callback")),u&&e.push(u);}}return e},a.prototype.prepareAndUpload=function(b,c){return __awaiter(this,void 0,void 0,function(){var d,e,f,g,h,i,j,k=this;return __generator(this,function(l){switch(l.label){case 0:return d=this.mpInstance.Identity.getCurrentUser(),e=this.pendingEvents,this.pendingEvents=[],f=a.createNewUploads(e,d,this.mpInstance),f&&f.length&&(i=this.pendingUploads).push.apply(i,f),g=this.pendingUploads,this.pendingUploads=[],[4/*yield*/,this.upload(this.mpInstance.Logger,g,c)];case 1:return h=l.sent(),h&&h.length&&(j=this.pendingUploads).unshift.apply(j,h),b&&setTimeout(function(){k.prepareAndUpload(!0,!1);},this.uploadIntervalMillis),[2/*return*/];}})})},a.prototype.upload=function(b,c,d){return __awaiter(this,void 0,void 0,function(){var e,f,g,h,j,k,l;return __generator(this,function(i){switch(i.label){case 0:if(f=c.filter(function(a){return !isEmpty(a.events)}),isEmpty(f))return [2/*return*/,null];b.verbose("Uploading batches: ".concat(JSON.stringify(f))),b.verbose("Batch count: ".concat(f.length)),g=0,i.label=1;case 1:return g<f.length?(h={method:"POST",headers:{Accept:a.CONTENT_TYPE,"Content-Type":"text/plain;charset=UTF-8"},body:JSON.stringify(f[g])},!(d&&this.isBeaconAvailable()))?[3/*break*/,2]:(j=new Blob([h.body],{type:"text/plain;charset=UTF-8"}),navigator.sendBeacon(this.uploadUrl,j),[3/*break*/,6]):[3/*break*/,7];case 2:e||(window.fetch?e=new FetchUploader(this.uploadUrl,b):e=new XHRUploader(this.uploadUrl,b)),i.label=3;case 3:return i.trys.push([3,5,,6]),[4/*yield*/,e.upload(h,f,g)];case 4:if(k=i.sent(),200<=k.status&&300>k.status)b.verbose("Upload success for request ID: ".concat(f[g].source_request_id));else {if(500<=k.status||429===k.status)//server error, add back current events and try again later
444
- return b.error("HTTP error status ".concat(k.status," received")),[2/*return*/,f.slice(g,f.length)];if(401<=k.status)//if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
445
- return b.error("HTTP error status ".concat(k.status," while uploading - please verify your API key.")),[2/*return*/,null]}return [3/*break*/,6];case 5:return l=i.sent(),b.error("Error sending event to mParticle servers. ".concat(l)),[2/*return*/,f.slice(g,f.length)];case 6:return g++,[3/*break*/,1];case 7:return [2/*return*/,null];}})})},a.CONTENT_TYPE="text/plain;charset=UTF-8",a.MINIMUM_INTERVAL_MILLIS=500,a}();var AsyncUploader=/** @class */function(){function a(a,b){this.url=a,this.logger=b;}return a}(),FetchUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,fetch(this.url,a)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b}(AsyncUploader),XHRUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,this.makeRequest(this.url,this.logger,a.body)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b.prototype.makeRequest=function(a,b,c){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(){return b=new XMLHttpRequest,[2/*return*/,new Promise(function(d,e){b.onreadystatechange=function(){4!==b.readyState||(200<=b.status&&300>b.status?d(b):e(b));},b.open("post",a),b.send(c);})]})})},b}(AsyncUploader);
460
+ if(d=h[g],!d.MPID){var i=b.getMPID();d.MPID=i;}var j=f.get(d.MPID);j||(j=[]),j.push(d),f.set(d.MPID,j);}for(var k=0,l=Array.from(f.entries());k<l.length;k++){for(var m=l[k],i=m[0],n=m[1],o=new Map,p=0,q=n;p<q.length;p++){var d=q[p],j=o.get(d.SessionId);j||(j=[]),j.push(d),o.set(d.SessionId,j);}for(var r=0,s=Array.from(o.entries());r<s.length;r++){var t=s[r],u=convertEvents(i,t[1],c),v=c._Store.SDKConfig.onCreateBatch;v&&(u=v(u),u?u.modified=!0:c.Logger.warning("Skiping batch upload because no batch was returned from onCreateBatch callback")),u&&e.push(u);}}return e},a.prototype.prepareAndUpload=function(b,c){return __awaiter(this,void 0,void 0,function(){var d,e,f,g,h,i,j,k=this;return __generator(this,function(){return d=this.mpInstance.Identity.getCurrentUser(),e=this.eventsQueuedForProcessing,this.eventsQueuedForProcessing=[],f=a.createNewBatches(e,d,this.mpInstance),isEmpty(f)||(j=this.batchesQueuedForProcessing).push.apply(j,f),g=this.batchesQueuedForProcessing,h=[],this.batchesQueuedForProcessing=[],i=g.map(function(a){return k.upload(k.mpInstance.Logger,a,c)}),isEmpty(i)||Promise.all(i).then(function(a){a.forEach(function(a){return isEmpty(a)?null:h.push(a)});})["catch"](function(a){k.mpInstance.Logger.error("Error processing batches during upload attempt: ".concat(a));})["finally"](function(){var a;// Any batches that did not upload should be put back into the queue for processing
461
+ isEmpty(h)||(a=k.batchesQueuedForProcessing).unshift.apply(a,h);}),b&&this.triggerUploadInterval(b,!1),[2/*return*/]})})},a.prototype.upload=function(b,c,d){return __awaiter(this,void 0,void 0,function(){var e,f,g,h;return __generator(this,function(i){switch(i.label){case 0:return isEmpty(c)||isEmpty(c.events)?[2/*return*/,null]:(b.verbose("Uploading batches: ".concat(JSON.stringify(c))),e={method:"POST",headers:{Accept:a.CONTENT_TYPE,"Content-Type":"text/plain;charset=UTF-8"},body:JSON.stringify(c)},!(d&&this.isBeaconAvailable()))?[3/*break*/,1]:(f=new Blob([e.body],{type:"text/plain;charset=UTF-8"}),navigator.sendBeacon(this.uploadUrl,f),[3/*break*/,4]);case 1:return i.trys.push([1,3,,4]),[4/*yield*/,this.uploader.upload(e)];case 2:// TODO: Should we make this a switch statement instead?
462
+ return (g=i.sent(),200<=g.status&&300>g.status)?(b.verbose("Upload success for request ID: ".concat(c.source_request_id)),[2/*return*/,null]):500<=g.status||429===g.status?(b.error("HTTP error status ".concat(g.status," received")),[2/*return*/,c]):401<=g.status?(b.error("HTTP error status ".concat(g.status," while uploading - please verify your API key.")),[2/*return*/,c]):[3/*break*/,4];case 3:return h=i.sent(),b.error("Error sending event to mParticle servers. ".concat(h)),[2/*return*/,c];case 4:return [2/*return*/,null];}})})},a.CONTENT_TYPE="text/plain;charset=UTF-8",a.MINIMUM_INTERVAL_MILLIS=500,a}();var AsyncUploader=/** @class */function(){function a(a){this.url=a;}return a}(),FetchUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,fetch(this.url,a)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b}(AsyncUploader),XHRUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,this.makeRequest(this.url,a.body)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b.prototype.makeRequest=function(a,b){return __awaiter(this,void 0,void 0,function(){var c;return __generator(this,function(){return c=new XMLHttpRequest,[2/*return*/,new Promise(function(d,e){c.onreadystatechange=function(){4!==c.readyState||(200<=c.status&&300>c.status?d(c):e(c));},c.open("post",a),c.send(b);})]})})},b}(AsyncUploader);
446
463
 
447
464
  var Messages$9=Constants.Messages;function APIClient(a,b){this.uploader=null;var c=this;this.queueEventForBatchUpload=function(b){if(!this.uploader){var c=a._Helpers.getFeatureFlag(Constants.FeatureFlags.EventBatchingIntervalMillis);this.uploader=new BatchUploader(a,c);}this.uploader.queueEvent(b),a._Persistence.update();},this.shouldEnableBatching=function(){// Returns a string of a number that must be parsed
448
465
  // Invalid strings will be parsed to NaN which is falsey
@@ -32,7 +32,7 @@ function _defineProperty(obj, key, value) {
32
32
 
33
33
  var _TriggerUploadType,MessageType$1={SessionStart:1,SessionEnd:2,PageView:3,PageEvent:4,CrashReport:5,OptOut:6,AppStateTransition:10,Profile:14,Commerce:16,Media:20,UserAttributeChange:17,UserIdentityChange:18},TriggerUploadType=(_TriggerUploadType={},_defineProperty(_TriggerUploadType,MessageType$1.Commerce,1),_defineProperty(_TriggerUploadType,MessageType$1.UserAttributeChange,1),_defineProperty(_TriggerUploadType,MessageType$1.UserIdentityChange,1),_TriggerUploadType),EventType={Unknown:0,Navigation:1,Location:2,Search:3,Transaction:4,UserContent:5,UserPreference:6,Social:7,Other:8,Media:9,getName:function getName(a){return a===EventType.Unknown?"Unknown":a===EventType.Navigation?"Navigation":a===EventType.Location?"Location":a===EventType.Search?"Search":a===EventType.Transaction?"Transaction":a===EventType.UserContent?"User Content":a===EventType.UserPreference?"User Preference":a===EventType.Social?"Social":a===CommerceEventType.ProductAddToCart?"Product Added to Cart":a===CommerceEventType.ProductAddToWishlist?"Product Added to Wishlist":a===CommerceEventType.ProductCheckout?"Product Checkout":a===CommerceEventType.ProductCheckoutOption?"Product Checkout Options":a===CommerceEventType.ProductClick?"Product Click":a===CommerceEventType.ProductImpression?"Product Impression":a===CommerceEventType.ProductPurchase?"Product Purchased":a===CommerceEventType.ProductRefund?"Product Refunded":a===CommerceEventType.ProductRemoveFromCart?"Product Removed From Cart":a===CommerceEventType.ProductRemoveFromWishlist?"Product Removed from Wishlist":a===CommerceEventType.ProductViewDetail?"Product View Details":a===CommerceEventType.PromotionClick?"Promotion Click":a===CommerceEventType.PromotionView?"Promotion View":"Other"}},CommerceEventType={ProductAddToCart:10,ProductRemoveFromCart:11,ProductCheckout:12,ProductCheckoutOption:13,ProductClick:14,ProductViewDetail:15,ProductPurchase:16,ProductRefund:17,PromotionView:18,PromotionClick:19,ProductAddToWishlist:20,ProductRemoveFromWishlist:21,ProductImpression:22},IdentityType={Other:0,CustomerId:1,Facebook:2,Twitter:3,Google:4,Microsoft:5,Yahoo:6,Email:7,FacebookCustomAudienceId:9,Other2:10,Other3:11,Other4:12,Other5:13,Other6:14,Other7:15,Other8:16,Other9:17,Other10:18,MobileNumber:19,PhoneNumber2:20,PhoneNumber3:21};IdentityType.isValid=function(a){if("number"==typeof a)for(var b in IdentityType)if(IdentityType.hasOwnProperty(b)&&IdentityType[b]===a)return !0;return !1},IdentityType.getName=function(a){return a===window.mParticle.IdentityType.CustomerId?"Customer ID":a===window.mParticle.IdentityType.Facebook?"Facebook ID":a===window.mParticle.IdentityType.Twitter?"Twitter ID":a===window.mParticle.IdentityType.Google?"Google ID":a===window.mParticle.IdentityType.Microsoft?"Microsoft ID":a===window.mParticle.IdentityType.Yahoo?"Yahoo ID":a===window.mParticle.IdentityType.Email?"Email":a===window.mParticle.IdentityType.FacebookCustomAudienceId?"Facebook App User ID":"Other ID"},IdentityType.getIdentityType=function(a){return "other"===a?IdentityType.Other:"customerid"===a?IdentityType.CustomerId:"facebook"===a?IdentityType.Facebook:"twitter"===a?IdentityType.Twitter:"google"===a?IdentityType.Google:"microsoft"===a?IdentityType.Microsoft:"yahoo"===a?IdentityType.Yahoo:"email"===a?IdentityType.Email:"facebookcustomaudienceid"===a?IdentityType.FacebookCustomAudienceId:"other2"===a?IdentityType.Other2:"other3"===a?IdentityType.Other3:"other4"===a?IdentityType.Other4:"other5"===a?IdentityType.Other5:"other6"===a?IdentityType.Other6:"other7"===a?IdentityType.Other7:"other8"===a?IdentityType.Other8:"other9"===a?IdentityType.Other9:"other10"===a?IdentityType.Other10:"mobile_number"===a?IdentityType.MobileNumber:"phone_number_2"===a?IdentityType.PhoneNumber2:!("phone_number_3"!=a)&&IdentityType.PhoneNumber3},IdentityType.getIdentityName=function(a){return a===IdentityType.Other?"other":a===IdentityType.CustomerId?"customerid":a===IdentityType.Facebook?"facebook":a===IdentityType.Twitter?"twitter":a===IdentityType.Google?"google":a===IdentityType.Microsoft?"microsoft":a===IdentityType.Yahoo?"yahoo":a===IdentityType.Email?"email":a===IdentityType.FacebookCustomAudienceId?"facebookcustomaudienceid":a===IdentityType.Other2?"other2":a===IdentityType.Other3?"other3":a===IdentityType.Other4?"other4":a===IdentityType.Other5?"other5":a===IdentityType.Other6?"other6":a===IdentityType.Other7?"other7":a===IdentityType.Other8?"other8":a===IdentityType.Other9?"other9":a===IdentityType.Other10?"other10":a===IdentityType.MobileNumber?"mobile_number":a===IdentityType.PhoneNumber2?"phone_number_2":a===IdentityType.PhoneNumber3?"phone_number_3":void 0};var ProductActionType={Unknown:0,AddToCart:1,RemoveFromCart:2,Checkout:3,CheckoutOption:4,Click:5,ViewDetail:6,Purchase:7,Refund:8,AddToWishlist:9,RemoveFromWishlist:10};ProductActionType.getName=function(a){return a===ProductActionType.AddToCart?"Add to Cart":a===ProductActionType.RemoveFromCart?"Remove from Cart":a===ProductActionType.Checkout?"Checkout":a===ProductActionType.CheckoutOption?"Checkout Option":a===ProductActionType.Click?"Click":a===ProductActionType.ViewDetail?"View Detail":a===ProductActionType.Purchase?"Purchase":a===ProductActionType.Refund?"Refund":a===ProductActionType.AddToWishlist?"Add to Wishlist":a===ProductActionType.RemoveFromWishlist?"Remove from Wishlist":"Unknown"},ProductActionType.getExpansionName=function(a){return a===ProductActionType.AddToCart?"add_to_cart":a===ProductActionType.RemoveFromCart?"remove_from_cart":a===ProductActionType.Checkout?"checkout":a===ProductActionType.CheckoutOption?"checkout_option":a===ProductActionType.Click?"click":a===ProductActionType.ViewDetail?"view_detail":a===ProductActionType.Purchase?"purchase":a===ProductActionType.Refund?"refund":a===ProductActionType.AddToWishlist?"add_to_wishlist":a===ProductActionType.RemoveFromWishlist?"remove_from_wishlist":"unknown"};var PromotionActionType={Unknown:0,PromotionView:1,PromotionClick:2};PromotionActionType.getName=function(a){return a===PromotionActionType.PromotionView?"view":a===PromotionActionType.PromotionClick?"click":"unknown"},PromotionActionType.getExpansionName=function(a){return a===PromotionActionType.PromotionView?"view":a===PromotionActionType.PromotionClick?"click":"unknown"};var ProfileMessageType={Logout:3},ApplicationTransitionType$1={AppInit:1},Environment={Production:"production",Development:"development"};var Types = {MessageType:MessageType$1,EventType:EventType,CommerceEventType:CommerceEventType,IdentityType:IdentityType,ProfileMessageType:ProfileMessageType,ApplicationTransitionType:ApplicationTransitionType$1,ProductActionType:ProductActionType,PromotionActionType:PromotionActionType,TriggerUploadType:TriggerUploadType,Environment:Environment};
34
34
 
35
- var version = "2.18.3";
35
+ var version = "2.19.0";
36
36
 
37
37
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{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"},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
38
38
  },DefaultUrls:{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/"},Base64CookieKeys:{csm:1,sa:1,ss:1,ua:1,ui:1,csd:1,ia:1,con:1},SDKv2NonMPIDCookieKeys:{gs:1,cu:1,l:1,globalSettings:1,currentUserMPID:1},HTTPCodes:{noHttpCoverage:-1,activeIdentityRequest:-2,activeSession:-3,validationIssue:-4,nativeIdentityRequest:-5,loggingDisabledOrMissingAPIKey:-6,tooManyRequests:429},FeatureFlags:{ReportBatching:"reportBatching",EventsV3:"eventsV3",EventBatchingIntervalMillis:"eventBatchingIntervalMillis"},DefaultInstance:"default_instance"};
@@ -438,11 +438,28 @@ return null;case Types.MessageType.SessionEnd:return convertSessionEndEvent(a);c
438
438
  //spanning_mpids: sdkEvent.SessionMpids
439
439
  };return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.sessionEnd,data:c}}function convertSessionStartEvent(a){var b=convertBaseEventData(a),c={};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.sessionStart,data:c}}function convertPageViewEvent(a){var b=convertBaseEventData(a),c={custom_flags:a.CustomFlags,screen_name:a.EventName};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.screenView,data:c}}function convertOptOutEvent(a){var b=convertBaseEventData(a),c={is_opted_out:a.OptOut};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.optOut,data:c}}function convertCustomEvent(a){var b=convertBaseEventData(a),c={custom_event_type:convertSdkEventType(a.EventCategory),custom_flags:a.CustomFlags,event_name:a.EventName};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.customEvent,data:c}}function convertSdkEventType(a){return a===Types.EventType.Other?dist.CustomEventDataCustomEventTypeEnum.other:a===Types.EventType.Location?dist.CustomEventDataCustomEventTypeEnum.location:a===Types.EventType.Navigation?dist.CustomEventDataCustomEventTypeEnum.navigation:a===Types.EventType.Search?dist.CustomEventDataCustomEventTypeEnum.search:a===Types.EventType.Social?dist.CustomEventDataCustomEventTypeEnum.social:a===Types.EventType.Transaction?dist.CustomEventDataCustomEventTypeEnum.transaction:a===Types.EventType.UserContent?dist.CustomEventDataCustomEventTypeEnum.userContent:a===Types.EventType.UserPreference?dist.CustomEventDataCustomEventTypeEnum.userPreference:a===Types.EventType.Media?dist.CustomEventDataCustomEventTypeEnum.media:a===Types.CommerceEventType.ProductAddToCart?dist.CommerceEventDataCustomEventTypeEnum.addToCart:a===Types.CommerceEventType.ProductAddToWishlist?dist.CommerceEventDataCustomEventTypeEnum.addToWishlist:a===Types.CommerceEventType.ProductCheckout?dist.CommerceEventDataCustomEventTypeEnum.checkout:a===Types.CommerceEventType.ProductCheckoutOption?dist.CommerceEventDataCustomEventTypeEnum.checkoutOption:a===Types.CommerceEventType.ProductClick?dist.CommerceEventDataCustomEventTypeEnum.click:a===Types.CommerceEventType.ProductImpression?dist.CommerceEventDataCustomEventTypeEnum.impression:a===Types.CommerceEventType.ProductPurchase?dist.CommerceEventDataCustomEventTypeEnum.purchase:a===Types.CommerceEventType.ProductRefund?dist.CommerceEventDataCustomEventTypeEnum.refund:a===Types.CommerceEventType.ProductRemoveFromCart?dist.CommerceEventDataCustomEventTypeEnum.removeFromCart:a===Types.CommerceEventType.ProductRemoveFromWishlist?dist.CommerceEventDataCustomEventTypeEnum.removeFromWishlist:a===Types.CommerceEventType.ProductViewDetail?dist.CommerceEventDataCustomEventTypeEnum.viewDetail:a===Types.CommerceEventType.PromotionClick?dist.CommerceEventDataCustomEventTypeEnum.promotionClick:a===Types.CommerceEventType.PromotionView?dist.CommerceEventDataCustomEventTypeEnum.promotionView:dist.CustomEventDataCustomEventTypeEnum.unknown}function convertBaseEventData(a){var b={timestamp_unixtime_ms:a.Timestamp,session_uuid:a.SessionId,session_start_unixtime_ms:a.SessionStartDate,custom_attributes:a.EventAttributes,location:convertSDKLocation(a.Location),source_message_id:a.SourceMessageId};return b}function convertSDKLocation(a){return a&&Object.keys(a).length?{latitude:a.lat,longitude:a.lng}:null}function convertUserAttributeChangeEvent(a){var b=convertBaseEventData(a),c={user_attribute_name:a.UserAttributeChanges.UserAttributeName,new:a.UserAttributeChanges.New,old:a.UserAttributeChanges.Old,deleted:a.UserAttributeChanges.Deleted,is_new_attribute:a.UserAttributeChanges.IsNewAttribute};return c=__assign(__assign({},c),b),{event_type:dist.EventTypeEnum.userAttributeChange,data:c}}function convertUserIdentityChangeEvent(a){var b=convertBaseEventData(a),c={new:{identity_type:convertUserIdentityTypeToServerIdentityType(a.UserIdentityChanges.New.IdentityType),identity:a.UserIdentityChanges.New.Identity||null,timestamp_unixtime_ms:a.Timestamp,created_this_batch:a.UserIdentityChanges.New.CreatedThisBatch},old:{identity_type:convertUserIdentityTypeToServerIdentityType(a.UserIdentityChanges.Old.IdentityType),identity:a.UserIdentityChanges.Old.Identity||null,timestamp_unixtime_ms:a.Timestamp,created_this_batch:a.UserIdentityChanges.Old.CreatedThisBatch}};return c=Object.assign(c,b),{event_type:dist.EventTypeEnum.userIdentityChange,data:c}}function convertUserIdentityTypeToServerIdentityType(a){return a===SDKIdentityTypeEnum.other?dist.IdentityTypeEnum.other:a===SDKIdentityTypeEnum.customerId?dist.IdentityTypeEnum.customerId:a===SDKIdentityTypeEnum.facebook?dist.IdentityTypeEnum.facebook:a===SDKIdentityTypeEnum.twitter?dist.IdentityTypeEnum.twitter:a===SDKIdentityTypeEnum.google?dist.IdentityTypeEnum.google:a===SDKIdentityTypeEnum.microsoft?dist.IdentityTypeEnum.microsoft:a===SDKIdentityTypeEnum.yahoo?dist.IdentityTypeEnum.yahoo:a===SDKIdentityTypeEnum.email?dist.IdentityTypeEnum.email:a===SDKIdentityTypeEnum.alias?dist.IdentityTypeEnum.alias:a===SDKIdentityTypeEnum.facebookCustomAudienceId?dist.IdentityTypeEnum.facebookCustomAudienceId:a===SDKIdentityTypeEnum.otherId2?dist.IdentityTypeEnum.otherId2:a===SDKIdentityTypeEnum.otherId3?dist.IdentityTypeEnum.otherId3:a===SDKIdentityTypeEnum.otherId4?dist.IdentityTypeEnum.otherId4:a===SDKIdentityTypeEnum.otherId5?dist.IdentityTypeEnum.otherId5:a===SDKIdentityTypeEnum.otherId6?dist.IdentityTypeEnum.otherId6:a===SDKIdentityTypeEnum.otherId7?dist.IdentityTypeEnum.otherId7:a===SDKIdentityTypeEnum.otherId8?dist.IdentityTypeEnum.otherId8:a===SDKIdentityTypeEnum.otherId9?dist.IdentityTypeEnum.otherId9:a===SDKIdentityTypeEnum.otherId10?dist.IdentityTypeEnum.otherId10:a===SDKIdentityTypeEnum.mobileNumber?dist.IdentityTypeEnum.mobileNumber:a===SDKIdentityTypeEnum.phoneNumber2?dist.IdentityTypeEnum.phoneNumber2:a===SDKIdentityTypeEnum.phoneNumber3?dist.IdentityTypeEnum.phoneNumber3:void 0}
440
440
 
441
- var BatchUploader=/** @class */function(){function a(b,c){var d=this;this.mpInstance=b,this.uploadIntervalMillis=c,this.batchingEnabled=c>=a.MINIMUM_INTERVAL_MILLIS,this.uploadIntervalMillis<a.MINIMUM_INTERVAL_MILLIS&&(this.uploadIntervalMillis=a.MINIMUM_INTERVAL_MILLIS),this.pendingEvents=[],this.pendingUploads=[];var e=this.mpInstance._Store,f=e.SDKConfig,g=e.devToken,h=this.mpInstance._Helpers.createServiceUrl(f.v3SecureServiceUrl,g);this.uploadUrl="".concat(h,"/events"),setTimeout(function(){d.prepareAndUpload(!0,!1);},this.uploadIntervalMillis),this.addEventListeners();}return a.prototype.addEventListeners=function(){var a=this;document.addEventListener("visibilitychange",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("beforeunload",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("pagehide",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());});},a.prototype.isBeaconAvailable=function(){return !!navigator.sendBeacon},a.prototype.queueEvent=function(a){isEmpty(a)||(this.pendingEvents.push(a),this.mpInstance.Logger.verbose("Queuing event: ".concat(JSON.stringify(a))),this.mpInstance.Logger.verbose("Queued event count: ".concat(this.pendingEvents.length)),(!this.batchingEnabled||Types.TriggerUploadType[a.EventDataType])&&this.prepareAndUpload(!1,!1));},a.createNewUploads=function(a,b,c){if(!b||!a||!a.length)return null;//bucket by MPID, and then by session, ordered by timestamp
441
+ /**
442
+ * BatchUploader contains all the logic to upload batches to mParticle.
443
+ * It queues events as they come in and at set intervals turns them into batches.
444
+ * It then attempts to upload them to mParticle.
445
+ *
446
+ * These uploads happen on an interval basis using window.fetch or XHR
447
+ * requests, depending on what is available in the browser.
448
+ *
449
+ * Uploads can also be triggered on browser visibility/focus changes via an
450
+ * event listener, which then uploads to mPartice via the browser's Beacon API.
451
+ */var BatchUploader=/** @class */function(){/**
452
+ * Creates an instance of a BatchUploader
453
+ * @param {MParticleWebSDK} mpInstance - the mParticle SDK instance
454
+ * @param {number} uploadInterval - the desired upload interval in milliseconds
455
+ */function a(b,c){this.mpInstance=b,this.uploadIntervalMillis=c,this.batchingEnabled=c>=a.MINIMUM_INTERVAL_MILLIS,this.uploadIntervalMillis<a.MINIMUM_INTERVAL_MILLIS&&(this.uploadIntervalMillis=a.MINIMUM_INTERVAL_MILLIS),this.eventsQueuedForProcessing=[],this.batchesQueuedForProcessing=[];var d=this.mpInstance._Store,e=d.SDKConfig,f=d.devToken,g=this.mpInstance._Helpers.createServiceUrl(e.v3SecureServiceUrl,f);this.uploadUrl="".concat(g,"/events"),this.uploader=window.fetch?new FetchUploader(this.uploadUrl):new XHRUploader(this.uploadUrl),this.triggerUploadInterval(!0,!1),this.addEventListeners();}// Adds listeners to be used trigger Navigator.sendBeacon if the browser
456
+ // loses focus for any reason, such as closing browser tab or minimizing window
457
+ return a.prototype.addEventListeners=function(){var a=this;// visibility change is a document property, not window
458
+ document.addEventListener("visibilitychange",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("beforeunload",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());}),window.addEventListener("pagehide",function(){a.prepareAndUpload(!1,a.isBeaconAvailable());});},a.prototype.isBeaconAvailable=function(){return !!navigator.sendBeacon},a.prototype.triggerUploadInterval=function(a,b){var c=this;void 0===a&&(a=!1),void 0===b&&(b=!1),setTimeout(function(){c.prepareAndUpload(a,b);},this.uploadIntervalMillis);},a.prototype.queueEvent=function(a){isEmpty(a)||(this.eventsQueuedForProcessing.push(a),this.mpInstance.Logger.verbose("Queuing event: ".concat(JSON.stringify(a))),this.mpInstance.Logger.verbose("Queued event count: ".concat(this.eventsQueuedForProcessing.length)),(!this.batchingEnabled||Types.TriggerUploadType[a.EventDataType])&&this.prepareAndUpload(!1,!1));},a.createNewBatches=function(a,b,c){if(!b||!a||!a.length)return null;//bucket by MPID, and then by session, ordered by timestamp
442
459
  for(var d,e=[],f=new Map,g=0,h=a;g<h.length;g++){//on initial startup, there may be events logged without an mpid.
443
- if(d=h[g],!d.MPID){var i=b.getMPID();d.MPID=i;}var j=f.get(d.MPID);j||(j=[]),j.push(d),f.set(d.MPID,j);}for(var k=0,l=Array.from(f.entries());k<l.length;k++){for(var m=l[k],i=m[0],n=m[1],o=new Map,p=0,q=n;p<q.length;p++){var d=q[p],j=o.get(d.SessionId);j||(j=[]),j.push(d),o.set(d.SessionId,j);}for(var r=0,s=Array.from(o.entries());r<s.length;r++){var t=s[r],u=convertEvents(i,t[1],c),v=c._Store.SDKConfig.onCreateBatch;v&&(u=v(u),u?u.modified=!0:c.Logger.warning("Skiping batch upload because no batch was returned from onCreateBatch callback")),u&&e.push(u);}}return e},a.prototype.prepareAndUpload=function(b,c){return __awaiter(this,void 0,void 0,function(){var d,e,f,g,h,i,j,k=this;return __generator(this,function(l){switch(l.label){case 0:return d=this.mpInstance.Identity.getCurrentUser(),e=this.pendingEvents,this.pendingEvents=[],f=a.createNewUploads(e,d,this.mpInstance),f&&f.length&&(i=this.pendingUploads).push.apply(i,f),g=this.pendingUploads,this.pendingUploads=[],[4/*yield*/,this.upload(this.mpInstance.Logger,g,c)];case 1:return h=l.sent(),h&&h.length&&(j=this.pendingUploads).unshift.apply(j,h),b&&setTimeout(function(){k.prepareAndUpload(!0,!1);},this.uploadIntervalMillis),[2/*return*/];}})})},a.prototype.upload=function(b,c,d){return __awaiter(this,void 0,void 0,function(){var e,f,g,h,j,k,l;return __generator(this,function(i){switch(i.label){case 0:if(f=c.filter(function(a){return !isEmpty(a.events)}),isEmpty(f))return [2/*return*/,null];b.verbose("Uploading batches: ".concat(JSON.stringify(f))),b.verbose("Batch count: ".concat(f.length)),g=0,i.label=1;case 1:return g<f.length?(h={method:"POST",headers:{Accept:a.CONTENT_TYPE,"Content-Type":"text/plain;charset=UTF-8"},body:JSON.stringify(f[g])},!(d&&this.isBeaconAvailable()))?[3/*break*/,2]:(j=new Blob([h.body],{type:"text/plain;charset=UTF-8"}),navigator.sendBeacon(this.uploadUrl,j),[3/*break*/,6]):[3/*break*/,7];case 2:e||(window.fetch?e=new FetchUploader(this.uploadUrl,b):e=new XHRUploader(this.uploadUrl,b)),i.label=3;case 3:return i.trys.push([3,5,,6]),[4/*yield*/,e.upload(h,f,g)];case 4:if(k=i.sent(),200<=k.status&&300>k.status)b.verbose("Upload success for request ID: ".concat(f[g].source_request_id));else {if(500<=k.status||429===k.status)//server error, add back current events and try again later
444
- return b.error("HTTP error status ".concat(k.status," received")),[2/*return*/,f.slice(g,f.length)];if(401<=k.status)//if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
445
- return b.error("HTTP error status ".concat(k.status," while uploading - please verify your API key.")),[2/*return*/,null]}return [3/*break*/,6];case 5:return l=i.sent(),b.error("Error sending event to mParticle servers. ".concat(l)),[2/*return*/,f.slice(g,f.length)];case 6:return g++,[3/*break*/,1];case 7:return [2/*return*/,null];}})})},a.CONTENT_TYPE="text/plain;charset=UTF-8",a.MINIMUM_INTERVAL_MILLIS=500,a}();var AsyncUploader=/** @class */function(){function a(a,b){this.url=a,this.logger=b;}return a}(),FetchUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,fetch(this.url,a)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b}(AsyncUploader),XHRUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,this.makeRequest(this.url,this.logger,a.body)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b.prototype.makeRequest=function(a,b,c){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(){return b=new XMLHttpRequest,[2/*return*/,new Promise(function(d,e){b.onreadystatechange=function(){4!==b.readyState||(200<=b.status&&300>b.status?d(b):e(b));},b.open("post",a),b.send(c);})]})})},b}(AsyncUploader);
460
+ if(d=h[g],!d.MPID){var i=b.getMPID();d.MPID=i;}var j=f.get(d.MPID);j||(j=[]),j.push(d),f.set(d.MPID,j);}for(var k=0,l=Array.from(f.entries());k<l.length;k++){for(var m=l[k],i=m[0],n=m[1],o=new Map,p=0,q=n;p<q.length;p++){var d=q[p],j=o.get(d.SessionId);j||(j=[]),j.push(d),o.set(d.SessionId,j);}for(var r=0,s=Array.from(o.entries());r<s.length;r++){var t=s[r],u=convertEvents(i,t[1],c),v=c._Store.SDKConfig.onCreateBatch;v&&(u=v(u),u?u.modified=!0:c.Logger.warning("Skiping batch upload because no batch was returned from onCreateBatch callback")),u&&e.push(u);}}return e},a.prototype.prepareAndUpload=function(b,c){return __awaiter(this,void 0,void 0,function(){var d,e,f,g,h,i,j,k=this;return __generator(this,function(){return d=this.mpInstance.Identity.getCurrentUser(),e=this.eventsQueuedForProcessing,this.eventsQueuedForProcessing=[],f=a.createNewBatches(e,d,this.mpInstance),isEmpty(f)||(j=this.batchesQueuedForProcessing).push.apply(j,f),g=this.batchesQueuedForProcessing,h=[],this.batchesQueuedForProcessing=[],i=g.map(function(a){return k.upload(k.mpInstance.Logger,a,c)}),isEmpty(i)||Promise.all(i).then(function(a){a.forEach(function(a){return isEmpty(a)?null:h.push(a)});})["catch"](function(a){k.mpInstance.Logger.error("Error processing batches during upload attempt: ".concat(a));})["finally"](function(){var a;// Any batches that did not upload should be put back into the queue for processing
461
+ isEmpty(h)||(a=k.batchesQueuedForProcessing).unshift.apply(a,h);}),b&&this.triggerUploadInterval(b,!1),[2/*return*/]})})},a.prototype.upload=function(b,c,d){return __awaiter(this,void 0,void 0,function(){var e,f,g,h;return __generator(this,function(i){switch(i.label){case 0:return isEmpty(c)||isEmpty(c.events)?[2/*return*/,null]:(b.verbose("Uploading batches: ".concat(JSON.stringify(c))),e={method:"POST",headers:{Accept:a.CONTENT_TYPE,"Content-Type":"text/plain;charset=UTF-8"},body:JSON.stringify(c)},!(d&&this.isBeaconAvailable()))?[3/*break*/,1]:(f=new Blob([e.body],{type:"text/plain;charset=UTF-8"}),navigator.sendBeacon(this.uploadUrl,f),[3/*break*/,4]);case 1:return i.trys.push([1,3,,4]),[4/*yield*/,this.uploader.upload(e)];case 2:// TODO: Should we make this a switch statement instead?
462
+ return (g=i.sent(),200<=g.status&&300>g.status)?(b.verbose("Upload success for request ID: ".concat(c.source_request_id)),[2/*return*/,null]):500<=g.status||429===g.status?(b.error("HTTP error status ".concat(g.status," received")),[2/*return*/,c]):401<=g.status?(b.error("HTTP error status ".concat(g.status," while uploading - please verify your API key.")),[2/*return*/,c]):[3/*break*/,4];case 3:return h=i.sent(),b.error("Error sending event to mParticle servers. ".concat(h)),[2/*return*/,c];case 4:return [2/*return*/,null];}})})},a.CONTENT_TYPE="text/plain;charset=UTF-8",a.MINIMUM_INTERVAL_MILLIS=500,a}();var AsyncUploader=/** @class */function(){function a(a){this.url=a;}return a}(),FetchUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,fetch(this.url,a)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b}(AsyncUploader),XHRUploader=/** @class */function(a){function b(){return null!==a&&a.apply(this,arguments)||this}return __extends(b,a),b.prototype.upload=function(a){return __awaiter(this,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:return [4/*yield*/,this.makeRequest(this.url,a.body)];case 1:return b=c.sent(),[2/*return*/,b];}})})},b.prototype.makeRequest=function(a,b){return __awaiter(this,void 0,void 0,function(){var c;return __generator(this,function(){return c=new XMLHttpRequest,[2/*return*/,new Promise(function(d,e){c.onreadystatechange=function(){4!==c.readyState||(200<=c.status&&300>c.status?d(c):e(c));},c.open("post",a),c.send(b);})]})})},b}(AsyncUploader);
446
463
 
447
464
  var Messages$9=Constants.Messages;function APIClient(a,b){this.uploader=null;var c=this;this.queueEventForBatchUpload=function(b){if(!this.uploader){var c=a._Helpers.getFeatureFlag(Constants.FeatureFlags.EventBatchingIntervalMillis);this.uploader=new BatchUploader(a,c);}this.uploader.queueEvent(b),a._Persistence.update();},this.shouldEnableBatching=function(){// Returns a string of a number that must be parsed
448
465
  // Invalid strings will be parsed to NaN which is falsey
package/dist/mparticle.js CHANGED
@@ -725,7 +725,7 @@ var mParticle = (function () {
725
725
  Environment: Environment
726
726
  };
727
727
 
728
- var version = "2.18.3";
728
+ var version = "2.19.0";
729
729
 
730
730
  var Constants = {
731
731
  sdkVersion: version,
@@ -2111,12 +2111,27 @@ var mParticle = (function () {
2111
2111
  }
2112
2112
  }
2113
2113
 
2114
+ /**
2115
+ * BatchUploader contains all the logic to upload batches to mParticle.
2116
+ * It queues events as they come in and at set intervals turns them into batches.
2117
+ * It then attempts to upload them to mParticle.
2118
+ *
2119
+ * These uploads happen on an interval basis using window.fetch or XHR
2120
+ * requests, depending on what is available in the browser.
2121
+ *
2122
+ * Uploads can also be triggered on browser visibility/focus changes via an
2123
+ * event listener, which then uploads to mPartice via the browser's Beacon API.
2124
+ */
2125
+
2114
2126
  var BatchUploader =
2115
2127
  /** @class */
2116
2128
  function () {
2129
+ /**
2130
+ * Creates an instance of a BatchUploader
2131
+ * @param {MParticleWebSDK} mpInstance - the mParticle SDK instance
2132
+ * @param {number} uploadInterval - the desired upload interval in milliseconds
2133
+ */
2117
2134
  function BatchUploader(mpInstance, uploadInterval) {
2118
- var _this_1 = this;
2119
-
2120
2135
  this.mpInstance = mpInstance;
2121
2136
  this.uploadIntervalMillis = uploadInterval;
2122
2137
  this.batchingEnabled = uploadInterval >= BatchUploader.MINIMUM_INTERVAL_MILLIS;
@@ -2125,8 +2140,8 @@ var mParticle = (function () {
2125
2140
  this.uploadIntervalMillis = BatchUploader.MINIMUM_INTERVAL_MILLIS;
2126
2141
  }
2127
2142
 
2128
- this.pendingEvents = [];
2129
- this.pendingUploads = [];
2143
+ this.eventsQueuedForProcessing = [];
2144
+ this.batchesQueuedForProcessing = [];
2130
2145
  var _a = this.mpInstance._Store,
2131
2146
  SDKConfig = _a.SDKConfig,
2132
2147
  devToken = _a.devToken;
@@ -2134,14 +2149,16 @@ var mParticle = (function () {
2134
2149
  var baseUrl = this.mpInstance._Helpers.createServiceUrl(SDKConfig.v3SecureServiceUrl, devToken);
2135
2150
 
2136
2151
  this.uploadUrl = "".concat(baseUrl, "/events");
2137
- setTimeout(function () {
2138
- _this_1.prepareAndUpload(true, false);
2139
- }, this.uploadIntervalMillis);
2152
+ this.uploader = window.fetch ? new FetchUploader(this.uploadUrl) : new XHRUploader(this.uploadUrl);
2153
+ this.triggerUploadInterval(true, false);
2140
2154
  this.addEventListeners();
2141
- }
2155
+ } // Adds listeners to be used trigger Navigator.sendBeacon if the browser
2156
+ // loses focus for any reason, such as closing browser tab or minimizing window
2157
+
2142
2158
 
2143
2159
  BatchUploader.prototype.addEventListeners = function () {
2144
- var _this = this;
2160
+ var _this = this; // visibility change is a document property, not window
2161
+
2145
2162
 
2146
2163
  document.addEventListener('visibilitychange', function () {
2147
2164
  _this.prepareAndUpload(false, _this.isBeaconAvailable());
@@ -2160,13 +2177,36 @@ var mParticle = (function () {
2160
2177
  }
2161
2178
 
2162
2179
  return false;
2180
+ }; // Triggers a setTimeout for prepareAndUpload
2181
+
2182
+
2183
+ BatchUploader.prototype.triggerUploadInterval = function (triggerFuture, useBeacon) {
2184
+ var _this_1 = this;
2185
+
2186
+ if (triggerFuture === void 0) {
2187
+ triggerFuture = false;
2188
+ }
2189
+
2190
+ if (useBeacon === void 0) {
2191
+ useBeacon = false;
2192
+ }
2193
+
2194
+ setTimeout(function () {
2195
+ _this_1.prepareAndUpload(triggerFuture, useBeacon);
2196
+ }, this.uploadIntervalMillis);
2163
2197
  };
2198
+ /**
2199
+ * This method will queue a single Event which will eventually be processed into a Batch
2200
+ * @param event event that should be queued
2201
+ */
2202
+
2164
2203
 
2165
2204
  BatchUploader.prototype.queueEvent = function (event) {
2166
2205
  if (!isEmpty(event)) {
2167
- this.pendingEvents.push(event);
2206
+ this.eventsQueuedForProcessing.push(event);
2168
2207
  this.mpInstance.Logger.verbose("Queuing event: ".concat(JSON.stringify(event)));
2169
- this.mpInstance.Logger.verbose("Queued event count: ".concat(this.pendingEvents.length));
2208
+ this.mpInstance.Logger.verbose("Queued event count: ".concat(this.eventsQueuedForProcessing.length)); // TODO: Remove this check once the v2 code path is removed
2209
+ // https://go.mparticle.com/work/SQDSDKS-3720
2170
2210
 
2171
2211
  if (!this.batchingEnabled || Types.TriggerUploadType[event.EventDataType]) {
2172
2212
  this.prepareAndUpload(false, false);
@@ -2184,7 +2224,7 @@ var mParticle = (function () {
2184
2224
  */
2185
2225
 
2186
2226
 
2187
- BatchUploader.createNewUploads = function (sdkEvents, defaultUser, mpInstance) {
2227
+ BatchUploader.createNewBatches = function (sdkEvents, defaultUser, mpInstance) {
2188
2228
  if (!defaultUser || !sdkEvents || !sdkEvents.length) {
2189
2229
  return null;
2190
2230
  } //bucket by MPID, and then by session, ordered by timestamp
@@ -2264,157 +2304,139 @@ var mParticle = (function () {
2264
2304
 
2265
2305
  BatchUploader.prototype.prepareAndUpload = function (triggerFuture, useBeacon) {
2266
2306
  return __awaiter(this, void 0, void 0, function () {
2267
- var currentUser, currentEvents, newUploads, currentUploads, remainingUploads;
2307
+ var currentUser, currentEvents, newBatches, batchesToUpload, batchesThatDidNotUpload, promises;
2268
2308
 
2269
- var _a, _b;
2309
+ var _a;
2270
2310
 
2271
2311
  var _this_1 = this;
2272
2312
 
2273
- return __generator(this, function (_c) {
2274
- switch (_c.label) {
2275
- case 0:
2276
- currentUser = this.mpInstance.Identity.getCurrentUser();
2277
- currentEvents = this.pendingEvents;
2278
- this.pendingEvents = [];
2279
- newUploads = BatchUploader.createNewUploads(currentEvents, currentUser, this.mpInstance);
2313
+ return __generator(this, function (_b) {
2314
+ currentUser = this.mpInstance.Identity.getCurrentUser();
2315
+ currentEvents = this.eventsQueuedForProcessing;
2316
+ this.eventsQueuedForProcessing = [];
2317
+ newBatches = BatchUploader.createNewBatches(currentEvents, currentUser, this.mpInstance);
2280
2318
 
2281
- if (newUploads && newUploads.length) {
2282
- (_a = this.pendingUploads).push.apply(_a, newUploads);
2283
- }
2319
+ if (!isEmpty(newBatches)) {
2320
+ (_a = this.batchesQueuedForProcessing).push.apply(_a, newBatches);
2321
+ }
2284
2322
 
2285
- currentUploads = this.pendingUploads;
2286
- this.pendingUploads = [];
2287
- return [4
2288
- /*yield*/
2289
- , this.upload(this.mpInstance.Logger, currentUploads, useBeacon)];
2323
+ batchesToUpload = this.batchesQueuedForProcessing;
2324
+ batchesThatDidNotUpload = [];
2325
+ this.batchesQueuedForProcessing = [];
2326
+ promises = batchesToUpload.map(function (upload) {
2327
+ return _this_1.upload(_this_1.mpInstance.Logger, upload, useBeacon);
2328
+ }); // Iterate through fulfilled promises and store any remaining batches
2329
+ // for future re-transmission attempts
2290
2330
 
2291
- case 1:
2292
- remainingUploads = _c.sent();
2331
+ if (!isEmpty(promises)) {
2332
+ Promise.all(promises).then(function (batchResponses) {
2333
+ batchResponses.forEach(function (batch) {
2334
+ return !isEmpty(batch) ? batchesThatDidNotUpload.push(batch) : null;
2335
+ });
2336
+ })["catch"](function (error) {
2337
+ _this_1.mpInstance.Logger.error("Error processing batches during upload attempt: ".concat(error));
2338
+ })["finally"](function () {
2339
+ var _a; // Any batches that did not upload should be put back into the queue for processing
2293
2340
 
2294
- if (remainingUploads && remainingUploads.length) {
2295
- (_b = this.pendingUploads).unshift.apply(_b, remainingUploads);
2296
- }
2297
2341
 
2298
- if (triggerFuture) {
2299
- setTimeout(function () {
2300
- _this_1.prepareAndUpload(true, false);
2301
- }, this.uploadIntervalMillis);
2342
+ if (!isEmpty(batchesThatDidNotUpload)) {
2343
+ (_a = _this_1.batchesQueuedForProcessing).unshift.apply(_a, batchesThatDidNotUpload);
2302
2344
  }
2345
+ });
2346
+ }
2303
2347
 
2304
- return [2
2305
- /*return*/
2306
- ];
2348
+ if (triggerFuture) {
2349
+ this.triggerUploadInterval(triggerFuture, false);
2307
2350
  }
2351
+
2352
+ return [2
2353
+ /*return*/
2354
+ ];
2308
2355
  });
2309
2356
  });
2310
2357
  };
2311
2358
 
2312
- BatchUploader.prototype.upload = function (logger, _uploads, useBeacon) {
2359
+ BatchUploader.prototype.upload = function (logger, batch, useBeacon) {
2313
2360
  return __awaiter(this, void 0, void 0, function () {
2314
- var uploader, uploads, i, fetchPayload, blob, response, e_1;
2361
+ var fetchPayload, blob, response, error_1;
2315
2362
  return __generator(this, function (_a) {
2316
2363
  switch (_a.label) {
2317
2364
  case 0:
2318
- uploads = _uploads.filter(function (upload) {
2319
- return !isEmpty(upload.events);
2320
- });
2321
-
2322
- if (isEmpty(uploads)) {
2365
+ if (isEmpty(batch) || isEmpty(batch.events)) {
2323
2366
  return [2
2324
2367
  /*return*/
2325
2368
  , null];
2326
2369
  }
2327
2370
 
2328
- logger.verbose("Uploading batches: ".concat(JSON.stringify(uploads)));
2329
- logger.verbose("Batch count: ".concat(uploads.length));
2330
- i = 0;
2331
- _a.label = 1;
2332
-
2333
- case 1:
2334
- if (!(i < uploads.length)) return [3
2335
- /*break*/
2336
- , 7];
2371
+ logger.verbose("Uploading batches: ".concat(JSON.stringify(batch)));
2337
2372
  fetchPayload = {
2338
2373
  method: 'POST',
2339
2374
  headers: {
2340
2375
  Accept: BatchUploader.CONTENT_TYPE,
2341
2376
  'Content-Type': 'text/plain;charset=UTF-8'
2342
2377
  },
2343
- body: JSON.stringify(uploads[i])
2378
+ body: JSON.stringify(batch)
2344
2379
  };
2345
2380
  if (!(useBeacon && this.isBeaconAvailable())) return [3
2346
2381
  /*break*/
2347
- , 2];
2382
+ , 1];
2348
2383
  blob = new Blob([fetchPayload.body], {
2349
2384
  type: 'text/plain;charset=UTF-8'
2350
2385
  });
2351
2386
  navigator.sendBeacon(this.uploadUrl, blob);
2352
2387
  return [3
2353
2388
  /*break*/
2354
- , 6];
2389
+ , 4];
2355
2390
 
2356
- case 2:
2357
- if (!uploader) {
2358
- if (window.fetch) {
2359
- uploader = new FetchUploader(this.uploadUrl, logger);
2360
- } else {
2361
- uploader = new XHRUploader(this.uploadUrl, logger);
2362
- }
2363
- }
2364
-
2365
- _a.label = 3;
2366
-
2367
- case 3:
2368
- _a.trys.push([3, 5,, 6]);
2391
+ case 1:
2392
+ _a.trys.push([1, 3,, 4]);
2369
2393
 
2370
2394
  return [4
2371
2395
  /*yield*/
2372
- , uploader.upload(fetchPayload, uploads, i)];
2396
+ , this.uploader.upload(fetchPayload)];
2373
2397
 
2374
- case 4:
2375
- response = _a.sent();
2398
+ case 2:
2399
+ response = _a.sent(); // TODO: Should we make this a switch statement instead?
2376
2400
 
2377
2401
  if (response.status >= 200 && response.status < 300) {
2378
- logger.verbose("Upload success for request ID: ".concat(uploads[i].source_request_id));
2402
+ logger.verbose("Upload success for request ID: ".concat(batch.source_request_id));
2403
+ return [2
2404
+ /*return*/
2405
+ , null];
2379
2406
  } else if (response.status >= 500 || response.status === 429) {
2380
- logger.error("HTTP error status ".concat(response.status, " received")); //server error, add back current events and try again later
2407
+ logger.error("HTTP error status ".concat(response.status, " received")); // Server error, return current batch and try again later
2381
2408
 
2382
2409
  return [2
2383
2410
  /*return*/
2384
- , uploads.slice(i, uploads.length)];
2411
+ , batch];
2385
2412
  } else if (response.status >= 401) {
2386
- logger.error("HTTP error status ".concat(response.status, " while uploading - please verify your API key.")); //if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
2413
+ logger.error("HTTP error status ".concat(response.status, " while uploading - please verify your API key.")); // if we're getting a 401, assume we'll keep getting a 401
2414
+ // so return the upload so it can be stored for later use
2387
2415
 
2388
2416
  return [2
2389
2417
  /*return*/
2390
- , null];
2418
+ , batch];
2391
2419
  }
2392
2420
 
2393
2421
  return [3
2394
2422
  /*break*/
2395
- , 6];
2423
+ , 4];
2396
2424
 
2397
- case 5:
2398
- e_1 = _a.sent();
2399
- logger.error("Error sending event to mParticle servers. ".concat(e_1));
2425
+ case 3:
2426
+ error_1 = _a.sent();
2427
+ logger.error("Error sending event to mParticle servers. ".concat(error_1));
2400
2428
  return [2
2401
2429
  /*return*/
2402
- , uploads.slice(i, uploads.length)];
2403
-
2404
- case 6:
2405
- i++;
2406
- return [3
2407
- /*break*/
2408
- , 1];
2430
+ , batch];
2409
2431
 
2410
- case 7:
2432
+ case 4:
2411
2433
  return [2
2412
2434
  /*return*/
2413
2435
  , null];
2414
2436
  }
2415
2437
  });
2416
2438
  });
2417
- }; //we upload JSON, but this content type is required to avoid a CORS preflight request
2439
+ }; // We upload JSON, but this content type is required to avoid a CORS preflight request
2418
2440
 
2419
2441
 
2420
2442
  BatchUploader.CONTENT_TYPE = 'text/plain;charset=UTF-8';
@@ -2425,9 +2447,8 @@ var mParticle = (function () {
2425
2447
  var AsyncUploader =
2426
2448
  /** @class */
2427
2449
  function () {
2428
- function AsyncUploader(url, logger) {
2450
+ function AsyncUploader(url) {
2429
2451
  this.url = url;
2430
- this.logger = logger;
2431
2452
  }
2432
2453
 
2433
2454
  return AsyncUploader;
@@ -2442,7 +2463,7 @@ var mParticle = (function () {
2442
2463
  return _super !== null && _super.apply(this, arguments) || this;
2443
2464
  }
2444
2465
 
2445
- FetchUploader.prototype.upload = function (fetchPayload, uploads, i) {
2466
+ FetchUploader.prototype.upload = function (fetchPayload) {
2446
2467
  return __awaiter(this, void 0, void 0, function () {
2447
2468
  var response;
2448
2469
  return __generator(this, function (_a) {
@@ -2474,7 +2495,7 @@ var mParticle = (function () {
2474
2495
  return _super !== null && _super.apply(this, arguments) || this;
2475
2496
  }
2476
2497
 
2477
- XHRUploader.prototype.upload = function (fetchPayload, uploads, i) {
2498
+ XHRUploader.prototype.upload = function (fetchPayload) {
2478
2499
  return __awaiter(this, void 0, void 0, function () {
2479
2500
  var response;
2480
2501
  return __generator(this, function (_a) {
@@ -2482,7 +2503,7 @@ var mParticle = (function () {
2482
2503
  case 0:
2483
2504
  return [4
2484
2505
  /*yield*/
2485
- , this.makeRequest(this.url, this.logger, fetchPayload.body)];
2506
+ , this.makeRequest(this.url, fetchPayload.body)];
2486
2507
 
2487
2508
  case 1:
2488
2509
  response = _a.sent();
@@ -2494,7 +2515,7 @@ var mParticle = (function () {
2494
2515
  });
2495
2516
  };
2496
2517
 
2497
- XHRUploader.prototype.makeRequest = function (url, logger, data) {
2518
+ XHRUploader.prototype.makeRequest = function (url, data) {
2498
2519
  return __awaiter(this, void 0, void 0, function () {
2499
2520
  var xhr;
2500
2521
  return __generator(this, function (_a) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.18.3",
3
+ "version": "2.19.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -9,17 +9,35 @@ import { convertEvents } from './sdkToEventsApiConverter';
9
9
  import Types from './types';
10
10
  import { isEmpty } from './utils';
11
11
 
12
+ /**
13
+ * BatchUploader contains all the logic to upload batches to mParticle.
14
+ * It queues events as they come in and at set intervals turns them into batches.
15
+ * It then attempts to upload them to mParticle.
16
+ *
17
+ * These uploads happen on an interval basis using window.fetch or XHR
18
+ * requests, depending on what is available in the browser.
19
+ *
20
+ * Uploads can also be triggered on browser visibility/focus changes via an
21
+ * event listener, which then uploads to mPartice via the browser's Beacon API.
22
+ */
23
+
12
24
  export class BatchUploader {
13
- //we upload JSON, but this content type is required to avoid a CORS preflight request
25
+ // We upload JSON, but this content type is required to avoid a CORS preflight request
14
26
  static readonly CONTENT_TYPE: string = 'text/plain;charset=UTF-8';
15
27
  static readonly MINIMUM_INTERVAL_MILLIS: number = 500;
16
28
  uploadIntervalMillis: number;
17
- pendingEvents: SDKEvent[];
18
- pendingUploads: Batch[];
29
+ eventsQueuedForProcessing: SDKEvent[];
30
+ batchesQueuedForProcessing: Batch[];
19
31
  mpInstance: MParticleWebSDK;
20
32
  uploadUrl: string;
21
33
  batchingEnabled: boolean;
34
+ private uploader: AsyncUploader;
22
35
 
36
+ /**
37
+ * Creates an instance of a BatchUploader
38
+ * @param {MParticleWebSDK} mpInstance - the mParticle SDK instance
39
+ * @param {number} uploadInterval - the desired upload interval in milliseconds
40
+ */
23
41
  constructor(mpInstance: MParticleWebSDK, uploadInterval: number) {
24
42
  this.mpInstance = mpInstance;
25
43
  this.uploadIntervalMillis = uploadInterval;
@@ -28,8 +46,8 @@ export class BatchUploader {
28
46
  if (this.uploadIntervalMillis < BatchUploader.MINIMUM_INTERVAL_MILLIS) {
29
47
  this.uploadIntervalMillis = BatchUploader.MINIMUM_INTERVAL_MILLIS;
30
48
  }
31
- this.pendingEvents = [];
32
- this.pendingUploads = [];
49
+ this.eventsQueuedForProcessing = [];
50
+ this.batchesQueuedForProcessing = [];
33
51
 
34
52
  const { SDKConfig, devToken } = this.mpInstance._Store;
35
53
  const baseUrl = this.mpInstance._Helpers.createServiceUrl(
@@ -38,15 +56,20 @@ export class BatchUploader {
38
56
  );
39
57
  this.uploadUrl = `${baseUrl}/events`;
40
58
 
41
- setTimeout(() => {
42
- this.prepareAndUpload(true, false);
43
- }, this.uploadIntervalMillis);
59
+ this.uploader = window.fetch
60
+ ? new FetchUploader(this.uploadUrl)
61
+ : new XHRUploader(this.uploadUrl);
62
+
63
+ this.triggerUploadInterval(true, false);
44
64
  this.addEventListeners();
45
65
  }
46
66
 
67
+ // Adds listeners to be used trigger Navigator.sendBeacon if the browser
68
+ // loses focus for any reason, such as closing browser tab or minimizing window
47
69
  private addEventListeners() {
48
70
  const _this = this;
49
71
 
72
+ // visibility change is a document property, not window
50
73
  document.addEventListener('visibilitychange', () => {
51
74
  _this.prepareAndUpload(false, _this.isBeaconAvailable());
52
75
  });
@@ -65,16 +88,32 @@ export class BatchUploader {
65
88
  return false;
66
89
  }
67
90
 
91
+ // Triggers a setTimeout for prepareAndUpload
92
+ private triggerUploadInterval(
93
+ triggerFuture: boolean = false,
94
+ useBeacon: boolean = false
95
+ ): void {
96
+ setTimeout(() => {
97
+ this.prepareAndUpload(triggerFuture, useBeacon);
98
+ }, this.uploadIntervalMillis);
99
+ }
100
+
101
+ /**
102
+ * This method will queue a single Event which will eventually be processed into a Batch
103
+ * @param event event that should be queued
104
+ */
68
105
  queueEvent(event: SDKEvent): void {
69
106
  if (!isEmpty(event)) {
70
- this.pendingEvents.push(event);
107
+ this.eventsQueuedForProcessing.push(event);
71
108
  this.mpInstance.Logger.verbose(
72
109
  `Queuing event: ${JSON.stringify(event)}`
73
110
  );
74
111
  this.mpInstance.Logger.verbose(
75
- `Queued event count: ${this.pendingEvents.length}`
112
+ `Queued event count: ${this.eventsQueuedForProcessing.length}`
76
113
  );
77
114
 
115
+ // TODO: Remove this check once the v2 code path is removed
116
+ // https://go.mparticle.com/work/SQDSDKS-3720
78
117
  if (
79
118
  !this.batchingEnabled ||
80
119
  Types.TriggerUploadType[event.EventDataType]
@@ -93,7 +132,7 @@ export class BatchUploader {
93
132
  * @param sdkEvents current pending events
94
133
  * @param defaultUser the user to reference for events that are missing data
95
134
  */
96
- private static createNewUploads(
135
+ private static createNewBatches(
97
136
  sdkEvents: SDKEvent[],
98
137
  defaultUser: MParticleUser,
99
138
  mpInstance: MParticleWebSDK
@@ -141,9 +180,8 @@ export class BatchUploader {
141
180
  mpInstance._Store.SDKConfig.onCreateBatch;
142
181
 
143
182
  if (onCreateBatchCallback) {
144
- uploadBatchObject = onCreateBatchCallback(
145
- uploadBatchObject
146
- );
183
+ uploadBatchObject =
184
+ onCreateBatchCallback(uploadBatchObject);
147
185
  if (uploadBatchObject) {
148
186
  uploadBatchObject.modified = true;
149
187
  } else {
@@ -169,112 +207,122 @@ export class BatchUploader {
169
207
  * @param triggerFuture whether to trigger the loop again - for manual/forced uploads this should be false
170
208
  * @param useBeacon whether to use the beacon API - used when the page is being unloaded
171
209
  */
172
- private async prepareAndUpload(triggerFuture: boolean, useBeacon: boolean) {
210
+ private async prepareAndUpload(
211
+ triggerFuture: boolean,
212
+ useBeacon: boolean
213
+ ): Promise<void> {
173
214
  const currentUser = this.mpInstance.Identity.getCurrentUser();
174
215
 
175
- const currentEvents = this.pendingEvents;
176
- this.pendingEvents = [];
177
- const newUploads = BatchUploader.createNewUploads(
216
+ const currentEvents = this.eventsQueuedForProcessing;
217
+ this.eventsQueuedForProcessing = [];
218
+
219
+ const newBatches = BatchUploader.createNewBatches(
178
220
  currentEvents,
179
221
  currentUser,
180
222
  this.mpInstance
181
223
  );
182
- if (newUploads && newUploads.length) {
183
- this.pendingUploads.push(...newUploads);
224
+ if (!isEmpty(newBatches)) {
225
+ this.batchesQueuedForProcessing.push(...newBatches);
184
226
  }
185
227
 
186
- const currentUploads = this.pendingUploads;
187
- this.pendingUploads = [];
188
- const remainingUploads: Batch[] = await this.upload(
189
- this.mpInstance.Logger,
190
- currentUploads,
191
- useBeacon
192
- );
193
- if (remainingUploads && remainingUploads.length) {
194
- this.pendingUploads.unshift(...remainingUploads);
228
+ const batchesToUpload = this.batchesQueuedForProcessing;
229
+ const batchesThatDidNotUpload: Batch[] = [];
230
+ this.batchesQueuedForProcessing = [];
231
+
232
+ // Create an array of promises as we try to upload each batch indvidually
233
+ const promises: Promise<Batch>[] = batchesToUpload.map((upload) => {
234
+ return this.upload(this.mpInstance.Logger, upload, useBeacon);
235
+ });
236
+
237
+ // Iterate through fulfilled promises and store any remaining batches
238
+ // for future re-transmission attempts
239
+ if (!isEmpty(promises)) {
240
+ Promise.all(promises)
241
+ .then((batchResponses) => {
242
+ batchResponses.forEach((batch) =>
243
+ !isEmpty(batch)
244
+ ? batchesThatDidNotUpload.push(batch)
245
+ : null
246
+ );
247
+ })
248
+ .catch((error) => {
249
+ this.mpInstance.Logger.error(
250
+ `Error processing batches during upload attempt: ${error}`
251
+ );
252
+ })
253
+ .finally(() => {
254
+ // Any batches that did not upload should be put back into the queue for processing
255
+ if (!isEmpty(batchesThatDidNotUpload)) {
256
+ this.batchesQueuedForProcessing.unshift(
257
+ ...batchesThatDidNotUpload
258
+ );
259
+ }
260
+ });
195
261
  }
196
262
 
197
263
  if (triggerFuture) {
198
- setTimeout(() => {
199
- this.prepareAndUpload(true, false);
200
- }, this.uploadIntervalMillis);
264
+ this.triggerUploadInterval(triggerFuture, false);
201
265
  }
202
266
  }
203
267
 
204
268
  private async upload(
205
269
  logger: SDKLoggerApi,
206
- _uploads: Batch[],
270
+ batch: Batch,
207
271
  useBeacon: boolean
208
- ): Promise<Batch[]> {
209
- let uploader;
210
-
211
- // Filter out any batches that don't have events
212
- const uploads = _uploads.filter(upload => !isEmpty(upload.events));
213
-
214
- if (isEmpty(uploads)) {
272
+ ): Promise<Batch | null> {
273
+ if (isEmpty(batch) || isEmpty(batch.events)) {
215
274
  return null;
216
275
  }
217
276
 
218
- logger.verbose(`Uploading batches: ${JSON.stringify(uploads)}`);
219
- logger.verbose(`Batch count: ${uploads.length}`);
220
-
221
- for (let i = 0; i < uploads.length; i++) {
222
- const fetchPayload: fetchPayload = {
223
- method: 'POST',
224
- headers: {
225
- Accept: BatchUploader.CONTENT_TYPE,
226
- 'Content-Type': 'text/plain;charset=UTF-8',
227
- },
228
- body: JSON.stringify(uploads[i]),
229
- };
277
+ logger.verbose(`Uploading batches: ${JSON.stringify(batch)}`);
278
+
279
+ const fetchPayload: fetchPayload = {
280
+ method: 'POST',
281
+ headers: {
282
+ Accept: BatchUploader.CONTENT_TYPE,
283
+ 'Content-Type': 'text/plain;charset=UTF-8',
284
+ },
285
+ body: JSON.stringify(batch),
286
+ };
287
+
288
+ // TODO: Make beacon its own function
289
+ // beacon is only used on onbeforeunload onpagehide events
290
+ if (useBeacon && this.isBeaconAvailable()) {
291
+ let blob = new Blob([fetchPayload.body], {
292
+ type: 'text/plain;charset=UTF-8',
293
+ });
294
+ navigator.sendBeacon(this.uploadUrl, blob);
295
+ } else {
296
+ try {
297
+ const response = await this.uploader.upload(fetchPayload);
298
+
299
+ // TODO: Should we make this a switch statement instead?
300
+ if (response.status >= 200 && response.status < 300) {
301
+ logger.verbose(
302
+ `Upload success for request ID: ${batch.source_request_id}`
303
+ );
230
304
 
231
- // beacon is only used on onbeforeunload onpagehide events
232
- if (useBeacon && this.isBeaconAvailable()) {
233
- let blob = new Blob([fetchPayload.body], {
234
- type: 'text/plain;charset=UTF-8',
235
- });
236
- navigator.sendBeacon(this.uploadUrl, blob);
237
- } else {
238
- if (!uploader) {
239
- if (window.fetch) {
240
- uploader = new FetchUploader(this.uploadUrl, logger);
241
- } else {
242
- uploader = new XHRUploader(this.uploadUrl, logger);
243
- }
244
- }
245
- try {
246
- const response = await uploader.upload(
247
- fetchPayload,
248
- uploads,
249
- i
305
+ return null;
306
+ } else if (response.status >= 500 || response.status === 429) {
307
+ logger.error(
308
+ `HTTP error status ${response.status} received`
250
309
  );
251
310
 
252
- if (response.status >= 200 && response.status < 300) {
253
- logger.verbose(
254
- `Upload success for request ID: ${uploads[i].source_request_id}`
255
- );
256
- } else if (
257
- response.status >= 500 ||
258
- response.status === 429
259
- ) {
260
- logger.error(
261
- `HTTP error status ${response.status} received`
262
- );
263
- //server error, add back current events and try again later
264
- return uploads.slice(i, uploads.length);
265
- } else if (response.status >= 401) {
266
- logger.error(
267
- `HTTP error status ${response.status} while uploading - please verify your API key.`
268
- );
269
- //if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
270
- return null;
271
- }
272
- } catch (e) {
311
+ // Server error, return current batch and try again later
312
+ return batch;
313
+ } else if (response.status >= 401) {
273
314
  logger.error(
274
- `Error sending event to mParticle servers. ${e}`
315
+ `HTTP error status ${response.status} while uploading - please verify your API key.`
275
316
  );
276
- return uploads.slice(i, uploads.length);
317
+ // if we're getting a 401, assume we'll keep getting a 401
318
+ // so return the upload so it can be stored for later use
319
+ return batch;
277
320
  }
321
+ } catch (error) {
322
+ logger.error(
323
+ `Error sending event to mParticle servers. ${error}`
324
+ );
325
+ return batch;
278
326
  }
279
327
  }
280
328
  return null;
@@ -283,34 +331,24 @@ export class BatchUploader {
283
331
 
284
332
  abstract class AsyncUploader {
285
333
  url: string;
286
- logger: SDKLoggerApi;
334
+ public abstract upload(fetchPayload: fetchPayload): Promise<XHRResponse>;
287
335
 
288
- constructor(url: string, logger: SDKLoggerApi) {
336
+ constructor(url: string) {
289
337
  this.url = url;
290
- this.logger = logger;
291
338
  }
292
339
  }
293
340
 
294
341
  class FetchUploader extends AsyncUploader {
295
- private async upload(
296
- fetchPayload: fetchPayload,
297
- uploads: Batch[],
298
- i: number
299
- ) {
342
+ public async upload(fetchPayload: fetchPayload): Promise<XHRResponse> {
300
343
  const response: XHRResponse = await fetch(this.url, fetchPayload);
301
344
  return response;
302
345
  }
303
346
  }
304
347
 
305
348
  class XHRUploader extends AsyncUploader {
306
- private async upload(
307
- fetchPayload: fetchPayload,
308
- uploads: Batch[],
309
- i: number
310
- ) {
349
+ public async upload(fetchPayload: fetchPayload): Promise<XHRResponse> {
311
350
  const response: XHRResponse = await this.makeRequest(
312
351
  this.url,
313
- this.logger,
314
352
  fetchPayload.body
315
353
  );
316
354
  return response;
@@ -318,7 +356,6 @@ class XHRUploader extends AsyncUploader {
318
356
 
319
357
  private async makeRequest(
320
358
  url: string,
321
- logger: SDKLoggerApi,
322
359
  data: string
323
360
  ): Promise<XMLHttpRequest> {
324
361
  const xhr: XMLHttpRequest = new XMLHttpRequest();
package/src/vault.ts ADDED
@@ -0,0 +1,141 @@
1
+ import { Logger } from '@mparticle/web-sdk';
2
+ import { Dictionary, isEmpty } from './utils';
3
+
4
+ interface IVaultOptions {
5
+ logger?: Logger;
6
+ offlineStorageEnabled?: boolean;
7
+ }
8
+
9
+ export default class Vault<StorableItem extends Dictionary> {
10
+ public contents: Dictionary<StorableItem>;
11
+ private readonly _storageKey: string;
12
+ private readonly _itemKey: keyof StorableItem;
13
+ private logger?: Logger;
14
+ private offlineStorageEnabled: boolean = false;
15
+
16
+ /**
17
+ *
18
+ * @param {string} storageKey the local storage key string
19
+ * @param {string} itemKey an element within your StorableItem to use as a key
20
+ * @param {IVaultOptions} options A Dictionary of IVaultOptions
21
+ */
22
+ constructor(
23
+ storageKey: string,
24
+ itemKey: keyof StorableItem,
25
+ options?: IVaultOptions
26
+ ) {
27
+ this._storageKey = storageKey;
28
+ this._itemKey = itemKey;
29
+ this.contents = this.getItems() || {};
30
+
31
+ this.offlineStorageEnabled = options?.offlineStorageEnabled;
32
+
33
+ // Add a fake logger in case one is not provided or needed
34
+ this.logger = options?.logger || {
35
+ verbose: () => {},
36
+ warning: () => {},
37
+ error: () => {},
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Stores a single Item using `itemId` as an index
43
+ * @method storeItem
44
+ * @param item {StorableItem} a Dictonary with key to store
45
+ */
46
+ public storeItem(item: StorableItem): void {
47
+ this.contents = this.getItems();
48
+
49
+ if (item[this._itemKey]) {
50
+ this.contents[item[this._itemKey]] = item;
51
+ this.logger.verbose(
52
+ `Saved items to vault with key: ${item[this._itemKey]}`
53
+ );
54
+ }
55
+
56
+ this.saveItems(this.contents);
57
+ }
58
+
59
+ /**
60
+ * Stores Items using `itemId` as an index
61
+ * @method storeItems
62
+ * @param {StorableItem[]} an Array of StorableItems
63
+ */
64
+ public storeItems(items: StorableItem[]): void {
65
+ items.forEach((item) => this.storeItem(item));
66
+ }
67
+
68
+ /**
69
+ * Removes a single StorableItem based on `indexId`
70
+ * @method removeItem
71
+ * @param {String} indexId
72
+ */
73
+ public removeItem(indexId: string): void {
74
+ this.logger.verbose(`Removing from vault: ${indexId}`);
75
+ this.contents = this.getItems() || {};
76
+
77
+ try {
78
+ delete this.contents[indexId];
79
+
80
+ this.saveItems(this.contents);
81
+ } catch (error) {
82
+ this.logger.error(
83
+ `Unable to remove item without a matching ID ${indexId}`
84
+ );
85
+ this.logger.error(error as string);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Retrieves all StorableItems from local storage as an array
91
+ * @method retrieveItems
92
+ * @returns {StorableItem[]} an array of Items
93
+ */
94
+ public retrieveItems(): StorableItem[] {
95
+ this.contents = this.getItems();
96
+
97
+ return Object.keys(this.contents).map((item) => this.contents[item]);
98
+ }
99
+
100
+ /**
101
+ * Removes all persisted data from local storage based on this vault's `key`
102
+ * @method purge
103
+ */
104
+ public purge(): void {
105
+ this.contents = {};
106
+ this.removeItems();
107
+ }
108
+
109
+ private saveItems(items: Dictionary<StorableItem>): void {
110
+ if (!this.offlineStorageEnabled) {
111
+ return;
112
+ }
113
+ try {
114
+ window.localStorage.setItem(
115
+ this._storageKey,
116
+ !isEmpty(items) ? JSON.stringify(items) : ''
117
+ );
118
+ } catch (error) {
119
+ this.logger.error(`Cannot Save items to Local Storage: ${items}`);
120
+ this.logger.error(error as string);
121
+ }
122
+ }
123
+
124
+ private getItems(): Dictionary<StorableItem> {
125
+ if (!this.offlineStorageEnabled) {
126
+ return this.contents;
127
+ }
128
+ // TODO: Handle cases where Local Storage is unavailable
129
+ // https://go.mparticle.com/work/SQDSDKS-5022
130
+ const itemString = window.localStorage.getItem(this._storageKey);
131
+
132
+ return itemString ? JSON.parse(itemString) : {};
133
+ }
134
+
135
+ private removeItems(): void {
136
+ if (!this.offlineStorageEnabled) {
137
+ return;
138
+ }
139
+ window.localStorage.removeItem(this._storageKey);
140
+ }
141
+ }