@mparticle/web-sdk 2.79.1 → 2.79.3

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.
@@ -1,7 +1,12 @@
1
1
  import { IMParticleWebSDKInstance } from './mp-instance';
2
+ import { BaseEvent } from './sdkRuntimeModels';
2
3
  import { EventType, MessageType } from './types';
3
4
 
4
5
  type HistoryStateMethod = History['pushState'];
6
+ type HistoryMethodName = 'pushState' | 'replaceState';
7
+ type NavigationSource = HistoryMethodName | 'popstate';
8
+
9
+ const HISTORY_METHODS: HistoryMethodName[] = ['pushState', 'replaceState'];
5
10
 
6
11
  const WRAPPED_MARKER = '__mpApvWrapped__';
7
12
 
@@ -9,259 +14,429 @@ type MarkedHistoryMethod = HistoryStateMethod & {
9
14
  [WRAPPED_MARKER]?: boolean;
10
15
  };
11
16
 
12
- export class PageViewTracker {
13
- mpInstance: IMParticleWebSDKInstance;
17
+ // All APV state hangs off one window key, and it is the public debugging
18
+ // contract: `window.__mpApv__` is what you inspect in a console to see whether
19
+ // tracking is live.
20
+ //
21
+ // It lives on `window` rather than in module scope because Next.js re-executes
22
+ // the SDK bundle on every SPA navigation: module-level state is reset each time,
23
+ // but `window` persists for the lifetime of the tab.
24
+ //
25
+ // One object rather than a key per flag, so resetting is a single reassignment.
26
+ // Nothing has to be deleted, and there is no ordering in which half the state
27
+ // survives the reset.
28
+ export const WIN_APV_KEY = '__mpApv__';
29
+
30
+ interface IApvState {
31
+ // The tracker that currently owns the history patch. May have been
32
+ // registered by a module scope that no longer exists.
33
+ tracker?: PageViewTracker;
34
+
35
+ // Guards the initial page view so repeated mParticle.init() calls from SPA
36
+ // re-renders don't fire duplicate logPageView() events for the same page.
37
+ initialPageViewFired: boolean;
38
+ }
14
39
 
15
- private lastPath: string | null = null;
16
- private isActive = false;
40
+ type WindowWithApv = Window & {
41
+ [WIN_APV_KEY]?: IApvState;
42
+ };
17
43
 
18
- private originalPushState: HistoryStateMethod | null = null;
19
- private originalReplaceState: HistoryStateMethod | null = null;
44
+ interface IPageViewData {
45
+ hostname: string;
46
+ title: string;
47
+ path: string;
48
+ }
20
49
 
21
- private pushStateWrapper: HistoryStateMethod | null = null;
22
- private replaceStateWrapper: HistoryStateMethod | null = null;
50
+ interface IPendingNavigation {
51
+ path: string;
52
+ timeoutId: ReturnType<typeof setTimeout>;
53
+ }
23
54
 
24
- private popStateListener: (() => void) | null = null;
55
+ // ---------------------------------------------------------------------------
56
+ // Pure helpers. No `this`, no globals, no side effects — callable and assertable
57
+ // on their own, which is where the interesting rules live.
58
+ // ---------------------------------------------------------------------------
59
+
60
+ // Dedup keys on pathname only: a query-string- or hash-only change is the same
61
+ // page and must not fire a view.
62
+ export const isNewPage = (
63
+ lastPath: string | null,
64
+ candidatePath: string
65
+ ): boolean => candidatePath !== lastPath;
66
+
67
+ export const supportsHistoryTracking = (win: Window | null): boolean =>
68
+ !!win &&
69
+ win.history !== undefined &&
70
+ typeof win.history.pushState === 'function' &&
71
+ typeof win.addEventListener === 'function';
72
+
73
+ // Mirrors the event shape of the public mParticle.logPageView(), but carries the
74
+ // path captured when the navigation was accepted rather than the live location.
75
+ export const buildPageViewEvent = (data: IPageViewData): BaseEvent => ({
76
+ messageType: MessageType.PageView,
77
+ name: 'PageView',
78
+ data: { ...data },
79
+ eventType: EventType.Unknown,
80
+ });
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Window-scoped APV state. Every read and write goes through these helpers, so
84
+ // neither the tracker nor mp-instance touches `window` directly and
85
+ // `typeof window` is checked in exactly one place.
86
+ // ---------------------------------------------------------------------------
87
+
88
+ const apvWindow = (): WindowWithApv | null =>
89
+ typeof window === 'undefined' ? null : (window as WindowWithApv);
90
+
91
+ const freshState = (): IApvState => ({ initialPageViewFired: false });
92
+
93
+ // Reads tolerate absent state, so a read never mutates `window`. Only the
94
+ // writers below create it.
95
+ const readState = (): IApvState | undefined => {
96
+ const win = apvWindow();
97
+ return win ? win[WIN_APV_KEY] : undefined;
98
+ };
25
99
 
26
- constructor(mpInstance: IMParticleWebSDKInstance) {
27
- this.mpInstance = mpInstance;
100
+ const writeState = (): IApvState | null => {
101
+ const win = apvWindow();
102
+ if (!win) {
103
+ return null;
28
104
  }
29
105
 
30
- private isSupportedEnvironment(): boolean {
31
- return (
32
- typeof window !== 'undefined' &&
33
- typeof window.history !== 'undefined' &&
34
- typeof window.history.pushState === 'function' &&
35
- typeof window.addEventListener === 'function'
36
- );
106
+ if (!win[WIN_APV_KEY]) {
107
+ win[WIN_APV_KEY] = freshState();
37
108
  }
38
109
 
39
- public init(): void {
40
- this.mpInstance.Logger.verbose(
41
- 'mParticle APV: [init] PageViewTracker Init'
42
- );
43
- if (!this.isSupportedEnvironment()) {
44
- this.mpInstance.Logger.verbose(
45
- 'mParticle APV: [init] unsupported environment (no History API), not starting'
46
- );
47
- return;
48
- }
110
+ return win[WIN_APV_KEY];
111
+ };
49
112
 
50
- if (this.isActive) {
51
- this.mpInstance.Logger.verbose(
52
- 'mParticle APV: [init] starting (teardown-first for idempotency)'
53
- );
54
- this.teardown();
55
- }
113
+ export const getActiveTracker = (): PageViewTracker | undefined => {
114
+ const state = readState();
115
+ return state ? state.tracker : undefined;
116
+ };
56
117
 
57
- this.isActive = true;
118
+ export const hasInitialPageViewFired = (): boolean => {
119
+ const state = readState();
120
+ return !!(state && state.initialPageViewFired);
121
+ };
58
122
 
59
- this.lastPath = this.getCurrentKey();
123
+ export const markInitialPageViewFired = (): void => {
124
+ const state = writeState();
125
+ if (state) {
126
+ state.initialPageViewFired = true;
127
+ }
128
+ };
60
129
 
61
- this.mpInstance.Logger.verbose(
62
- `mParticle APV: [init] seeded lastPath: ${this.lastPath}`
63
- );
130
+ // Returns the page to its pre-APV state: stops the active tracker (restoring the
131
+ // history methods it patched), then swaps in fresh state. The state object is the
132
+ // authoritative handle, so this reaches trackers whose owning module is gone.
133
+ export const resetPageViewTracking = (): void => {
134
+ const win = apvWindow();
135
+ if (!win) {
136
+ return;
137
+ }
64
138
 
65
- this.patchHistoryMethods();
66
- this.addNavigationListeners();
139
+ const active = getActiveTracker();
140
+ if (active) {
141
+ active.teardown();
142
+ }
67
143
 
68
- this.mpInstance.Logger.verbose(
69
- 'mParticle APV: [init] patched pushState/replaceState + listening for popstate'
70
- );
144
+ // One reassignment replaces every flag at once, so nothing can be left behind
145
+ // whatever shape the previous state was in.
146
+ win[WIN_APV_KEY] = freshState();
147
+ };
148
+
149
+ const setActiveTracker = (tracker: PageViewTracker): void => {
150
+ const state = writeState();
151
+ if (state) {
152
+ state.tracker = tracker;
71
153
  }
154
+ };
72
155
 
73
- private patchHistoryMethods(): void {
74
- const self = this;
156
+ const clearActiveTracker = (tracker: PageViewTracker): void => {
157
+ const state = readState();
158
+ if (state && state.tracker === tracker) {
159
+ state.tracker = undefined;
160
+ }
161
+ };
75
162
 
76
- const installed = window.history.pushState as MarkedHistoryMethod;
77
- if (installed[WRAPPED_MARKER]) {
78
- this.mpInstance.Logger.verbose(
79
- 'mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap'
80
- );
81
- return;
82
- }
163
+ const currentPathname = (): string => window.location.pathname;
164
+
165
+ // ---------------------------------------------------------------------------
166
+ // History patching
167
+ // ---------------------------------------------------------------------------
168
+
169
+ // Wraps pushState/replaceState so `onNavigate` runs after the real method, and
170
+ // returns the single function that undoes it — or null when nothing was
171
+ // installed (history is already wrapped, or a frozen/sealed History rejected the
172
+ // assignment). Handing back one undo closure keeps the wrapper and original
173
+ // references out of the tracker entirely: there is no half-patched state for a
174
+ // caller to inspect, and no way to restore the wrong pair.
175
+ export const patchHistory = (
176
+ onNavigate: (source: NavigationSource) => void,
177
+ log: (message: string) => void
178
+ ): (() => void) | null => {
179
+ if ((window.history.pushState as MarkedHistoryMethod)[WRAPPED_MARKER]) {
180
+ log(
181
+ '[patch] history already wrapped, skipping to avoid double-wrap — STACKED WRAPPER DETECTED'
182
+ );
183
+ return null;
184
+ }
83
185
 
84
- const originalPushState = window.history.pushState;
85
- const originalReplaceState = window.history.replaceState;
86
- this.originalPushState = originalPushState;
87
- this.originalReplaceState = originalReplaceState;
186
+ const originals = {} as Record<HistoryMethodName, HistoryStateMethod>;
187
+ const wrappers = {} as Record<HistoryMethodName, HistoryStateMethod>;
88
188
 
89
- const pushStateWrapper = function(
90
- this: History,
91
- ...args: Parameters<HistoryStateMethod>
92
- ): void {
93
- const result = originalPushState.apply(this, args);
94
- self.safeHandleNavigation('pushState');
95
- return result;
96
- };
189
+ HISTORY_METHODS.forEach(name => {
190
+ const original = window.history[name];
191
+ originals[name] = original;
97
192
 
98
- const replaceStateWrapper = function(
193
+ const wrapper = function(
99
194
  this: History,
100
195
  ...args: Parameters<HistoryStateMethod>
101
196
  ): void {
102
- const result = originalReplaceState.apply(this, args);
103
- self.safeHandleNavigation('replaceState');
197
+ const result = original.apply(this, args);
198
+ onNavigate(name);
104
199
  return result;
105
200
  };
106
201
 
107
- Object.defineProperty(pushStateWrapper, WRAPPED_MARKER, {
108
- value: true,
109
- enumerable: false,
110
- });
111
- Object.defineProperty(replaceStateWrapper, WRAPPED_MARKER, {
202
+ Object.defineProperty(wrapper, WRAPPED_MARKER, {
112
203
  value: true,
113
204
  enumerable: false,
114
205
  });
115
206
 
116
- this.pushStateWrapper = pushStateWrapper;
117
- this.replaceStateWrapper = replaceStateWrapper;
207
+ wrappers[name] = wrapper;
208
+ });
209
+
210
+ // Restore per method: a third party may have patched one of the two on top of
211
+ // ours after we installed. Clobbering theirs would break their tracking, so
212
+ // leave anything that is no longer ours in place.
213
+ const restore = (): void =>
214
+ HISTORY_METHODS.forEach(name => {
215
+ if (window.history[name] === wrappers[name]) {
216
+ window.history[name] = originals[name];
217
+ log(`[teardown] restored original ${name}`);
218
+ } else {
219
+ log(
220
+ `[teardown] ${name} no longer ours; leaving in place, gating callback to no-op`
221
+ );
222
+ }
223
+ });
118
224
 
225
+ try {
226
+ HISTORY_METHODS.forEach(name => {
227
+ window.history[name] = wrappers[name];
228
+ });
229
+ } catch (e) {
230
+ log(
231
+ `[error] failed to patch history methods (frozen/sealed), rolling back: ${e}`
232
+ );
119
233
  try {
120
- window.history.pushState = pushStateWrapper;
121
- window.history.replaceState = replaceStateWrapper;
122
- } catch (e) {
123
- this.mpInstance.Logger.verbose(
124
- `mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ${e}`
234
+ restore();
235
+ } catch (restoreError) {
236
+ log(
237
+ `[error] failed to restore history methods after patch failure: ${restoreError}`
125
238
  );
126
- try {
127
- if (window.history.pushState === pushStateWrapper) {
128
- window.history.pushState = originalPushState;
129
- }
130
- if (window.history.replaceState === replaceStateWrapper) {
131
- window.history.replaceState = originalReplaceState;
132
- }
133
- } catch (restoreError) {
134
- this.mpInstance.Logger.verbose(
135
- `mParticle APV: [error] failed to restore history methods after patch failure: ${restoreError}`
136
- );
137
- }
138
- this.pushStateWrapper = null;
139
- this.replaceStateWrapper = null;
140
- this.originalPushState = null;
141
- this.originalReplaceState = null;
142
239
  }
240
+ return null;
143
241
  }
144
242
 
145
- private addNavigationListeners(): void {
243
+ return restore;
244
+ };
245
+
246
+ // ---------------------------------------------------------------------------
247
+ // Tracker
248
+ // ---------------------------------------------------------------------------
249
+
250
+ export class PageViewTracker {
251
+ private readonly mpInstance: IMParticleWebSDKInstance;
252
+
253
+ private lastPath: string | null = null;
254
+ private active = false;
255
+ private pendingNavigations: IPendingNavigation[] = [];
256
+
257
+ private undoHistoryPatch: (() => void) | null = null;
258
+ private popStateListener: (() => void) | null = null;
259
+
260
+ constructor(mpInstance: IMParticleWebSDKInstance) {
261
+ this.mpInstance = mpInstance;
262
+ }
263
+
264
+ // True while this tracker owns the history patch and is listening for
265
+ // navigations. Flips to false on teardown, including the teardown a
266
+ // successor tracker performs during a handoff. Read-only, and the one thing
267
+ // worth checking on `window.__mpApv__.tracker` from a console.
268
+ public get isActive(): boolean {
269
+ return this.active;
270
+ }
271
+
272
+ public init(): void {
273
+ this.log('[init] PageViewTracker Init');
274
+
275
+ if (!supportsHistoryTracking(apvWindow())) {
276
+ this.log(
277
+ '[init] unsupported environment (no History API), not starting'
278
+ );
279
+ return;
280
+ }
281
+
282
+ // Take over from whichever tracker is currently active: one left behind by
283
+ // a previous module load, or this same tracker on a repeat
284
+ // mParticle.init(). Their pending navigations are transferred rather than
285
+ // dropped — a route change may have queued a deferred fire that has not
286
+ // flushed yet, and dropping it loses a page view entirely.
287
+ const active = getActiveTracker();
288
+ let inheritedPaths = this.retire(active);
289
+ if (this.active && active !== this) {
290
+ inheritedPaths = inheritedPaths.concat(this.retire(this));
291
+ }
292
+
293
+ this.active = true;
294
+ this.lastPath = currentPathname();
295
+ this.log(`[init] seeded lastPath: ${this.lastPath}`);
296
+
297
+ this.undoHistoryPatch = patchHistory(
298
+ source => this.safeHandleNavigation(source),
299
+ message => this.log(message)
300
+ );
146
301
  this.popStateListener = () => this.safeHandleNavigation('popstate');
147
302
  window.addEventListener('popstate', this.popStateListener);
303
+
304
+ setActiveTracker(this);
305
+
306
+ this.log(
307
+ '[init] patched pushState/replaceState + listening for popstate'
308
+ );
309
+
310
+ inheritedPaths.forEach(path => this.scheduleFire(path));
311
+ }
312
+
313
+ public teardown(): void {
314
+ // Discard pending deferred fires (e.g. AutoLogPageView switched off on
315
+ // re-init). A handoff calls takePendingNavigations() first, so by this
316
+ // point those paths are already transferred rather than dropped.
317
+ this.takePendingNavigations();
318
+
319
+ if (this.popStateListener) {
320
+ window.removeEventListener('popstate', this.popStateListener);
321
+ this.popStateListener = null;
322
+ }
323
+
324
+ if (this.undoHistoryPatch) {
325
+ this.undoHistoryPatch();
326
+ this.undoHistoryPatch = null;
327
+ }
328
+
329
+ this.active = false;
330
+ clearActiveTracker(this);
148
331
  }
149
332
 
150
- private safeHandleNavigation(source: string): void {
333
+ // Stops the outgoing tracker and returns the paths it had queued so this
334
+ // tracker can re-schedule them once it is active. Private, but reachable
335
+ // across instances: the handoff protocol stays internal to the class.
336
+ private retire(outgoing: PageViewTracker | undefined): string[] {
337
+ if (!outgoing) {
338
+ return [];
339
+ }
340
+
341
+ this.log(
342
+ outgoing === this
343
+ ? '[init] retiring this tracker for a repeat mParticle.init() — transferring pending navigations and tearing down'
344
+ : '[init] retiring a tracker from a previous module load — transferring pending navigations and tearing down'
345
+ );
346
+
347
+ const paths = outgoing.takePendingNavigations();
348
+ outgoing.teardown();
349
+ return paths;
350
+ }
351
+
352
+ // Cancels this tracker's deferred fires and hands back their captured paths.
353
+ private takePendingNavigations(): string[] {
354
+ const pending = this.pendingNavigations;
355
+ this.pendingNavigations = [];
356
+ pending.forEach(p => clearTimeout(p.timeoutId));
357
+ return pending.map(p => p.path);
358
+ }
359
+
360
+ private safeHandleNavigation(source: NavigationSource): void {
361
+ // An orphaned wrapper still calls in: teardown can only restore a history
362
+ // method that is still ours, so one a third party patched over stays
363
+ // installed and keeps firing at a dead tracker. Bail before touching
364
+ // lastPath or queueing a fire the flush would just abort. Guarding here
365
+ // rather than at each wrapper also covers the popstate path.
366
+ if (!this.active) {
367
+ return;
368
+ }
369
+
151
370
  try {
152
371
  this.handleNavigation(source);
153
372
  } catch (e) {
154
- this.mpInstance.Logger.verbose(
155
- `mParticle APV: [error] navigation handler threw (${source}), page view skipped: ${e}`
373
+ this.log(
374
+ `[error] navigation handler threw (${source}), page view skipped: ${e}`
156
375
  );
157
376
  }
158
377
  }
159
378
 
160
- private getCurrentKey(): string {
161
- return window.location.pathname;
162
- }
163
-
164
- private handleNavigation(source: string): void {
165
- const candidatePath = this.getCurrentKey();
379
+ private handleNavigation(source: NavigationSource): void {
380
+ const candidatePath = currentPathname();
166
381
 
167
- this.mpInstance.Logger.verbose(
168
- `mParticle APV: [detect] navigation signal (source: ${source}, candidatePath: ${candidatePath}, lastPath: ${this.lastPath})`
382
+ this.log(
383
+ `[detect] navigation signal (source: ${source}, candidatePath: ${candidatePath}, lastPath: ${this.lastPath})`
169
384
  );
170
385
 
171
- if (candidatePath === this.lastPath) {
172
- this.mpInstance.Logger.verbose(
173
- `mParticle APV: [dedupe] pathname unchanged, skipping (source: ${source}, path: ${candidatePath})`
386
+ if (!isNewPage(this.lastPath, candidatePath)) {
387
+ this.log(
388
+ `[dedupe] pathname unchanged, skipping (source: ${source}, path: ${candidatePath})`
174
389
  );
175
390
  return;
176
391
  }
177
392
 
178
- this.mpInstance.Logger.verbose(
179
- `mParticle APV: [accept] pathname changed, scheduling fire (source: ${source}, from: ${this.lastPath}, to: ${candidatePath})`
393
+ this.log(
394
+ `[accept] pathname changed, scheduling fire (source: ${source}, from: ${this.lastPath}, to: ${candidatePath})`
180
395
  );
181
396
  this.lastPath = candidatePath;
397
+ this.scheduleFire(candidatePath);
398
+ }
399
+
400
+ // Deferred by a macrotask so the router's post-navigation render commit has a
401
+ // chance to update document.title before the event is built. The path is
402
+ // snapshotted now because it is already settled, whereas a same-tick
403
+ // navigation would overwrite window.location before the flush reads it.
404
+ private scheduleFire(path: string): void {
405
+ const timeoutId = setTimeout(() => {
406
+ this.pendingNavigations = this.pendingNavigations.filter(
407
+ p => p.timeoutId !== timeoutId
408
+ );
182
409
 
183
- // Snapshot the path now: it is already settled at this point, and a
184
- // same-tick navigation would otherwise overwrite window.location
185
- // before the deferred flush reads it. The title is intentionally read
186
- // later (in the deferred flush) so the router's post-navigation render
187
- // commit has a chance to update document.title first.
188
- const capturedPath = candidatePath;
189
-
190
- setTimeout(() => {
191
- if (!this.isActive) {
192
- this.mpInstance.Logger.verbose(
193
- 'mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)'
410
+ if (!this.active) {
411
+ this.log(
412
+ '[defer] fire aborted, tracker inactive (torn down before flush)'
194
413
  );
195
414
  return;
196
415
  }
197
416
 
198
417
  this.mpInstance._SessionManager.resetSessionTimer();
199
- this.firePageView(capturedPath);
418
+ this.firePageView(path);
200
419
  }, 0);
420
+
421
+ this.pendingNavigations.push({ path, timeoutId });
201
422
  }
202
423
 
203
- // Mirrors the event shape of the public mParticle.logPageView(), but carries
204
- // the captured SPA path rather than reading the live location.
205
424
  private firePageView(path: string): void {
206
425
  const title = window.document.title;
426
+ const event = buildPageViewEvent({
427
+ hostname: window.location.hostname,
428
+ title,
429
+ path,
430
+ });
207
431
 
208
- this.mpInstance.Logger.verbose(
209
- `mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ${path}, title: ${title})`
432
+ this.log(
433
+ `[fire] deferred flush -> _Events.logEvent(PageView) (path: ${path}, title: ${title})`
210
434
  );
211
435
 
212
- this.mpInstance._Events.logEvent({
213
- messageType: MessageType.PageView,
214
- name: 'PageView',
215
- data: {
216
- hostname: window.location.hostname,
217
- title,
218
- path,
219
- },
220
- eventType: EventType.Unknown,
221
- });
436
+ this.mpInstance._Events.logEvent(event);
222
437
  }
223
438
 
224
- public teardown(): void {
225
- if (this.popStateListener) {
226
- window.removeEventListener('popstate', this.popStateListener);
227
- this.popStateListener = null;
228
- }
229
- const pushStateStillOurs =
230
- this.pushStateWrapper !== null &&
231
- window.history.pushState === this.pushStateWrapper;
232
- if (this.originalPushState) {
233
- if (pushStateStillOurs) {
234
- window.history.pushState = this.originalPushState;
235
- this.mpInstance.Logger.verbose(
236
- 'mParticle APV: [teardown] restored original pushState'
237
- );
238
- } else {
239
- this.mpInstance.Logger.verbose(
240
- 'mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op'
241
- );
242
- }
243
- this.originalPushState = null;
244
- this.pushStateWrapper = null;
245
- }
246
-
247
- const replaceStateStillOurs =
248
- this.replaceStateWrapper !== null &&
249
- window.history.replaceState === this.replaceStateWrapper;
250
- if (this.originalReplaceState) {
251
- if (replaceStateStillOurs) {
252
- window.history.replaceState = this.originalReplaceState;
253
- this.mpInstance.Logger.verbose(
254
- 'mParticle APV: [teardown] restored original replaceState'
255
- );
256
- } else {
257
- this.mpInstance.Logger.verbose(
258
- 'mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op'
259
- );
260
- }
261
- this.originalReplaceState = null;
262
- this.replaceStateWrapper = null;
263
- }
264
-
265
- this.isActive = false;
439
+ private log(message: string): void {
440
+ this.mpInstance.Logger.verbose(`mParticle APV: ${message}`);
266
441
  }
267
442
  }
@@ -297,6 +297,7 @@ export interface IMParticleInstanceManager extends MParticleWebSDK {
297
297
 
298
298
  // Public Methods
299
299
  getInstance(instanceName?: string): IMParticleWebSDKInstance;
300
+ uiwebviewBridgeName?: string;
300
301
  }
301
302
 
302
303
  // Used in cases where server requires booleans as strings
@@ -373,7 +374,7 @@ export interface SDKHelpersApi {
373
374
  createXHR?(cb: () => void): XMLHttpRequest;
374
375
  extend?(...args: any[]);
375
376
  findKeyInObject?(obj: any, key: string): string;
376
- parseNumber?(value: string | number): number;
377
+ parseNumber(value: string | number): number;
377
378
  generateUniqueId();
378
379
  generateHash?(value: string): number;
379
380
  // https://go.mparticle.com/work/SQDSDKS-6317
@@ -382,11 +383,11 @@ export interface SDKHelpersApi {
382
383
  parseStringOrNumber?(value: string | number): string | number | null;
383
384
  inArray?(items: any[], value: any): boolean;
384
385
  converted?(s: string): string;
385
- filterUserIdentitiesForForwarders?(
386
+ filterUserIdentitiesForForwarders(
386
387
  userIdentities: Dictionary,
387
388
  filterList: number[]
388
389
  ): Dictionary;
389
- filterUserAttributes?(
390
+ filterUserAttributes(
390
391
  userAttributes: Dictionary,
391
392
  filterList: number[]
392
393
  ): Dictionary;
package/src/store.ts CHANGED
@@ -207,6 +207,8 @@ export interface IStore {
207
207
  pixelConfigurations: IPixelConfiguration[];
208
208
  integrationDelayTimeoutStart: number; // UNIX Timestamp
209
209
  webviewBridgeEnabled?: boolean;
210
+ bridgeV1Available?: boolean;
211
+ bridgeV2Available?: boolean;
210
212
  wrapperSDKInfo: WrapperSDKInfo;
211
213
  roktAccountId: string | null;
212
214
  integrationName: string | null;