@mparticle/web-sdk 3.21.0 → 3.23.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 +230 -161
- package/dist/mparticle.esm.js +230 -161
- package/dist/mparticle.js +4536 -4111
- package/dist/types/src/batchUploader.d.ts +2 -0
- package/dist/types/src/buttonTracking/buttonDescription.d.ts +2 -1
- package/dist/types/src/buttonTracking/buttonTracker.d.ts +6 -0
- package/dist/types/src/buttonTracking/checkoutIntent.d.ts +7 -1
- package/dist/types/src/buttonTracking/controls.d.ts +3 -0
- package/dist/types/src/pageUrl.d.ts +10 -0
- package/dist/types/src/pageViewTracker.d.ts +2 -0
- package/dist/types/src/roktManager.d.ts +9 -2
- package/dist/types/src/sdkRuntimeModels.d.ts +2 -0
- package/package.json +1 -1
- package/src/batchUploader.ts +36 -3
- package/src/buttonTracking/buttonClickEvent.ts +3 -0
- package/src/buttonTracking/buttonDescription.ts +24 -3
- package/src/buttonTracking/buttonTracker.ts +92 -8
- package/src/buttonTracking/checkoutIntent.ts +199 -66
- package/src/buttonTracking/controls.ts +150 -32
- package/src/mp-instance.ts +5 -8
- package/src/mparticle-instance-manager.ts +3 -0
- package/src/pageUrl.ts +150 -0
- package/src/pageViewTracker.ts +60 -8
- package/src/roktManager.ts +50 -17
- package/src/sdkRuntimeModels.ts +2 -0
- package/src/serverModel.ts +3 -3
- package/src/utils.ts +1 -1
package/src/pageUrl.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { isAllowedQueryParam } from './pageViewTracker';
|
|
2
|
+
import { getHref, valueof } from './utils';
|
|
3
|
+
|
|
4
|
+
// The one rule for a URL that leaves the SDK, shared with kits through
|
|
5
|
+
// `mParticle._cleanUrl`. `path` keeps scheme, host and path. `full` adds the
|
|
6
|
+
// query params an auto page view may capture, then the fragment.
|
|
7
|
+
export const UrlMode = {
|
|
8
|
+
Path: 'path',
|
|
9
|
+
Full: 'full',
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export type UrlMode = valueof<typeof UrlMode>;
|
|
13
|
+
|
|
14
|
+
// OAuth 2.0 / OIDC authorization-flow params. They stay off URLs even where the
|
|
15
|
+
// page view allowlist admits them, because a URL travels on every event.
|
|
16
|
+
export const EXCLUDED_URL_QUERY_PARAMS: string[] = [
|
|
17
|
+
'client_id',
|
|
18
|
+
'redirect_uri',
|
|
19
|
+
'response_type',
|
|
20
|
+
'scope',
|
|
21
|
+
'state',
|
|
22
|
+
'code',
|
|
23
|
+
'nonce',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// An OAuth implicit or OIDC fragment response puts its tokens in `#k=v&...`.
|
|
27
|
+
export const EXCLUDED_URL_FRAGMENT_PARAMS: string[] = EXCLUDED_URL_QUERY_PARAMS.concat(
|
|
28
|
+
[
|
|
29
|
+
'access_token',
|
|
30
|
+
'id_token',
|
|
31
|
+
'refresh_token',
|
|
32
|
+
'provider_token',
|
|
33
|
+
'provider_refresh_token',
|
|
34
|
+
]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Allowlisted params whose value is itself a URL, so it can carry a query.
|
|
38
|
+
const URL_VALUED_QUERY_PARAMS: string[] = ['ref', 'referrer'];
|
|
39
|
+
|
|
40
|
+
const USERINFO = /^((?:[a-z][a-z0-9+.-]*:)?\/\/)[^/]*@/i;
|
|
41
|
+
|
|
42
|
+
const decodeQueryComponent = (component: string): string =>
|
|
43
|
+
decodeURIComponent(component.replace(/\+/g, ' '));
|
|
44
|
+
|
|
45
|
+
const cleanQueryPair = (pair: string): string | null => {
|
|
46
|
+
const separator = pair.indexOf('=');
|
|
47
|
+
const rawName = separator === -1 ? pair : pair.slice(0, separator);
|
|
48
|
+
let name: string;
|
|
49
|
+
try {
|
|
50
|
+
name = decodeQueryComponent(rawName);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const lowerName = name.toLowerCase();
|
|
56
|
+
if (
|
|
57
|
+
!name ||
|
|
58
|
+
!isAllowedQueryParam(name) ||
|
|
59
|
+
EXCLUDED_URL_QUERY_PARAMS.indexOf(lowerName) !== -1
|
|
60
|
+
) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (separator === -1 || URL_VALUED_QUERY_PARAMS.indexOf(lowerName) === -1) {
|
|
65
|
+
return pair;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let value: string;
|
|
69
|
+
try {
|
|
70
|
+
value = decodeQueryComponent(pair.slice(separator + 1));
|
|
71
|
+
} catch (e) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const cleanedValue = cleanUrl(value, UrlMode.Path);
|
|
75
|
+
return cleanedValue === value
|
|
76
|
+
? pair
|
|
77
|
+
: `${rawName}=${encodeURIComponent(cleanedValue)}`;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const cleanQuery = (query: string): string =>
|
|
81
|
+
query
|
|
82
|
+
.split('&')
|
|
83
|
+
.map(cleanQueryPair)
|
|
84
|
+
.filter((pair): pair is string => pair !== null)
|
|
85
|
+
.join('&');
|
|
86
|
+
|
|
87
|
+
const keepsFragmentPair = (pair: string): boolean => {
|
|
88
|
+
try {
|
|
89
|
+
const name = decodeQueryComponent(pair.split('=')[0]);
|
|
90
|
+
return EXCLUDED_URL_FRAGMENT_PARAMS.indexOf(name.toLowerCase()) === -1;
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// A hash route (`#/...`, `#!...`, or a path with no `=` before its `?`) keeps
|
|
97
|
+
// its path; a query inside it gets the same allowlist as the URL's own query.
|
|
98
|
+
// A fragment of bare pairs loses the OAuth/OIDC ones.
|
|
99
|
+
const cleanFragment = (hash: string): string => {
|
|
100
|
+
const queryStart = hash.indexOf('?');
|
|
101
|
+
const route = queryStart === -1 ? hash : hash.slice(0, queryStart);
|
|
102
|
+
const isRoute =
|
|
103
|
+
/^#[/!]/.test(hash) || (queryStart !== -1 && route.indexOf('=') === -1);
|
|
104
|
+
|
|
105
|
+
if (isRoute && queryStart !== -1) {
|
|
106
|
+
const query = cleanQuery(hash.slice(queryStart + 1));
|
|
107
|
+
return route + (query ? `?${query}` : '');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (hash.indexOf('=') === -1) {
|
|
111
|
+
return hash;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const pairs = hash
|
|
115
|
+
.slice(1)
|
|
116
|
+
.split('&')
|
|
117
|
+
.filter(keepsFragmentPair)
|
|
118
|
+
.join('&');
|
|
119
|
+
return pairs ? `#${pairs}` : '';
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// String-based rather than `new URL`, so kept pairs and the fragment go out
|
|
123
|
+
// exactly as the browser reported them and no environment can parse differently.
|
|
124
|
+
// An unknown mode is treated as `path`.
|
|
125
|
+
export const cleanUrl = (href: string, mode: UrlMode): string => {
|
|
126
|
+
if (typeof href !== 'string' || !href) {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const hashStart = href.indexOf('#');
|
|
131
|
+
const hash = hashStart === -1 ? '' : href.slice(hashStart);
|
|
132
|
+
const beforeHash = hashStart === -1 ? href : href.slice(0, hashStart);
|
|
133
|
+
const queryStart = beforeHash.indexOf('?');
|
|
134
|
+
const path = (queryStart === -1
|
|
135
|
+
? beforeHash
|
|
136
|
+
: beforeHash.slice(0, queryStart)
|
|
137
|
+
).replace(USERINFO, '$1');
|
|
138
|
+
|
|
139
|
+
if (mode !== UrlMode.Full) {
|
|
140
|
+
return path;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const query =
|
|
144
|
+
queryStart === -1 ? '' : cleanQuery(beforeHash.slice(queryStart + 1));
|
|
145
|
+
|
|
146
|
+
return path + (query ? `?${query}` : '') + cleanFragment(hash);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// The current page as it may appear on an event.
|
|
150
|
+
export const cleanPageHref = (): string => cleanUrl(getHref(), UrlMode.Full);
|
package/src/pageViewTracker.ts
CHANGED
|
@@ -55,6 +55,12 @@ export const ALLOWED_QUERY_PARAMS: string[] = [
|
|
|
55
55
|
'utm_term',
|
|
56
56
|
'utm_content',
|
|
57
57
|
'utm_id',
|
|
58
|
+
'utm_source_platform',
|
|
59
|
+
'utm_creative_format',
|
|
60
|
+
'utm_marketing_tactic',
|
|
61
|
+
'gad_source',
|
|
62
|
+
'gad_campaignid',
|
|
63
|
+
'mc_cid',
|
|
58
64
|
|
|
59
65
|
// Ad-network click ids
|
|
60
66
|
'gclid',
|
|
@@ -66,6 +72,12 @@ export const ALLOWED_QUERY_PARAMS: string[] = [
|
|
|
66
72
|
'twclid',
|
|
67
73
|
'li_fat_id',
|
|
68
74
|
'dclid',
|
|
75
|
+
'srsltid',
|
|
76
|
+
'epik',
|
|
77
|
+
'ScCid',
|
|
78
|
+
'irclickid',
|
|
79
|
+
'rtid',
|
|
80
|
+
'rclid',
|
|
69
81
|
|
|
70
82
|
// OAuth / OIDC — see the SECURITY note above
|
|
71
83
|
'client_id',
|
|
@@ -84,8 +96,11 @@ export const ALLOWED_QUERY_PARAMS: string[] = [
|
|
|
84
96
|
'per_page',
|
|
85
97
|
|
|
86
98
|
// Site search term, as the common platforms spell it. Matching is
|
|
87
|
-
// case-insensitive
|
|
99
|
+
// case-insensitive. `s` is WordPress and WooCommerce search; other
|
|
100
|
+
// single-letter names are too generic. `oq` is a storefront search term.
|
|
88
101
|
'q',
|
|
102
|
+
's',
|
|
103
|
+
'oq',
|
|
89
104
|
'search',
|
|
90
105
|
'query',
|
|
91
106
|
'keyword',
|
|
@@ -105,6 +120,9 @@ export const ALLOWED_QUERY_PARAMS: string[] = [
|
|
|
105
120
|
'sku',
|
|
106
121
|
'skuId',
|
|
107
122
|
'variant',
|
|
123
|
+
'color',
|
|
124
|
+
'colour',
|
|
125
|
+
'size',
|
|
108
126
|
|
|
109
127
|
// Category
|
|
110
128
|
'category',
|
|
@@ -114,6 +132,27 @@ export const ALLOWED_QUERY_PARAMS: string[] = [
|
|
|
114
132
|
'referrer',
|
|
115
133
|
];
|
|
116
134
|
|
|
135
|
+
// Params matched by name prefix, for platforms that put a per-product id in the
|
|
136
|
+
// name. Salesforce Commerce Cloud spells a variant choice
|
|
137
|
+
// `dwvar_<productId>_<attribute>`, e.g. `dwvar_12345_color=navy`.
|
|
138
|
+
export const ALLOWED_QUERY_PARAM_PREFIXES: string[] = ['dwvar_'];
|
|
139
|
+
|
|
140
|
+
const hasAllowedPrefix = (name: string): boolean =>
|
|
141
|
+
ALLOWED_QUERY_PARAM_PREFIXES.some(prefix => name.indexOf(prefix) === 0);
|
|
142
|
+
|
|
143
|
+
const ALLOWED_QUERY_PARAMS_LOWER: string[] = ALLOWED_QUERY_PARAMS.map(name =>
|
|
144
|
+
name.toLowerCase()
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Whether a query param name, in any case, is one an auto page view captures.
|
|
148
|
+
export const isAllowedQueryParam = (name: string): boolean => {
|
|
149
|
+
const lower = name.toLowerCase();
|
|
150
|
+
return (
|
|
151
|
+
ALLOWED_QUERY_PARAMS_LOWER.indexOf(lower) !== -1 ||
|
|
152
|
+
hasAllowedPrefix(lower)
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
117
156
|
interface IPageSnapshot {
|
|
118
157
|
path: string;
|
|
119
158
|
params: Dictionary<string>;
|
|
@@ -146,8 +185,18 @@ interface IPendingNavigation {
|
|
|
146
185
|
// attribute), drops empty values, and carries the fallback for browsers without
|
|
147
186
|
// URLSearchParams. Tolerates an empty href, so SSR yields no params rather than
|
|
148
187
|
// throwing.
|
|
149
|
-
export const allowedQueryParams = (href: string): Dictionary<string> =>
|
|
150
|
-
queryStringParser(href, ALLOWED_QUERY_PARAMS);
|
|
188
|
+
export const allowedQueryParams = (href: string): Dictionary<string> => {
|
|
189
|
+
const params = queryStringParser(href, ALLOWED_QUERY_PARAMS);
|
|
190
|
+
const allParams = queryStringParser(href);
|
|
191
|
+
|
|
192
|
+
Object.keys(allParams)
|
|
193
|
+
.filter(name => hasAllowedPrefix(name) && allParams[name])
|
|
194
|
+
.forEach(name => {
|
|
195
|
+
params[name] = allParams[name];
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return params;
|
|
199
|
+
};
|
|
151
200
|
|
|
152
201
|
export const AUTO_PAGE_VIEW_ATTRIBUTE = 'is_auto_page_view';
|
|
153
202
|
|
|
@@ -159,10 +208,9 @@ export const autoPageViewAttribute = (
|
|
|
159
208
|
): Dictionary<boolean> =>
|
|
160
209
|
isAutoPageView ? { [AUTO_PAGE_VIEW_ATTRIBUTE]: true } : {};
|
|
161
210
|
|
|
162
|
-
// The captured params, in allowlist order
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
// cannot produce a different key.
|
|
211
|
+
// The captured params, in allowlist order, then prefix-matched names sorted.
|
|
212
|
+
// Neither depends on the object's insertion order, so reordering the query
|
|
213
|
+
// string cannot produce a different key.
|
|
166
214
|
//
|
|
167
215
|
// Membership is an own-property check, not `in`. `in` walks the prototype
|
|
168
216
|
// chain, so a name matching an Object.prototype member reports as present on
|
|
@@ -170,7 +218,11 @@ export const autoPageViewAttribute = (
|
|
|
170
218
|
// only thing making `in` safe here — and it stops being a safe assumption the
|
|
171
219
|
// moment this list can be extended from configuration.
|
|
172
220
|
const capturedNames = (params: Dictionary<string>): string[] =>
|
|
173
|
-
ALLOWED_QUERY_PARAMS.filter(name => hasOwnProp(params, name))
|
|
221
|
+
ALLOWED_QUERY_PARAMS.filter(name => hasOwnProp(params, name)).concat(
|
|
222
|
+
Object.keys(params)
|
|
223
|
+
.filter(hasAllowedPrefix)
|
|
224
|
+
.sort()
|
|
225
|
+
);
|
|
174
226
|
|
|
175
227
|
// The dedup key: pathname plus the allowlisted params in a fixed order, so that
|
|
176
228
|
// reordering the query string is not a new page. Params outside the allowlist
|
package/src/roktManager.ts
CHANGED
|
@@ -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";
|
|
@@ -23,7 +23,7 @@ import { PARTNER_MODULE_IDS } from "./cookieSyncManager";
|
|
|
23
23
|
import IntegrationCapture from "./integrationCapture";
|
|
24
24
|
import Constants from "./constants";
|
|
25
25
|
import { subscribeToRouteChange } from "./routeChangeMonitor";
|
|
26
|
-
import type { CheckoutIntentKind } from "./buttonTracking/checkoutIntent";
|
|
26
|
+
import type { CheckoutIntentKind, CheckoutIntentSource } from "./buttonTracking/checkoutIntent";
|
|
27
27
|
|
|
28
28
|
const PASSBACK_CONVERSION_TRACKING_ID = 'passbackconversiontrackingid';
|
|
29
29
|
const CHECKOUT_INTENT_BUFFER_LIMIT = 4;
|
|
@@ -74,9 +74,14 @@ export interface RoktKitFilterSettings {
|
|
|
74
74
|
filteredUser?: IMParticleUser | null;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// What a kit receives on checkout intent: the kind, when the shopper acted,
|
|
78
|
+
// which rule recognised it and the path it happened on. Older kits read only
|
|
79
|
+
// kind and t.
|
|
77
80
|
export interface ICheckoutIntentSignal {
|
|
78
81
|
kind: CheckoutIntentKind;
|
|
79
82
|
t: number;
|
|
83
|
+
source?: CheckoutIntentSource;
|
|
84
|
+
pathname?: string;
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
export interface IRoktKitSettings {
|
|
@@ -131,6 +136,7 @@ export default class RoktManager {
|
|
|
131
136
|
public kit: IRoktKit = null;
|
|
132
137
|
public filters: RoktKitFilterSettings = {};
|
|
133
138
|
private currentUser: IMParticleUser | null = null;
|
|
139
|
+
private createFilteredUser?: (mpid: MPID) => IMParticleUser;
|
|
134
140
|
private messageQueue: Map<string, IRoktMessage> = new Map();
|
|
135
141
|
private sandbox: boolean | null = null;
|
|
136
142
|
private placementAttributesMapping: Dictionary<string>[] = [];
|
|
@@ -173,6 +179,7 @@ export default class RoktManager {
|
|
|
173
179
|
* @param {Function} captureTiming - Function to capture performance timing marks
|
|
174
180
|
* @param {IErrorReportingService} errorReporter - Dispatcher for error/warning reporting
|
|
175
181
|
* @param {ILoggingService} loggingService - Dispatcher for informational logging
|
|
182
|
+
* @param {Function} createFilteredUser - Builds a user as the Rokt connection's filters and the data plan allow
|
|
176
183
|
*
|
|
177
184
|
* @throws Logs error to console if placementAttributesMapping parsing fails
|
|
178
185
|
*/
|
|
@@ -187,6 +194,7 @@ export default class RoktManager {
|
|
|
187
194
|
errorReporter?: IErrorReportingService,
|
|
188
195
|
loggingService?: ILoggingService,
|
|
189
196
|
integrationCapture?: IntegrationCapture,
|
|
197
|
+
createFilteredUser?: (mpid: MPID) => IMParticleUser,
|
|
190
198
|
): void {
|
|
191
199
|
const { userAttributeFilters, settings } = roktConfig || {};
|
|
192
200
|
const { placementAttributesMapping, hashedEmailUserIdentityType } = settings || {};
|
|
@@ -199,6 +207,7 @@ export default class RoktManager {
|
|
|
199
207
|
this.loggingService = loggingService;
|
|
200
208
|
this.captureTiming = captureTiming;
|
|
201
209
|
this.integrationCapture = integrationCapture;
|
|
210
|
+
this.createFilteredUser = createFilteredUser;
|
|
202
211
|
|
|
203
212
|
this.captureTiming?.(PerformanceMarkType.JointSdkRoktKitInit);
|
|
204
213
|
|
|
@@ -314,6 +323,16 @@ export default class RoktManager {
|
|
|
314
323
|
return !!nav && nav.globalPrivacyControl === true;
|
|
315
324
|
}
|
|
316
325
|
|
|
326
|
+
// True when the kit can wait for the shopper's confirm click: signals are not
|
|
327
|
+
// suppressed and the browser exposes the composed path the detector reads.
|
|
328
|
+
// Decided from config alone, so tracker setup and teardown never change it.
|
|
329
|
+
public isCheckoutIntentLive(): boolean {
|
|
330
|
+
if (this.isCheckoutIntentSuppressed()) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
return typeof Event !== 'undefined' && isFunction(Event.prototype.composedPath);
|
|
334
|
+
}
|
|
335
|
+
|
|
317
336
|
// Reads `this.kit` at call time; before a kit attaches the signal is buffered
|
|
318
337
|
// with the path it was raised on.
|
|
319
338
|
public notifyCheckoutIntent(signal: ICheckoutIntentSignal, pathname: string = window.location.pathname): void {
|
|
@@ -489,11 +508,13 @@ export default class RoktManager {
|
|
|
489
508
|
}
|
|
490
509
|
}
|
|
491
510
|
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
511
|
+
// The identify above needs every identity, so currentUserIdentities stays unfiltered.
|
|
512
|
+
// Without createFilteredUser the connection's filters cannot be applied, so no identity is added.
|
|
513
|
+
const roktUser =
|
|
514
|
+
this.currentUser && this.createFilteredUser
|
|
515
|
+
? this.createFilteredUser(this.currentUser.getMPID())
|
|
516
|
+
: null;
|
|
517
|
+
const roktUserIdentities = roktUser?.getUserIdentities()?.userIdentities || {};
|
|
497
518
|
|
|
498
519
|
const timeOnSite = this.store?.getTimeOnSite?.();
|
|
499
520
|
const totalTimeOnSite = this.store?.getTotalTimeOnSite?.();
|
|
@@ -506,19 +527,25 @@ export default class RoktManager {
|
|
|
506
527
|
};
|
|
507
528
|
|
|
508
529
|
// Propagate email from current user identities if not already in attributes
|
|
509
|
-
if (
|
|
510
|
-
|
|
530
|
+
if (!enrichedAttributes.email) {
|
|
531
|
+
if (roktUserIdentities.email) {
|
|
532
|
+
enrichedAttributes.email = roktUserIdentities.email;
|
|
533
|
+
} else if (currentUserIdentities.email) {
|
|
534
|
+
this.logFilteredIdentity('email');
|
|
535
|
+
}
|
|
511
536
|
}
|
|
512
537
|
|
|
513
538
|
// Propagate emailsha256 from current user identities if not already in attributes
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
) {
|
|
539
|
+
if (
|
|
540
|
+
isValidHashedEmailIdentityType &&
|
|
541
|
+
!enrichedAttributes.emailsha256 &&
|
|
542
|
+
!enrichedAttributes[this.mappedEmailShaIdentityType]
|
|
543
|
+
) {
|
|
544
|
+
const hashedEmail = roktUserIdentities[this.mappedEmailShaIdentityType];
|
|
545
|
+
if (hashedEmail) {
|
|
521
546
|
enrichedAttributes.emailsha256 = hashedEmail;
|
|
547
|
+
} else if (currentUserIdentities[this.mappedEmailShaIdentityType]) {
|
|
548
|
+
this.logFilteredIdentity(this.mappedEmailShaIdentityType);
|
|
522
549
|
}
|
|
523
550
|
}
|
|
524
551
|
|
|
@@ -531,7 +558,7 @@ export default class RoktManager {
|
|
|
531
558
|
}
|
|
532
559
|
}
|
|
533
560
|
|
|
534
|
-
this.filters.filteredUser =
|
|
561
|
+
this.filters.filteredUser = roktUser || this.filters.filteredUser || null;
|
|
535
562
|
|
|
536
563
|
const enrichedOptions = {
|
|
537
564
|
...options,
|
|
@@ -544,6 +571,12 @@ export default class RoktManager {
|
|
|
544
571
|
}
|
|
545
572
|
}
|
|
546
573
|
|
|
574
|
+
private logFilteredIdentity(identityType: string): void {
|
|
575
|
+
this.logger?.verbose(
|
|
576
|
+
`mParticle.Rokt selectPlacements did not add the ${identityType} user identity to the attributes because the Rokt connection does not allow it`
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
547
580
|
/**
|
|
548
581
|
* Hashes attributes and returns both original and hashed versions
|
|
549
582
|
* with Rokt-compatible key names (like emailsha256, mobilesha256)
|
package/src/sdkRuntimeModels.ts
CHANGED
|
@@ -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;
|
package/src/serverModel.ts
CHANGED
|
@@ -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:
|
|
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 =
|
|
355
|
+
eventObject.LaunchReferral = cleanPageHref() || null;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
// FIXME: Remove duplicate occurence
|
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 => {
|