@mparticle/web-sdk 3.23.0 → 3.25.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 (39) hide show
  1. package/dist/mparticle.common.js +1 -1688
  2. package/dist/mparticle.esm.js +1 -1688
  3. package/dist/mparticle.js +1015 -253
  4. package/dist/types/src/buttonTracking/buttonClickEvent.d.ts +7 -0
  5. package/dist/types/src/buttonTracking/buttonDescription.d.ts +10 -3
  6. package/dist/types/src/buttonTracking/buttonTracker.d.ts +8 -0
  7. package/dist/types/src/buttonTracking/checkoutIntent.d.ts +8 -3
  8. package/dist/types/src/buttonTracking/controls.d.ts +8 -4
  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/constants.d.ts +1 -0
  12. package/dist/types/src/cookieConsentManager.d.ts +17 -3
  13. package/dist/types/src/forwarder-utils.d.ts +3 -0
  14. package/dist/types/src/pageViewTracker.d.ts +19 -3
  15. package/dist/types/src/roktLauncherOptions.d.ts +1 -0
  16. package/dist/types/src/roktManager.d.ts +13 -2
  17. package/dist/types/src/sdkRuntimeModels.d.ts +1 -0
  18. package/dist/types/src/store.d.ts +3 -0
  19. package/package.json +21 -14
  20. package/src/buttonTracking/buttonClickEvent.ts +126 -8
  21. package/src/buttonTracking/buttonDescription.ts +336 -53
  22. package/src/buttonTracking/buttonTracker.ts +231 -19
  23. package/src/buttonTracking/checkoutIntent.ts +117 -23
  24. package/src/buttonTracking/controls.ts +169 -55
  25. package/src/buttonTracking/labelMasking.ts +4 -1
  26. package/src/configAPIClient.ts +1 -0
  27. package/src/constants.ts +1 -0
  28. package/src/cookieConsentManager.ts +23 -3
  29. package/src/cookieSyncManager.ts +11 -3
  30. package/src/events.ts +9 -3
  31. package/src/forwarder-utils.ts +38 -0
  32. package/src/forwarders.ts +2 -28
  33. package/src/mp-instance.ts +41 -7
  34. package/src/pageUrl.ts +43 -2
  35. package/src/pageViewTracker.ts +193 -111
  36. package/src/roktLauncherOptions.ts +1 -0
  37. package/src/roktManager.ts +66 -11
  38. package/src/sdkRuntimeModels.ts +1 -0
  39. package/src/store.ts +17 -0
@@ -1,5 +1,12 @@
1
+ import { IKitFilterSettings } from '../configAPIClient';
2
+ import type KitBlocker from '../kitBlocking';
1
3
  import { BaseEvent } from '../sdkRuntimeModels';
2
4
  import { IButtonClick } from './buttonDescription';
3
5
  export declare const AUTO_BUTTON_ATTRIBUTE = "is_auto_button_click";
4
6
  export declare const BUTTON_CLICK_EVENT_NAME = "Auto Button Click";
7
+ export declare const AUTO_LINK_ATTRIBUTE = "is_auto_link_click";
8
+ export declare const LINK_CLICK_EVENT_NAME = "Auto Link Click";
5
9
  export declare const buildButtonClickEvent: (click: IButtonClick, pathname: string) => BaseEvent;
10
+ export declare const buildLinkClickEvent: (click: IButtonClick, pathname: string) => BaseEvent;
11
+ export type ButtonClickBlocker = 'data plan' | 'forwarding rule' | 'event name filter' | 'event type filter';
12
+ export declare const getButtonClickBlocker: (forwarder: IKitFilterSettings, kitBlocker?: KitBlocker, link?: boolean) => ButtonClickBlocker | undefined;
@@ -1,6 +1,6 @@
1
1
  export type ButtonElementKind = 'button' | 'input' | 'role' | 'wallet' | 'link';
