@mparticle/web-sdk 2.77.0 → 2.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ map:function map(a,b){var c,d,e;if(null===this)throw new TypeError(" this is nul
15
15
  filter:function filter(a/*, thisArg*/){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f<c;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g);}return d},// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
16
16
  isArray:function isArray(a){return "[object Array]"===Object.prototype.toString.call(a)},Base64:Base64$1};
17
17
 
18
- var version = "2.77.0";
18
+ var version = "2.78.0";
19
19
 
20
20
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{DeprecationMessages:{MethodHasBeenDeprecated:"has been deprecated.",MethodMarkedForDeprecationPostfix:"is a deprecated method and will be removed in future releases.",AlternativeMethodPrefix:"Please use the alternate method:"},ErrorMessages:{NoToken:"A token must be specified.",EventNameInvalidType:"Event name must be a valid string value.",EventDataInvalidType:"Event data must be a valid object hash.",LoggingDisabled:"Event logging is currently disabled.",CookieParseError:"Could not parse cookie",EventEmpty:"Event object is null or undefined, cancelling send",APIRequestEmpty:"APIRequest is null or undefined, cancelling send",NoEventType:"Event type must be specified.",TransactionIdRequired:"Transaction ID is required",TransactionRequired:"A transaction attributes object is required",PromotionIdRequired:"Promotion ID is required",BadAttribute:"Attribute value cannot be object or array",BadKey:"Key value cannot be object or array",BadLogPurchase:"Transaction attributes and a product are both required to log a purchase, https://docs.mparticle.com/?javascript#measuring-transactions",AudienceAPINotEnabled:"Your workspace is not enabled to retrieve user audiences."},InformationMessages:{CookieSearch:"Searching for cookie",CookieFound:"Cookie found, parsing values",CookieNotFound:"Cookies not found",CookieSet:"Setting cookie",CookieSync:"Performing cookie sync",SendBegin:"Starting to send event",SendIdentityBegin:"Starting to send event to identity server",SendWindowsPhone:"Sending event to Windows Phone container",SendIOS:"Calling iOS path: ",SendAndroid:"Calling Android JS interface method: ",SendHttp:"Sending event to mParticle HTTP service",SendAliasHttp:"Sending alias request to mParticle HTTP service",SendIdentityHttp:"Sending event to mParticle HTTP service",StartingNewSession:"Starting new Session",StartingLogEvent:"Starting to log event",StartingLogOptOut:"Starting to log user opt in/out",StartingEndSession:"Starting to end session",StartingInitialization:"Starting to initialize",StartingLogCommerceEvent:"Starting to log commerce event",StartingAliasRequest:"Starting to Alias MPIDs",LoadingConfig:"Loading configuration options",AbandonLogEvent:"Cannot log event, logging disabled or developer token not set",AbandonAliasUsers:"Cannot Alias Users, logging disabled or developer token not set",AbandonStartSession:"Cannot start session, logging disabled or developer token not set",AbandonEndSession:"Cannot end session, logging disabled or developer token not set",NoSessionToEnd:"Cannot end session, no active session found"},ValidationMessages:{ModifyIdentityRequestUserIdentitiesPresent:"identityRequests to modify require userIdentities to be present. Request not sent to server. Please fix and try again",IdentityRequesetInvalidKey:"There is an invalid key on your identityRequest object. It can only contain a `userIdentities` object and a `onUserAlias` function. Request not sent to server. Please fix and try again.",OnUserAliasType:"The onUserAlias value must be a function.",UserIdentities:"The userIdentities key must be an object with keys of identityTypes and values of strings. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidKey:"There is an invalid identity key on your `userIdentities` object within the identityRequest. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidValues:"All user identity values must be strings or null. Request not sent to server. Please fix and try again.",AliasMissingMpid:"Alias Request must contain both a destinationMpid and a sourceMpid",AliasNonUniqueMpid:"Alias Request's destinationMpid and sourceMpid must be unique",AliasMissingTime:"Alias Request must have both a startTime and an endTime",AliasStartBeforeEndTime:"Alias Request's endTime must be later than its startTime"}},NativeSdkPaths:{LogEvent:"logEvent",SetUserTag:"setUserTag",RemoveUserTag:"removeUserTag",SetUserAttribute:"setUserAttribute",RemoveUserAttribute:"removeUserAttribute",SetSessionAttribute:"setSessionAttribute",AddToCart:"addToCart",RemoveFromCart:"removeFromCart",ClearCart:"clearCart",LogOut:"logOut",SetUserAttributeList:"setUserAttributeList",RemoveAllUserAttributes:"removeAllUserAttributes",GetUserAttributesLists:"getUserAttributesLists",GetAllUserAttributes:"getAllUserAttributes",Identify:"identify",Logout:"logout",Login:"login",Modify:"modify",Alias:"aliasUsers",Upload:"upload"},StorageNames:{localStorageName:"mprtcl-api",localStorageNameV3:"mprtcl-v3",cookieName:"mprtcl-api",cookieNameV2:"mprtcl-v2",cookieNameV3:"mprtcl-v3",localStorageNameV4:"mprtcl-v4",localStorageProductsV4:"mprtcl-prodv4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4",currentStorageProductsName:"mprtcl-prodv4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,maxProducts:20,forwarderStatsTimeout:5e3,integrationDelayTimeout:5e3,maxCookieSize:3e3,aliasMaxWindow:90,uploadInterval:0// Maximum milliseconds in between batch uploads, below 500 will mean immediate upload. The server returns this as a string, but we are using it as a number internally
21
21
  },DefaultBaseUrls:{v1SecureServiceUrl:"jssdks.mparticle.com/v1/JS/",v2SecureServiceUrl:"jssdks.mparticle.com/v2/JS/",v3SecureServiceUrl:"jssdks.mparticle.com/v3/JS/",configUrl:"jssdkcdns.mparticle.com/JS/v2/",identityUrl:"identity.mparticle.com/v1/",aliasUrl:"jssdks.mparticle.com/v1/identity/",userAudienceUrl:"nativesdks.mparticle.com/v1/"},// These are the paths that are used to construct the CNAME urls
