@mparticle/web-sdk 2.51.0 → 2.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mparticle.js CHANGED
@@ -203,7 +203,7 @@ var mParticle = (function () {
203
203
  Base64: Base64$1
204
204
  };
205
205
 
206
- var version = "2.51.0";
206
+ var version = "2.52.0";
207
207
 
208
208
  var Constants = {
209
209
  sdkVersion: version,
@@ -667,12 +667,17 @@ var mParticle = (function () {
667
667
  var replaceAmpWithAmpersand = function replaceAmpWithAmpersand(value) {
668
668
  return value.replace(/&/g, '&');
669
669
  };
670
- var createCookieSyncUrl = function createCookieSyncUrl(mpid, pixelUrl, redirectUrl) {
670
+ var createCookieSyncUrl = function createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain) {
671
671
  var modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
672
672
  var modifiedDirectUrl = redirectUrl ? replaceAmpWithAmpersand(redirectUrl) : null;
673
673
  var url = replaceMPID(modifiedPixelUrl, mpid);
674
674
  var redirect = modifiedDirectUrl ? replaceMPID(modifiedDirectUrl, mpid) : '';
675
- return url + encodeURIComponent(redirect);
675
+ var fullUrl = url + encodeURIComponent(redirect);
676
+ if (domain) {
677
+ var separator = fullUrl.includes('?') ? '&' : '?';
678
+ fullUrl += "".concat(separator, "domain=").concat(domain);
679
+ }
680
+ return fullUrl;
676
681
  };
677
682
  // FIXME: REFACTOR for V3
678
683
  // only used in store.js to sanitize server-side formatting of
@@ -3474,6 +3479,15 @@ var mParticle = (function () {
3474
3479
  var Messages$7 = Constants.Messages;
3475
3480
  var InformationMessages = Messages$7.InformationMessages;
3476
3481
  var DAYS_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
3482
+ // Partner module IDs for cookie sync configurations
3483
+ var PARTNER_MODULE_IDS = {
3484
+ AdobeEventForwarder: 11,
3485
+ DoubleclickDFP: 41,
3486
+ AppNexus: 50,
3487
+ Lotame: 58,
3488
+ TradeDesk: 103,
3489
+ VerizonMedia: 155
3490
+ };
3477
3491
  function CookieSyncManager(mpInstance) {
3478
3492
  var self = this;
3479
3493
  // Public
@@ -3522,8 +3536,12 @@ var mParticle = (function () {
3522
3536
  if (!isLastSyncDateExpired(frequencyCap, lastSyncDateForModule)) {
3523
3537
  return;
3524
3538
  }
3525
- // Url for cookie sync pixel
3526
- var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl);
3539
+ // The Trade Desk requires a URL parameter for GDPR enabled users.
3540
+ // It is optional but to simplify the code, we add it for all Trade
3541
+ // // Desk cookie syncs.
3542
+ var domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
3543
+ // Add domain parameter for Trade Desk
3544
+ var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain);
3527
3545
  self.performCookieSync(fullUrl, moduleId.toString(), mpid, cookieSyncDates);
3528
3546
  });
3529
3547
  };
@@ -3710,17 +3728,20 @@ var mParticle = (function () {
3710
3728
  var self = this;
3711
3729
  this.initialize = function () {
3712
3730
  if (mpInstance._Store.sessionId) {
3713
- var sessionTimeoutInMilliseconds = mpInstance._Store.SDKConfig.sessionTimeout * 60000;
3714
- if (new Date() > new Date(mpInstance._Store.dateLastEventSent.getTime() + sessionTimeoutInMilliseconds)) {
3731
+ var _a = mpInstance._Store,
3732
+ dateLastEventSent = _a.dateLastEventSent,
3733
+ SDKConfig = _a.SDKConfig;
3734
+ var sessionTimeout = SDKConfig.sessionTimeout;
3735
+ if (hasSessionTimedOut(dateLastEventSent === null || dateLastEventSent === void 0 ? void 0 : dateLastEventSent.getTime(), sessionTimeout)) {
3715
3736
  self.endSession();
3716
3737
  self.startNewSession();
3717
3738
  } else {
3718
3739
  // https://go.mparticle.com/work/SQDSDKS-6045
3719
3740
  // https://go.mparticle.com/work/SQDSDKS-6323
3720
3741
  var currentUser = mpInstance.Identity.getCurrentUser();
3721
- var sdkIdentityRequest = mpInstance._Store.SDKConfig.identifyRequest;
3742
+ var sdkIdentityRequest = SDKConfig.identifyRequest;
3722
3743
  if (hasIdentityRequestChanged(currentUser, sdkIdentityRequest)) {
3723
- mpInstance.Identity.identify(mpInstance._Store.SDKConfig.identifyRequest, mpInstance._Store.SDKConfig.identityCallback);
3744
+ mpInstance.Identity.identify(sdkIdentityRequest, SDKConfig.identityCallback);
3724
3745
  mpInstance._Store.identifyCalled = true;
3725
3746
  mpInstance._Store.SDKConfig.identityCallback = null;
3726
3747
  }
@@ -3764,14 +3785,10 @@ var mParticle = (function () {
3764
3785
  }
3765
3786
  };
3766
3787
  this.endSession = function (override) {
3767
- var _a, _b, _c, _d, _e;
3788
+ var _a, _b, _c, _d;
3768
3789
  mpInstance.Logger.verbose(Messages$6.InformationMessages.StartingEndSession);
3769
3790
  if (override) {
3770
- mpInstance._Events.logEvent({
3771
- messageType: Types.MessageType.SessionEnd
3772
- });
3773
- mpInstance._Store.nullifySession();
3774
- (_a = mpInstance._timeOnSiteTimer) === null || _a === void 0 ? void 0 : _a.resetTimer();
3791
+ performSessionEnd();
3775
3792
  return;
3776
3793
  }
3777
3794
  if (!mpInstance._Helpers.canLog()) {
@@ -3780,36 +3797,28 @@ var mParticle = (function () {
3780
3797
  // - the devToken is undefined
3781
3798
  // - webviewBridgeEnabled is set to false
3782
3799
  mpInstance.Logger.verbose(Messages$6.InformationMessages.AbandonEndSession);
3783
- (_b = mpInstance._timeOnSiteTimer) === null || _b === void 0 ? void 0 : _b.resetTimer();
3800
+ (_a = mpInstance._timeOnSiteTimer) === null || _a === void 0 ? void 0 : _a.resetTimer();
3784
3801
  return;
3785
3802
  }
3786
- var sessionTimeoutInMilliseconds;
3787
- var timeSinceLastEventSent;
3788
3803
  var cookies = mpInstance._Persistence.getPersistence();
3789
3804
  if (!cookies || cookies.gs && !cookies.gs.sid) {
3790
3805
  mpInstance.Logger.verbose(Messages$6.InformationMessages.NoSessionToEnd);
3791
- (_c = mpInstance._timeOnSiteTimer) === null || _c === void 0 ? void 0 : _c.resetTimer();
3806
+ (_b = mpInstance._timeOnSiteTimer) === null || _b === void 0 ? void 0 : _b.resetTimer();
3792
3807
  return;
3793
3808
  }
3794
3809
  // sessionId is not equal to cookies.sid if cookies.sid is changed in another tab
3795
3810
  if (cookies.gs.sid && mpInstance._Store.sessionId !== cookies.gs.sid) {
3796
3811
  mpInstance._Store.sessionId = cookies.gs.sid;
3797
3812
  }
3798
- if ((_d = cookies === null || cookies === void 0 ? void 0 : cookies.gs) === null || _d === void 0 ? void 0 : _d.les) {
3799
- sessionTimeoutInMilliseconds = mpInstance._Store.SDKConfig.sessionTimeout * 60000;
3800
- var newDate = new Date().getTime();
3801
- timeSinceLastEventSent = newDate - cookies.gs.les;
3802
- if (timeSinceLastEventSent < sessionTimeoutInMilliseconds) {
3803
- self.setSessionTimer();
3813
+ if ((_c = cookies === null || cookies === void 0 ? void 0 : cookies.gs) === null || _c === void 0 ? void 0 : _c.les) {
3814
+ var sessionTimeout = mpInstance._Store.SDKConfig.sessionTimeout;
3815
+ if (hasSessionTimedOut(cookies.gs.les, sessionTimeout)) {
3816
+ performSessionEnd();
3804
3817
  } else {
3805
- mpInstance._Events.logEvent({
3806
- messageType: Types.MessageType.SessionEnd
3807
- });
3808
- mpInstance._Store.sessionStartDate = null;
3809
- mpInstance._Store.nullifySession();
3818
+ self.setSessionTimer();
3819
+ (_d = mpInstance._timeOnSiteTimer) === null || _d === void 0 ? void 0 : _d.resetTimer();
3810
3820
  }
3811
3821
  }
3812
- (_e = mpInstance._timeOnSiteTimer) === null || _e === void 0 ? void 0 : _e.resetTimer();
3813
3822
  };
3814
3823
  this.setSessionTimer = function () {
3815
3824
  var sessionTimeoutInMilliseconds = mpInstance._Store.SDKConfig.sessionTimeout * 60000;
@@ -3842,6 +3851,34 @@ var mParticle = (function () {
3842
3851
  }
3843
3852
  }
3844
3853
  };
3854
+ /**
3855
+ * Checks if the session has expired based on the last event timestamp
3856
+ * @param lastEventTimestamp - Unix timestamp in milliseconds of the last event
3857
+ * @param sessionTimeout - Session timeout in minutes
3858
+ * @returns true if the session has expired, false otherwise
3859
+ */
3860
+ function hasSessionTimedOut(lastEventTimestamp, sessionTimeout) {
3861
+ if (!lastEventTimestamp || !sessionTimeout || sessionTimeout <= 0) {
3862
+ return false;
3863
+ }
3864
+ var sessionTimeoutInMilliseconds = sessionTimeout * 60000;
3865
+ var timeSinceLastEvent = Date.now() - lastEventTimestamp;
3866
+ return timeSinceLastEvent >= sessionTimeoutInMilliseconds;
3867
+ }
3868
+ /**
3869
+ * Performs session end operations:
3870
+ * - Logs a SessionEnd event
3871
+ * - Nullifies the session ID and related data
3872
+ * - Resets the time-on-site timer
3873
+ */
3874
+ function performSessionEnd() {
3875
+ var _a;
3876
+ mpInstance._Events.logEvent({
3877
+ messageType: Types.MessageType.SessionEnd
3878
+ });
3879
+ mpInstance._Store.nullifySession();
3880
+ (_a = mpInstance._timeOnSiteTimer) === null || _a === void 0 ? void 0 : _a.resetTimer();
3881
+ }
3845
3882
  }
3846
3883
 
3847
3884
  var Messages$5 = Constants.Messages;
@@ -4702,6 +4739,7 @@ var mParticle = (function () {
4702
4739
  this.nullifySession = function () {
4703
4740
  _this.sessionId = null;
4704
4741
  _this.dateLastEventSent = null;
4742
+ _this.sessionStartDate = null;
4705
4743
  _this.sessionAttributes = {};
4706
4744
  _this.localSessionAttributes = {};
4707
4745
  mpInstance._Persistence.update();
@@ -7362,7 +7400,8 @@ var mParticle = (function () {
7362
7400
  DestinationMpid: aliasRequest.destinationMpid,
7363
7401
  SourceMpid: aliasRequest.sourceMpid,
7364
7402
  StartUnixtimeMs: aliasRequest.startTime,
7365
- EndUnixtimeMs: aliasRequest.endTime
7403
+ EndUnixtimeMs: aliasRequest.endTime,
7404
+ Scope: aliasRequest.scope
7366
7405
  };
7367
7406
  },
7368
7407
  convertToNative: function convertToNative(identityApiData) {
@@ -7662,7 +7701,7 @@ var mParticle = (function () {
7662
7701
  after applying this adjustment it will be impossible to create an aliasRequest passes the aliasUsers()
7663
7702
  validation that the startTime must be less than the endTime
7664
7703
  */
7665
- createAliasRequest: function createAliasRequest(sourceUser, destinationUser) {
7704
+ createAliasRequest: function createAliasRequest(sourceUser, destinationUser, scope) {
7666
7705
  try {
7667
7706
  if (!destinationUser || !sourceUser) {
7668
7707
  mpInstance.Logger.error("'destinationUser' and 'sourceUser' must both be present");
@@ -7689,7 +7728,8 @@ var mParticle = (function () {
7689
7728
  destinationMpid: destinationUser.getMPID(),
7690
7729
  sourceMpid: sourceUser.getMPID(),
7691
7730
  startTime: startTime,
7692
- endTime: endTime
7731
+ endTime: endTime,
7732
+ scope: scope || 'device'
7693
7733
  };
7694
7734
  } catch (e) {
7695
7735
  mpInstance.Logger.error('There was a problem with creating an alias request: ' + e);
@@ -9790,17 +9830,73 @@ var mParticle = (function () {
9790
9830
  });
9791
9831
  });
9792
9832
  };
9793
-
9833
+ /**
9834
+ * Hashes attributes and returns both original and hashed versions
9835
+ * with Rokt-compatible key names (like emailsha256, mobilesha256)
9836
+ *
9837
+ *
9838
+ * @param {IRoktPartnerAttributes} attributes - Attributes to hash
9839
+ * @returns {Promise<IRoktPartnerAttributes>} Object with both original and hashed attributes
9840
+ *
9841
+ */
9794
9842
  RoktManager.prototype.hashAttributes = function (attributes) {
9795
- if (!this.isReady()) {
9796
- return this.deferredCall('hashAttributes', attributes);
9797
- }
9798
- try {
9799
- return this.kit.hashAttributes(attributes);
9800
- } catch (error) {
9801
- return Promise.reject(error instanceof Error ? error : new Error('Unknown error occurred'));
9802
- }
9843
+ return __awaiter(this, void 0, void 0, function () {
9844
+ var keys, hashPromises, results, hashedAttributes, _i, results_1, _a, key, attributeValue, hashedValue, error_3, errorMessage;
9845
+ var _this = this;
9846
+ return __generator(this, function (_b) {
9847
+ switch (_b.label) {
9848
+ case 0:
9849
+ _b.trys.push([0, 2,, 3]);
9850
+ if (!attributes || _typeof$1(attributes) !== 'object') {
9851
+ return [2 /*return*/, {}];
9852
+ }
9853
+ keys = Object.keys(attributes);
9854
+ if (keys.length === 0) {
9855
+ return [2 /*return*/, {}];
9856
+ }
9857
+ hashPromises = keys.map(function (key) {
9858
+ return __awaiter(_this, void 0, void 0, function () {
9859
+ var attributeValue, hashedValue;
9860
+ return __generator(this, function (_a) {
9861
+ switch (_a.label) {
9862
+ case 0:
9863
+ attributeValue = attributes[key];
9864
+ return [4 /*yield*/, this.hashSha256(attributeValue)];
9865
+ case 1:
9866
+ hashedValue = _a.sent();
9867
+ return [2 /*return*/, {
9868
+ key: key,
9869
+ attributeValue: attributeValue,
9870
+ hashedValue: hashedValue
9871
+ }];
9872
+ }
9873
+ });
9874
+ });
9875
+ });
9876
+ return [4 /*yield*/, Promise.all(hashPromises)];
9877
+ case 1:
9878
+ results = _b.sent();
9879
+ hashedAttributes = {};
9880
+ for (_i = 0, results_1 = results; _i < results_1.length; _i++) {
9881
+ _a = results_1[_i], key = _a.key, attributeValue = _a.attributeValue, hashedValue = _a.hashedValue;
9882
+ hashedAttributes[key] = attributeValue;
9883
+ if (hashedValue) {
9884
+ hashedAttributes["".concat(key, "sha256")] = hashedValue;
9885
+ }
9886
+ }
9887
+ return [2 /*return*/, hashedAttributes];
9888
+ case 2:
9889
+ error_3 = _b.sent();
9890
+ errorMessage = error_3 instanceof Error ? error_3.message : String(error_3);
9891
+ this.logger.error("Failed to hashAttributes, returning an empty object: ".concat(errorMessage));
9892
+ return [2 /*return*/, {}];
9893
+ case 3:
9894
+ return [2 /*return*/];
9895
+ }
9896
+ });
9897
+ });
9803
9898
  };
9899
+
9804
9900
  RoktManager.prototype.setExtensionData = function (extensionData) {
9805
9901
  if (!this.isReady()) {
9806
9902
  this.deferredCall('setExtensionData', extensionData);
@@ -9823,6 +9919,42 @@ var mParticle = (function () {
9823
9919
  return Promise.reject(error instanceof Error ? error : new Error('Error using extension: ' + name));
9824
9920
  }
9825
9921
  };
9922
+ /**
9923
+ * Hashes an attribute using SHA-256
9924
+ *
9925
+ * @param {string | number | boolean | undefined | null} attribute - The value to hash
9926
+ * @returns {Promise<string | undefined | null>} SHA-256 hashed value or undefined/null
9927
+ *
9928
+ */
9929
+ RoktManager.prototype.hashSha256 = function (attribute) {
9930
+ return __awaiter(this, void 0, void 0, function () {
9931
+ var normalizedValue, error_4, errorMessage;
9932
+ return __generator(this, function (_a) {
9933
+ switch (_a.label) {
9934
+ case 0:
9935
+ if (attribute === null || attribute === undefined) {
9936
+ this.logger.warning("hashSha256 received null/undefined as input");
9937
+ return [2 /*return*/, attribute];
9938
+ }
9939
+ _a.label = 1;
9940
+ case 1:
9941
+ _a.trys.push([1, 3,, 4]);
9942
+ normalizedValue = String(attribute).trim().toLocaleLowerCase();
9943
+ return [4 /*yield*/, this.sha256Hex(normalizedValue)];
9944
+ case 2:
9945
+ return [2 /*return*/, _a.sent()];
9946
+ case 3:
9947
+ error_4 = _a.sent();
9948
+ errorMessage = error_4 instanceof Error ? error_4.message : String(error_4);
9949
+ this.logger.error("Failed to hashSha256, returning undefined: ".concat(errorMessage));
9950
+ return [2 /*return*/, undefined];
9951
+ case 4:
9952
+ return [2 /*return*/];
9953
+ }
9954
+ });
9955
+ });
9956
+ };
9957
+
9826
9958
  RoktManager.prototype.getLocalSessionAttributes = function () {
9827
9959
  return this.store.getLocalSessionAttributes();
9828
9960
  };
@@ -9918,6 +10050,45 @@ var mParticle = (function () {
9918
10050
  }
9919
10051
  this.messageQueue["delete"](messageId);
9920
10052
  };
10053
+ /**
10054
+ * Hashes a string input using SHA-256 and returns the hex digest
10055
+ * Uses the Web Crypto API for secure hashing
10056
+ *
10057
+ * @param {string} input - The string to hash
10058
+ * @returns {Promise<string>} The SHA-256 hash as a hexadecimal string
10059
+ */
10060
+ RoktManager.prototype.sha256Hex = function (input) {
10061
+ return __awaiter(this, void 0, void 0, function () {
10062
+ var encoder, encodedInput, digest;
10063
+ return __generator(this, function (_a) {
10064
+ switch (_a.label) {
10065
+ case 0:
10066
+ encoder = new TextEncoder();
10067
+ encodedInput = encoder.encode(input);
10068
+ return [4 /*yield*/, crypto.subtle.digest('SHA-256', encodedInput)];
10069
+ case 1:
10070
+ digest = _a.sent();
10071
+ return [2 /*return*/, this.arrayBufferToHex(digest)];
10072
+ }
10073
+ });
10074
+ });
10075
+ };
10076
+ /**
10077
+ * Converts an ArrayBuffer to a hexadecimal string representation
10078
+ * Each byte is converted to a 2-character hex string with leading zeros
10079
+ *
10080
+ * @param {ArrayBuffer} buffer - The buffer to convert
10081
+ * @returns {string} The hexadecimal string representation
10082
+ */
10083
+ RoktManager.prototype.arrayBufferToHex = function (buffer) {
10084
+ var bytes = new Uint8Array(buffer);
10085
+ var hexString = '';
10086
+ for (var i = 0; i < bytes.length; i++) {
10087
+ var hexByte = bytes[i].toString(16).padStart(2, '0');
10088
+ hexString += hexByte;
10089
+ }
10090
+ return hexString;
10091
+ };
9921
10092
  /**
9922
10093
  * Checks if an identity value has changed by comparing current and new values
9923
10094
  *
@@ -11467,8 +11638,8 @@ var mParticle = (function () {
11467
11638
  aliasUsers: function aliasUsers(aliasRequest, callback) {
11468
11639
  self.getInstance().Identity.aliasUsers(aliasRequest, callback);
11469
11640
  },
11470
- createAliasRequest: function createAliasRequest(sourceUser, destinationUser) {
11471
- return self.getInstance().Identity.createAliasRequest(sourceUser, destinationUser);
11641
+ createAliasRequest: function createAliasRequest(sourceUser, destinationUser, scope) {
11642
+ return self.getInstance().Identity.createAliasRequest(sourceUser, destinationUser, scope);
11472
11643
  },
11473
11644
  getCurrentUser: function getCurrentUser() {
11474
11645
  return self.getInstance().Identity.getCurrentUser();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.51.0",
3
+ "version": "2.52.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -13,6 +13,16 @@ const { InformationMessages } = Messages;
13
13
 
14
14
  export const DAYS_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
15
15
 
16
+ // Partner module IDs for cookie sync configurations
17
+ export const PARTNER_MODULE_IDS = {
18
+ AdobeEventForwarder: 11,
19
+ DoubleclickDFP: 41,
20
+ AppNexus: 50,
21
+ Lotame: 58,
22
+ TradeDesk: 103,
23
+ VerizonMedia: 155,
24
+ } as const;
25
+
16
26
  export type CookieSyncDates = Dictionary<number>;
17
27
 
18
28
  export interface IPixelConfiguration {
@@ -111,8 +121,12 @@ export default function CookieSyncManager(
111
121
  return;
112
122
  }
113
123
 
114
- // Url for cookie sync pixel
115
- const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl)
124
+ // The Trade Desk requires a URL parameter for GDPR enabled users.
125
+ // It is optional but to simplify the code, we add it for all Trade
126
+ // // Desk cookie syncs.
127
+ const domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
128
+ // Add domain parameter for Trade Desk
129
+ const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain);
116
130
 
117
131
  self.performCookieSync(
118
132
  fullUrl,
@@ -107,12 +107,14 @@ export interface IIdentityRequest {
107
107
  ): IdentityPreProcessResult;
108
108
  }
109
109
 
110
+ export type AliasRequestScope = 'device' | 'mpid';
111
+
110
112
  export interface IAliasRequest {
111
113
  destinationMpid: MPID;
112
114
  sourceMpid: MPID;
113
115
  startTime: number;
114
116
  endTime: number;
115
- scope?: string;
117
+ scope?: AliasRequestScope;
116
118
  }
117
119
 
118
120
  export interface IAliasCallback {
@@ -151,7 +153,8 @@ export interface SDKIdentityApi {
151
153
  ): void;
152
154
  createAliasRequest?(
153
155
  sourceUser: IMParticleUser,
154
- destinationUser: IMParticleUser
156
+ destinationUser: IMParticleUser,
157
+ scope?: AliasRequestScope
155
158
  ): IAliasRequest;
156
159
  }
157
160
 
package/src/identity.js CHANGED
@@ -195,6 +195,7 @@ export default function Identity(mpInstance) {
195
195
  SourceMpid: aliasRequest.sourceMpid,
196
196
  StartUnixtimeMs: aliasRequest.startTime,
197
197
  EndUnixtimeMs: aliasRequest.endTime,
198
+ Scope: aliasRequest.scope,
198
199
  };
199
200
  },
200
201
 
@@ -744,7 +745,7 @@ export default function Identity(mpInstance) {
744
745
  after applying this adjustment it will be impossible to create an aliasRequest passes the aliasUsers()
745
746
  validation that the startTime must be less than the endTime
746
747
  */
747
- createAliasRequest: function(sourceUser, destinationUser) {
748
+ createAliasRequest: function(sourceUser, destinationUser, scope) {
748
749
  try {
749
750
  if (!destinationUser || !sourceUser) {
750
751
  mpInstance.Logger.error(
@@ -788,6 +789,7 @@ export default function Identity(mpInstance) {
788
789
  sourceMpid: sourceUser.getMPID(),
789
790
  startTime: startTime,
790
791
  endTime: endTime,
792
+ scope: scope || 'device',
791
793
  };
792
794
  } catch (e) {
793
795
  mpInstance.Logger.error(
@@ -368,10 +368,10 @@ function mParticleInstanceManager(this: IMParticleInstanceManager) {
368
368
  aliasUsers: function(aliasRequest, callback) {
369
369
  self.getInstance().Identity.aliasUsers(aliasRequest, callback);
370
370
  },
371
- createAliasRequest: function(sourceUser, destinationUser) {
371
+ createAliasRequest: function(sourceUser, destinationUser, scope) {
372
372
  return self
373
373
  .getInstance()
374
- .Identity.createAliasRequest(sourceUser, destinationUser);
374
+ .Identity.createAliasRequest(sourceUser, destinationUser, scope);
375
375
  },
376
376
  getCurrentUser: function() {
377
377
  return self.getInstance().Identity.getCurrentUser();
@@ -252,15 +252,51 @@ export default class RoktManager {
252
252
  }
253
253
  }
254
254
 
255
- public hashAttributes(attributes: IRoktPartnerAttributes): Promise<IRoktPartnerAttributes> {
256
- if (!this.isReady()) {
257
- return this.deferredCall<IRoktPartnerAttributes>('hashAttributes', attributes);
258
- }
259
-
255
+ /**
256
+ * Hashes attributes and returns both original and hashed versions
257
+ * with Rokt-compatible key names (like emailsha256, mobilesha256)
258
+ *
259
+ *
260
+ * @param {IRoktPartnerAttributes} attributes - Attributes to hash
261
+ * @returns {Promise<IRoktPartnerAttributes>} Object with both original and hashed attributes
262
+ *
263
+ */
264
+ public async hashAttributes(attributes: IRoktPartnerAttributes): Promise<IRoktPartnerAttributes> {
260
265
  try {
261
- return this.kit.hashAttributes(attributes);
266
+ if (!attributes || typeof attributes !== 'object') {
267
+ return {};
268
+ }
269
+
270
+ // Get own property keys only
271
+ const keys = Object.keys(attributes);
272
+
273
+ if (keys.length === 0) {
274
+ return {};
275
+ }
276
+
277
+ // Hash all attributes in parallel
278
+ const hashPromises = keys.map(async (key) => {
279
+ const attributeValue = attributes[key];
280
+ const hashedValue = await this.hashSha256(attributeValue);
281
+ return { key, attributeValue, hashedValue };
282
+ });
283
+
284
+ const results = await Promise.all(hashPromises);
285
+
286
+ // Build the result object
287
+ const hashedAttributes: IRoktPartnerAttributes = {};
288
+ for (const { key, attributeValue, hashedValue } of results) {
289
+ hashedAttributes[key] = attributeValue;
290
+ if (hashedValue) {
291
+ hashedAttributes[`${key}sha256`] = hashedValue;
292
+ }
293
+ }
294
+ return hashedAttributes;
295
+
262
296
  } catch (error) {
263
- return Promise.reject(error instanceof Error ? error : new Error('Unknown error occurred'));
297
+ const errorMessage = error instanceof Error ? error.message : String(error);
298
+ this.logger.error(`Failed to hashAttributes, returning an empty object: ${errorMessage}`);
299
+ return {};
264
300
  }
265
301
  }
266
302
 
@@ -290,6 +326,29 @@ export default class RoktManager {
290
326
  }
291
327
  }
292
328
 
329
+ /**
330
+ * Hashes an attribute using SHA-256
331
+ *
332
+ * @param {string | number | boolean | undefined | null} attribute - The value to hash
333
+ * @returns {Promise<string | undefined | null>} SHA-256 hashed value or undefined/null
334
+ *
335
+ */
336
+ public async hashSha256(attribute: string | number | boolean | undefined | null): Promise<string | undefined | null> {
337
+ if (attribute === null || attribute === undefined) {
338
+ this.logger.warning(`hashSha256 received null/undefined as input`);
339
+ return attribute as null | undefined;
340
+ }
341
+
342
+ try {
343
+ const normalizedValue = String(attribute).trim().toLocaleLowerCase();
344
+ return await this.sha256Hex(normalizedValue);
345
+ } catch (error) {
346
+ const errorMessage = error instanceof Error ? error.message : String(error);
347
+ this.logger.error(`Failed to hashSha256, returning undefined: ${errorMessage}`);
348
+ return undefined;
349
+ }
350
+ }
351
+
293
352
  public getLocalSessionAttributes(): LocalSessionAttributes {
294
353
  return this.store.getLocalSessionAttributes();
295
354
  }
@@ -400,6 +459,37 @@ export default class RoktManager {
400
459
  this.messageQueue.delete(messageId);
401
460
  }
402
461
 
462
+ /**
463
+ * Hashes a string input using SHA-256 and returns the hex digest
464
+ * Uses the Web Crypto API for secure hashing
465
+ *
466
+ * @param {string} input - The string to hash
467
+ * @returns {Promise<string>} The SHA-256 hash as a hexadecimal string
468
+ */
469
+ private async sha256Hex(input: string): Promise<string> {
470
+ const encoder = new TextEncoder();
471
+ const encodedInput = encoder.encode(input);
472
+ const digest = await crypto.subtle.digest('SHA-256', encodedInput);
473
+ return this.arrayBufferToHex(digest);
474
+ }
475
+
476
+ /**
477
+ * Converts an ArrayBuffer to a hexadecimal string representation
478
+ * Each byte is converted to a 2-character hex string with leading zeros
479
+ *
480
+ * @param {ArrayBuffer} buffer - The buffer to convert
481
+ * @returns {string} The hexadecimal string representation
482
+ */
483
+ private arrayBufferToHex(buffer: ArrayBuffer): string {
484
+ const bytes = new Uint8Array(buffer);
485
+ let hexString = '';
486
+ for (let i = 0; i < bytes.length; i++) {
487
+ const hexByte = bytes[i].toString(16).padStart(2, '0');
488
+ hexString += hexByte;
489
+ }
490
+ return hexString;
491
+ }
492
+
403
493
  /**
404
494
  * Checks if an identity value has changed by comparing current and new values
405
495
  *