2
- export type LinkType = 'same_page' | 'outbound' | 'download';
3
- export type ButtonLabelSource = 'labelledby' | 'aria' | 'text' | 'label' | 'alt' | 'title' | 'value' | 'none';
2
+ export type LinkType = 'same_page' | 'internal' | 'outbound' | 'download';
3
+ export type ButtonLabelSource = 'labelledby' | 'aria' | 'text' | 'alt' | 'title' | 'value' | 'none';
4
4
  export interface IButtonClick {
5
5
  label?: string;
6
6
  labelSource: ButtonLabelSource;
@@ -18,4 +18,11 @@ export interface IButtonClick {
18
18
  fileExtension?: string;
19
19
  checked?: boolean;
20
20
  }
21
- export declare function describeButtonClick(control: Element): IButtonClick | undefined;
21
+ export declare function isLinkClick(click: IButtonClick): boolean;
22
+ export declare function describeButtonClick(control: Element, path?: Array<EventTarget | null>): IButtonClick | undefined;
23
+ export interface IPaymentChoice {
24
+ radio: Element;
25
+ names: string[];
26
+ }
27
+ export declare function describePaymentChoice(control: Element, path?: Array<EventTarget | null>): IPaymentChoice | undefined;
28
+ export declare function isPicked(radio: Element): boolean;
@@ -2,6 +2,8 @@ import { IMParticleWebSDKInstance } from '../mp-instance';
2
2
  export declare const WIN_BTN_KEY = "__mpBtn__";
3
3
  export interface IButtonTrackerOptions {
4
4
  hasRoktKit: boolean;
5
+ logButtons: boolean;
6
+ logLinks: boolean;
5
7
  }
6
8
  export declare const getActiveButtonTracker: () => ButtonTracker | undefined;
7
9
  export declare const resetButtonTracking: () => void;
@@ -9,8 +11,11 @@ export declare class ButtonTracker {
9
11
  private readonly mpInstance;
10
12
  private active;
11
13
  private hasRoktKit;
14
+ private logButtons;
15
+ private logLinks;
12
16
  private generation;
13
17
  private lastCommitClick;
18
+ private lastClick;
14
19
  private lastSent;
15
20
  private pagePath;
16
21
  private pageEvents;
@@ -21,6 +26,7 @@ export declare class ButtonTracker {
21
26
  constructor(mpInstance: IMParticleWebSDKInstance);
22
27
  get isActive(): boolean;
23
28
  init(options: IButtonTrackerOptions): void;
29
+ onApplePayBegin(): void;
24
30
  teardown(): void;
25
31
  clearPending(): void;
26
32
  private reset;
@@ -35,7 +41,9 @@ export declare class ButtonTracker {
35
41
  private uploadNow;
36
42
  private cancelUpload;
37
43
  private emit;
44
+ private pickPayment;
38
45
  private eventsEnabled;
46
+ private hasTargetingOptOut;
39
47
  private isKitSuppressed;
40
48
  private log;
41
49
  }
@@ -1,9 +1,14 @@
1
- export type CheckoutIntentKind = 'commit' | 'wallet' | 'checkout_step' | 'frame';
2
- export type CheckoutIntentSource = 'vendor' | 'label' | 'brand' | 'frame' | 'submit';
1
+ export type CheckoutIntentKind = 'commit' | 'wallet' | 'checkout_step' | 'frame' | 'cart';
2
+ export type PaymentFrameFamily = 'apple_pay' | 'google_pay' | 'amazon_pay' | 'shop_pay' | 'card';
3
+ export type CheckoutIntentSource = 'vendor' | 'label' | 'brand' | 'frame' | 'submit' | 'name' | 'api';
3
4
  export interface ICheckoutIntent {
4
5
  kind: Exclude<CheckoutIntentKind, 'frame'>;
5
6
  source: CheckoutIntentSource;
6
7
  }
7
8
  export declare function classifyIntent(control: Element, pathname: string): ICheckoutIntent | undefined;
8
9
  export declare function classifyIntentControl(control: Element, pathname: string): Exclude<CheckoutIntentKind, 'frame'> | undefined;
9
- export declare function classifyIntentFrame(target: EventTarget | null, win: Window): 'wallet' | 'frame' | undefined;
10
+ export interface IPaymentFrame {
11
+ kind: 'wallet' | 'frame';
12
+ family?: PaymentFrameFamily;
13
+ }
14
+ export declare function classifyPaymentFrame(target: EventTarget | null, win: Window): IPaymentFrame | undefined;
@@ -1,9 +1,13 @@
1
1
  export declare const CONTROL_SELECTOR = "button, input[type=\"submit\"], input[type=\"button\"], input[type=\"image\"], [role=\"button\"]";
2
+ export declare function someComposedAncestor(element: Element, test: (node: Element) => boolean, until?: Element): boolean;
2
3
  export declare function hasComposedAncestor(element: Element, selector: string): boolean;
3
4
  export declare function isRoktElement(element: Element): boolean;
4
5
  export declare function vendorButtonKind(control: Element): 'wallet' | 'checkout_step' | 'none' | undefined;
6
+ export declare function nameWords(value: string | null): string;
7
+ export declare function isWalletNamed(element: Element): boolean;
5
8
  export declare function isWalletElement(element: Element): boolean;
6
- export declare function isQuietIntentControl(control: Element, isVendorWallet: boolean): boolean;
7
- export declare function findControl(path: Array<EventTarget | null>): Element | undefined;
8
- export declare function isUnsafeTextSource(element: Element): boolean;
9
- export declare function isUnsafeLabelSource(label: Element): boolean;
9
+ export declare function isQuietIntentControl(control: Element, isVendorWallet: boolean, isCart?: boolean): boolean;
10
+ export declare function isWalletNamedControl(element: Element): boolean;
11
+ export declare function isExcludedPath(path: Array<EventTarget | null>): boolean;
12
+ export declare function findControl(path: Array<EventTarget | null>, included?: boolean): Element | undefined;
13
+ export declare function isUnsafeTextSource(element: Element, own?: Element): boolean;
@@ -1 +1,2 @@
1
+ export declare const MAX_RAW_LABEL_LENGTH = 128;
1
2
  export declare function maskLabel(raw: string | null | undefined): string | undefined;
@@ -59,6 +59,7 @@ export interface IConfigResponse {
59
59
  workspaceToken: string;
60
60
  pixelConfigs: IPixelConfiguration[];
61
61
  flags: SDKEventCustomFlags;
62
+ autoLogPageViewQueryParams?: string;
62
63
  }
63
64
  export interface IConfigAPIClient {
64
65
  apiKey: string;
@@ -163,6 +163,7 @@ declare const Constants: {
163
163
  readonly AstBackgroundEvents: "astBackgroundEvents";
164
164
  readonly AutoLogPageView: "autoLogPageView";
165
165
  readonly AutoButtonTracking: "autoButtonTracking";
166
+ readonly AutoLinkTracking: "autoLinkTracking";
166
167
  };
167
168
  readonly DefaultInstance: "default_instance";
168
169
  readonly CCPAPurpose: "data_sale_opt_out";
@@ -15,6 +15,11 @@ export interface ICookieConsentFlags {
15
15
  * Default: false (targeting is allowed)
16
16
  */
17
17
  noTargeting: boolean;
18
+ /**
19
+ * When true, indicates the user has opted out of the sale or sharing of their data.
20
+ * Default: false (sharing is allowed)
21
+ */
22
+ doNotShareOrSell?: boolean;
18
23
  }
19
24
  export interface ICookieConsentManager {
20
25
  /**
@@ -25,6 +30,10 @@ export interface ICookieConsentManager {
25
30
  * Functional tracking is allowed when noFunctional is false (default)
26
31
  */
27
32
  getNoFunctional: () => boolean;
33
+ /**
34
+ * Sharing is allowed when doNotShareOrSell is false (default)
35
+ */
36
+ getDoNotShareOrSell: () => boolean;
28
37
  /**
29
38
  * Syncs the $NoTargeting user attribute based on the current noTargeting flag.
30
39
  * Sets the attribute when noTargeting is true, removes it when false.
@@ -32,12 +41,12 @@ export interface ICookieConsentManager {
32
41
  syncNoTargetingAttribute: (user: IMParticleUser | null) => void;
33
42
  }
34
43
  /**
35
- * CookieConsentManager handles storage and access of consent flags (noFunctional, noTargeting)
36
- * that are passed via launcherOptions during SDK initialization.
44
+ * CookieConsentManager handles storage and access of consent flags (noFunctional, noTargeting,
45
+ * doNotShareOrSell) that are passed via launcherOptions during SDK initialization.
37
46
  *
38
47
  * These flags allow Rokt integration to respect user privacy choices.
39
48
  *
40
- * Default behavior: Both flags default to false, meaning all features are allowed
49
+ * Default behavior: All flags default to false, meaning all features are allowed
41
50
  * unless explicitly opted out by the user.
42
51
  */
43
52
  export default class CookieConsentManager implements ICookieConsentManager {
@@ -53,5 +62,10 @@ export default class CookieConsentManager implements ICookieConsentManager {
53
62
  * Functional tracking is allowed when noFunctional is false (default).
54
63
  */
55
64
  getNoFunctional(): boolean;
65
+ /**
66
+ * Returns true if the user has opted out of the sale or sharing of their data.
67
+ * Sharing is allowed when doNotShareOrSell is false (default).
68
+ */
69
+ getDoNotShareOrSell(): boolean;
56
70
  syncNoTargetingAttribute(user: IMParticleUser | null): void;
57
71
  }
@@ -1,8 +1,11 @@
1
1
  import * as EventsApi from '@mparticle/event-models';
2
2
  import { IKitFilterSettings } from './configAPIClient';
3
+ import type { SDKEvent } from './sdkRuntimeModels';
4
+ import type KitBlocker from './kitBlocking';
3
5
  type ForwarderFilterConfig = Pick<IKitFilterSettings, 'eventNameFilters' | 'eventTypeFilters' | 'screenNameFilters' | 'screenAttributeFilters' | 'attributeFilters' | 'userIdentityFilters' | 'filteringEventAttributeValue'>;
4
6
  export declare function isBlockedByForwardingRule(messageType: number, attributes: Record<string, unknown> | null, forwarder: ForwarderFilterConfig): boolean;
5
7
  export declare function isBlockedByEventFilter(messageType: number, hashedEventName: number, hashedEventType: number, forwarder: ForwarderFilterConfig): boolean;
8
+ export declare function isEventForwarded(event: SDKEvent, forwarder: ForwarderFilterConfig, kitBlocker?: KitBlocker): boolean;
6
9
  export declare function filterEventAttributes(messageType: number, eventCategory: number, eventName: string, attributes: Record<string, unknown> | null, forwarder: ForwarderFilterConfig): Record<string, unknown> | null;
7
10
  export declare function filterUserIdentities(userIdentities: Array<{
8
11
  Type: number;
@@ -3,11 +3,23 @@ import { BaseEvent } from './sdkRuntimeModels';
3
3
  import { Dictionary } from './utils';
4
4
  export declare const WIN_APV_KEY = "__mpApv__";
5
5
  export declare const ALLOWED_QUERY_PARAMS: string[];
6
+ export declare const AUTO_PAGE_VIEW_ATTRIBUTE = "is_auto_page_view";
7
+ export declare const MAX_CUSTOM_QUERY_PARAMS = 25;
8
+ export interface IQueryParamAllowlist {
9
+ allowed: string[];
10
+ rejectedPositions: number[];
11
+ overLimit: number;
12
+ }
13
+ export declare const MAX_CUSTOM_QUERY_PARAM_VALUE_LENGTH = 512;
14
+ export interface ICapturedParam {
15
+ name: string;
16
+ value: string;
17
+ }
6
18
  export declare const ALLOWED_QUERY_PARAM_PREFIXES: string[];
7
19
  export declare const isAllowedQueryParam: (name: string) => boolean;
8
20
  interface IPageSnapshot {
9
21
  path: string;
10
- params: Dictionary<string>;
22
+ params: ICapturedParam[];
11
23
  }
12
24
  interface IPageViewData extends IPageSnapshot {
13
25
  hostname: string;
@@ -17,8 +29,10 @@ interface IPageViewData extends IPageSnapshot {
17
29
  export interface IPageViewOptions {
18
30
  isAutoPageView?: boolean;
19
31
  }
20
- export declare const allowedQueryParams: (href: string) => Dictionary<string>;
21
- export declare const AUTO_PAGE_VIEW_ATTRIBUTE = "is_auto_page_view";
32
+ export declare const parseQueryParamAllowlist: (configured: unknown) => IQueryParamAllowlist;
33
+ export declare const effectiveAllowlist: (extras?: string[]) => string[];
34
+ export declare const allowedQueryParams: (href: string, extras?: string[]) => ICapturedParam[];
35
+ export declare const paramsToAttributes: (params: ICapturedParam[]) => Dictionary<string>;
22
36
  export declare const autoPageViewAttribute: (isAutoPageView?: boolean) => Dictionary<boolean>;
23
37
  export declare const pageKey: (page: IPageSnapshot) => string;
24
38
  export declare const isNewPage: (lastKey: string | null, candidateKey: string) => boolean;
@@ -31,6 +45,7 @@ export declare class PageViewTracker {
31
45
  private readonly mpInstance;
32
46
  private lastPage;
33
47
  private active;
48
+ private customQueryParams;
34
49
  private pendingNavigations;
35
50
  private undoHistoryPatch;
36
51
  private readonly isAutoPageView;
@@ -38,6 +53,7 @@ export declare class PageViewTracker {
38
53
  get isActive(): boolean;
39
54
  init(): void;
40
55
  teardown(): void;
56
+ private readCustomQueryParams;
41
57
  private retire;
42
58
  private takePendingNavigations;
43
59
  private safeHandleNavigation;
@@ -4,6 +4,7 @@ export interface IRoktLauncherOptions extends Dictionary<any> {
4
4
  noDeviceID?: boolean;
5
5
  noFunctional?: boolean;
6
6
  noTargeting?: boolean;
7
+ doNotShareOrSell?: boolean;
7
8
  sessionId?: string;
8
9
  sessionToken?: string;
9
10
  }
@@ -9,7 +9,7 @@ import { MPID } from "@mparticle/web-sdk";
9
9
  import { IErrorReportingService, ILoggingService } from "./reporting/types";
10
10
  import { IRoktLauncherOptions } from "./roktLauncherOptions";
11
11
  import IntegrationCapture from "./integrationCapture";
12
- import type { CheckoutIntentKind, CheckoutIntentSource } from "./buttonTracking/checkoutIntent";
12
+ import type { CheckoutIntentKind, CheckoutIntentSource, PaymentFrameFamily } from "./buttonTracking/checkoutIntent";
13
13
  export type RoktAttributeValueArray = Array<string | number | boolean>;
14
14
  export type RoktAttributeValueType = string | number | boolean | undefined | null;
15
15
  export type RoktAttributeValue = RoktAttributeValueType | RoktAttributeValueArray;
@@ -50,6 +50,12 @@ export interface ICheckoutIntentSignal {
50
50
  source?: CheckoutIntentSource;
51
51
  pathname?: string;
52
52
  }
53
+ export interface IPaymentPickSignal {
54
+ t: number;
55
+ pathname: string;
56
+ names?: readonly string[];
57
+ family?: PaymentFrameFamily;
58
+ }
53
59
  export interface IRoktKitSettings {
54
60
  accountId?: string;
55
61
  placementAttributesMapping?: string;
@@ -68,6 +74,7 @@ export interface IRoktKit {
68
74
  terminate?: () => Promise<void>;
69
75
  onRouteChange?: () => void;
70
76
  onCheckoutIntent?: (signal: ICheckoutIntentSignal) => void;
77
+ onPaymentPick?: (signal: IPaymentPickSignal) => void;
71
78
  launcherOptions?: Dictionary<any>;
72
79
  settings?: IRoktKitSettings;
73
80
  integrationName?: string;
@@ -102,6 +109,7 @@ export default class RoktManager {
102
109
  private isShoppableAdsLoaded;
103
110
  private stopRouteChangeWatch;
104
111
  private pendingCheckoutIntents;
112
+ private autoButtonClicksForwarded;
105
113
  constructor(instanceName?: string);
106
114
  /**
107
115
  * Sets a callback to be invoked when RoktManager becomes ready
@@ -127,12 +135,15 @@ export default class RoktManager {
127
135
  attachKit(kit: IRoktKit): void;
128
136
  private watchRouteChanges;
129
137
  private notifyRouteChange;
138
+ setAutoButtonClicksForwarded(forwarded: boolean): void;
139
+ hasTargetingOptOut(): boolean;
130
140
  isCheckoutIntentSuppressed(): boolean;
131
141
  isCheckoutIntentLive(): boolean;
132
142
  notifyCheckoutIntent(signal: ICheckoutIntentSignal, pathname?: string): void;
133
143
  discardCheckoutIntents(): void;
144
+ notifyPaymentPick(signal: IPaymentPickSignal): void;
134
145
  private flushCheckoutIntents;
135
- private isAutoLogPageViewEnabled;
146
+ isAutoLogPageViewEnabled(): boolean;
136
147
  /**
137
148
  * Renders ads based on the options provided
138
149
  *
@@ -223,6 +223,7 @@ export interface SDKInitConfig extends Omit<MPConfiguration, 'dataPlan' | 'logLe
223
223
  dataPlanOptions?: KitBlockerOptions;
224
224
  flags?: Dictionary;
225
225
  pixelConfigs?: IPixelConfiguration[];
226
+ autoLogPageViewQueryParams?: string;
226
227
  aliasMaxWindow?: number;
227
228
  deviceId?: string;
228
229
  forceHttps?: boolean;
@@ -1,6 +1,7 @@
1
1
  import { Batch } from '@mparticle/event-models';
2
2
  import { MPID, IdentifyRequest, SDKEventCustomFlags, ConsentState, UserIdentities } from '@mparticle/web-sdk';
3
3
  import { IKitConfigs } from './configAPIClient';
4
+ import { IQueryParamAllowlist } from './pageViewTracker';
4
5
  import { DataPlanResult, KitBlockerOptions, LogLevelType, SDKDataPlan, SDKEvent, SDKGeoLocation, SDKInitConfig, SDKProduct } from './sdkRuntimeModels';
5
6
  import { Dictionary, AttributeValue } from './utils';
6
7
  import { SDKConsentState } from './consent';
@@ -25,6 +26,7 @@ export interface SDKConfig {
25
26
  appVersion?: string;
26
27
  package?: string;
27
28
  flags?: IFeatureFlags;
29
+ autoLogPageViewQueryParams?: IQueryParamAllowlist;
28
30
  kitConfigs: IKitConfigs[];
29
31
  kits: Dictionary<UnregisteredKit>;
30
32
  logLevel?: LogLevelType;
@@ -80,6 +82,7 @@ export interface IFeatureFlags {
80
82
  astBackgroundEvents?: boolean;
81
83
  autoLogPageView?: boolean;
82
84
  autoButtonTracking?: boolean;
85
+ autoLinkTracking?: boolean;
83
86
  }
84
87
  export interface IStore {
85
88
  isEnabled: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "3.23.0",
3
+ "version": "3.25.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -101,20 +101,18 @@
101
101
  "lint"
102
102
  ],
103
103
  "devDependencies": {
104
- "@babel/core": "^7.18.6",
105
- "@babel/eslint-parser": "^7.18.2",
106
- "@babel/eslint-plugin": "^7.17.7",
107
- "@babel/plugin-proposal-class-properties": "^7.5.5",
108
- "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
109
- "@babel/plugin-transform-runtime": "^7.6.0",
110
- "@babel/preset-env": "^7.6.0",
111
- "@babel/preset-typescript": "^7.6.0",
104
+ "@babel/core": "^8.0.7",
105
+ "@babel/plugin-transform-runtime": "^8.0.6",
106
+ "@babel/preset-env": "^8.0.7",
107
+ "@babel/preset-typescript": "^8.0.7",
108
+ "@babel/runtime": "^8.0.7",
112
109
  "@mparticle/data-planning-models": "^0.1.0",
113
110
  "@mparticle/event-models": "^1.3.1",
114
- "@rollup/plugin-babel": "6.0.3",
111
+ "@rollup/plugin-babel": "^7.1.0",
115
112
  "@rollup/plugin-commonjs": "25.0.4",
116
113
  "@rollup/plugin-json": "^5.0.2",
117
114
  "@rollup/plugin-node-resolve": "15.2.1",
115
+ "@rollup/plugin-terser": "^1.0.1",
118
116
  "@rollup/plugin-typescript": "^11.1.0",
119
117
  "@semantic-release/changelog": "^6.0.3",
120
118
  "@semantic-release/exec": "^6.0.3",
@@ -126,7 +124,7 @@
126
124
  "@types/mocha": "^9.0.0",
127
125
  "@types/mparticle__web-sdk": "^2.66.0",
128
126
  "@types/node": "^20.1.0",
129
- "babel-preset-minify": "^0.5.1",
127
+ "babel-plugin-polyfill-corejs3": "^1.0.0",
130
128
  "browser-sync": "^2.26.3",
131
129
  "browserify": "^17.0.0",
132
130
  "chai": "^4.2.0",
@@ -159,7 +157,7 @@
159
157
  "shelljs": "^0.8.4",
160
158
  "should": "^7.1.0",
161
159
  "sinon": "^16.1.3",
162
- "ts-jest": "^29.1.0",
160
+ "ts-jest": "^29.4.15",
163
161
  "tslib": "^2.1.0",
164
162
  "typescript": "^5.0.2",
165
163
  "uglify-js": "^3.4.9",
@@ -169,7 +167,16 @@
169
167
  "peerDependencies": {
170
168
  "@mparticle/event-models": "^1.3.1"
171
169
  },
172
- "dependencies": {
173
- "@babel/runtime": "^7.23.2"
170
+ "overrides": {
171
+ "@rollup/plugin-babel": {
172
+ "@babel/core": "$@babel/core"
173
+ }
174
+ },
175
+ "devEngines": {
176
+ "runtime": {
177
+ "name": "node",
178
+ "version": "^22.18.0 || >=24.11.0",
179
+ "onFail": "warn"
180
+ }
174
181
  }
175
182
  }
@@ -1,4 +1,8 @@
1
- import { BaseEvent } from '../sdkRuntimeModels';
1
+ import { IKitFilterSettings } from '../configAPIClient';
2
+ import { isEventForwarded } from '../forwarder-utils';
3
+ import type KitBlocker from '../kitBlocking';
4
+ import KitFilterHelper from '../kitFilterHelper';
5
+ import { BaseEvent, SDKEvent } from '../sdkRuntimeModels';
2
6
  import { EventType, MessageType } from '../types';
3
7
  import { IButtonClick } from './buttonDescription';
4
8
 
@@ -8,22 +12,25 @@ export const AUTO_BUTTON_ATTRIBUTE = 'is_auto_button_click';
8
12
  // Specific enough not to merge with a customer's own 'Button Click' event.
9
13
  export const BUTTON_CLICK_EVENT_NAME = 'Auto Button Click';
10
14
 
11
- // One custom event per click, logged like logLink() with the auto marker and the
12
- // page fields that auto page views carry. No intent: classifying clicks is left
13
- // to whoever reads the data.
14
- export const buildButtonClickEvent = (
15
+ // Marks an automatically logged click on a link that leaves the page.
16
+ export const AUTO_LINK_ATTRIBUTE = 'is_auto_link_click';
17
+
18
+ export const LINK_CLICK_EVENT_NAME = 'Auto Link Click';
19
+
20
+ // The attributes both click events carry, with the page fields that auto page
21
+ // views carry. No intent: classifying clicks is left to whoever reads the data.
22
+ const clickAttributes = (
15
23
  click: IButtonClick,
16
24
  pathname: string
17
- ): BaseEvent => {
25
+ ): { [key: string]: string | boolean } => {
18
26
  const data: { [key: string]: string | boolean } = {
19
27
  label_source: click.labelSource,
20
28
  element: click.element,
21
29
  type: click.type,
22
30
  path: pathname,
23
31
  hostname: window.location.hostname,
24
- [AUTO_BUTTON_ATTRIBUTE]: true,
25
32
  };
26
- // Optional fields are left out when the button has no safe value for them.
33
+ // Optional fields are left out when the control has no safe value for them.
27
34
  const optional: { [key: string]: string | undefined } = {
28
35
  label: click.label,
29
36
  aria_label: click.ariaLabel,
@@ -45,6 +52,16 @@ export const buildButtonClickEvent = (
45
52
  if (click.checked !== undefined) {
46
53
  data.checked = click.checked;
47
54
  }
55
+ return data;
56
+ };
57
+
58
+ // One custom event per button click, logged like logLink() with the auto marker.
59
+ export const buildButtonClickEvent = (
60
+ click: IButtonClick,
61
+ pathname: string
62
+ ): BaseEvent => {
63
+ const data = clickAttributes(click, pathname);
64
+ data[AUTO_BUTTON_ATTRIBUTE] = true;
48
65
  return {
49
66
  messageType: MessageType.PageEvent,
50
67
  name: BUTTON_CLICK_EVENT_NAME,
@@ -52,3 +69,104 @@ export const buildButtonClickEvent = (
52
69
  eventType: EventType.Other,
53
70
  };
54
71
  };
72
+
73
+ // One custom event per click on a link that leaves the page. Typed Other, not
74
+ // Navigation, so no kit reads it as a page change. A plain link to another page
75
+ // of the site goes without its text, which can name the shopper ("Hi, Jane");
76
+ // a button or wallet acting as a link keeps its name.
77
+ export const buildLinkClickEvent = (
78
+ click: IButtonClick,
79
+ pathname: string
80
+ ): BaseEvent => {
81
+ const data = clickAttributes(
82
+ click.linkType === 'internal' && click.element === 'link'
83
+ ? {
84
+ ...click,
85
+ label: undefined,
86
+ labelSource: 'none',
87
+ ariaLabel: undefined,
88
+ }
89
+ : click,
90
+ pathname
91
+ );
92
+ data[AUTO_LINK_ATTRIBUTE] = true;
93
+ return {
94
+ messageType: MessageType.PageEvent,
95
+ name: LINK_CLICK_EVENT_NAME,
96
+ data,
97
+ eventType: EventType.Other,
98
+ };
99
+ };
100
+
101
+ // Every attribute buildButtonClickEvent can set apart from the marker and the
102
+ // hostname, which is the same on every click on this page. A spec checks the
103
+ // two stay in step.
104
+ const CLICK_ATTRIBUTES = [
105
+ 'label_source',
106
+ 'element',
107
+ 'type',
108
+ 'path',
109
+ 'label',
110
+ 'aria_label',
111
+ 'icon',
112
+ 'element_id',
113
+ 'element_name',
114
+ 'test_id',
115
+ 'controls',
116
+ 'form',
117
+ 'link_type',
118
+ 'link_domain',
119
+ 'file_extension',
120
+ 'checked',
121
+ ];
122
+
123
+ export type ButtonClickBlocker =
124
+ | 'data plan'
125
+ | 'forwarding rule'
126
+ | 'event name filter'
127
+ | 'event type filter';
128
+
129
+ // Which check keeps some or all auto button clicks, or auto link clicks with
130
+ // `link`, from a kit, or undefined when the kit receives every one. A forwarding
131
+ // rule on an attribute whose value differs between clicks lets some through and
132
+ // not others. Each other check goes through isEventForwarded on its own, so
133
+ // together they decide as it does for each click.
134
+ export const getButtonClickBlocker = (
135
+ forwarder: IKitFilterSettings,
136
+ kitBlocker?: KitBlocker,
137
+ link = false
138
+ ): ButtonClickBlocker | undefined => {
139
+ const rule = forwarder.filteringEventAttributeValue;
140
+ if (
141
+ rule &&
142
+ rule.eventAttributeValue &&
143
+ CLICK_ATTRIBUTES.some(
144
+ name =>
145
+ KitFilterHelper.hashAttributeConditionalForwarding(name) ===
146
+ rule.eventAttributeName
147
+ )
148
+ ) {
149
+ return 'forwarding rule';
150
+ }
151
+ const attributes: { [key: string]: string | boolean } = {
152
+ hostname: window.location.hostname,
153
+ };
154
+ attributes[link ? AUTO_LINK_ATTRIBUTE : AUTO_BUTTON_ATTRIBUTE] = true;
155
+ const click = ({
156
+ EventName: link ? LINK_CLICK_EVENT_NAME : BUTTON_CLICK_EVENT_NAME,
157
+ EventCategory: EventType.Other,
158
+ EventDataType: MessageType.PageEvent,
159
+ EventAttributes: attributes,
160
+ } as unknown) as SDKEvent;
161
+ const checks: Array<[ButtonClickBlocker, Partial<IKitFilterSettings>]> = [
162
+ ['data plan', {}],
163
+ ['forwarding rule', { filteringEventAttributeValue: rule }],
164
+ ['event name filter', { eventNameFilters: forwarder.eventNameFilters }],
165
+ ['event type filter', { eventTypeFilters: forwarder.eventTypeFilters }],
166
+ ];
167
+ const failed = checks.find(
168
+ ([, filters]) =>
169
+ !isEventForwarded(click, filters as IKitFilterSettings, kitBlocker)
170
+ );
171
+ return failed ? failed[0] : undefined;
172
+ };