@@ -147,7 +147,7 @@ var Messages$a=Constants.Messages,createCookieString=function(a){return replaceC
147
147
  // -1e11 -> //-100000000000,
148
148
  "".concat(1e7,"-").concat(1e3,"-").concat(4e3,"-").concat(8e3,"-").concat(1e11).replace(/[018]/g,// zeroes, ones, and eights with
149
149
  generateUniqueId// random hex digits
150
- )},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/&quot;/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&amp;/g,"&")},createCookieSyncUrl=function(a,b,c,d){var e=replaceAmpWithAmpersand(b),f=c?replaceAmpWithAmpersand(c):null,g=replaceMPID(e,a),h=f?replaceMPID(f,a):"",i=g+encodeURIComponent(h);if(d){var j=i.includes("?")?"&":"?";i+="".concat(j,"domain=").concat(d);}return i},returnConvertedBoolean=function(a){return "false"!==a&&"0"!==a&&!!a},decoded=function(a){return decodeURIComponent(a.replace(/\+/g," "))},converted=function(a){return 0===a.indexOf("\"")&&(a=a.slice(1,-1).replace(/\\"/g,"\"").replace(/\\\\/g,"\\")),a},isString=function(a){return "string"==typeof a},isNumber=function(a){return "number"==typeof a},isBoolean=function(a){return "boolean"==typeof a},isFunction=function(a){return "function"==typeof a},isValidAttributeValue=function(a){return a!==void 0&&!isObject(a)&&!Array.isArray(a)},isValidCustomFlagProperty=function(a){return isNumber(a)||isString(a)||isBoolean(a)},toDataPlanSlug=function(a){// Make sure we are only acting on strings or numbers
150
+ )},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/&quot;/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&amp;/g,"&")},createCookieSyncUrl=function(a,b,c,d,e){var f=replaceAmpWithAmpersand(b),g=c?replaceAmpWithAmpersand(c):null,h=replaceMPID(f,a),i=g?replaceMPID(g,a):"",j=h+encodeURIComponent(i);if(d){var k=j.includes("?")?"&":"?";j+="".concat(k,"domain=").concat(d);}if(e){var k=j.includes("?")?"&":"?";j+="".concat(k,"google_hm=").concat(e);}return j},WEB_SAFE_BASE64_REPLACEMENTS={"+":"-","/":"_","=":""},toWebSafeBase64=function(a){return btoa(a).replace(/[+/=]/g,function(a){return WEB_SAFE_BASE64_REPLACEMENTS[a]})},returnConvertedBoolean=function(a){return "false"!==a&&"0"!==a&&!!a},decoded=function(a){return decodeURIComponent(a.replace(/\+/g," "))},converted=function(a){return 0===a.indexOf("\"")&&(a=a.slice(1,-1).replace(/\\"/g,"\"").replace(/\\\\/g,"\\")),a},isString=function(a){return "string"==typeof a},isNumber=function(a){return "number"==typeof a},isBoolean=function(a){return "boolean"==typeof a},isFunction=function(a){return "function"==typeof a},isValidAttributeValue=function(a){return a!==void 0&&!isObject(a)&&!Array.isArray(a)},isValidCustomFlagProperty=function(a){return isNumber(a)||isString(a)||isBoolean(a)},toDataPlanSlug=function(a){// Make sure we are only acting on strings or numbers
151
151
  return isStringOrNumber(a)?a.toString().toLowerCase().replace(/[^0-9a-zA-Z]+/g,"_"):""},isDataPlanSlug=function(a){return a===toDataPlanSlug(a)},isStringOrNumber=function(a){return isString(a)||isNumber(a)},isEmpty=function(a){return null==a||!(Object.keys(a)||a).length},moveElementToEnd=function(a,b){return a.slice(0,b).concat(a.slice(b+1),a[b])},queryStringParser=function(a,b){void 0===b&&(b=[]);var c,d={},e={};if(!a)return d;if("undefined"!=typeof URL&&"undefined"!=typeof URLSearchParams){var f=new URL(a);c=new URLSearchParams(f.search);}else c=queryStringParserFallback(a);return (c.forEach(function(a,b){e[b.toLowerCase()]=a;}),isEmpty(b))?e:(b.forEach(function(a){var b=e[a.toLowerCase()];b&&(d[a]=b);}),d)},queryStringParserFallback=function(a){var b={},c=a.split("?")[1]||"",d=c.split("&");return d.forEach(function(a){var c=a.split("="),d=c[0],e=c.slice(1),f=e.join("=");if(d&&void 0!==f)try{b[d]=decodeURIComponent(f||"");}catch(a){console.error("Failed to decode value for key ".concat(d,": ").concat(a));}}),{get:function get(a){return b[a]},forEach:function forEach(a){for(var c in b)b.hasOwnProperty(c)&&a(b[c],c);}}},getCookies=function(a){// Helper function to parse cookies from document.cookie
152
152
  var b=function parseCookies(){try{return "undefined"==typeof window?[]:window.document.cookie.split(";").map(function(a){return a.trim()})}catch(a){return console.error("Unable to parse cookies",a),[]}}();// Helper function to filter cookies by keys
153
153
  // Parse cookies from document.cookie
@@ -158,7 +158,11 @@ if(Array.isArray(a))return a.map(function(a){return obfuscateData(a)});// Handle
158
158
  if(isObject(a)){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=obfuscateData(a[c]));return b}// For primitives and other types, return the type as a string
