@mparticle/web-sdk 3.22.0 → 3.24.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.
Files changed (37) hide show
  1. package/dist/mparticle.common.js +1 -1669
  2. package/dist/mparticle.esm.js +1 -1669
  3. package/dist/mparticle.js +4827 -4184
  4. package/dist/types/src/buttonTracking/buttonClickEvent.d.ts +4 -0
  5. package/dist/types/src/buttonTracking/buttonDescription.d.ts +2 -2
  6. package/dist/types/src/buttonTracking/buttonTracker.d.ts +2 -0
  7. package/dist/types/src/buttonTracking/checkoutIntent.d.ts +2 -2
  8. package/dist/types/src/buttonTracking/controls.d.ts +6 -3
  9. package/dist/types/src/buttonTracking/labelMasking.d.ts +1 -0
  10. package/dist/types/src/configAPIClient.d.ts +1 -0
  11. package/dist/types/src/forwarder-utils.d.ts +3 -0
  12. package/dist/types/src/pageUrl.d.ts +10 -0
  13. package/dist/types/src/pageViewTracker.d.ts +21 -3
  14. package/dist/types/src/roktManager.d.ts +8 -2
  15. package/dist/types/src/sdkRuntimeModels.d.ts +3 -0
  16. package/dist/types/src/store.d.ts +2 -0
  17. package/package.json +21 -14
  18. package/src/batchUploader.ts +3 -2
  19. package/src/buttonTracking/buttonClickEvent.ts +76 -1
  20. package/src/buttonTracking/buttonDescription.ts +236 -46
  21. package/src/buttonTracking/buttonTracker.ts +122 -7
  22. package/src/buttonTracking/checkoutIntent.ts +51 -9
  23. package/src/buttonTracking/controls.ts +160 -50
  24. package/src/buttonTracking/labelMasking.ts +4 -1
  25. package/src/configAPIClient.ts +1 -0
  26. package/src/events.ts +9 -3
  27. package/src/forwarder-utils.ts +38 -0
  28. package/src/forwarders.ts +2 -28
  29. package/src/mp-instance.ts +23 -10
  30. package/src/mparticle-instance-manager.ts +3 -0
  31. package/src/pageUrl.ts +191 -0
  32. package/src/pageViewTracker.ts +206 -78
  33. package/src/roktManager.ts +53 -20
  34. package/src/sdkRuntimeModels.ts +3 -0
  35. package/src/serverModel.ts +3 -3
  36. package/src/store.ts +14 -0
  37. package/src/utils.ts +1 -1
