@mparticle/web-sdk 2.58.0 → 2.60.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.common.js +110 -86
- package/dist/mparticle.esm.js +110 -86
- package/dist/mparticle.js +467 -276
- package/package.json +1 -1
- package/src/apiClient.ts +42 -4
- package/src/batchUploader.ts +23 -26
- package/src/cookieSyncManager.ts +6 -0
- package/src/foregroundTimeTracker.ts +5 -3
- package/src/identity-utils.ts +24 -1
- package/src/identityApiClient.ts +15 -5
- package/src/logger.ts +4 -0
- package/src/mp-instance.ts +65 -23
- package/src/persistence.js +15 -0
- package/src/roktManager.ts +12 -4
- package/src/sdkRuntimeModels.ts +2 -0
- package/src/sessionManager.ts +11 -2
- package/src/store.ts +2 -1
- package/src/utils.ts +63 -2
- package/src/vault.ts +28 -35
package/package.json
CHANGED
package/src/apiClient.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { IMParticleUser, ISDKUserAttributes } from './identity-user-interfaces';
|
|
|
10
10
|
import { AsyncUploader, FetchUploader, XHRUploader } from './uploaders';
|
|
11
11
|
import { IMParticleWebSDKInstance } from './mp-instance';
|
|
12
12
|
import { appendUserInfo } from './user-utils';
|
|
13
|
+
import { hasExplicitIdentifier } from './identity-utils';
|
|
13
14
|
|
|
14
15
|
export interface IAPIClient {
|
|
15
16
|
uploader: BatchUploader | null;
|
|
@@ -88,6 +89,35 @@ export default function APIClient(
|
|
|
88
89
|
});
|
|
89
90
|
};
|
|
90
91
|
|
|
92
|
+
// When noFunctional is set and there are no identities passed, the SDK will not fully initialize.
|
|
93
|
+
// In this case, there will be no MPID, but we still want kits to initialize and forward the event to kits.
|
|
94
|
+
// The original event is queued for the MP server upload path so it can be sent once an MPID is returned.
|
|
95
|
+
// Returns true if the event was handled by this path (caller should return early).
|
|
96
|
+
const handleNoFunctionalPreMpidEvent = (event: SDKEvent, mpid: string | undefined): boolean => {
|
|
97
|
+
const noFunctionalWithoutId =
|
|
98
|
+
mpInstance._CookieConsentManager?.getNoFunctional() &&
|
|
99
|
+
!hasExplicitIdentifier(mpInstance._Store);
|
|
100
|
+
|
|
101
|
+
if (!noFunctionalWithoutId || mpid || !mpInstance._Store.configurationLoaded || mpInstance._Store.requireDelay) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let forwarderEvent = event;
|
|
106
|
+
if (kitBlocker?.kitBlockingEnabled) {
|
|
107
|
+
forwarderEvent = kitBlocker.createBlockedEvent(event);
|
|
108
|
+
}
|
|
109
|
+
if (forwarderEvent) {
|
|
110
|
+
mpInstance._Forwarders.sendEventToForwarders(forwarderEvent);
|
|
111
|
+
event.AlreadySentToForwarders = true;
|
|
112
|
+
}
|
|
113
|
+
mpInstance.Logger.verbose(
|
|
114
|
+
'noFunctional event forwarded to kits and queued for MP server upload when MPID is available.'
|
|
115
|
+
);
|
|
116
|
+
mpInstance._Store.eventQueue.push(event);
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
};
|
|
120
|
+
|
|
91
121
|
this.sendEventToServer = function(event, _options) {
|
|
92
122
|
const defaultOptions = {
|
|
93
123
|
shouldUploadEvent: true,
|
|
@@ -112,8 +142,14 @@ export default function APIClient(
|
|
|
112
142
|
mpInstance._Store.integrationDelayTimeoutStart,
|
|
113
143
|
Date.now()
|
|
114
144
|
);
|
|
115
|
-
|
|
116
|
-
|
|
145
|
+
|
|
146
|
+
if (handleNoFunctionalPreMpidEvent(event, mpid)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// We queue events if there is no MPID (MPID is null, or === 0), or there are integrations that
|
|
151
|
+
// require this to stall because integration attributes need to be set, or if we are still
|
|
152
|
+
// fetching the config (self hosted only), and so require delaying events
|
|
117
153
|
if (
|
|
118
154
|
!mpid ||
|
|
119
155
|
mpInstance._Store.requireDelay ||
|
|
@@ -146,8 +182,10 @@ export default function APIClient(
|
|
|
146
182
|
}
|
|
147
183
|
|
|
148
184
|
// We need to check event again, because kitblocking
|
|
149
|
-
// can nullify the event
|
|
150
|
-
if
|
|
185
|
+
// can nullify the event.
|
|
186
|
+
// Skip if forwarders were already called in the noFunctional pre-MPID path
|
|
187
|
+
// to prevent double-sending when the event queue is later flushed.
|
|
188
|
+
if (event && !event.AlreadySentToForwarders) {
|
|
151
189
|
mpInstance._Forwarders.sendEventToForwarders(event);
|
|
152
190
|
}
|
|
153
191
|
}
|
package/src/batchUploader.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Batch } from '@mparticle/event-models';
|
|
2
2
|
import Constants from './constants';
|
|
3
|
-
import { SDKEvent, SDKEventCustomFlags
|
|
3
|
+
import { SDKEvent, SDKEventCustomFlags } from './sdkRuntimeModels';
|
|
4
4
|
import { convertEvents } from './sdkToEventsApiConverter';
|
|
5
5
|
import { MessageType, EventType } from './types';
|
|
6
|
-
import { getRampNumber, isEmpty } from './utils';
|
|
6
|
+
import { getRampNumber, isEmpty, obfuscateDevData } from './utils';
|
|
7
7
|
import { SessionStorageVault, LocalStorageVault } from './vault';
|
|
8
8
|
import {
|
|
9
9
|
AsyncUploader,
|
|
@@ -74,19 +74,16 @@ export class BatchUploader {
|
|
|
74
74
|
// so that we don't have to check it every time
|
|
75
75
|
this.offlineStorageEnabled = this.isOfflineStorageAvailable();
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
// When noFunctional is true, prevent events/batches storage
|
|
78
|
+
const noFunctional = mpInstance._CookieConsentManager?.getNoFunctional();
|
|
79
|
+
|
|
80
|
+
if (this.offlineStorageEnabled && !noFunctional) {
|
|
78
81
|
this.eventVault = new SessionStorageVault<SDKEvent[]>(
|
|
79
|
-
`${mpInstance._Store.storageName}-events
|
|
80
|
-
{
|
|
81
|
-
logger: mpInstance.Logger,
|
|
82
|
-
}
|
|
82
|
+
`${mpInstance._Store.storageName}-events`
|
|
83
83
|
);
|
|
84
84
|
|
|
85
85
|
this.batchVault = new LocalStorageVault<Batch[]>(
|
|
86
|
-
`${mpInstance._Store.storageName}-batches
|
|
87
|
-
{
|
|
88
|
-
logger: mpInstance.Logger,
|
|
89
|
-
}
|
|
86
|
+
`${mpInstance._Store.storageName}-batches`
|
|
90
87
|
);
|
|
91
88
|
|
|
92
89
|
// Load Events from Session Storage in case we have any in storage
|
|
@@ -257,15 +254,16 @@ export class BatchUploader {
|
|
|
257
254
|
return;
|
|
258
255
|
}
|
|
259
256
|
|
|
260
|
-
const { Logger } = this.mpInstance;
|
|
261
|
-
|
|
262
257
|
this.eventsQueuedForProcessing.push(event);
|
|
263
258
|
if (this.offlineStorageEnabled && this.eventVault) {
|
|
264
259
|
this.eventVault.store(this.eventsQueuedForProcessing);
|
|
265
260
|
}
|
|
266
261
|
|
|
267
|
-
Logger.
|
|
268
|
-
|
|
262
|
+
if (this.mpInstance.Logger.isVerbose()) {
|
|
263
|
+
const eventToLog = obfuscateDevData(event, this.mpInstance._Store.SDKConfig.isDevelopmentMode);
|
|
264
|
+
this.mpInstance.Logger.verbose(`Queuing event: ${JSON.stringify(eventToLog)}`);
|
|
265
|
+
this.mpInstance.Logger.verbose(`Queued event count: ${this.eventsQueuedForProcessing.length}`);
|
|
266
|
+
}
|
|
269
267
|
|
|
270
268
|
if (this.shouldTriggerImmediateUpload(event.EventDataType)) {
|
|
271
269
|
this.prepareAndUpload(false, false);
|
|
@@ -409,7 +407,6 @@ export class BatchUploader {
|
|
|
409
407
|
this.batchesQueuedForProcessing = [];
|
|
410
408
|
|
|
411
409
|
const batchesThatDidNotUpload = await this.uploadBatches(
|
|
412
|
-
this.mpInstance.Logger,
|
|
413
410
|
batchesToUpload,
|
|
414
411
|
useBeacon
|
|
415
412
|
);
|
|
@@ -429,8 +426,8 @@ export class BatchUploader {
|
|
|
429
426
|
// therefore NOT overwrite Offline Storage when beacon returns, so that we can retry
|
|
430
427
|
// uploading saved batches at a later time. Batches should only be removed from
|
|
431
428
|
// Local Storage once we can confirm they are successfully uploaded.
|
|
429
|
+
|
|
432
430
|
this.batchVault.store(this.batchesQueuedForProcessing);
|
|
433
|
-
|
|
434
431
|
// Clear batch queue since everything should be in Offline Storage
|
|
435
432
|
this.batchesQueuedForProcessing = [];
|
|
436
433
|
}
|
|
@@ -440,10 +437,7 @@ export class BatchUploader {
|
|
|
440
437
|
}
|
|
441
438
|
}
|
|
442
439
|
|
|
443
|
-
// TODO: Refactor to use logger as a class method
|
|
444
|
-
// https://go.mparticle.com/work/SQDSDKS-5167
|
|
445
440
|
private async uploadBatches(
|
|
446
|
-
logger: SDKLoggerApi,
|
|
447
441
|
batches: Batch[],
|
|
448
442
|
useBeacon: boolean
|
|
449
443
|
): Promise<Batch[] | null> {
|
|
@@ -454,8 +448,11 @@ export class BatchUploader {
|
|
|
454
448
|
return null;
|
|
455
449
|
}
|
|
456
450
|
|
|
457
|
-
|
|
458
|
-
|
|
451
|
+
if (this.mpInstance.Logger.isVerbose()) {
|
|
452
|
+
const uploadsToLog = obfuscateDevData(uploads, this.mpInstance._Store.SDKConfig.isDevelopmentMode);
|
|
453
|
+
this.mpInstance.Logger.verbose(`Uploading batches: ${JSON.stringify(uploadsToLog)}`);
|
|
454
|
+
this.mpInstance.Logger.verbose(`Batch count: ${uploads.length}`);
|
|
455
|
+
}
|
|
459
456
|
|
|
460
457
|
for (let i = 0; i < uploads.length; i++) {
|
|
461
458
|
const fetchPayload: IFetchPayload = {
|
|
@@ -479,20 +476,20 @@ export class BatchUploader {
|
|
|
479
476
|
const response = await this.uploader.upload(fetchPayload);
|
|
480
477
|
|
|
481
478
|
if (response.status >= 200 && response.status < 300) {
|
|
482
|
-
|
|
479
|
+
this.mpInstance.Logger.verbose(
|
|
483
480
|
`Upload success for request ID: ${uploads[i].source_request_id}`
|
|
484
481
|
);
|
|
485
482
|
} else if (
|
|
486
483
|
response.status >= 500 ||
|
|
487
484
|
response.status === 429
|
|
488
485
|
) {
|
|
489
|
-
|
|
486
|
+
this.mpInstance.Logger.error(
|
|
490
487
|
`HTTP error status ${response.status} received`
|
|
491
488
|
);
|
|
492
489
|
// Server error, add back current batches and try again later
|
|
493
490
|
return uploads.slice(i, uploads.length);
|
|
494
491
|
} else if (response.status >= 401) {
|
|
495
|
-
|
|
492
|
+
this.mpInstance.Logger.error(
|
|
496
493
|
`HTTP error status ${response.status} while uploading - please verify your API key.`
|
|
497
494
|
);
|
|
498
495
|
//if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
|
|
@@ -509,7 +506,7 @@ export class BatchUploader {
|
|
|
509
506
|
);
|
|
510
507
|
}
|
|
511
508
|
} catch (e) {
|
|
512
|
-
|
|
509
|
+
this.mpInstance.Logger.error(
|
|
513
510
|
`Error sending event to mParticle servers. ${e}`
|
|
514
511
|
);
|
|
515
512
|
return uploads.slice(i, uploads.length);
|
package/src/cookieSyncManager.ts
CHANGED
|
@@ -73,6 +73,12 @@ export default function CookieSyncManager(
|
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// When noFunctional is true, persistence is not saved, so we cannot track cookie sync
|
|
77
|
+
// dates. Skip cookie sync to avoid running it on every page load.
|
|
78
|
+
if (mpInstance._CookieConsentManager?.getNoFunctional()) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
const persistence = mpInstance._Persistence.getPersistence();
|
|
77
83
|
|
|
78
84
|
if (isEmpty(persistence)) {
|
|
@@ -8,10 +8,12 @@ export default class ForegroundTimeTracker {
|
|
|
8
8
|
public startTime: number = 0;
|
|
9
9
|
public totalTime: number = 0;
|
|
10
10
|
|
|
11
|
-
constructor(timerKey: string) {
|
|
11
|
+
constructor(timerKey: string, private noFunctional: boolean = false) {
|
|
12
12
|
this.localStorageName = `mprtcl-tos-${timerKey}`;
|
|
13
13
|
this.timerVault = new LocalStorageVault<number>(this.localStorageName);
|
|
14
|
-
this.
|
|
14
|
+
if (!this.noFunctional) {
|
|
15
|
+
this.loadTimeFromStorage();
|
|
16
|
+
}
|
|
15
17
|
this.addHandlers();
|
|
16
18
|
if (document.hidden === false) {
|
|
17
19
|
this.startTracking();
|
|
@@ -63,7 +65,7 @@ export default class ForegroundTimeTracker {
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
public updateTimeInPersistence(): void {
|
|
66
|
-
if (this.isTrackerActive) {
|
|
68
|
+
if (this.isTrackerActive && !this.noFunctional) {
|
|
67
69
|
this.timerVault.store(Math.round(this.totalTime));
|
|
68
70
|
}
|
|
69
71
|
}
|
package/src/identity-utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Constants, { ONE_DAY_IN_SECONDS, MILLIS_IN_ONE_SEC } from './constants';
|
|
2
|
-
import { Dictionary, parseNumber, isObject, generateHash } from './utils';
|
|
2
|
+
import { Dictionary, parseNumber, isObject, generateHash, isEmpty } from './utils';
|
|
3
3
|
import { BaseVault } from './vault';
|
|
4
4
|
import Types from './types';
|
|
5
5
|
import {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
IIdentityResponse,
|
|
14
14
|
IMParticleUser,
|
|
15
15
|
} from './identity-user-interfaces';
|
|
16
|
+
import { IStore } from './store';
|
|
16
17
|
|
|
17
18
|
const { Identify, Modify, Login, Logout } = Constants.IdentityMethods;
|
|
18
19
|
export const CACHE_HEADER = 'x-mp-max-age' as const;
|
|
@@ -298,3 +299,25 @@ export const hasIdentityRequestChanged = (
|
|
|
298
299
|
JSON.stringify(currentUserIdentities) !== JSON.stringify(newIdentities)
|
|
299
300
|
);
|
|
300
301
|
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Checks if deviceId or other user identifiers (like email) were explicitly provided
|
|
305
|
+
* by the partner via config.deviceId or config.identifyRequest.userIdentities.
|
|
306
|
+
* When noFunctional is true, then cookies are blocked, so the partner must explicitly
|
|
307
|
+
* pass deviceId or other identifiers to prevent new users from being created on each page load.
|
|
308
|
+
*
|
|
309
|
+
* @param store - The SDK store (provides SDKConfig.deviceId and SDKConfig.identifyRequest.userIdentities)
|
|
310
|
+
* @returns true if deviceId or other identifiers were explicitly provided in config, false otherwise
|
|
311
|
+
*/
|
|
312
|
+
export const hasExplicitIdentifier = (store: IStore | undefined | null): boolean => {
|
|
313
|
+
const userIdentities = store?.SDKConfig?.identifyRequest?.userIdentities;
|
|
314
|
+
if (
|
|
315
|
+
userIdentities &&
|
|
316
|
+
isObject(userIdentities) &&
|
|
317
|
+
!isEmpty(userIdentities) &&
|
|
318
|
+
Object.values(userIdentities).some(Boolean)
|
|
319
|
+
) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return !!store?.SDKConfig?.deviceId;
|
|
323
|
+
};
|
package/src/identityApiClient.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
IFetchPayload,
|
|
7
7
|
} from './uploaders';
|
|
8
8
|
import { CACHE_HEADER } from './identity-utils';
|
|
9
|
-
import { parseNumber, valueof } from './utils';
|
|
9
|
+
import { obfuscateData, parseNumber, valueof } from './utils';
|
|
10
10
|
import {
|
|
11
11
|
IAliasCallback,
|
|
12
12
|
IAliasRequest,
|
|
@@ -277,9 +277,17 @@ export default function IdentityAPIClient(
|
|
|
277
277
|
message += ' - ' + errorMessage;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
} else {
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
} else if (Logger.isVerbose()) {
|
|
281
|
+
const responseText = identityResponse.responseText;
|
|
282
|
+
const { isDevelopmentMode } = mpInstance._Store.SDKConfig;
|
|
283
|
+
let responseToLog = responseText;
|
|
284
|
+
if (!isDevelopmentMode && responseText?.matched_identities) {
|
|
285
|
+
responseToLog = {
|
|
286
|
+
...responseText,
|
|
287
|
+
matched_identities: obfuscateData(responseText.matched_identities)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
message = 'Received Identity Response from server: ' + JSON.stringify(responseToLog);
|
|
283
291
|
}
|
|
284
292
|
|
|
285
293
|
break;
|
|
@@ -306,7 +314,9 @@ export default function IdentityAPIClient(
|
|
|
306
314
|
mpInstance._Store.identityCallInFlight = false;
|
|
307
315
|
mpInstance._Store.identityCallFailed = false;
|
|
308
316
|
|
|
309
|
-
|
|
317
|
+
if (message) {
|
|
318
|
+
Logger.verbose(message);
|
|
319
|
+
}
|
|
310
320
|
if (mpInstance._RoktManager?.isInitialized) {
|
|
311
321
|
const requestCount = mpInstance._Store.identifyRequestCount;
|
|
312
322
|
mpInstance.captureTiming(`${requestCount}-identityRequestEnd`);
|
package/src/logger.ts
CHANGED
package/src/mp-instance.ts
CHANGED
|
@@ -37,8 +37,8 @@ import KitBlocker from './kitBlocking';
|
|
|
37
37
|
import ConfigAPIClient, { IKitConfigs } from './configAPIClient';
|
|
38
38
|
import IdentityAPIClient from './identityApiClient';
|
|
39
39
|
import { isFunction, parseConfig, valueof, generateDeprecationMessage } from './utils';
|
|
40
|
-
import { LocalStorageVault } from './vault';
|
|
41
|
-
import { removeExpiredIdentityCacheDates } from './identity-utils';
|
|
40
|
+
import { DisabledVault, LocalStorageVault } from './vault';
|
|
41
|
+
import { removeExpiredIdentityCacheDates, hasExplicitIdentifier } from './identity-utils';
|
|
42
42
|
import IntegrationCapture from './integrationCapture';
|
|
43
43
|
import { IPreInit, processReadyQueue } from './pre-init-utils';
|
|
44
44
|
import { BaseEvent, MParticleWebSDK, SDKHelpersApi } from './sdkRuntimeModels';
|
|
@@ -97,6 +97,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
|
|
|
97
97
|
getLauncherInstanceGuid: () => string;
|
|
98
98
|
captureTiming(metricName: string);
|
|
99
99
|
processQueueOnIdentityFailure?: () => void;
|
|
100
|
+
processQueueOnNoFunctional?: () => void;
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
const { Messages, HTTPCodes, FeatureFlags, CaptureIntegrationSpecificIdsV2Modes } = Constants;
|
|
@@ -150,13 +151,30 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
|
|
|
150
151
|
if (self._Store?.isInitialized) {
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
|
-
|
|
154
|
+
|
|
154
155
|
if (self._Store?.identityCallFailed && self._RoktManager?.isReady()) {
|
|
155
156
|
self._RoktManager.processMessageQueue();
|
|
156
157
|
self._preInit.readyQueue = processReadyQueue(self._preInit.readyQueue);
|
|
157
158
|
}
|
|
158
159
|
};
|
|
159
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Drains the ready queue for noFunctional sessions with no explicit identity.
|
|
163
|
+
*/
|
|
164
|
+
this.processQueueOnNoFunctional = function() {
|
|
165
|
+
if (self._Store?.isInitialized) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const noFunctionalWithoutId =
|
|
170
|
+
self._CookieConsentManager?.getNoFunctional() &&
|
|
171
|
+
!hasExplicitIdentifier(self._Store);
|
|
172
|
+
|
|
173
|
+
if (noFunctionalWithoutId) {
|
|
174
|
+
self._preInit.readyQueue = processReadyQueue(self._preInit.readyQueue);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
160
178
|
// required for forwarders once they reference the mparticle instance
|
|
161
179
|
this.IdentityType = IdentityType;
|
|
162
180
|
this.EventType = EventType;
|
|
@@ -280,11 +298,16 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
|
|
|
280
298
|
* @param {Function} f Callback to execute
|
|
281
299
|
*/
|
|
282
300
|
this.ready = function(f) {
|
|
301
|
+
const noFunctionalWithoutId =
|
|
302
|
+
self._CookieConsentManager?.getNoFunctional() &&
|
|
303
|
+
!hasExplicitIdentifier(self._Store);
|
|
304
|
+
|
|
283
305
|
const shouldExecute = isFunction(f) && (
|
|
284
|
-
self._Store?.isInitialized ||
|
|
285
|
-
(self._Store?.identityCallFailed && self._RoktManager.isReady())
|
|
306
|
+
self._Store?.isInitialized ||
|
|
307
|
+
(self._Store?.identityCallFailed && self._RoktManager.isReady()) ||
|
|
308
|
+
noFunctionalWithoutId
|
|
286
309
|
);
|
|
287
|
-
|
|
310
|
+
|
|
288
311
|
if (shouldExecute) {
|
|
289
312
|
f();
|
|
290
313
|
} else {
|
|
@@ -1135,11 +1158,16 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
|
|
|
1135
1158
|
* @param {String or Number} value value for session attribute
|
|
1136
1159
|
*/
|
|
1137
1160
|
this.setSessionAttribute = function(key, value) {
|
|
1138
|
-
const
|
|
1139
|
-
self.
|
|
1140
|
-
|
|
1161
|
+
const skipQueue =
|
|
1162
|
+
self._CookieConsentManager?.getNoFunctional() &&
|
|
1163
|
+
!hasExplicitIdentifier(self._Store);
|
|
1141
1164
|
|
|
1142
|
-
if (
|
|
1165
|
+
if (!skipQueue) {
|
|
1166
|
+
const queued = queueIfNotInitialized(function() {
|
|
1167
|
+
self.setSessionAttribute(key, value);
|
|
1168
|
+
}, self);
|
|
1169
|
+
if (queued) return;
|
|
1170
|
+
}
|
|
1143
1171
|
|
|
1144
1172
|
// Logs to cookie
|
|
1145
1173
|
// And logs to in-memory object
|
|
@@ -1185,7 +1213,7 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
|
|
|
1185
1213
|
* @method setOptOut
|
|
1186
1214
|
* @param {Boolean} isOptingOut boolean to opt out or not. When set to true, opt out of logging.
|
|
1187
1215
|
*/
|
|
1188
|
-
this.setOptOut = function(isOptingOut) {
|
|
1216
|
+
this.setOptOut = function(isOptingOut: boolean) {
|
|
1189
1217
|
const queued = queueIfNotInitialized(function() {
|
|
1190
1218
|
self.setOptOut(isOptingOut);
|
|
1191
1219
|
}, self);
|
|
@@ -1512,6 +1540,10 @@ function completeSDKInitialization(apiKey, config, mpInstance) {
|
|
|
1512
1540
|
);
|
|
1513
1541
|
}
|
|
1514
1542
|
|
|
1543
|
+
// For noFunctional sessions with no identity, drain any pre-init ready callbacks
|
|
1544
|
+
// that were queued before _CookieConsentManager was available to evaluate the condition.
|
|
1545
|
+
mpInstance.processQueueOnNoFunctional?.();
|
|
1546
|
+
|
|
1515
1547
|
// https://go.mparticle.com/work/SQDSDKS-6040
|
|
1516
1548
|
if (mpInstance._Store.isFirstRun) {
|
|
1517
1549
|
mpInstance._Store.isFirstRun = false;
|
|
@@ -1584,9 +1616,13 @@ function createKitBlocker(config, mpInstance) {
|
|
|
1584
1616
|
}
|
|
1585
1617
|
|
|
1586
1618
|
function createIdentityCache(mpInstance) {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1619
|
+
// Identity expects mpInstance._Identity.idCache to always exist. DisabledVault
|
|
1620
|
+
// ensures no identity response data is written to localStorage when noFunctional is true
|
|
1621
|
+
if (mpInstance._CookieConsentManager?.getNoFunctional()) {
|
|
1622
|
+
return new DisabledVault(`${mpInstance._Store.storageName}-id-cache`);
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
return new LocalStorageVault(`${mpInstance._Store.storageName}-id-cache`);
|
|
1590
1626
|
}
|
|
1591
1627
|
|
|
1592
1628
|
function runPreConfigFetchInitialization(mpInstance, apiKey, config) {
|
|
@@ -1664,15 +1700,21 @@ function processIdentityCallback(
|
|
|
1664
1700
|
}
|
|
1665
1701
|
|
|
1666
1702
|
function queueIfNotInitialized(func, self) {
|
|
1667
|
-
//
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1703
|
+
// When noFunctional is true with no explicit identifier, the SDK will never
|
|
1704
|
+
// receive an MPID. Let these calls through so events can still reach forwarders immediately.
|
|
1705
|
+
// sendEventToServer handles queuing for the MP server upload path separately.
|
|
1706
|
+
const noFunctionalWithoutId =
|
|
1707
|
+
self._CookieConsentManager?.getNoFunctional() &&
|
|
1708
|
+
!hasExplicitIdentifier(self._Store);
|
|
1709
|
+
|
|
1710
|
+
if (self._Store?.isInitialized || noFunctionalWithoutId) {
|
|
1711
|
+
return false;
|
|
1675
1712
|
}
|
|
1676
|
-
|
|
1713
|
+
self._preInit.readyQueue.push(function() {
|
|
1714
|
+
if (self._Store?.isInitialized) {
|
|
1715
|
+
func();
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
return true;
|
|
1677
1719
|
}
|
|
1678
1720
|
|
package/src/persistence.js
CHANGED
|
@@ -230,6 +230,11 @@ export default function _Persistence(mpInstance) {
|
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// Block mprtcl-v4 localStorage when noFunctional is true
|
|
234
|
+
if (mpInstance._CookieConsentManager?.getNoFunctional()) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
233
238
|
var key = mpInstance._Store.storageName,
|
|
234
239
|
localStorageData = self.getLocalStorage() || {},
|
|
235
240
|
currentUser = mpInstance.Identity.getCurrentUser(),
|
|
@@ -398,6 +403,11 @@ export default function _Persistence(mpInstance) {
|
|
|
398
403
|
// https://go.mparticle.com/work/SQDSDKS-5022
|
|
399
404
|
// https://go.mparticle.com/work/SQDSDKS-6021
|
|
400
405
|
this.setCookie = function() {
|
|
406
|
+
// Block mprtcl-v4 cookies when noFunctional is true
|
|
407
|
+
if (mpInstance._CookieConsentManager?.getNoFunctional()) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
401
411
|
var mpid,
|
|
402
412
|
currentUser = mpInstance.Identity.getCurrentUser();
|
|
403
413
|
if (currentUser) {
|
|
@@ -803,6 +813,11 @@ export default function _Persistence(mpInstance) {
|
|
|
803
813
|
|
|
804
814
|
// https://go.mparticle.com/work/SQDSDKS-6021
|
|
805
815
|
this.savePersistence = function(persistence) {
|
|
816
|
+
// Block mprtcl-v4 persistence when noFunctional is true
|
|
817
|
+
if (mpInstance._CookieConsentManager?.getNoFunctional()) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
|
|
806
821
|
var encodedPersistence = self.encodePersistence(
|
|
807
822
|
JSON.stringify(persistence)
|
|
808
823
|
),
|
package/src/roktManager.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isFunction,
|
|
10
10
|
AttributeValue,
|
|
11
11
|
isEmpty,
|
|
12
|
+
obfuscateDevData,
|
|
12
13
|
} from "./utils";
|
|
13
14
|
import { SDKIdentityApi } from "./identity.interfaces";
|
|
14
15
|
import { SDKLoggerApi } from "./sdkRuntimeModels";
|
|
@@ -234,7 +235,10 @@ export default class RoktManager {
|
|
|
234
235
|
const { attributes } = options;
|
|
235
236
|
const sandboxValue = attributes?.sandbox || null;
|
|
236
237
|
const mappedAttributes = this.mapPlacementAttributes(attributes, this.placementAttributesMapping);
|
|
237
|
-
this.logger?.
|
|
238
|
+
if (this.logger?.isVerbose()) {
|
|
239
|
+
const attributesToLog = obfuscateDevData(attributes, this.store?.SDKConfig?.isDevelopmentMode);
|
|
240
|
+
this.logger.verbose(`mParticle.Rokt selectPlacements called with attributes:\n${JSON.stringify(attributesToLog, null, 2)}`);
|
|
241
|
+
}
|
|
238
242
|
|
|
239
243
|
this.currentUser = this.identityService.getCurrentUser();
|
|
240
244
|
const currentUserIdentities = this.currentUser?.getUserIdentities()?.userIdentities || {};
|
|
@@ -273,7 +277,7 @@ export default class RoktManager {
|
|
|
273
277
|
newIdentities[this.mappedEmailShaIdentityType] = newHashedEmail;
|
|
274
278
|
this.logger.warning(`emailsha256 mismatch detected. Current mParticle hashedEmail differs from hashedEmail passed to selectPlacements call. Proceeding to call identify with hashedEmail from selectPlacements call. Please verify your implementation.`);
|
|
275
279
|
}
|
|
276
|
-
|
|
280
|
+
|
|
277
281
|
if (!isEmpty(newIdentities)) {
|
|
278
282
|
// Call identify with the new user identities
|
|
279
283
|
try {
|
|
@@ -288,7 +292,8 @@ export default class RoktManager {
|
|
|
288
292
|
});
|
|
289
293
|
});
|
|
290
294
|
} catch (error) {
|
|
291
|
-
|
|
295
|
+
const errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
296
|
+
this.logger.error('Failed to identify user with updated identities: ' + errorMessage);
|
|
292
297
|
}
|
|
293
298
|
}
|
|
294
299
|
|
|
@@ -504,7 +509,10 @@ export default class RoktManager {
|
|
|
504
509
|
return;
|
|
505
510
|
}
|
|
506
511
|
|
|
507
|
-
this.logger?.
|
|
512
|
+
if (this.logger?.isVerbose()) {
|
|
513
|
+
const payloadToLog = obfuscateDevData(message.payload, this.store?.SDKConfig?.isDevelopmentMode);
|
|
514
|
+
this.logger.verbose(`RoktManager: Processing queued message: ${message.methodName} with payload: ${JSON.stringify(payloadToLog)}`);
|
|
515
|
+
}
|
|
508
516
|
|
|
509
517
|
// Capture resolve/reject functions before async processing
|
|
510
518
|
const resolve = message.resolve;
|
package/src/sdkRuntimeModels.ts
CHANGED
|
@@ -94,6 +94,7 @@ export interface SDKEvent {
|
|
|
94
94
|
ExpandedEventCount: number;
|
|
95
95
|
ActiveTimeOnSite: number;
|
|
96
96
|
IsBackgroundAST?: boolean;
|
|
97
|
+
AlreadySentToForwarders?: boolean;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
export interface SDKGeoLocation {
|
|
@@ -393,6 +394,7 @@ export interface SDKLoggerApi {
|
|
|
393
394
|
error(msg: string, code?: ErrorCodes): void;
|
|
394
395
|
verbose(msg: string): void;
|
|
395
396
|
warning(msg: string): void;
|
|
397
|
+
isVerbose(): boolean;
|
|
396
398
|
setLogLevel(logLevel: LogLevelType): void;
|
|
397
399
|
}
|
|
398
400
|
|
package/src/sessionManager.ts
CHANGED
|
@@ -5,7 +5,7 @@ import Types from './types';
|
|
|
5
5
|
import { generateDeprecationMessage } from './utils';
|
|
6
6
|
import { IMParticleUser } from './identity-user-interfaces';
|
|
7
7
|
import { IMParticleWebSDKInstance } from './mp-instance';
|
|
8
|
-
import { hasIdentityRequestChanged } from './identity-utils';
|
|
8
|
+
import { hasIdentityRequestChanged, hasExplicitIdentifier } from './identity-utils';
|
|
9
9
|
|
|
10
10
|
const { Messages } = Constants;
|
|
11
11
|
|
|
@@ -45,7 +45,12 @@ export default function SessionManager(
|
|
|
45
45
|
const currentUser = mpInstance.Identity.getCurrentUser();
|
|
46
46
|
const sdkIdentityRequest = SDKConfig.identifyRequest;
|
|
47
47
|
|
|
48
|
+
const shouldSuppressIdentify =
|
|
49
|
+
mpInstance._CookieConsentManager?.getNoFunctional() &&
|
|
50
|
+
!hasExplicitIdentifier(mpInstance._Store);
|
|
51
|
+
|
|
48
52
|
if (
|
|
53
|
+
!shouldSuppressIdentify &&
|
|
49
54
|
hasIdentityRequestChanged(currentUser, sdkIdentityRequest)
|
|
50
55
|
) {
|
|
51
56
|
mpInstance.Identity.identify(
|
|
@@ -102,7 +107,11 @@ export default function SessionManager(
|
|
|
102
107
|
|
|
103
108
|
self.setSessionTimer();
|
|
104
109
|
|
|
105
|
-
|
|
110
|
+
const shouldSuppressIdentify =
|
|
111
|
+
mpInstance._CookieConsentManager?.getNoFunctional() &&
|
|
112
|
+
!hasExplicitIdentifier(mpInstance._Store);
|
|
113
|
+
|
|
114
|
+
if (!mpInstance._Store.identifyCalled && !shouldSuppressIdentify) {
|
|
106
115
|
mpInstance.Identity.identify(
|
|
107
116
|
mpInstance._Store.SDKConfig.identifyRequest,
|
|
108
117
|
mpInstance._Store.SDKConfig.identityCallback
|
package/src/store.ts
CHANGED
|
@@ -732,7 +732,8 @@ export default function Store(
|
|
|
732
732
|
|
|
733
733
|
if (workspaceToken) {
|
|
734
734
|
this.SDKConfig.workspaceToken = workspaceToken;
|
|
735
|
-
|
|
735
|
+
const noFunctional = config?.launcherOptions?.noFunctional === true;
|
|
736
|
+
mpInstance._timeOnSiteTimer = new ForegroundTimer(workspaceToken, noFunctional);
|
|
736
737
|
} else {
|
|
737
738
|
mpInstance.Logger.warning(
|
|
738
739
|
'You should have a workspaceToken on your config object for security purposes.'
|