159
159
  return _typeof$1(a)},obfuscateDevData=function(a,b){return b?a:obfuscateData(a)};/**
160
160
  * Returns a value between 1-100 inclusive.
161
- */ // FIXME: REFACTOR for V3
161
+ */ // Google's cookie matching service requires `google_hm` values to be web-safe
162
+ // base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
163
+ // match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
164
+ // (String#replaceAll is not available in our ES5 browser targets.)
165
+ // FIXME: REFACTOR for V3
162
166
  // only used in store.js to sanitize server-side formatting of
163
167
  // booleans when checking for `isDevelopmentMode`
164
168
  // Should be removed in v3
@@ -632,19 +636,23 @@ var PARTNER_MODULE_IDS={AdobeEventForwarder:11,DoubleclickDFP:41,AppNexus:50,Lot
632
636
  // Private
633
637
  this.attemptCookieSync=function(c,d){var e,f=a._Store,g=f.pixelConfigurations,h=f.webviewBridgeEnabled;if(c&&!h&&(null===(e=a._CookieConsentManager)||void 0===e||!e.getNoFunctional()))// When noFunctional is true, persistence is not saved, so we cannot track cookie sync
634
638
  // dates. Skip cookie sync to avoid running it on every page load.
635
- {var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h=!1,j=e.filteringConsentRuleValues,k=e.pixelUrl,l=e.redirectUrl,m=e.moduleId,// Tells you how often we should do a cookie sync (in days)
636
- n=e.frequencyCap,o=(j||{}).values;// set requiresConsent to false to start each additional pixel configuration
639
+ {var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h,j=!1,k=e.filteringConsentRuleValues,l=e.pixelUrl,m=e.redirectUrl,n=e.moduleId,o=e.settings,// Tells you how often we should do a cookie sync (in days)
640
+ p=e.frequencyCap,q=(k||{}).values;// set requiresConsent to false to start each additional pixel configuration
637
641
  // set to true only if filteringConsenRuleValues.values.length exists
638
642
  // Filtering rules as defined in UI
639
- if(!isEmpty(k)&&(isEmpty(o)||(h=!0),!(h&&d))&&!(m===PARTNER_MODULE_IDS.Rokt&&a._CookieConsentManager.getNoTargeting()))// For Rokt, block cookie sync when noTargeting privacy flag is true
643
+ if(!isEmpty(l)&&(isEmpty(q)||(j=!0),!(j&&d))&&!(n===PARTNER_MODULE_IDS.Rokt&&a._CookieConsentManager.getNoTargeting()))// For Rokt, block cookie sync when noTargeting privacy flag is true
640
644
  // If MPID is new to cookies, we should not try to perform the cookie sync
641
645
  // because a cookie sync can only occur once a user either consents or doesn't.
642
646
  // we should not check if it's enabled if the user has a blank consent
643
647
  // The Trade Desk requires a URL parameter for GDPR enabled users.
644
648
  // It is optional but to simplify the code, we add it for all Trade
645
649
  // // Desk cookie syncs.
646
- // Add domain parameter for Trade Desk
647
- {var p=a._Consent.isEnabledForUserConsent;if(p(j,a.Identity.getCurrentUser())){var q=null!==(g=null===(f=i[c])||void 0===f?void 0:f.csd)&&void 0!==g?g:{},r=q[m]||null;if(isLastSyncDateExpired(n,r)){var s=m===PARTNER_MODULE_IDS.TradeDesk?window.location.hostname:void 0,t=createCookieSyncUrl(c,k,l,s);b.performCookieSync(t,m.toString(),c,q);}}}});}},this.performCookieSync=function(b,c,d,e){var f=document.createElement("img");a.Logger.verbose(InformationMessages.CookieSync),f.onload=function(){e[c]=new Date().getTime(),a._Persistence.saveUserCookieSyncDatesToPersistence(d,e);},f.src=b;};}var isLastSyncDateExpired=function(a,b){// If there is no lastSyncDate, then there is no previous cookie sync, so we should sync the cookie
650
+ // Google Marketing Platform accepts a web-safe base64-encoded MPID via the
651
+ // `google_hm` query parameter, but only for accounts provisioned for Google
652
+ // Hosted Matching — Google returns errors otherwise. It is therefore gated
653
+ // behind the opt-in `enableHmTag` setting (off by default), which the server
654
+ // delivers as the string 'True' when the UI toggle is enabled.
655
+ {var r=a._Consent.isEnabledForUserConsent;if(r(k,a.Identity.getCurrentUser())){var s=null!==(g=null===(f=i[c])||void 0===f?void 0:f.csd)&&void 0!==g?g:{},t=s[n]||null;if(isLastSyncDateExpired(p,t)){var u=n===PARTNER_MODULE_IDS.TradeDesk?window.location.hostname:void 0,v="true"===(null===(h=null===o||void 0===o?void 0:o.enableHmTag)||void 0===h?void 0:h.toLowerCase()),w=n===PARTNER_MODULE_IDS.DoubleclickDFP,x=w&&v?toWebSafeBase64(c):void 0,y=createCookieSyncUrl(c,l,m,u,x);b.performCookieSync(y,n.toString(),c,s);}}}});}},this.performCookieSync=function(b,c,d,e){var f=document.createElement("img");a.Logger.verbose(InformationMessages.CookieSync),f.onload=function(){e[c]=new Date().getTime(),a._Persistence.saveUserCookieSyncDatesToPersistence(d,e);},f.src=b;};}var isLastSyncDateExpired=function(a,b){// If there is no lastSyncDate, then there is no previous cookie sync, so we should sync the cookie
648
656
  return !b||new Date().getTime()>new Date(b).getTime()+a*DAYS_IN_MILLISECONDS;// Otherwise, compare the last sync date to determine if it should do a cookie sync again
649
657
  };
650
658
 