@@ -15,7 +15,7 @@ import {
15
15
  import { SDKIdentityApi } from "./identity.interfaces";
16
16
  import { SDKLoggerApi } from "./sdkRuntimeModels";
17
17
  import { IStore, LocalSessionAttributes } from "./store";
18
- import { UserIdentities } from "@mparticle/web-sdk";
18
+ import { MPID, UserIdentities } from "@mparticle/web-sdk";
19
19
  import { IdentityType, PerformanceMarkType } from "./types";
20
20
  import { ErrorCodes, IErrorReportingService, ILoggingService, WSDKErrorSeverity } from "./reporting/types";
21
21
  import { IRoktLauncherOptions, normalizeRoktLauncherOptions } from "./roktLauncherOptions";
@@ -26,7 +26,8 @@ import { subscribeToRouteChange } from "./routeChangeMonitor";
26
26
  import type { CheckoutIntentKind, CheckoutIntentSource } from "./buttonTracking/checkoutIntent";
27
27
 
28
28
  const PASSBACK_CONVERSION_TRACKING_ID = 'passbackconversiontrackingid';
29
- const CHECKOUT_INTENT_BUFFER_LIMIT = 4;
29
+ // One signal per kind (commit, wallet, checkout_step, frame and cart), so a new kind never evicts another.
30
+ const CHECKOUT_INTENT_BUFFER_LIMIT = 5;
30
31
  const CHECKOUT_INTENT_MAX_AGE_MS = 30000;
31
32
 
32
33
  type AttributeTargetUser = Pick<IMParticleUser, 'getUserIdentities' | 'setUserAttributes'>;
@@ -136,6 +137,7 @@ export default class RoktManager {
136
137
  public kit: IRoktKit = null;
137
138
  public filters: RoktKitFilterSettings = {};
138
139
  private currentUser: IMParticleUser | null = null;
140
+ private createFilteredUser?: (mpid: MPID) => IMParticleUser;
139
141
  private messageQueue: Map<string, IRoktMessage> = new Map();
140
142
  private sandbox: boolean | null = null;
141
143
  private placementAttributesMapping: Dictionary<string>[] = [];
@@ -155,6 +157,8 @@ export default class RoktManager {
155
157
  private stopRouteChangeWatch: (() => void) | null = null;
156
158
  // Checkout intent raised before the kit attached: latest per kind.
157
159
  private pendingCheckoutIntents: Array<{ signal: ICheckoutIntentSignal; pathname: string }> = [];
160
+ // Off until SDK init finds the Rokt connection receives auto button clicks.
161
+ private autoButtonClicksForwarded = false;
158
162
 
159
163
  // Keys this manager's route subscription, so a re-executed bundle's manager replaces
160
164
  // it while a second named instance keeps its own.
@@ -178,6 +182,7 @@ export default class RoktManager {
178
182
  * @param {Function} captureTiming - Function to capture performance timing marks
179
183
  * @param {IErrorReportingService} errorReporter - Dispatcher for error/warning reporting
180
184
  * @param {ILoggingService} loggingService - Dispatcher for informational logging
185
+ * @param {Function} createFilteredUser - Builds a user as the Rokt connection's filters and the data plan allow
181
186
  *
182
187
  * @throws Logs error to console if placementAttributesMapping parsing fails
183
188
  */
@@ -192,6 +197,7 @@ export default class RoktManager {
192
197
  errorReporter?: IErrorReportingService,
193
198
  loggingService?: ILoggingService,
194
199
  integrationCapture?: IntegrationCapture,
200
+ createFilteredUser?: (mpid: MPID) => IMParticleUser,
195
201
  ): void {
196
202
  const { userAttributeFilters, settings } = roktConfig || {};
197
203
  const { placementAttributesMapping, hashedEmailUserIdentityType } = settings || {};
@@ -204,6 +210,7 @@ export default class RoktManager {
204
210
  this.loggingService = loggingService;
205
211
  this.captureTiming = captureTiming;
206
212
  this.integrationCapture = integrationCapture;
213
+ this.createFilteredUser = createFilteredUser;
207
214
 
208
215
  this.captureTiming?.(PerformanceMarkType.JointSdkRoktKitInit);
209
216
 
@@ -304,11 +311,21 @@ export default class RoktManager {
304
311
  }
305
312
  }
306
313
 
314
+ public setAutoButtonClicksForwarded(forwarded: boolean): void {
315
+ this.autoButtonClicksForwarded = forwarded;
316
+ }
317
+
307
318
  // Checkout intent is off unless the flag is on, and under opt-out, any Rokt
308
- // privacy launcher option, or Global Privacy Control.
319
+ // privacy launcher option, Global Privacy Control, or a Rokt connection that
320
+ // does not receive auto button clicks.
309
321
  public isCheckoutIntentSuppressed(): boolean {
310
322
  const flags = this.store?.SDKConfig?.flags;
311
- if (!flags || flags[Constants.FeatureFlags.AutoButtonTracking] !== true || !this.store.isEnabled) {
323
+ if (
324
+ !flags ||
325
+ flags[Constants.FeatureFlags.AutoButtonTracking] !== true ||
326
+ !this.store.isEnabled ||
327
+ !this.autoButtonClicksForwarded
328
+ ) {
312
329
  return true;
313
330
  }
314
331
  const options = this.launcherOptions || {};
@@ -377,7 +394,9 @@ export default class RoktManager {
377
394
  .forEach(({ signal }) => this.notifyCheckoutIntent(signal));
378
395
  }
379
396
 
380
- private isAutoLogPageViewEnabled(): boolean {
397
+ // The Rokt kit reads this to tell core's page views, whose title and utm
398
+ // attributes a filter may have removed, from ones logged by hand.
399
+ public isAutoLogPageViewEnabled(): boolean {
381
400
  return (
382
401
  this.store?.SDKConfig?.flags?.[
383
402
  Constants.FeatureFlags.AutoLogPageView
@@ -504,11 +523,13 @@ export default class RoktManager {
504
523
  }
505
524
  }
506
525
 
507
- // Optimistic merge: apply the pending identity changes locally so
508
- // enrichedAttributes are correct without waiting for the identify round-trip.
509
- // The next call to selectPlacements re-fetches currentUser (see line above),
510
- // so stale identity state does not persist beyond this invocation.
511
- const finalUserIdentities = { ...currentUserIdentities, ...newIdentities };
526
+ // The identify above needs every identity, so currentUserIdentities stays unfiltered.
527
+ // Without createFilteredUser the connection's filters cannot be applied, so no identity is added.
528
+ const roktUser =
529
+ this.currentUser && this.createFilteredUser
530
+ ? this.createFilteredUser(this.currentUser.getMPID())
531
+ : null;
532
+ const roktUserIdentities = roktUser?.getUserIdentities()?.userIdentities || {};
512
533
 
513
534
  const timeOnSite = this.store?.getTimeOnSite?.();
514
535
  const totalTimeOnSite = this.store?.getTotalTimeOnSite?.();
@@ -521,19 +542,25 @@ export default class RoktManager {
521
542
  };
522
543
 
523
544
  // Propagate email from current user identities if not already in attributes
524
- if (finalUserIdentities.email && !enrichedAttributes.email) {
525
- enrichedAttributes.email = finalUserIdentities.email;
545
+ if (!enrichedAttributes.email) {
546
+ if (roktUserIdentities.email) {
547
+ enrichedAttributes.email = roktUserIdentities.email;
548
+ } else if (currentUserIdentities.email) {
549
+ this.logFilteredIdentity('email');
550
+ }
526
551
  }
527
552
 
528
553
  // Propagate emailsha256 from current user identities if not already in attributes
529
- if (isValidHashedEmailIdentityType) {
530
- const hashedEmail = finalUserIdentities[this.mappedEmailShaIdentityType];
531
- if (
532
- hashedEmail &&
533
- !enrichedAttributes.emailsha256 &&
534
- !enrichedAttributes[this.mappedEmailShaIdentityType]
535
- ) {
554
+ if (
555
+ isValidHashedEmailIdentityType &&
556
+ !enrichedAttributes.emailsha256 &&
557
+ !enrichedAttributes[this.mappedEmailShaIdentityType]
558
+ ) {
559
+ const hashedEmail = roktUserIdentities[this.mappedEmailShaIdentityType];
560
+ if (hashedEmail) {
536
561
  enrichedAttributes.emailsha256 = hashedEmail;
562
+ } else if (currentUserIdentities[this.mappedEmailShaIdentityType]) {
563
+ this.logFilteredIdentity(this.mappedEmailShaIdentityType);
537
564
  }
538
565
  }
539
566
 
@@ -546,7 +573,7 @@ export default class RoktManager {
546
573
  }
547
574
  }
548
575
 
549
- this.filters.filteredUser = this.currentUser || this.filters.filteredUser || null;
576
+ this.filters.filteredUser = roktUser || this.filters.filteredUser || null;
550
577
 
551
578
  const enrichedOptions = {
552
579
  ...options,
@@ -559,6 +586,12 @@ export default class RoktManager {
559
586
  }
560
587
  }
561
588
 
589
+ private logFilteredIdentity(identityType: string): void {
590
+ this.logger?.verbose(
591
+ `mParticle.Rokt selectPlacements did not add the ${identityType} user identity to the attributes because the Rokt connection does not allow it`
592
+ );
593
+ }
594
+
562
595
  /**
563
596
  * Hashes attributes and returns both original and hashed versions
564
597
  * with Rokt-compatible key names (like emailsha256, mobilesha256)
@@ -53,6 +53,7 @@ import {
53
53
  import Constants from './constants';
54
54
  import RoktManager, { IRoktLauncherOptions } from './roktManager';
55
55
  import { IConsoleLogger } from './logger';
56
+ import { UrlMode } from './pageUrl';
56
57
  import { ErrorCodes, IErrorReportingService, ILoggingService } from './reporting/types';
57
58
 
58
59
  // TODO: Resolve this with version in @mparticle/web-sdk
@@ -282,6 +283,7 @@ export interface IMParticleInstanceManager extends MParticleWebSDK {
282
283
  _instances: Dictionary<IMParticleWebSDKInstance>;
283
284
  _isTestEnv?: boolean;
284
285
  _forceNoLocalStorage?: boolean;
286
+ _cleanUrl(href: string, mode: UrlMode): string;
285
287
 
286
288
  // Public Properties
287
289
  config: SDKInitConfig;
@@ -326,6 +328,7 @@ export interface SDKInitConfig
326
328
  dataPlanOptions?: KitBlockerOptions;
327
329
  flags?: Dictionary;
328
330
  pixelConfigs?: IPixelConfiguration[];
331
+ autoLogPageViewQueryParams?: string;
329
332
 
330
333
  aliasMaxWindow?: number;
331
334
  deviceId?: string;
@@ -17,8 +17,8 @@ import {
17
17
  Dictionary,
18
18
  isValidCustomFlagProperty,
19
19
  extend,
20
- getHref,
21
20
  } from './utils';
21
+ import { cleanPageHref } from './pageUrl';
22
22
  import { IntegrationAttributes, ServerSettings } from './store';
23
23
  import { MPID } from '@mparticle/web-sdk';
24
24
  import {
@@ -310,7 +310,7 @@ export default function ServerModel(
310
310
  ),
311
311
  ActiveTimeOnSite: mpInstance._timeOnSiteTimer?.getTimeInForeground(),
312
312
  TotalTimeOnSite: mpInstance._Store.getTotalTimeOnSite?.(),
313
- PageUrl: getHref() || null,
313
+ PageUrl: cleanPageHref() || null,
314
314
  SourceMessageId:
315
315
  event.sourceMessageId ||
316
316
  mpInstance._Helpers.generateUniqueId(),
@@ -352,7 +352,7 @@ export default function ServerModel(
352
352
 
353
353
  if (eventObject.EventDataType === MessageType.AppStateTransition) {
354
354
  eventObject.IsFirstRun = mpInstance._Store.isFirstRun;
355
- eventObject.LaunchReferral = window.location.href || null;
355
+ eventObject.LaunchReferral = cleanPageHref() || null;
356
356
  }
357
357
 
358
358
  // FIXME: Remove duplicate occurence
package/src/store.ts CHANGED
@@ -9,6 +9,10 @@ import {
9
9
  } from '@mparticle/web-sdk';
10
10
  import { IKitConfigs } from './configAPIClient';
11
11
  import Constants from './constants';
12
+ import {
13
+ IQueryParamAllowlist,
14
+ parseQueryParamAllowlist,
15
+ } from './pageViewTracker';
12
16
  import {
13
17
  DataPlanResult,
14
18
  KitBlockerOptions,
@@ -65,6 +69,7 @@ export interface SDKConfig {
65
69
  appVersion?: string;
66
70
  package?: string;
67
71
  flags?: IFeatureFlags;
72
+ autoLogPageViewQueryParams?: IQueryParamAllowlist;
68
73
  kitConfigs: IKitConfigs[];
69
74
  kits: Dictionary<UnregisteredKit>;
70
75
  logLevel?: LogLevelType;
@@ -332,6 +337,9 @@ export default function Store(
332
337
  }
333
338
 
334
339
  this.SDKConfig.flags = processFlags(config);
340
+ this.SDKConfig.autoLogPageViewQueryParams = parseQueryParamAllowlist(
341
+ config.autoLogPageViewQueryParams
342
+ );
335
343
 
336
344
  if (config.deviceId) {
337
345
  this.deviceId = config.deviceId;
@@ -728,6 +736,12 @@ export default function Store(
728
736
  this.SDKConfig.flags = processFlags(config);
729
737
  }
730
738
 
739
+ // Stored whole: the tracker reports the rejections, and cannot recover them by
740
+ // re-parsing a list that has already had them removed.
741
+ this.SDKConfig.autoLogPageViewQueryParams = parseQueryParamAllowlist(
742
+ config.autoLogPageViewQueryParams
743
+ );
744
+
731
745
  const baseUrls: Dictionary<string> = processBaseUrls(
732
746
  config,
733
747
  this.SDKConfig.flags,
package/src/utils.ts CHANGED
@@ -385,7 +385,7 @@ interface URLSearchParamsFallback {
385
385
 
386
386
  const queryStringParserFallback = (url: string): URLSearchParamsFallback => {
387
387
  const params: Dictionary<string> = {};
388
- const queryString = url.split('?')[1] || '';
388
+ const queryString = url.split('#')[0].split('?')[1] || '';
389
389
  const pairs = queryString.split('&');
390
390
 
391
391
  pairs.forEach(pair => {