@mparticle/web-sdk 2.50.1 → 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/README.md +1 -1
- package/dist/mparticle.common.js +38 -19
- package/dist/mparticle.esm.js +38 -19
- package/dist/mparticle.js +232 -45
- package/package.json +9 -5
- package/src/cookieSyncManager.ts +16 -2
- package/src/helpers.js +15 -0
- package/src/identity.interfaces.ts +5 -2
- package/src/identity.js +3 -1
- package/src/integrationCapture.ts +6 -0
- package/src/mparticle-instance-manager.ts +2 -2
- package/src/roktManager.ts +97 -7
- package/src/sessionManager.ts +45 -40
- package/src/store.ts +1 -0
- package/src/stub/mparticle.stub.js +1 -0
- package/src/utils.ts +13 -3
package/src/roktManager.ts
CHANGED
|
@@ -252,15 +252,51 @@ export default class RoktManager {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
*
|
package/src/sessionManager.ts
CHANGED
|
@@ -33,31 +33,24 @@ export default function SessionManager(
|
|
|
33
33
|
|
|
34
34
|
this.initialize = function (): void {
|
|
35
35
|
if (mpInstance._Store.sessionId) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
new Date() >
|
|
41
|
-
new Date(
|
|
42
|
-
mpInstance._Store.dateLastEventSent.getTime() +
|
|
43
|
-
sessionTimeoutInMilliseconds
|
|
44
|
-
)
|
|
45
|
-
) {
|
|
36
|
+
const { dateLastEventSent, SDKConfig } = mpInstance._Store;
|
|
37
|
+
const { sessionTimeout } = SDKConfig;
|
|
38
|
+
|
|
39
|
+
if (hasSessionTimedOut(dateLastEventSent?.getTime(), sessionTimeout)) {
|
|
46
40
|
self.endSession();
|
|
47
41
|
self.startNewSession();
|
|
48
42
|
} else {
|
|
49
43
|
// https://go.mparticle.com/work/SQDSDKS-6045
|
|
50
44
|
// https://go.mparticle.com/work/SQDSDKS-6323
|
|
51
45
|
const currentUser = mpInstance.Identity.getCurrentUser();
|
|
52
|
-
const sdkIdentityRequest =
|
|
53
|
-
mpInstance._Store.SDKConfig.identifyRequest;
|
|
46
|
+
const sdkIdentityRequest = SDKConfig.identifyRequest;
|
|
54
47
|
|
|
55
48
|
if (
|
|
56
49
|
hasIdentityRequestChanged(currentUser, sdkIdentityRequest)
|
|
57
50
|
) {
|
|
58
51
|
mpInstance.Identity.identify(
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
sdkIdentityRequest,
|
|
53
|
+
SDKConfig.identityCallback
|
|
61
54
|
);
|
|
62
55
|
mpInstance._Store.identifyCalled = true;
|
|
63
56
|
mpInstance._Store.SDKConfig.identityCallback = null;
|
|
@@ -134,12 +127,7 @@ export default function SessionManager(
|
|
|
134
127
|
);
|
|
135
128
|
|
|
136
129
|
if (override) {
|
|
137
|
-
|
|
138
|
-
messageType: Types.MessageType.SessionEnd,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
mpInstance._Store.nullifySession();
|
|
142
|
-
mpInstance._timeOnSiteTimer?.resetTimer();
|
|
130
|
+
performSessionEnd();
|
|
143
131
|
return;
|
|
144
132
|
}
|
|
145
133
|
|
|
@@ -152,13 +140,9 @@ export default function SessionManager(
|
|
|
152
140
|
Messages.InformationMessages.AbandonEndSession
|
|
153
141
|
);
|
|
154
142
|
mpInstance._timeOnSiteTimer?.resetTimer();
|
|
155
|
-
|
|
156
143
|
return;
|
|
157
144
|
}
|
|
158
145
|
|
|
159
|
-
let sessionTimeoutInMilliseconds: number;
|
|
160
|
-
let timeSinceLastEventSent: number;
|
|
161
|
-
|
|
162
146
|
const cookies: IPersistenceMinified =
|
|
163
147
|
mpInstance._Persistence.getPersistence();
|
|
164
148
|
|
|
@@ -167,7 +151,6 @@ export default function SessionManager(
|
|
|
167
151
|
Messages.InformationMessages.NoSessionToEnd
|
|
168
152
|
);
|
|
169
153
|
mpInstance._timeOnSiteTimer?.resetTimer();
|
|
170
|
-
|
|
171
154
|
return;
|
|
172
155
|
}
|
|
173
156
|
|
|
@@ -177,24 +160,15 @@ export default function SessionManager(
|
|
|
177
160
|
}
|
|
178
161
|
|
|
179
162
|
if (cookies?.gs?.les) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (timeSinceLastEventSent < sessionTimeoutInMilliseconds) {
|
|
186
|
-
self.setSessionTimer();
|
|
163
|
+
const sessionTimeout = mpInstance._Store.SDKConfig.sessionTimeout;
|
|
164
|
+
|
|
165
|
+
if (hasSessionTimedOut(cookies.gs.les, sessionTimeout)) {
|
|
166
|
+
performSessionEnd();
|
|
187
167
|
} else {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
mpInstance._Store.sessionStartDate = null;
|
|
193
|
-
mpInstance._Store.nullifySession();
|
|
168
|
+
self.setSessionTimer();
|
|
169
|
+
mpInstance._timeOnSiteTimer?.resetTimer();
|
|
194
170
|
}
|
|
195
171
|
}
|
|
196
|
-
|
|
197
|
-
mpInstance._timeOnSiteTimer?.resetTimer();
|
|
198
172
|
};
|
|
199
173
|
|
|
200
174
|
this.setSessionTimer = function (): void {
|
|
@@ -234,4 +208,35 @@ export default function SessionManager(
|
|
|
234
208
|
}
|
|
235
209
|
}
|
|
236
210
|
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Checks if the session has expired based on the last event timestamp
|
|
214
|
+
* @param lastEventTimestamp - Unix timestamp in milliseconds of the last event
|
|
215
|
+
* @param sessionTimeout - Session timeout in minutes
|
|
216
|
+
* @returns true if the session has expired, false otherwise
|
|
217
|
+
*/
|
|
218
|
+
function hasSessionTimedOut(lastEventTimestamp: number, sessionTimeout: number): boolean {
|
|
219
|
+
if (!lastEventTimestamp || !sessionTimeout || sessionTimeout <= 0) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const sessionTimeoutInMilliseconds: number = sessionTimeout * 60000;
|
|
224
|
+
const timeSinceLastEvent: number = Date.now() - lastEventTimestamp;
|
|
225
|
+
|
|
226
|
+
return timeSinceLastEvent >= sessionTimeoutInMilliseconds;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Performs session end operations:
|
|
231
|
+
* - Logs a SessionEnd event
|
|
232
|
+
* - Nullifies the session ID and related data
|
|
233
|
+
* - Resets the time-on-site timer
|
|
234
|
+
*/
|
|
235
|
+
function performSessionEnd(): void {
|
|
236
|
+
mpInstance._Events.logEvent({
|
|
237
|
+
messageType: Types.MessageType.SessionEnd,
|
|
238
|
+
});
|
|
239
|
+
mpInstance._Store.nullifySession();
|
|
240
|
+
mpInstance._timeOnSiteTimer?.resetTimer();
|
|
241
|
+
}
|
|
237
242
|
}
|
package/src/store.ts
CHANGED
|
@@ -683,6 +683,7 @@ export default function Store(
|
|
|
683
683
|
this.nullifySession = (): void => {
|
|
684
684
|
this.sessionId = null;
|
|
685
685
|
this.dateLastEventSent = null;
|
|
686
|
+
this.sessionStartDate = null;
|
|
686
687
|
this.sessionAttributes = {};
|
|
687
688
|
this.localSessionAttributes = {};
|
|
688
689
|
mpInstance._Persistence.update();
|
package/src/utils.ts
CHANGED
|
@@ -206,18 +206,28 @@ const replaceAmpWithAmpersand = (value: string): string => value.replace(/&/
|
|
|
206
206
|
const createCookieSyncUrl = (
|
|
207
207
|
mpid: MPID,
|
|
208
208
|
pixelUrl: string,
|
|
209
|
-
redirectUrl?: string
|
|
209
|
+
redirectUrl?: string,
|
|
210
|
+
domain?: string
|
|
210
211
|
): string => {
|
|
211
212
|
const modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
|
|
212
213
|
const modifiedDirectUrl = redirectUrl
|
|
213
214
|
? replaceAmpWithAmpersand(redirectUrl)
|
|
214
215
|
: null;
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
let url = replaceMPID(modifiedPixelUrl, mpid);
|
|
218
|
+
|
|
217
219
|
const redirect = modifiedDirectUrl
|
|
218
220
|
? replaceMPID(modifiedDirectUrl, mpid)
|
|
219
221
|
: '';
|
|
220
|
-
|
|
222
|
+
|
|
223
|
+
let fullUrl = url + encodeURIComponent(redirect);
|
|
224
|
+
|
|
225
|
+
if (domain) {
|
|
226
|
+
const separator = fullUrl.includes('?') ? '&' : '?';
|
|
227
|
+
fullUrl += `${separator}domain=${domain}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return fullUrl;
|
|
221
231
|
};
|
|
222
232
|
|
|
223
233
|
// FIXME: REFACTOR for V3
|