@@ -15,7 +15,7 @@ map:function map(a,b){var c,d,e;if(null===this)throw new TypeError(" this is nul
15
15
  filter:function filter(a/*, thisArg*/){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f<c;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g);}return d},// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
16
16
  isArray:function isArray(a){return "[object Array]"===Object.prototype.toString.call(a)},Base64:Base64$1};
17
17
 
18
- var version = "2.77.0";
18
+ var version = "2.78.0";
19
19
 
20
20
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{DeprecationMessages:{MethodHasBeenDeprecated:"has been deprecated.",MethodMarkedForDeprecationPostfix:"is a deprecated method and will be removed in future releases.",AlternativeMethodPrefix:"Please use the alternate method:"},ErrorMessages:{NoToken:"A token must be specified.",EventNameInvalidType:"Event name must be a valid string value.",EventDataInvalidType:"Event data must be a valid object hash.",LoggingDisabled:"Event logging is currently disabled.",CookieParseError:"Could not parse cookie",EventEmpty:"Event object is null or undefined, cancelling send",APIRequestEmpty:"APIRequest is null or undefined, cancelling send",NoEventType:"Event type must be specified.",TransactionIdRequired:"Transaction ID is required",TransactionRequired:"A transaction attributes object is required",PromotionIdRequired:"Promotion ID is required",BadAttribute:"Attribute value cannot be object or array",BadKey:"Key value cannot be object or array",BadLogPurchase:"Transaction attributes and a product are both required to log a purchase, https://docs.mparticle.com/?javascript#measuring-transactions",AudienceAPINotEnabled:"Your workspace is not enabled to retrieve user audiences."},InformationMessages:{CookieSearch:"Searching for cookie",CookieFound:"Cookie found, parsing values",CookieNotFound:"Cookies not found",CookieSet:"Setting cookie",CookieSync:"Performing cookie sync",SendBegin:"Starting to send event",SendIdentityBegin:"Starting to send event to identity server",SendWindowsPhone:"Sending event to Windows Phone container",SendIOS:"Calling iOS path: ",SendAndroid:"Calling Android JS interface method: ",SendHttp:"Sending event to mParticle HTTP service",SendAliasHttp:"Sending alias request to mParticle HTTP service",SendIdentityHttp:"Sending event to mParticle HTTP service",StartingNewSession:"Starting new Session",StartingLogEvent:"Starting to log event",StartingLogOptOut:"Starting to log user opt in/out",StartingEndSession:"Starting to end session",StartingInitialization:"Starting to initialize",StartingLogCommerceEvent:"Starting to log commerce event",StartingAliasRequest:"Starting to Alias MPIDs",LoadingConfig:"Loading configuration options",AbandonLogEvent:"Cannot log event, logging disabled or developer token not set",AbandonAliasUsers:"Cannot Alias Users, logging disabled or developer token not set",AbandonStartSession:"Cannot start session, logging disabled or developer token not set",AbandonEndSession:"Cannot end session, logging disabled or developer token not set",NoSessionToEnd:"Cannot end session, no active session found"},ValidationMessages:{ModifyIdentityRequestUserIdentitiesPresent:"identityRequests to modify require userIdentities to be present. Request not sent to server. Please fix and try again",IdentityRequesetInvalidKey:"There is an invalid key on your identityRequest object. It can only contain a `userIdentities` object and a `onUserAlias` function. Request not sent to server. Please fix and try again.",OnUserAliasType:"The onUserAlias value must be a function.",UserIdentities:"The userIdentities key must be an object with keys of identityTypes and values of strings. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidKey:"There is an invalid identity key on your `userIdentities` object within the identityRequest. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidValues:"All user identity values must be strings or null. Request not sent to server. Please fix and try again.",AliasMissingMpid:"Alias Request must contain both a destinationMpid and a sourceMpid",AliasNonUniqueMpid:"Alias Request's destinationMpid and sourceMpid must be unique",AliasMissingTime:"Alias Request must have both a startTime and an endTime",AliasStartBeforeEndTime:"Alias Request's endTime must be later than its startTime"}},NativeSdkPaths:{LogEvent:"logEvent",SetUserTag:"setUserTag",RemoveUserTag:"removeUserTag",SetUserAttribute:"setUserAttribute",RemoveUserAttribute:"removeUserAttribute",SetSessionAttribute:"setSessionAttribute",AddToCart:"addToCart",RemoveFromCart:"removeFromCart",ClearCart:"clearCart",LogOut:"logOut",SetUserAttributeList:"setUserAttributeList",RemoveAllUserAttributes:"removeAllUserAttributes",GetUserAttributesLists:"getUserAttributesLists",GetAllUserAttributes:"getAllUserAttributes",Identify:"identify",Logout:"logout",Login:"login",Modify:"modify",Alias:"aliasUsers",Upload:"upload"},StorageNames:{localStorageName:"mprtcl-api",localStorageNameV3:"mprtcl-v3",cookieName:"mprtcl-api",cookieNameV2:"mprtcl-v2",cookieNameV3:"mprtcl-v3",localStorageNameV4:"mprtcl-v4",localStorageProductsV4:"mprtcl-prodv4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4",currentStorageProductsName:"mprtcl-prodv4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,maxProducts:20,forwarderStatsTimeout:5e3,integrationDelayTimeout:5e3,maxCookieSize:3e3,aliasMaxWindow:90,uploadInterval:0// Maximum milliseconds in between batch uploads, below 500 will mean immediate upload. The server returns this as a string, but we are using it as a number internally
21
21
  },DefaultBaseUrls:{v1SecureServiceUrl:"jssdks.mparticle.com/v1/JS/",v2SecureServiceUrl:"jssdks.mparticle.com/v2/JS/",v3SecureServiceUrl:"jssdks.mparticle.com/v3/JS/",configUrl:"jssdkcdns.mparticle.com/JS/v2/",identityUrl:"identity.mparticle.com/v1/",aliasUrl:"jssdks.mparticle.com/v1/identity/",userAudienceUrl:"nativesdks.mparticle.com/v1/"},// These are the paths that are used to construct the CNAME urls
