@mparticle/web-sdk 2.79.3 → 2.80.1

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 +220 -574
  2. package/dist/mparticle.esm.js +220 -574
  3. package/dist/mparticle.js +1489 -1793
  4. package/dist/types/src/configAPIClient.d.ts +1 -0
  5. package/dist/types/src/ecommerce.d.ts +3 -53
  6. package/dist/types/src/ecommerce.interfaces.d.ts +11 -12
  7. package/dist/types/src/events.interfaces.d.ts +1 -1
  8. package/dist/types/src/forwarders.d.ts +4 -26
  9. package/dist/types/src/forwarders.interfaces.d.ts +41 -10
  10. package/dist/types/src/identity-utils.d.ts +3 -3
  11. package/dist/types/src/identity.d.ts +3 -0
  12. package/dist/types/src/identity.interfaces.d.ts +45 -11
  13. package/dist/types/src/identityApiClient.d.ts +5 -4
  14. package/dist/types/src/mp-instance.d.ts +2 -2
  15. package/dist/types/src/pageViewTracker.d.ts +12 -5
  16. package/dist/types/src/persistence.d.ts +3 -44
  17. package/dist/types/src/persistence.interfaces.d.ts +12 -13
  18. package/dist/types/src/sdkRuntimeModels.d.ts +3 -2
  19. package/dist/types/src/store.d.ts +5 -3
  20. package/package.json +1 -1
  21. package/src/configAPIClient.ts +1 -0
  22. package/src/ecommerce.interfaces.ts +12 -21
  23. package/src/{ecommerce.js → ecommerce.ts} +144 -73
  24. package/src/events.interfaces.ts +1 -1
  25. package/src/events.ts +10 -2
  26. package/src/forwarders.interfaces.ts +73 -10
  27. package/src/{forwarders.js → forwarders.ts} +214 -176
  28. package/src/helpers.ts +7 -3
  29. package/src/identity-utils.ts +2 -2
  30. package/src/identity.interfaces.ts +58 -14
  31. package/src/{identity.js → identity.ts} +237 -192
  32. package/src/identityApiClient.ts +9 -9
  33. package/src/mp-instance.ts +2 -2
  34. package/src/pageViewTracker.ts +176 -42
  35. package/src/persistence.interfaces.ts +12 -13
  36. package/src/persistence.ts +1210 -0
  37. package/src/sdkRuntimeModels.ts +3 -1
  38. package/src/store.ts +5 -3
  39. package/src/persistence.js +0 -985
@@ -5,19 +5,19 @@ import {
5
5
  XHRUploader,
6
6
  IFetchPayload,
7
7
  } from './uploaders';
8
- import { CACHE_HEADER } from './identity-utils';
8
+ import { CACHE_HEADER, IKnownIdentities } from './identity-utils';
9
9
  import { obfuscateData, parseNumber, valueof, getErrorMessage } from './utils';
10
10
  import {
11
11
  IAliasCallback,
12
- IAliasRequest,
12
+ IAliasNetworkRequest,
13
13
  IdentityAPIMethod,
14
14
  IIdentity,
15
15
  IIdentityAPIRequestData,
16
+ IIdentityAPIModifyRequestData,
16
17
  } from './identity.interfaces';
17
18
  import {
18
19
  IdentityApiData,
19
20
  MPID,
20
- UserIdentities,
21
21
  } from '@mparticle/web-sdk';