@@ -147,7 +147,7 @@ var Messages$a=Constants.Messages,createCookieString=function(a){return replaceC
147
147
  // -1e11 -> //-100000000000,
148
148
  "".concat(1e7,"-").concat(1e3,"-").concat(4e3,"-").concat(8e3,"-").concat(1e11).replace(/[018]/g,// zeroes, ones, and eights with
149
149
  generateUniqueId// random hex digits
150
- )},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/&quot;/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&amp;/g,"&")},createCookieSyncUrl=function(a,b,c,d){var e=replaceAmpWithAmpersand(b),f=c?replaceAmpWithAmpersand(c):null,g=replaceMPID(e,a),h=f?replaceMPID(f,a):"",i=g+encodeURIComponent(h);if(d){var j=i.includes("?")?"&":"?";i+="".concat(j,"domain=").concat(d);}return i},returnConvertedBoolean=function(a){return "false"!==a&&"0"!==a&&!!a},decoded=function(a){return decodeURIComponent(a.replace(/\+/g," "))},converted=function(a){return 0===a.indexOf("\"")&&(a=a.slice(1,-1).replace(/\\"/g,"\"").replace(/\\\\/g,"\\")),a},isString=function(a){return "string"==typeof a},isNumber=function(a){return "number"==typeof a},isBoolean=function(a){return "boolean"==typeof a},isFunction=function(a){return "function"==typeof a},isValidAttributeValue=function(a){return a!==void 0&&!isObject(a)&&!Array.isArray(a)},isValidCustomFlagProperty=function(a){return isNumber(a)||isString(a)||isBoolean(a)},toDataPlanSlug=function(a){// Make sure we are only acting on strings or numbers
150
+ )},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/&quot;/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&amp;/g,"&")},createCookieSyncUrl=function(a,b,c,d,e){var f=replaceAmpWithAmpersand(b),g=c?replaceAmpWithAmpersand(c):null,h=replaceMPID(f,a),i=g?replaceMPID(g,a):"",j=h+encodeURIComponent(i);if(d){var k=j.includes("?")?"&":"?";j+="".concat(k,"domain=").concat(d);}if(e){var k=j.includes("?")?"&":"?";j+="".concat(k,"google_hm=").concat(e);}return j},WEB_SAFE_BASE64_REPLACEMENTS={"+":"-","/":"_","=":""},toWebSafeBase64=function(a){return btoa(a).replace(/[+/=]/g,function(a){return WEB_SAFE_BASE64_REPLACEMENTS[a]})},returnConvertedBoolean=function(a){return "false"!==a&&"0"!==a&&!!a},decoded=function(a){return decodeURIComponent(a.replace(/\+/g," "))},converted=function(a){return 0===a.indexOf("\"")&&(a=a.slice(1,-1).replace(/\\"/g,"\"").replace(/\\\\/g,"\\")),a},isString=function(a){return "string"==typeof a},isNumber=function(a){return "number"==typeof a},isBoolean=function(a){return "boolean"==typeof a},isFunction=function(a){return "function"==typeof a},isValidAttributeValue=function(a){return a!==void 0&&!isObject(a)&&!Array.isArray(a)},isValidCustomFlagProperty=function(a){return isNumber(a)||isString(a)||isBoolean(a)},toDataPlanSlug=function(a){// Make sure we are only acting on strings or numbers
151
151
  return isStringOrNumber(a)?a.toString().toLowerCase().replace(/[^0-9a-zA-Z]+/g,"_"):""},isDataPlanSlug=function(a){return a===toDataPlanSlug(a)},isStringOrNumber=function(a){return isString(a)||isNumber(a)},isEmpty=function(a){return null==a||!(Object.keys(a)||a).length},moveElementToEnd=function(a,b){return a.slice(0,b).concat(a.slice(b+1),a[b])},queryStringParser=function(a,b){void 0===b&&(b=[]);var c,d={},e={};if(!a)return d;if("undefined"!=typeof URL&&"undefined"!=typeof URLSearchParams){var f=new URL(a);c=new URLSearchParams(f.search);}else c=queryStringParserFallback(a);return (c.forEach(function(a,b){e[b.toLowerCase()]=a;}),isEmpty(b))?e:(b.forEach(function(a){var b=e[a.toLowerCase()];b&&(d[a]=b);}),d)},queryStringParserFallback=function(a){var b={},c=a.split("?")[1]||"",d=c.split("&");return d.forEach(function(a){var c=a.split("="),d=c[0],e=c.slice(1),f=e.join("=");if(d&&void 0!==f)try{b[d]=decodeURIComponent(f||"");}catch(a){console.error("Failed to decode value for key ".concat(d,": ").concat(a));}}),{get:function get(a){return b[a]},forEach:function forEach(a){for(var c in b)b.hasOwnProperty(c)&&a(b[c],c);}}},getCookies=function(a){// Helper function to parse cookies from document.cookie
152
152
  var b=function parseCookies(){try{return "undefined"==typeof window?[]:window.document.cookie.split(";").map(function(a){return a.trim()})}catch(a){return console.error("Unable to parse cookies",a),[]}}();// Helper function to filter cookies by keys
153
153
  // Parse cookies from document.cookie
@@ -158,7 +158,11 @@ if(Array.isArray(a))return a.map(function(a){return obfuscateData(a)});// Handle
158
158
  if(isObject(a)){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=obfuscateData(a[c]));return b}// For primitives and other types, return the type as a string
159
159
  return _typeof$1(a)},obfuscateDevData=function(a,b){return b?a:obfuscateData(a)};/**
160
160
  * Returns a value between 1-100 inclusive.
161
- */ // FIXME: REFACTOR for V3
161
+ */ // Google's cookie matching service requires `google_hm` values to be web-safe
162
+ // base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
163
+ // match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
164
+ // (String#replaceAll is not available in our ES5 browser targets.)
165
+ // FIXME: REFACTOR for V3
162
166
  // only used in store.js to sanitize server-side formatting of
163
167
  // booleans when checking for `isDevelopmentMode`
164
168
  // Should be removed in v3
@@ -632,19 +636,23 @@ var PARTNER_MODULE_IDS={AdobeEventForwarder:11,DoubleclickDFP:41,AppNexus:50,Lot
632
636
  // Private
633
637
  this.attemptCookieSync=function(c,d){var e,f=a._Store,g=f.pixelConfigurations,h=f.webviewBridgeEnabled;if(c&&!h&&(null===(e=a._CookieConsentManager)||void 0===e||!e.getNoFunctional()))// When noFunctional is true, persistence is not saved, so we cannot track cookie sync
634
638
  // dates. Skip cookie sync to avoid running it on every page load.
635
- {var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h=!1,j=e.filteringConsentRuleValues,k=e.pixelUrl,l=e.redirectUrl,m=e.moduleId,// Tells you how often we should do a cookie sync (in days)
636
- n=e.frequencyCap,o=(j||{}).values;// set requiresConsent to false to start each additional pixel configuration
639
+ {var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h,j=!1,k=e.filteringConsentRuleValues,l=e.pixelUrl,m=e.redirectUrl,n=e.moduleId,o=e.settings,// Tells you how often we should do a cookie sync (in days)
640
+ p=e.frequencyCap,q=(k||{}).values;// set requiresConsent to false to start each additional pixel configuration
637
641
  // set to true only if filteringConsenRuleValues.values.length exists
638
642
  // Filtering rules as defined in UI
639
- if(!isEmpty(k)&&(isEmpty(o)||(h=!0),!(h&&d))&&!(m===PARTNER_MODULE_IDS.Rokt&&a._CookieConsentManager.getNoTargeting()))// For Rokt, block cookie sync when noTargeting privacy flag is true
643
+ if(!isEmpty(l)&&(isEmpty(q)||(j=!0),!(j&&d))&&!(n===PARTNER_MODULE_IDS.Rokt&&a._CookieConsentManager.getNoTargeting()))// For Rokt, block cookie sync when noTargeting privacy flag is true
640
644
  // If MPID is new to cookies, we should not try to perform the cookie sync
641
645
  // because a cookie sync can only occur once a user either consents or doesn't.
642
646
  // we should not check if it's enabled if the user has a blank consent
643
647
  // The Trade Desk requires a URL parameter for GDPR enabled users.
644
648
  // It is optional but to simplify the code, we add it for all Trade
645
649
  // // Desk cookie syncs.
646
- // Add domain parameter for Trade Desk
647
- {var p=a._Consent.isEnabledForUserConsent;if(p(j,a.Identity.getCurrentUser())){var q=null!==(g=null===(f=i[c])||void 0===f?void 0:f.csd)&&void 0!==g?g:{},r=q[m]||null;if(isLastSyncDateExpired(n,r)){var s=m===PARTNER_MODULE_IDS.TradeDesk?window.location.hostname:void 0,t=createCookieSyncUrl(c,k,l,s);b.performCookieSync(t,m.toString(),c,q);}}}});}},this.performCookieSync=function(b,c,d,e){var f=document.createElement("img");a.Logger.verbose(InformationMessages.CookieSync),f.onload=function(){e[c]=new Date().getTime(),a._Persistence.saveUserCookieSyncDatesToPersistence(d,e);},f.src=b;};}var isLastSyncDateExpired=function(a,b){// If there is no lastSyncDate, then there is no previous cookie sync, so we should sync the cookie
650
+ // Google Marketing Platform accepts a web-safe base64-encoded MPID via the
651
+ // `google_hm` query parameter, but only for accounts provisioned for Google
652
+ // Hosted Matching — Google returns errors otherwise. It is therefore gated
653
+ // behind the opt-in `enableHmTag` setting (off by default), which the server
654
+ // delivers as the string 'True' when the UI toggle is enabled.
655
+ {var r=a._Consent.isEnabledForUserConsent;if(r(k,a.Identity.getCurrentUser())){var s=null!==(g=null===(f=i[c])||void 0===f?void 0:f.csd)&&void 0!==g?g:{},t=s[n]||null;if(isLastSyncDateExpired(p,t)){var u=n===PARTNER_MODULE_IDS.TradeDesk?window.location.hostname:void 0,v="true"===(null===(h=null===o||void 0===o?void 0:o.enableHmTag)||void 0===h?void 0:h.toLowerCase()),w=n===PARTNER_MODULE_IDS.DoubleclickDFP,x=w&&v?toWebSafeBase64(c):void 0,y=createCookieSyncUrl(c,l,m,u,x);b.performCookieSync(y,n.toString(),c,s);}}}});}},this.performCookieSync=function(b,c,d,e){var f=document.createElement("img");a.Logger.verbose(InformationMessages.CookieSync),f.onload=function(){e[c]=new Date().getTime(),a._Persistence.saveUserCookieSyncDatesToPersistence(d,e);},f.src=b;};}var isLastSyncDateExpired=function(a,b){// If there is no lastSyncDate, then there is no previous cookie sync, so we should sync the cookie
648
656
  return !b||new Date().getTime()>new Date(b).getTime()+a*DAYS_IN_MILLISECONDS;// Otherwise, compare the last sync date to determine if it should do a cookie sync again
649
657
  };
650
658
 