22
22
  import {
23
23
  IdentityCallback,
@@ -33,17 +33,17 @@ const { Modify } = IdentityMethods;
33
33
 
34
34
  export interface IIdentityApiClient {
35
35
  sendAliasRequest: (
36
- aliasRequest: IAliasRequest,
36
+ aliasRequest: IAliasNetworkRequest,
37
37
  aliasCallback: IAliasCallback
38
38
  ) => Promise<void>;
39
39
  sendIdentityRequest: (
40
- identityApiRequest: IIdentityAPIRequestData,
40
+ identityApiRequest: IIdentityAPIRequestData | IIdentityAPIModifyRequestData,
41
41
  method: IdentityAPIMethod,
42
42
  callback: IdentityCallback,
43
43
  originalIdentityApiData: IdentityApiData,
44
44
  parseIdentityResponse: IIdentity['parseIdentityResponse'],
45
45
  mpid: MPID,
46
- knownIdentities: UserIdentities
46
+ knownIdentities?: IKnownIdentities
47
47
  ) => Promise<void>;
48
48
  getUploadUrl: (method: IdentityAPIMethod, mpid: MPID) => string;
49
49
  getIdentityResponseFromFetch: (
@@ -86,7 +86,7 @@ export default function IdentityAPIClient(
86
86
  mpInstance: IMParticleWebSDKInstance
87
87
  ) {
88
88
  this.sendAliasRequest = async function(
89
- aliasRequest: IAliasRequest,
89
+ aliasRequest: IAliasNetworkRequest,
90
90
  aliasCallback: IAliasCallback
91
91
  ) {
92
92
  const { Logger } = mpInstance;
@@ -186,13 +186,13 @@ export default function IdentityAPIClient(
186
186
  };
187
187
 
188
188
  this.sendIdentityRequest = async function(
189
- identityApiRequest: IIdentityAPIRequestData,
189
+ identityApiRequest: IIdentityAPIRequestData | IIdentityAPIModifyRequestData,
190
190
  method: IdentityAPIMethod,
191
191
  callback: IdentityCallback,
192
192
  originalIdentityApiData: IdentityApiData,
193
193
  parseIdentityResponse: IIdentity['parseIdentityResponse'],
194
194
  mpid: MPID,
195
- knownIdentities: UserIdentities
195
+ knownIdentities?: IKnownIdentities
196
196
  ) {
197
197
  if (mpInstance._RoktManager?.isInitialized) {
198
198
  mpInstance._Store.identifyRequestCount = (mpInstance._Store.identifyRequestCount || 0) + 1;
@@ -37,7 +37,7 @@ import Identity from './identity';
37
37
  import Consent, { IConsent } from './consent';
38
38
  import KitBlocker from './kitBlocking';
39
39
  import ConfigAPIClient, { IKitConfigs } from './configAPIClient';
40
- import IdentityAPIClient from './identityApiClient';
40
+ import IdentityAPIClient, { IIdentityApiClient } from './identityApiClient';
41
41
  import { isFunction, parseConfig, valueof, generateDeprecationMessage, extend } from './utils';
42
42
  import { DisabledVault, LocalStorageVault } from './vault';
43
43
  import { removeExpiredIdentityCacheDates, hasExplicitIdentifier } from './identity-utils';
@@ -92,7 +92,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
92
92
  _ForwardingStatsUploader: IForwardingStatsUploader;
93
93
  _Helpers: SDKHelpersApi;
94
94
  _Identity: IIdentity;
95
- _IdentityAPIClient: typeof IdentityAPIClient;
95
+ _IdentityAPIClient: IIdentityApiClient;
96
96
  _IntegrationCapture: IntegrationCapture;
97
97
  _NativeSdkHelpers: INativeSdkHelpers;
98
98
  _PageViewTracker?: PageViewTracker;
@@ -1,6 +1,7 @@
1
1
  import { IMParticleWebSDKInstance } from './mp-instance';
2
2
  import { BaseEvent } from './sdkRuntimeModels';
3
3
  import { EventType, MessageType } from './types';
4
+ import { Dictionary, getHref, queryStringParser } from './utils';
4
5
 
5
6
  type HistoryStateMethod = History['pushState'];
6
7
  type HistoryMethodName = 'pushState' | 'replaceState';
@@ -41,14 +42,71 @@ type WindowWithApv = Window & {
41
42
  [WIN_APV_KEY]?: IApvState;
42
43
  };
43
44
 
44
- interface IPageViewData {
45
+ // The query params an auto page view may carry. An allowlist, not a denylist:
46
+ // partner URLs routinely hold order ids, email addresses and session tokens, and
47
+ // none of those should reach the event stream because someone forgot to exclude
48
+ // them. Anything absent from this list is dropped.
49
+ //
50
+ // SECURITY: `code`, `state` and `nonce` are the OAuth 2.0 / OIDC authorization
51
+ // code and the CSRF/replay tokens. They are credentials until redeemed, and
52
+ // attaching them here persists them in the event store and forwards them to
53
+ // every configured kit. They are on the list by explicit product decision —
54
+ // deleting that line is the whole of the fix if that decision is revisited.
55
+ export const ALLOWED_QUERY_PARAMS: string[] = [
56
+ // Campaign attribution
57
+ 'utm_source',
58
+ 'utm_medium',
59
+ 'utm_campaign',
60
+ 'utm_term',
61
+ 'utm_content',
62
+ 'utm_id',
63
+
64
+ // Ad-network click ids
65
+ 'gclid',
66
+ 'gbraid',
67
+ 'wbraid',
68
+ 'fbclid',
69
+ 'msclkid',
70
+ 'ttclid',
71
+ 'twclid',
72
+ 'li_fat_id',
73
+ 'dclid',
74
+
75
+ // OAuth / OIDC — see the SECURITY note above
76
+ 'client_id',
77
+ 'redirect_uri',
78
+ 'response_type',
79
+ 'scope',
80
+ 'state',
81
+ 'code',
82
+ 'nonce',
83
+
84
+ // Pagination and search
85
+ 'page',
86
+ 'limit',
87
+ 'offset',
88
+ 'cursor',
89
+ 'per_page',
90
+ 'q',
91
+ 'search',
92
+
93
+ // Referral
94
+ 'ref',
95
+ 'referrer',
96
+ ];
97
+
98
+ interface IPageSnapshot {
99
+ path: string;
100
+ params: Dictionary<string>;
101
+ }
102
+
103
+ interface IPageViewData extends IPageSnapshot {
45
104
  hostname: string;
46
105
  title: string;
47
- path: string;
48
106
  }
49
107
 
50
108
  interface IPendingNavigation {
51
- path: string;
109
+ page: IPageSnapshot;
52
110
  timeoutId: ReturnType<typeof setTimeout>;
53
111
  }
54
112
 
@@ -57,12 +115,46 @@ interface IPendingNavigation {
57
115
  // on their own, which is where the interesting rules live.
58
116
  // ---------------------------------------------------------------------------
59
117
 
60
- // Dedup keys on pathname only: a query-string- or hash-only change is the same
61
- // page and must not fire a view.
118
+ // Pulls the allowlisted query params off a URL. Delegates to the SDK's own
119
+ // parser, which lowercases keys (so `?UTM_Source=` and `?utm_source=` land on one
120
+ // attribute), drops empty values, and carries the fallback for browsers without
121
+ // URLSearchParams. Tolerates an empty href, so SSR yields no params rather than
122
+ // throwing.
123
+ export const allowedQueryParams = (href: string): Dictionary<string> =>
124
+ queryStringParser(href, ALLOWED_QUERY_PARAMS);
125
+
126
+ // The captured params, in allowlist order. Ordering comes from the constant
127
+ // rather than a sort: it is deterministic without needing a comparator, and it
128
+ // does not depend on the object's insertion order, so reordering the query string
129
+ // cannot produce a different key.
130
+ const capturedNames = (params: Dictionary<string>): string[] =>
131
+ ALLOWED_QUERY_PARAMS.filter(name => name in params);
132
+
133
+ // The dedup key: pathname plus the allowlisted params in a fixed order, so that
134
+ // reordering the query string is not a new page. Params outside the allowlist
135
+ // never make it into `page.params` and so cannot key a view — nor can the hash.
136
+ //
137
+ // Values are re-encoded because queryStringParser hands them back DECODED. A
138
+ // value holding the pair delimiters would otherwise serialize exactly like two
139
+ // separate params — `{q: 'a&search=b'}` and `{q: 'a', search: 'b'}` both becoming
140
+ // `q=a&search=b` — and dedup would treat a real navigation between them as the
141
+ // same page and drop the view. `q`, `search` and `redirect_uri` carry `&` and `=`
142
+ // routinely, so this is reachable rather than theoretical.
143
+ export const pageKey = (page: IPageSnapshot): string => {
144
+ const query = capturedNames(page.params)
145
+ .map(name => `${name}=${encodeURIComponent(page.params[name])}`)
146
+ .join('&');
147
+
148
+ return query ? `${page.path}?${query}` : page.path;
149
+ };
150
+
151
+ // Dedup keys on the pageKey above: a change to any captured param is a new page
152
+ // (`?page=2` is a distinct pagination view), while a hash-only change, or a
153
+ // change confined to params we do not capture, is the same page.
62
154
  export const isNewPage = (
63
- lastPath: string | null,
64
- candidatePath: string
65
- ): boolean => candidatePath !== lastPath;
155
+ lastKey: string | null,
156
+ candidateKey: string
157
+ ): boolean => candidateKey !== lastKey;
66
158
 
67
159
  export const supportsHistoryTracking = (win: Window | null): boolean =>
68
160
  !!win &&
@@ -71,11 +163,25 @@ export const supportsHistoryTracking = (win: Window | null): boolean =>
71
163
  typeof win.addEventListener === 'function';
72
164
 
73
165
  // 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 => ({
166
+ // path and query params captured when the navigation was accepted rather than the
167
+ // live location.
168
+ export const buildPageViewEvent = ({
169
+ params,
170
+ hostname,
171
+ title,
172
+ path,
173
+ }: IPageViewData): BaseEvent => ({
76
174
  messageType: MessageType.PageView,
77
175
  name: 'PageView',
78
- data: { ...data },
176
+ // Params spread first, then the core fields by name, so a core field always
177
+ // wins. No allowlist entry collides with hostname/title/path today; naming
178
+ // them here is what keeps a later addition from silently overwriting one.
179
+ data: {
180
+ ...params,
181
+ hostname,
182
+ title,
183
+ path,
184
+ },
79
185
  eventType: EventType.Unknown,
80
186
  });
81
187
 
@@ -160,7 +266,25 @@ const clearActiveTracker = (tracker: PageViewTracker): void => {
160
266
  }
161
267
  };
162
268
 
163
- const currentPathname = (): string => window.location.pathname;
269
+ const currentPage = (): IPageSnapshot => ({
270
+ path: window.location.pathname,
271
+ params: allowedQueryParams(getHref()),
272
+ });
273
+
274
+ // Log-safe description of a page: the path, plus the NAMES of the captured
275
+ // params. Values are deliberately omitted — verbose logging goes to the console,
276
+ // and session-replay tooling ships console output off-domain, which is not a
277
+ // place for an OAuth code or a consumer's search terms.
278
+ // Only reachable before init() assigns lastPage, which is ahead of any navigation
279
+ // handler being able to run, so this returns nothing rather than a sentinel.
280
+ const describePage = (page: IPageSnapshot | null): string => {
281
+ if (!page) {
282
+ return '';
283
+ }
284
+
285
+ const names = capturedNames(page.params);
286
+ return names.length ? `${page.path} (params: ${names.join()})` : page.path;
287
+ };
164
288
 
165
289
  // ---------------------------------------------------------------------------
166
290
  // History patching
@@ -250,7 +374,7 @@ export const patchHistory = (
250
374
  export class PageViewTracker {
251
375
  private readonly mpInstance: IMParticleWebSDKInstance;
252
376
 
253
- private lastPath: string | null = null;
377
+ private lastPage: IPageSnapshot | null = null;
254
378
  private active = false;
255
379
  private pendingNavigations: IPendingNavigation[] = [];
256
380
 
@@ -285,14 +409,14 @@ export class PageViewTracker {
285
409
  // dropped — a route change may have queued a deferred fire that has not
286
410
  // flushed yet, and dropping it loses a page view entirely.
287
411
  const active = getActiveTracker();
288
- let inheritedPaths = this.retire(active);
412
+ let inheritedPages = this.retire(active);
289
413
  if (this.active && active !== this) {
290
- inheritedPaths = inheritedPaths.concat(this.retire(this));
414
+ inheritedPages = inheritedPages.concat(this.retire(this));
291
415
  }
292
416
 
293
417
  this.active = true;
294
- this.lastPath = currentPathname();
295
- this.log(`[init] seeded lastPath: ${this.lastPath}`);
418
+ this.lastPage = currentPage();
419
+ this.log(`[init] seeded lastPage: ${describePage(this.lastPage)}`);
296
420
 
297
421
  this.undoHistoryPatch = patchHistory(
298
422
  source => this.safeHandleNavigation(source),
@@ -307,7 +431,7 @@ export class PageViewTracker {
307
431
  '[init] patched pushState/replaceState + listening for popstate'
308
432
  );
309
433
 
310
- inheritedPaths.forEach(path => this.scheduleFire(path));
434
+ inheritedPages.forEach(page => this.scheduleFire(page));
311
435
  }
312
436
 
313
437
  public teardown(): void {
@@ -330,10 +454,10 @@ export class PageViewTracker {
330
454
  clearActiveTracker(this);
331
455
  }
332
456
 
333
- // Stops the outgoing tracker and returns the paths it had queued so this
457
+ // Stops the outgoing tracker and returns the pages it had queued so this
334
458
  // tracker can re-schedule them once it is active. Private, but reachable
335
459
  // across instances: the handoff protocol stays internal to the class.
336
- private retire(outgoing: PageViewTracker | undefined): string[] {
460
+ private retire(outgoing: PageViewTracker | undefined): IPageSnapshot[] {
337
461
  if (!outgoing) {
338
462
  return [];
339
463
  }
@@ -344,17 +468,17 @@ export class PageViewTracker {
344
468
  : '[init] retiring a tracker from a previous module load — transferring pending navigations and tearing down'
345
469
  );
346
470
 
347
- const paths = outgoing.takePendingNavigations();
471
+ const pages = outgoing.takePendingNavigations();
348
472
  outgoing.teardown();
349
- return paths;
473
+ return pages;
350
474
  }
351
475
 
352
- // Cancels this tracker's deferred fires and hands back their captured paths.
353
- private takePendingNavigations(): string[] {
476
+ // Cancels this tracker's deferred fires and hands back their captured pages.
477
+ private takePendingNavigations(): IPageSnapshot[] {
354
478
  const pending = this.pendingNavigations;
355
479
  this.pendingNavigations = [];
356
- pending.forEach(p => clearTimeout(p.timeoutId));
357
- return pending.map(p => p.path);
480
+ pending.forEach(({ timeoutId }) => clearTimeout(timeoutId));
481
+ return pending.map(({ page }) => page);
358
482
  }
359
483
 
360
484
  private safeHandleNavigation(source: NavigationSource): void {
@@ -377,31 +501,39 @@ export class PageViewTracker {
377
501
  }
378
502
 
379
503
  private handleNavigation(source: NavigationSource): void {
380
- const candidatePath = currentPathname();
504
+ const candidate = currentPage();
505
+ const lastKey = this.lastPage ? pageKey(this.lastPage) : null;
381
506
 
382
507
  this.log(
383
- `[detect] navigation signal (source: ${source}, candidatePath: ${candidatePath}, lastPath: ${this.lastPath})`
508
+ `[detect] navigation signal (source: ${source}, candidate: ${describePage(
509
+ candidate
510
+ )}, last: ${describePage(this.lastPage)})`
384
511
  );
385
512
 
386
- if (!isNewPage(this.lastPath, candidatePath)) {
513
+ if (!isNewPage(lastKey, pageKey(candidate))) {
387
514
  this.log(
388
- `[dedupe] pathname unchanged, skipping (source: ${source}, path: ${candidatePath})`
515
+ `[dedupe] page unchanged, skipping (source: ${source}, page: ${describePage(
516
+ candidate
517
+ )})`
389
518
  );
390
519
  return;
391
520
  }
392
521
 
393
522
  this.log(
394
- `[accept] pathname changed, scheduling fire (source: ${source}, from: ${this.lastPath}, to: ${candidatePath})`
523
+ `[accept] page changed, scheduling fire (source: ${source}, from: ${describePage(
524
+ this.lastPage
525
+ )}, to: ${describePage(candidate)})`
395
526
  );
396
- this.lastPath = candidatePath;
397
- this.scheduleFire(candidatePath);
527
+ this.lastPage = candidate;
528
+ this.scheduleFire(candidate);
398
529
  }
399
530
 
400
531
  // 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 {
532
+ // chance to update document.title before the event is built. The path and
533
+ // query params are snapshotted now because they are already settled, whereas a
534
+ // same-tick navigation would overwrite window.location before the flush reads
535
+ // it.
536
+ private scheduleFire(page: IPageSnapshot): void {
405
537
  const timeoutId = setTimeout(() => {
406
538
  this.pendingNavigations = this.pendingNavigations.filter(
407
539
  p => p.timeoutId !== timeoutId
@@ -415,22 +547,24 @@ export class PageViewTracker {
415
547
  }
416
548
 
417
549
  this.mpInstance._SessionManager.resetSessionTimer();
418
- this.firePageView(path);
550
+ this.firePageView(page);
419
551
  }, 0);
420
552
 
421
- this.pendingNavigations.push({ path, timeoutId });
553
+ this.pendingNavigations.push({ page, timeoutId });
422
554
  }
423
555
 
424
- private firePageView(path: string): void {
556
+ private firePageView(page: IPageSnapshot): void {
425
557
  const title = window.document.title;
426
558
  const event = buildPageViewEvent({
559
+ ...page,
427
560
  hostname: window.location.hostname,
428
561
  title,
429
- path,
430
562
  });
431
563
 
432
564
  this.log(
433
- `[fire] deferred flush -> _Events.logEvent(PageView) (path: ${path}, title: ${title})`
565
+ `[fire] deferred flush -> _Events.logEvent(PageView) (page: ${describePage(
566
+ page
567
+ )}, title: ${title})`
434
568
  );
435
569
 
436
570
  this.mpInstance._Events.logEvent(event);
@@ -1,4 +1,3 @@
1
- import { Context } from '@mparticle/event-models';
2
1
  import {
3
2
  IdentityApiData,
4
3
  MPID,
@@ -34,7 +33,7 @@ export interface IGlobalStoreV2MinifiedKeys {
34
33
  cgid: string; // Client Generated ID
35
34
  das: string; // Device ID/ Device Application String
36
35
  ia: IntegrationAttributes;
37
- c: Context;
36
+ c: string | null;
38
37
  csm: MPID[]; // Current Session MPIDs
39
38
  les: number; // Last Event Sent Timestamp
40
39
  ssd: number; // Session Start Date
@@ -43,7 +42,7 @@ export interface IGlobalStoreV2MinifiedKeys {
43
42
  export interface IPersistenceMinified extends Dictionary {
44
43
  cu: MPID; // Current User MPID
45
44
  gs: IGlobalStoreV2MinifiedKeys;
46
- l: boolean; // IsLoggedIn
45
+ l: boolean | 0 | 1; // IsLoggedIn
47
46
 
48
47
  // Persistence Minified can also store optional dictionaries with
49
48
  // an idex of MPID
@@ -99,8 +98,8 @@ export interface IPersistence {
99
98
  useLocalStorage(): boolean;
100
99
  initializeStorage(): void;
101
100
  update(): void;
102
- storeDataInMemory(obj: IPersistenceMinified, currentMPID: MPID): void;
103
- determineLocalStorageAvailability(storage: Storage): boolean;
101
+ storeDataInMemory(obj: IPersistenceMinified, currentMPID?: MPID): void;
102
+ determineLocalStorageAvailability(storage?: Storage): boolean;
104
103
  setLocalStorage(): void;
105
104
  getLocalStorage(): IPersistenceMinified | null;
106
105
  expireCookies(cookieName: string): void;
@@ -113,17 +112,17 @@ export interface IPersistence {
113
112
  maxCookieSize: number
114
113
  ): string;
115
114
  findPrevCookiesBasedOnUI(identityApiData: IdentityApiData): void;
116
- encodePersistence(persistence: IPersistenceMinified): string;
117
- decodePersistence(persistenceString: string): string;
115
+ encodePersistence(persistence: string): string;
116
+ decodePersistence(persistenceString: string | null): string | void;
118
117
  getCookieDomain(): string;
119
- getDomain(doc: string, locationHostname: string): string;
118
+ getDomain(doc: Document, locationHostname: string): string;
120
119
  saveUserCookieSyncDatesToPersistence(mpid: MPID, csd: CookieSyncDates): void;
121
- savePersistence(persistance: IPersistenceMinified): void;
122
- getPersistence(): IPersistenceMinified;
123
- getFirstSeenTime(mpid: MPID): string | null;
124
- setFirstSeenTime(mpid: MPID, time: number): void;
120
+ savePersistence(persistence: IPersistenceMinified): void;
121
+ getPersistence(): IPersistenceMinified | null;
122
+ getFirstSeenTime(mpid: MPID): number | null;
123
+ setFirstSeenTime(mpid: MPID, time?: number): void;
125
124
  getLastSeenTime(mpid: MPID): number | null;
126
- setLastSeenTime(mpid: MPID, time: number): void;
125
+ setLastSeenTime(mpid: MPID, time?: number): void;
127
126
  getDeviceId(): string;
128
127
  setDeviceId(guid: string): void;
129
128
  resetPersistence(): void;