package/dist/mparticle.js CHANGED
@@ -203,7 +203,7 @@ var mParticle = (function () {
203
203
  Base64: Base64$1
204
204
  };
205
205
 
206
- var version = "2.77.0";
206
+ var version = "2.78.0";
207
207
 
208
208
  var Constants = {
209
209
  sdkVersion: version,
@@ -684,7 +684,7 @@ var mParticle = (function () {
684
684
  var replaceAmpWithAmpersand = function replaceAmpWithAmpersand(value) {
685
685
  return value.replace(/&amp;/g, '&');
686
686
  };
687
- var createCookieSyncUrl = function createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain) {
687
+ var createCookieSyncUrl = function createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid) {
688
688
  var modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
689
689
  var modifiedDirectUrl = redirectUrl ? replaceAmpWithAmpersand(redirectUrl) : null;
690
690
  var url = replaceMPID(modifiedPixelUrl, mpid);
@@ -694,8 +694,26 @@ var mParticle = (function () {
694
694
  var separator = fullUrl.includes('?') ? '&' : '?';
695
695
  fullUrl += "".concat(separator, "domain=").concat(domain);
696
696
  }
697
+ if (base64Mpid) {
698
+ var separator = fullUrl.includes('?') ? '&' : '?';
699
+ fullUrl += "".concat(separator, "google_hm=").concat(base64Mpid);
700
+ }
697
701
  return fullUrl;
698
702
  };
703
+ // Google's cookie matching service requires `google_hm` values to be web-safe
704
+ // base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
705
+ // match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
706
+ // (String#replaceAll is not available in our ES5 browser targets.)
707
+ var WEB_SAFE_BASE64_REPLACEMENTS = {
708
+ '+': '-',
709
+ '/': '_',
710
+ '=': ''
711
+ };
712
+ var toWebSafeBase64 = function toWebSafeBase64(value) {
713
+ return btoa(value).replace(/[+/=]/g, function (c) {
714
+ return WEB_SAFE_BASE64_REPLACEMENTS[c];
715
+ });
716
+ };
699
717
  // FIXME: REFACTOR for V3
700
718
  // only used in store.js to sanitize server-side formatting of
701
719
  // booleans when checking for `isDevelopmentMode`
@@ -4190,7 +4208,7 @@ var mParticle = (function () {
4190
4208
  return;
4191
4209
  }
4192
4210
  pixelConfigurations.forEach(function (pixelSettings) {
4193
- var _a, _b;
4211
+ var _a, _b, _c;
4194
4212
  // set requiresConsent to false to start each additional pixel configuration
4195
4213
  // set to true only if filteringConsenRuleValues.values.length exists
4196
4214
  var requiresConsent = false;
@@ -4199,6 +4217,7 @@ var mParticle = (function () {
4199
4217
  pixelUrl = pixelSettings.pixelUrl,
4200
4218
  redirectUrl = pixelSettings.redirectUrl,
4201
4219
  moduleId = pixelSettings.moduleId,
4220
+ settings = pixelSettings.settings,
4202
4221
  // Tells you how often we should do a cookie sync (in days)
4203
4222
  frequencyCap = pixelSettings.frequencyCap;
4204
4223
  var values = (filteringConsentRuleValues || {}).values;
@@ -4231,8 +4250,15 @@ var mParticle = (function () {
4231
4250
  // It is optional but to simplify the code, we add it for all Trade
4232
4251
  // // Desk cookie syncs.
4233
4252
  var domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
4234
- // Add domain parameter for Trade Desk
4235
- var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain);
4253
+ // Google Marketing Platform accepts a web-safe base64-encoded MPID via the
4254
+ // `google_hm` query parameter, but only for accounts provisioned for Google
4255
+ // Hosted Matching — Google returns errors otherwise. It is therefore gated
4256
+ // behind the opt-in `enableHmTag` setting (off by default), which the server
4257
+ // delivers as the string 'True' when the UI toggle is enabled.
4258
+ var enableHmTag = ((_c = settings === null || settings === void 0 ? void 0 : settings.enableHmTag) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'true';
4259
+ var isDoubleClickModule = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP;
4260
+ var base64Mpid = isDoubleClickModule && enableHmTag ? toWebSafeBase64(mpid) : undefined;
4261
+ var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid);
4236
4262
  self.performCookieSync(fullUrl, moduleId.toString(), mpid, cookieSyncDates);
4237
4263
  });
4238
4264
  };
@@ -27,7 +27,8 @@ declare const replaceApostrophesWithQuotes: (value: string) => string;
27
27
  declare const replaceQuotesWithApostrophes: (value: string) => string;
28
28
  declare const replaceMPID: (value: string, mpid: MPID) => string;
29
29
  declare const replaceAmpWithAmpersand: (value: string) => string;
30
- declare const createCookieSyncUrl: (mpid: MPID, pixelUrl: string, redirectUrl?: string, domain?: string) => string;
30
+ declare const createCookieSyncUrl: (mpid: MPID, pixelUrl: string, redirectUrl?: string, domain?: string, base64Mpid?: string) => string;
31
+ declare const toWebSafeBase64: (value: string) => string;
31
32
  declare const returnConvertedBoolean: (data: string | boolean | number) => boolean;
32
33
  declare const decoded: (s: string) => string;
33
34
  declare const converted: (s: string) => string;
@@ -81,4 +82,4 @@ declare const obfuscateData: (value: any) => any;
81
82
  declare const obfuscateDevData: (data: any, isDevelopmentMode?: boolean) => any;
82
83
  declare function extend(...args: any[]): any;
83
84
  declare const getErrorMessage: (e: unknown, fallback?: string) => string;
84
- export { createCookieString, revertCookieString, createCookieSyncUrl, valueof, AttributeValue, converted, decoded, obfuscateDevData, filterDictionaryWithHash, findKeyInObject, generateDeprecationMessage, generateHash, generateUniqueId, getErrorMessage, getRampNumber, inArray, isObject, isStringOrNumber, obfuscateData, parseConfig, parseNumber, parseSettingsString, parseStringOrNumber, replaceCommasWithPipes, replacePipesWithCommas, replaceApostrophesWithQuotes, replaceQuotesWithApostrophes, returnConvertedBoolean, toDataPlanSlug, isString, isNumber, isFunction, isDataPlanSlug, isEmpty, isValidAttributeValue, isValidCustomFlagProperty, mergeObjects, moveElementToEnd, queryStringParser, getCookies, getHref, replaceMPID, replaceAmpWithAmpersand, extend, };
85
+ export { createCookieString, revertCookieString, createCookieSyncUrl, toWebSafeBase64, valueof, AttributeValue, converted, decoded, obfuscateDevData, filterDictionaryWithHash, findKeyInObject, generateDeprecationMessage, generateHash, generateUniqueId, getErrorMessage, getRampNumber, inArray, isObject, isStringOrNumber, obfuscateData, parseConfig, parseNumber, parseSettingsString, parseStringOrNumber, replaceCommasWithPipes, replacePipesWithCommas, replaceApostrophesWithQuotes, replaceQuotesWithApostrophes, returnConvertedBoolean, toDataPlanSlug, isString, isNumber, isFunction, isDataPlanSlug, isEmpty, isValidAttributeValue, isValidCustomFlagProperty, mergeObjects, moveElementToEnd, queryStringParser, getCookies, getHref, replaceMPID, replaceAmpWithAmpersand, extend, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.77.0",
3
+ "version": "2.78.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -2,6 +2,7 @@ import {
2
2
  Dictionary,
3
3
  isEmpty,
4
4
  createCookieSyncUrl,
5
+ toWebSafeBase64,
5
6
  } from './utils';
6
7
  import Constants from './constants';
7
8
  import { MPID } from '@mparticle/web-sdk';
@@ -95,6 +96,7 @@ export default function CookieSyncManager(
95
96
  pixelUrl,
96
97
  redirectUrl,
97
98
  moduleId,
99
+ settings,
98
100
  // Tells you how often we should do a cookie sync (in days)
99
101
  frequencyCap,
100
102
  } = pixelSettings;
@@ -137,8 +139,17 @@ export default function CookieSyncManager(
137
139
  // It is optional but to simplify the code, we add it for all Trade
138
140
  // // Desk cookie syncs.
139
141
  const domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
140
- // Add domain parameter for Trade Desk
141
- const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain);
142
+
143
+ // Google Marketing Platform accepts a web-safe base64-encoded MPID via the
144
+ // `google_hm` query parameter, but only for accounts provisioned for Google
145
+ // Hosted Matching — Google returns errors otherwise. It is therefore gated
146
+ // behind the opt-in `enableHmTag` setting (off by default), which the server
147
+ // delivers as the string 'True' when the UI toggle is enabled.
148
+ const enableHmTag = settings?.enableHmTag?.toLowerCase() === 'true';
149
+ const isDoubleClickModule = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP;
150
+ const base64Mpid = isDoubleClickModule && enableHmTag ? toWebSafeBase64(mpid) : undefined;
151
+
152
+ const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid);
142
153
 
143
154
  self.performCookieSync(
144
155
  fullUrl,
package/src/utils.ts CHANGED
@@ -207,7 +207,8 @@ const createCookieSyncUrl = (
207
207
  mpid: MPID,
208
208
  pixelUrl: string,
209
209
  redirectUrl?: string,
210
- domain?: string
210
+ domain?: string,
211
+ base64Mpid?: string
211
212
  ): string => {
212
213
  const modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
213
214
  const modifiedDirectUrl = redirectUrl
@@ -215,21 +216,39 @@ const createCookieSyncUrl = (
215
216
  : null;
216
217
 
217
218
  let url = replaceMPID(modifiedPixelUrl, mpid);
218
-
219
+
219
220
  const redirect = modifiedDirectUrl
220
221
  ? replaceMPID(modifiedDirectUrl, mpid)
221
222
  : '';
222
-
223
+
223
224
  let fullUrl = url + encodeURIComponent(redirect);
224
-
225
+
225
226
  if (domain) {
226
227
  const separator = fullUrl.includes('?') ? '&' : '?';
227
228
  fullUrl += `${separator}domain=${domain}`;
228
229
  }
229
-
230
+
231
+ if (base64Mpid) {
232
+ const separator = fullUrl.includes('?') ? '&' : '?';
233
+ fullUrl += `${separator}google_hm=${base64Mpid}`;
234
+ }
235
+
230
236
  return fullUrl;
231
237
  };
232
238
 
239
+ // Google's cookie matching service requires `google_hm` values to be web-safe
240
+ // base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
241
+ // match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
242
+ // (String#replaceAll is not available in our ES5 browser targets.)
243
+ const WEB_SAFE_BASE64_REPLACEMENTS: Dictionary<string> = {
244
+ '+': '-',
245
+ '/': '_',
246
+ '=': '',
247
+ };
248
+
249
+ const toWebSafeBase64 = (value: string): string =>
250
+ btoa(value).replace(/[+/=]/g, c => WEB_SAFE_BASE64_REPLACEMENTS[c]);
251
+
233
252
  // FIXME: REFACTOR for V3
234
253
  // only used in store.js to sanitize server-side formatting of
235
254
  // booleans when checking for `isDevelopmentMode`
@@ -650,6 +669,7 @@ export {
650
669
  createCookieString,
651
670
  revertCookieString,
652
671
  createCookieSyncUrl,
672
+ toWebSafeBase64,
653
673
  valueof,
654
674
  AttributeValue,
655
675
  converted,