kv-test-lib 3.5.2-rc.1 → 3.5.2-rc.2

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.
@@ -104,9 +104,10 @@ import { KochavaMeasurementEvent } from "./measurementEvent";
104
104
  export { KochavaMeasurementEventType } from "./measurementEvent";
105
105
  export { Product } from "./product";
106
106
  import { DEFAULTS } from "./interfaces";
107
- import { deleteAllPersisted, readAndUpdatePersistedValue, updatePersistedValue, PersistKey, checkDuplicateIdLink, addPersistedIdLinks, checkInstallIdChange, readAndUpdateSessionCount, readAndUpdateDeviceId, readAndUpdateUTM, getItem, configureStorage, checkAndRecordDeviceIdChange, persistSentDeviceIds } from "./browser/persist";
107
+ import { deleteAllPersisted, readAndUpdatePersistedValue, updatePersistedValue, PersistKey, checkDuplicateIdLink, addPersistedIdLinks, checkInstallIdChange, readAndUpdateSessionCount, readAndUpdateUTM, getItem, setItem, configureStorage, checkAndRecordDeviceIdChange, persistSentDeviceIds, hasSyncStorage, getItemSync, setItemSync } from "./browser/persist";
108
108
  import * as utils from "./utils/utils";
109
- import { getPageName } from "./browser/browser";
109
+ import { getPageName, getUrlParameter } from "./browser/browser";
110
+ import { getCookie } from "./browser/cookies";
110
111
  import adapter from './adapters/adapter.browser.js';
111
112
  import { Product } from "./product";
112
113
  configureStorage(adapter);
@@ -115,6 +116,11 @@ var Kochava = /** @class */function () {
115
116
  // the constructor
116
117
  function Kochava() {
117
118
  this.product = Product.measurement;
119
+ // Readiness barrier for the persisted-identity restore. Resolves
120
+ // immediately in synchronous-storage environments (browser) and when the
121
+ // async restore completes otherwise (React Native). Assigned in
122
+ // _resetInstance().
123
+ this._stateRestored = Promise.resolve();
118
124
  this._resetInstance();
119
125
  this._jobQueue = new JobQueue();
120
126
  }
@@ -198,41 +204,112 @@ var Kochava = /** @class */function () {
198
204
  - Optionally enqueues an auto_page.
199
205
  - Starts the job queue.
200
206
  */
207
+ /**
208
+ * Start the SDK.
209
+ *
210
+ * Returns a promise that resolves once startup reaches the "started"
211
+ * milestone — install identity resolved, session/appGuid established, and
212
+ * kvinit/install kicked off. It intentionally does NOT wait for
213
+ * kvinit/install to complete: those retry indefinitely on network failure,
214
+ * and awaiting them would hang the caller with no connectivity. Existing
215
+ * void-context callers are unaffected by the added return value.
216
+ */
201
217
  Kochava.prototype.startWithAppGuid = function (appGuid) {
202
- this._startWithAppGuid(appGuid);
218
+ // shutdown() swaps in the fresh instance synchronously, so by the time this
219
+ // runs (even on the line right after a shutdown), this._instance is already
220
+ // the new instance — no serialization needed. _startWithAppGuid captures it
221
+ // up front and operates only on that captured reference.
222
+ return this._startWithAppGuid(appGuid);
203
223
  };
204
224
  Kochava.prototype._startWithAppGuid = function (appGuid) {
205
225
  return __awaiter(this, void 0, void 0, function () {
206
- return __generator(this, function (_a) {
207
- switch (_a.label) {
226
+ var instance, stateRestored, _a, _b;
227
+ return __generator(this, function (_c) {
228
+ switch (_c.label) {
208
229
  case 0:
230
+ // VALIDATE ELSE RETURN
209
231
  if (!appGuid) {
210
232
  Log.error("Invalid appGuid ".concat(appGuid, ", start failed."));
211
233
  return [2 /*return*/];
212
234
  }
213
- if (!this._instance) this._resetInstance();
214
- if (this._instance.started) {
235
+ instance = this._instance;
236
+ stateRestored = this._stateRestored;
237
+ if (instance.started) {
215
238
  Log.warn("Kochava SDK already started.");
216
239
  return [2 /*return*/];
217
240
  }
218
- Log.diagDebug("Host called API: Start With App Guid ".concat(appGuid));
219
- this._restorePersistedDeviceId();
220
- this._instance.started = true;
221
- return [4 /*yield*/, this._checkFirstLaunchAndMigrate()];
241
+ // Kick the install-identity resolution before control returns to the
242
+ // caller when restore has already completed (synchronous storage): the
243
+ // resolution's synchronous prefix populates kochavaDeviceId in place, so
244
+ // getDeviceId() on the caller's very next line is already correct once
245
+ // started flips true below.
246
+ if (instance.stateRestoredBool) {
247
+ this._ensureIdentityResolved(instance).catch(function () {});
248
+ }
249
+ // Gen4 gate: started flips true synchronously within this call — the
250
+ // long-standing contract getDeviceId() is gated on — so the identity
251
+ // resolved by the kick above is readable on the caller's very next line.
252
+ // (On asynchronous-storage platforms the id resolves during the awaits
253
+ // below, matching the current production behavior there.)
254
+ instance.started = true;
255
+ _c.label = 1;
222
256
  case 1:
223
- _a.sent();
224
- this._initInstance(appGuid);
225
- if (!this._instance.disableAutoPage) this.sendPageEvent();
226
- return [4 /*yield*/, this._checkPersistedState()];
257
+ _c.trys.push([1, 3,, 4]);
258
+ return [4 /*yield*/, stateRestored];
227
259
  case 2:
228
- _a.sent();
229
- return [4 /*yield*/, this._checkPersistedKvinit()];
260
+ _c.sent();
261
+ return [3 /*break*/, 4];
230
262
  case 3:
231
- _a.sent();
232
- this._printStartupMsgs(appGuid);
233
- return [4 /*yield*/, this._beginStart()];
263
+ _a = _c.sent();
264
+ return [3 /*break*/, 4];
234
265
  case 4:
235
- _a.sent();
266
+ if (instance.invalidatedBool) {
267
+ return [2 /*return*/];
268
+ }
269
+ _c.label = 5;
270
+ case 5:
271
+ _c.trys.push([5, 7,, 8]);
272
+ return [4 /*yield*/, this._ensureIdentityResolved(instance)];
273
+ case 6:
274
+ _c.sent();
275
+ return [3 /*break*/, 8];
276
+ case 7:
277
+ _b = _c.sent();
278
+ return [3 /*break*/, 8];
279
+ case 8:
280
+ if (instance.invalidatedBool) {
281
+ return [2 /*return*/];
282
+ }
283
+ // MAIN
284
+ Log.diagDebug("Host called API: Start With App Guid ".concat(appGuid));
285
+ return [4 /*yield*/, this._checkFirstLaunchAndMigrate(instance)];
286
+ case 9:
287
+ _c.sent();
288
+ if (instance.invalidatedBool) {
289
+ return [2 /*return*/];
290
+ }
291
+ this._initInstance(instance, appGuid);
292
+ if (!instance.disableAutoPage) this.sendPageEvent();
293
+ return [4 /*yield*/, this._checkPersistedState(instance)];
294
+ case 10:
295
+ _c.sent();
296
+ if (instance.invalidatedBool) {
297
+ return [2 /*return*/];
298
+ }
299
+ return [4 /*yield*/, this._checkPersistedKvinit(instance)];
300
+ case 11:
301
+ _c.sent();
302
+ if (instance.invalidatedBool) {
303
+ return [2 /*return*/];
304
+ }
305
+ this._printStartupMsgs(instance, appGuid);
306
+ // Kick off the network-dependent startup (kvinit, install, queue) WITHOUT
307
+ // awaiting it. Network completion must not gate this returned promise —
308
+ // retries are indefinite, so awaiting would hang a caller with no
309
+ // connectivity.
310
+ this._beginStart(instance).catch(function (e) {
311
+ return Log.error("Error during startup", e);
312
+ });
236
313
  return [2 /*return*/];
237
314
  }
238
315
  });
@@ -244,31 +321,97 @@ var Kochava = /** @class */function () {
244
321
  sdk and job queue.
245
322
  */
246
323
  Kochava.prototype.shutdown = function (deleteData) {
247
- this._shutdown(deleteData);
324
+ Log.diagDebug("Host called API: Shutdown and ".concat(deleteData ? "delete data" : "keep data"));
325
+ // Capture the instance being torn down. In-flight startup continuations
326
+ // hold this same reference and bail on its invalidatedBool, so swapping
327
+ // this._instance out from under them below can never strand them on the
328
+ // fresh instance.
329
+ var invalidatedInstance = this._instance;
330
+ if (!invalidatedInstance.started) {
331
+ Log.warn("SDK already shutdown.");
332
+ }
333
+ Log.info("SDK shutting down.");
334
+ // 1. Invalidate synchronously — every in-flight continuation checks this at
335
+ // its next await boundary and exits early (after fulfilling anything it
336
+ // owns), so no further side effects occur on this instance.
337
+ invalidatedInstance.invalidatedBool = true;
338
+ // 2. Resume/stop long-running work so it observes invalidation promptly
339
+ // rather than sitting in a retry sleep. cancelRetries()/stop() resolve
340
+ // the pending wait (they no longer merely clear the timer), so the loop
341
+ // wakes, sees invalidatedBool, and returns.
342
+ Kvinit.cancelRetries();
343
+ Install.cancelRetries();
344
+ this._jobQueue.stop();
345
+ this._jobQueue = new JobQueue();
346
+ // 3. Swap in a fresh instance synchronously so a start issued on the very
347
+ // next line (even without awaiting this shutdown) runs on the new
348
+ // instance. When deleting data on asynchronous storage, gate the fresh
349
+ // instance's restore on the clear so it never reads storage mid-wipe.
350
+ // On synchronous storage no gate is needed: an adapter exposing the sync
351
+ // fast-path is synchronous under the hood, so the wipe has already landed
352
+ // by the time deleteAllPersisted() returns.
353
+ if (deleteData) {
354
+ Log.debug("Deleting persisted values");
355
+ }
356
+ var clearPromise = deleteData ? deleteAllPersisted() : Promise.resolve();
357
+ this._resetInstance(deleteData && !hasSyncStorage() ? clearPromise : undefined);
358
+ // 4. Complete asynchronously: after the clear (if any) and a bounded drain
359
+ // of the invalidated instance's in-flight work, resolve.
360
+ return this._completeShutdown(clearPromise);
248
361
  };
249
- Kochava.prototype._shutdown = function (deleteData) {
362
+ Kochava.prototype._completeShutdown = function (clearPromise) {
250
363
  return __awaiter(this, void 0, void 0, function () {
364
+ var e_1;
251
365
  return __generator(this, function (_a) {
252
366
  switch (_a.label) {
253
367
  case 0:
254
- Log.diagDebug("Host called API: Shutdown and ".concat(deleteData ? "delete data" : "keep data"));
255
- if (!deleteData) return [3 /*break*/, 2];
256
- Log.debug("Deleting persisted values");
257
- return [4 /*yield*/, deleteAllPersisted()];
368
+ _a.trys.push([0, 2,, 3]);
369
+ return [4 /*yield*/, clearPromise];
258
370
  case 1:
259
371
  _a.sent();
260
- _a.label = 2;
372
+ return [3 /*break*/, 3];
261
373
  case 2:
262
- if (!this._instance.started) {
263
- Log.warn("SDK already shutdown.");
264
- }
265
- Log.info("SDK shutting down.");
266
- Kvinit.cancelRetries();
267
- Install.cancelRetries();
268
- this._jobQueue.stop();
269
- // wipe whatever was previously in the queue
270
- this._jobQueue = new JobQueue();
271
- this._resetInstance();
374
+ e_1 = _a.sent();
375
+ Log.error("Error deleting persisted values during shutdown", e_1);
376
+ return [3 /*break*/, 3];
377
+ case 3:
378
+ return [4 /*yield*/, this._drainInvalidated()];
379
+ case 4:
380
+ _a.sent();
381
+ return [2 /*return*/];
382
+ }
383
+ });
384
+ });
385
+ };
386
+ // Bounded event-loop drain. After invalidation, yield a few macrotask turns
387
+ // so in-flight work on the just-invalidated instance resumes (its retry waits
388
+ // were resolved by cancelRetries()/stop()) and bails before we consider
389
+ // shutdown complete. This is best-effort quiescence for callers who await
390
+ // shutdown, never a correctness dependency: a request already in flight
391
+ // (bounded by the 20s request timeout) can outlive this window, and the
392
+ // invalidation checks preceding each side effect are what keep any straggler
393
+ // harmless. A fixed hop count means it can never hang.
394
+ Kochava.prototype._drainInvalidated = function () {
395
+ return __awaiter(this, void 0, void 0, function () {
396
+ var DRAIN_HOPS, i;
397
+ return __generator(this, function (_a) {
398
+ switch (_a.label) {
399
+ case 0:
400
+ DRAIN_HOPS = 4;
401
+ i = 0;
402
+ _a.label = 1;
403
+ case 1:
404
+ if (!(i < DRAIN_HOPS)) return [3 /*break*/, 4];
405
+ return [4 /*yield*/, new Promise(function (resolve) {
406
+ return setTimeout(resolve, 0);
407
+ })];
408
+ case 2:
409
+ _a.sent();
410
+ _a.label = 3;
411
+ case 3:
412
+ i++;
413
+ return [3 /*break*/, 1];
414
+ case 4:
272
415
  return [2 /*return*/];
273
416
  }
274
417
  });
@@ -532,7 +675,7 @@ var Kochava = /** @class */function () {
532
675
  if (!(!sleep && this._instance.sleep)) return [3 /*break*/, 3];
533
676
  // only resume queueing if it was paused
534
677
  this._instance.sleep = sleep;
535
- return [4 /*yield*/, this._beginStart()];
678
+ return [4 /*yield*/, this._beginStart(this._instance)];
536
679
  case 2:
537
680
  _a.sent();
538
681
  _a.label = 3;
@@ -545,10 +688,16 @@ var Kochava = /** @class */function () {
545
688
  // ============================= =============================== //
546
689
  // Unintialized/invalid state.
547
690
  // start must be called before these values will be correct.
548
- Kochava.prototype._resetInstance = function () {
691
+ //
692
+ // gate: when provided (shutdown with deleteData on asynchronous storage), the
693
+ // fresh instance's restore is deferred until the gate resolves — so restore
694
+ // never reads storage mid-wipe.
695
+ Kochava.prototype._resetInstance = function (gate) {
696
+ var _this = this;
549
697
  this._instance = {
550
698
  appGuid: "",
551
699
  started: false,
700
+ invalidatedBool: false,
552
701
  installStarted: false,
553
702
  kvinitDone: false,
554
703
  installDone: false,
@@ -572,106 +721,258 @@ var Kochava = /** @class */function () {
572
721
  utm: "",
573
722
  installCount: 0,
574
723
  kochavaDeviceId: "",
724
+ stateRestoredBool: false,
725
+ identityResolvedPromise: null,
575
726
  kochavaInstallId: "",
576
727
  kochavaSessionCount: -1,
577
728
  kochavaInstallDate: -1,
578
729
  kochavaConfig: undefined
579
730
  };
731
+ // Restore the persisted identity so the resolution waterfall can run on
732
+ // demand. Synchronously when the storage adapter supports it (browser) —
733
+ // a returning device's id is in memory before control returns; async
734
+ // otherwise (React Native), with start waiting on _stateRestored. Restore
735
+ // only reads: a device with no stored identity keeps "" until the identity
736
+ // is resolved at start (_resolveInstallIdentity). Under the Gen4 started
737
+ // gate nothing can observe pre-start state, so the remaining persisted
738
+ // state loads at start (_checkPersistedState / _checkPersistedKvinit)
739
+ // exactly as before. When gated (shutdown+delete on async storage),
740
+ // restore runs only after the wipe settles — including a failed wipe, in
741
+ // which case restoring whatever survived is the honest state; a rejected
742
+ // gate must never leave the instance permanently unrestored.
743
+ var instance = this._instance;
744
+ this._stateRestored = gate ? gate.catch(function () {}).then(function () {
745
+ return _this._restoreState(instance);
746
+ }) : this._restoreState(instance);
580
747
  };
581
- Kochava.prototype._initInstance = function (appGuid) {
582
- // init instance with defaults
583
- this._instance.appGuid = appGuid;
584
- this._instance.disableAutoPage = this._instance.disableAutoPage || false;
585
- this._instance.useCookies = this._instance.useCookies || false;
586
- this._instance.version = Product.measurement.standardVersionInfoString;
587
- this._instance.buildDate = Product.measurement.sdkBuildDate;
588
- this._instance.kochavaSession = utils.uuidv4().substring(0, 5);
589
- this._instance.startTimeMS = utils.getCurrTimeMS();
590
- this._instance.retryWaterfall = [7, 30, 300, 1800];
591
- this._instance.kochavaConfig = JSON.parse(JSON.stringify(DEFAULTS));
748
+ // Generate a fresh Kochava device id (kvid).
749
+ Kochava.prototype._generateDeviceId = function () {
750
+ var kvId = "KB".concat(utils.getCurrTimeSec(), "T").concat(utils.uuidv4());
751
+ return kvId.replace(/-/g, "");
592
752
  };
593
- /*
594
- Reads (or generates) the device id directly from localStorage, synchronously,
595
- so kochavaDeviceId is populated before started flips true and control returns
596
- to the caller. Bypasses the async StorageAdapter deliberately -- the browser
597
- adapter is a zero-logic pass-through to localStorage, so this produces an
598
- identical result there, and the try/catch makes environments without
599
- window.localStorage (React Native) silently no-op and fall back to the
600
- existing async readAndUpdateDeviceId() call in _checkPersistedState().
601
- */
602
- Kochava.prototype._restorePersistedDeviceId = function () {
603
- var _a, _b;
604
- try {
605
- var ls = window.localStorage;
606
- var deviceId = (_a = ls.getItem(PersistKey.DeviceId)) !== null && _a !== void 0 ? _a : "";
607
- if (!deviceId) {
608
- var kvId = "KB".concat(utils.getCurrTimeSec(), "T").concat(utils.uuidv4());
609
- deviceId = kvId.replace(/-/g, "");
610
- ls.setItem(PersistKey.DeviceId, deviceId);
753
+ // Choose the restore path based on the configured storage adapter's
754
+ // capability. Sync when available (resolve immediately); async otherwise.
755
+ Kochava.prototype._restoreState = function (instance) {
756
+ if (hasSyncStorage()) {
757
+ try {
758
+ this._restoreSync(instance);
759
+ return Promise.resolve();
760
+ } catch (_a) {
761
+ // Synchronous storage access threw (e.g. localStorage disabled) —
762
+ // fall back to the async path rather than leaving state unrestored.
611
763
  }
612
- var overrideDeviceId = (_b = ls.getItem(PersistKey.OverrideDeviceId)) !== null && _b !== void 0 ? _b : "";
613
- this._instance.kochavaDeviceId = overrideDeviceId || deviceId;
614
- } catch ( /* no-op in environments without window.localStorage (e.g. React Native) */_c) {/* no-op in environments without window.localStorage (e.g. React Native) */}
764
+ }
765
+ return this._restoreAsync(instance);
766
+ };
767
+ // Synchronous identity restore through the adapter's sync fast-path.
768
+ // Pure reads — a device with no stored identity keeps "" until the identity
769
+ // is resolved on demand (_resolveInstallIdentity).
770
+ Kochava.prototype._restoreSync = function (instance) {
771
+ var _a, _b;
772
+ this._applyRestoredIdentity(instance, (_a = getItemSync(PersistKey.DeviceId)) !== null && _a !== void 0 ? _a : "", (_b = getItemSync(PersistKey.OverrideDeviceId)) !== null && _b !== void 0 ? _b : "");
773
+ };
774
+ // Asynchronous identity restore through the abstracted StorageAdapter, which
775
+ // routes to AsyncStorage on React Native. Pure reads, as above.
776
+ Kochava.prototype._restoreAsync = function (instance) {
777
+ return __awaiter(this, void 0, void 0, function () {
778
+ var _a, deviceId, overrideDeviceId;
779
+ return __generator(this, function (_b) {
780
+ switch (_b.label) {
781
+ case 0:
782
+ if (instance.invalidatedBool) return [2 /*return*/];
783
+ return [4 /*yield*/, Promise.all([getItem(PersistKey.DeviceId), getItem(PersistKey.OverrideDeviceId)])];
784
+ case 1:
785
+ _a = _b.sent(), deviceId = _a[0], overrideDeviceId = _a[1];
786
+ if (instance.invalidatedBool) return [2 /*return*/];
787
+ this._applyRestoredIdentity(instance, deviceId !== null && deviceId !== void 0 ? deviceId : "", overrideDeviceId !== null && overrideDeviceId !== void 0 ? overrideDeviceId : "");
788
+ return [2 /*return*/];
789
+ }
790
+ });
791
+ });
792
+ };
793
+ // Apply the restored identity to the instance. Both loaders funnel through
794
+ // here so the two paths can never drift.
795
+ Kochava.prototype._applyRestoredIdentity = function (instance, deviceId, overrideDeviceId) {
796
+ // kochavaDeviceId: server override wins over the stored id.
797
+ instance.kochavaDeviceId = overrideDeviceId || deviceId;
798
+ // Restore complete — the identity may now be resolved on demand.
799
+ instance.stateRestoredBool = true;
800
+ };
801
+ // Return this instance's install-identity resolution, creating it on first
802
+ // demand. Memoized on the instance so it can never pair with the wrong
803
+ // lifecycle generation across a shutdown swap.
804
+ Kochava.prototype._ensureIdentityResolved = function (instance) {
805
+ if (!instance.identityResolvedPromise) {
806
+ instance.identityResolvedPromise = this._resolveInstallIdentity(instance);
807
+ }
808
+ return instance.identityResolvedPromise;
615
809
  };
616
- Kochava.prototype._checkFirstLaunchAndMigrate = function () {
810
+ // Resolve the install identity for this instance. Precondition: the
811
+ // persisted-identity restore has completed (callers gate on stateRestoredBool
812
+ // or await _stateRestored first).
813
+ //
814
+ // The waterfall, in order — an id is generated only after every read source
815
+ // is exhausted, so nothing provisional is ever created, persisted, or shown:
816
+ // 1. Stored id (already in memory from restore).
817
+ // 2. Shared cookie (useCookies integrations share one identity across
818
+ // subdomains; a fresh subdomain's local storage starts empty while the
819
+ // shared cookie already holds the real id — adopting it here is what
820
+ // keeps a new subdomain from entrenching a new id and clobbering the
821
+ // shared identity). An id read from local storage still wins over the
822
+ // cookie, matching the long-standing storage-before-cookie precedence.
823
+ // 3. Legacy v2 kv_id (URL parameter or storage), only on a first-ever v3
824
+ // launch — the identity half of the v2->v3 migration; the bookkeeping
825
+ // half stays in _checkFirstLaunchAndMigrate.
826
+ // 4. Generate — the one and only generation point, logged as such.
827
+ //
828
+ // On synchronous-storage platforms every step through the in-memory
829
+ // assignment executes synchronously, so a resolution kicked from the
830
+ // synchronous start path makes getDeviceId() correct on the caller's very
831
+ // next line. Cookie and legacy-URL sources are DOM-only and are gated on
832
+ // the sync fast-path accordingly.
833
+ Kochava.prototype._resolveInstallIdentity = function (instance) {
834
+ return __awaiter(this, void 0, void 0, function () {
835
+ var cookieDeviceId, oldKvId, deviceId, cookieOverrideDeviceId;
836
+ return __generator(this, function (_a) {
837
+ switch (_a.label) {
838
+ case 0:
839
+ // WATERFALL
840
+ // 1. stored id
841
+ if (!instance.kochavaDeviceId) {
842
+ // 2. shared cookie
843
+ if (instance.useCookies && hasSyncStorage()) {
844
+ try {
845
+ cookieDeviceId = getCookie(PersistKey.DeviceId);
846
+ if (cookieDeviceId) {
847
+ instance.kochavaDeviceId = cookieDeviceId;
848
+ setItemSync(PersistKey.DeviceId, cookieDeviceId);
849
+ }
850
+ } catch ( /* sync storage unavailable — fall through */_b) {/* sync storage unavailable — fall through */}
851
+ }
852
+ }
853
+ if (!instance.kochavaDeviceId) {
854
+ // 3. legacy v2 kv_id (first-ever v3 launch only; web-legacy sources)
855
+ if (hasSyncStorage()) {
856
+ try {
857
+ if (!getItemSync(PersistKey.FirstStartDate)) {
858
+ oldKvId = getUrlParameter(PersistKey.OldKvid) || getItemSync(PersistKey.OldKvid);
859
+ if (oldKvId) {
860
+ instance.kochavaDeviceId = oldKvId;
861
+ setItemSync(PersistKey.DeviceId, oldKvId);
862
+ }
863
+ }
864
+ } catch ( /* sync storage unavailable — fall through */_c) {/* sync storage unavailable — fall through */}
865
+ }
866
+ }
867
+ if (!!instance.kochavaDeviceId) return [3 /*break*/, 2];
868
+ deviceId = this._generateDeviceId();
869
+ Log.diagDebug("Generated new kochava device id ".concat(deviceId));
870
+ instance.kochavaDeviceId = deviceId;
871
+ return [4 /*yield*/, setItem(PersistKey.DeviceId, deviceId)];
872
+ case 1:
873
+ _a.sent();
874
+ if (instance.invalidatedBool) return [2 /*return*/];
875
+ _a.label = 2;
876
+ case 2:
877
+ // overrideDeviceId
878
+ // ⓘ Adopt the shared cookie override when none is stored locally, so a
879
+ // subdomain that has not yet received the override from kvinit reports
880
+ // consistently with the subdomain that has.
881
+ if (instance.useCookies && hasSyncStorage()) {
882
+ try {
883
+ if (!getItemSync(PersistKey.OverrideDeviceId)) {
884
+ cookieOverrideDeviceId = getCookie(PersistKey.OverrideDeviceId);
885
+ if (cookieOverrideDeviceId) {
886
+ instance.kochavaDeviceId = cookieOverrideDeviceId;
887
+ setItemSync(PersistKey.OverrideDeviceId, cookieOverrideDeviceId);
888
+ }
889
+ }
890
+ } catch ( /* sync storage unavailable */_d) {/* sync storage unavailable */}
891
+ }
892
+ return [2 /*return*/];
893
+ }
894
+ });
895
+ });
896
+ };
897
+ Kochava.prototype._initInstance = function (instance, appGuid) {
898
+ // init instance with defaults
899
+ instance.appGuid = appGuid;
900
+ instance.disableAutoPage = instance.disableAutoPage || false;
901
+ instance.useCookies = instance.useCookies || false;
902
+ instance.version = Product.measurement.standardVersionInfoString;
903
+ instance.buildDate = Product.measurement.sdkBuildDate;
904
+ instance.kochavaSession = utils.uuidv4().substring(0, 5);
905
+ instance.startTimeMS = utils.getCurrTimeMS();
906
+ instance.retryWaterfall = [7, 30, 300, 1800];
907
+ instance.kochavaConfig = JSON.parse(JSON.stringify(DEFAULTS));
908
+ };
909
+ Kochava.prototype._checkFirstLaunchAndMigrate = function (instance) {
617
910
  return __awaiter(this, void 0, void 0, function () {
618
911
  var firstLaunchDate, oldKvId;
619
912
  return __generator(this, function (_a) {
620
913
  switch (_a.label) {
621
914
  case 0:
622
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.FirstStartDate, this._instance.useCookies)];
915
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.FirstStartDate, instance.useCookies)];
623
916
  case 1:
624
917
  firstLaunchDate = _a.sent();
625
- if (!!firstLaunchDate) return [3 /*break*/, 6];
626
- return [4 /*yield*/, updatePersistedValue(PersistKey.FirstStartDate, String(utils.getCurrTimeSec()), this._instance.useCookies)];
918
+ if (instance.invalidatedBool) return [2 /*return*/];
919
+ if (!!firstLaunchDate) return [3 /*break*/, 5];
920
+ return [4 /*yield*/, updatePersistedValue(PersistKey.FirstStartDate, String(utils.getCurrTimeSec()), instance.useCookies)];
627
921
  case 2:
628
922
  _a.sent();
629
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.OldKvid, this._instance.useCookies)];
923
+ if (instance.invalidatedBool) return [2 /*return*/];
924
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.OldKvid, instance.useCookies)];
630
925
  case 3:
631
926
  oldKvId = _a.sent();
632
- if (!oldKvId) return [3 /*break*/, 6];
633
- return [4 /*yield*/, updatePersistedValue(PersistKey.DeviceId, oldKvId, this._instance.useCookies)];
927
+ if (instance.invalidatedBool) return [2 /*return*/];
928
+ if (!oldKvId) return [3 /*break*/, 5];
929
+ // Identity adoption of the legacy id lives in the resolution waterfall
930
+ // (_resolveInstallIdentity), which already ran and wins precedence
931
+ // battles (an established cookie identity beats the legacy id). Here
932
+ // migration only records that this device has v2 history, so the
933
+ // install is not re-sent.
934
+ instance.installDone = true;
935
+ return [4 /*yield*/, updatePersistedValue(PersistKey.InstallSentDate, JSON.stringify(utils.getCurrTimeSec()), instance.useCookies)];
634
936
  case 4:
635
937
  _a.sent();
636
- return [4 /*yield*/, updatePersistedValue(PersistKey.InstallSentDate, JSON.stringify(utils.getCurrTimeSec()), this._instance.useCookies)];
938
+ _a.label = 5;
637
939
  case 5:
638
- _a.sent();
639
- _a.label = 6;
640
- case 6:
641
940
  return [2 /*return*/];
642
941
  }
643
942
  });
644
943
  });
645
944
  };
646
- Kochava.prototype._checkPersistedKvinit = function () {
945
+ Kochava.prototype._checkPersistedKvinit = function (instance) {
647
946
  return __awaiter(this, void 0, void 0, function () {
648
947
  var persistedKvinit, persistedKvinitStr, persistedKvinitDateStr, lastKvinitDate, refreshMin;
649
948
  return __generator(this, function (_a) {
650
949
  switch (_a.label) {
651
950
  case 0:
652
951
  persistedKvinit = {};
653
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.LastKvinit, this._instance.useCookies)];
952
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.LastKvinit, instance.useCookies)];
654
953
  case 1:
655
954
  persistedKvinitStr = _a.sent();
955
+ if (instance.invalidatedBool) return [2 /*return*/];
656
956
  if (!persistedKvinitStr) return [3 /*break*/, 3];
657
957
  persistedKvinit = JSON.parse(persistedKvinitStr);
658
958
  if (!persistedKvinit) return [3 /*break*/, 3];
659
959
  // if persisted kvinit, apply it
660
960
  Log.trace("Found persisted kvinit.", persistedKvinit);
661
- return [4 /*yield*/, Kvinit.applyKvinitResp(this._instance, persistedKvinit)];
961
+ return [4 /*yield*/, Kvinit.applyKvinitResp(instance, persistedKvinit)];
662
962
  case 2:
663
963
  _a.sent();
664
- Log.trace("KochavaConfig after persistedKvinit:", JSON.parse(JSON.stringify(this._instance.kochavaConfig)));
964
+ if (instance.invalidatedBool) return [2 /*return*/];
965
+ Log.trace("KochavaConfig after persistedKvinit:", JSON.parse(JSON.stringify(instance.kochavaConfig)));
665
966
  _a.label = 3;
666
967
  case 3:
667
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.KvinitSentDate, this._instance.useCookies)];
968
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.KvinitSentDate, instance.useCookies)];
668
969
  case 4:
669
970
  persistedKvinitDateStr = _a.sent();
670
971
  if (persistedKvinitDateStr) {
671
972
  lastKvinitDate = JSON.parse(persistedKvinitDateStr);
672
973
  if (lastKvinitDate) {
673
- refreshMin = this._instance.kochavaConfig.config.refresh_minimum;
674
- if (utils.getCurrTimeSec() - lastKvinitDate < refreshMin) this._instance.kvinitDone = true;
974
+ refreshMin = instance.kochavaConfig.config.refresh_minimum;
975
+ if (utils.getCurrTimeSec() - lastKvinitDate < refreshMin) instance.kvinitDone = true;
675
976
  }
676
977
  }
677
978
  return [2 /*return*/];
@@ -679,58 +980,72 @@ var Kochava = /** @class */function () {
679
980
  });
680
981
  });
681
982
  };
682
- Kochava.prototype._checkPersistedState = function () {
983
+ Kochava.prototype._checkPersistedState = function (instance) {
683
984
  return __awaiter(this, void 0, void 0, function () {
684
- var _a, _b, _c, _d, _e, installCountStr, parsedInstallCount;
685
- return __generator(this, function (_f) {
686
- switch (_f.label) {
985
+ var rawDeviceId, _a, _b, _c, _d, installCountStr, parsedInstallCount;
986
+ return __generator(this, function (_e) {
987
+ switch (_e.label) {
687
988
  case 0:
688
- _a = this._instance;
689
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.InstallSentDate, this._instance.useCookies)];
989
+ if (!instance.useCookies) return [3 /*break*/, 3];
990
+ return [4 /*yield*/, getItem(PersistKey.DeviceId)];
690
991
  case 1:
691
- _a.installDone = _f.sent().length > 0;
692
- _b = this._instance;
693
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.InstallId, this._instance.useCookies)];
992
+ rawDeviceId = _e.sent() || instance.kochavaDeviceId;
993
+ if (instance.invalidatedBool) return [2 /*return*/];
994
+ return [4 /*yield*/, updatePersistedValue(PersistKey.DeviceId, rawDeviceId, true)];
694
995
  case 2:
695
- _b.kochavaInstallId = _f.sent();
696
- _c = this._instance;
697
- return [4 /*yield*/, readAndUpdateDeviceId(this._instance.useCookies)];
996
+ _e.sent();
997
+ if (instance.invalidatedBool) return [2 /*return*/];
998
+ _e.label = 3;
698
999
  case 3:
699
- _c.kochavaDeviceId = _f.sent();
700
- _d = this._instance;
701
- return [4 /*yield*/, readAndUpdateSessionCount(this._instance.useCookies)];
1000
+ _a = instance;
1001
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.InstallSentDate, instance.useCookies)];
702
1002
  case 4:
703
- _d.kochavaSessionCount = _f.sent();
704
- _e = this._instance;
705
- return [4 /*yield*/, readAndUpdateUTM(this._instance.appGuid, this._instance.useCookies)];
1003
+ _a.installDone = _e.sent().length > 0;
1004
+ if (instance.invalidatedBool) return [2 /*return*/];
1005
+ _b = instance;
1006
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.InstallId, instance.useCookies)];
706
1007
  case 5:
707
- _e.utm = _f.sent();
708
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.InstallCount, this._instance.useCookies)];
1008
+ _b.kochavaInstallId = _e.sent();
1009
+ if (instance.invalidatedBool) return [2 /*return*/];
1010
+ _c = instance;
1011
+ return [4 /*yield*/, readAndUpdateSessionCount(instance.useCookies)];
709
1012
  case 6:
710
- installCountStr = _f.sent();
1013
+ _c.kochavaSessionCount = _e.sent();
1014
+ if (instance.invalidatedBool) return [2 /*return*/];
1015
+ _d = instance;
1016
+ return [4 /*yield*/, readAndUpdateUTM(instance.appGuid, instance.useCookies)];
1017
+ case 7:
1018
+ _d.utm = _e.sent();
1019
+ if (instance.invalidatedBool) return [2 /*return*/];
1020
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.InstallCount, instance.useCookies)];
1021
+ case 8:
1022
+ installCountStr = _e.sent();
711
1023
  parsedInstallCount = parseInt(installCountStr, 10);
712
- this._instance.installCount = isNaN(parsedInstallCount) ? 0 : parsedInstallCount;
1024
+ instance.installCount = isNaN(parsedInstallCount) ? 0 : parsedInstallCount;
713
1025
  return [2 /*return*/];
714
1026
  }
715
1027
  });
716
1028
  });
717
1029
  };
718
- Kochava.prototype._printStartupMsgs = function (appGuid) {
1030
+ Kochava.prototype._printStartupMsgs = function (instance, appGuid) {
719
1031
  var measurementProduct = Product.measurement;
720
1032
  Log.diagInfo("Started SDK ".concat(measurementProduct.standardVersionInfoString, "\n published ").concat(measurementProduct.sdkBuildDate));
721
1033
  Log.diagInfo("The log level is set to ".concat(Log.getLogLevel()));
722
- Log.diagDebug("This ".concat(!this._instance.installDone ? "is" : "is not", " the first tracker SDK launch"));
723
- Log.diagDebug("The kochava device id is ".concat(this._instance.kochavaDeviceId));
1034
+ Log.diagDebug("This ".concat(!instance.installDone ? "is" : "is not", " the first tracker SDK launch"));
1035
+ Log.diagDebug("The kochava device id is ".concat(instance.kochavaDeviceId));
724
1036
  Log.diagDebug("The kochava app GUID provided was ".concat(appGuid));
725
1037
  };
726
- Kochava.prototype._beginStart = function () {
1038
+ Kochava.prototype._beginStart = function (instance) {
727
1039
  return __awaiter(this, void 0, void 0, function () {
1040
+ var jobQueue;
728
1041
  return __generator(this, function (_a) {
729
1042
  switch (_a.label) {
730
1043
  case 0:
731
- if (!!this._instance.kvinitDone) return [3 /*break*/, 2];
1044
+ if (instance.invalidatedBool) return [2 /*return*/];
1045
+ jobQueue = this._jobQueue;
1046
+ if (!!instance.kvinitDone) return [3 /*break*/, 2];
732
1047
  Log.diagDebug("A new kvinit will be sent");
733
- return [4 /*yield*/, this.performNewKvinit()];
1048
+ return [4 /*yield*/, this.performNewKvinit(instance)];
734
1049
  case 1:
735
1050
  _a.sent();
736
1051
  return [3 /*break*/, 3];
@@ -738,20 +1053,23 @@ var Kochava = /** @class */function () {
738
1053
  Log.diagDebug("A new kvinit will not be sent");
739
1054
  _a.label = 3;
740
1055
  case 3:
741
- return [4 /*yield*/, this.checkResendId()];
1056
+ if (instance.invalidatedBool) return [2 /*return*/];
1057
+ return [4 /*yield*/, this.checkResendId(instance)];
742
1058
  case 4:
743
1059
  // if the install_id changed and thus a new install must go out
744
- if (_a.sent()) this._instance.installDone = false;
745
- Log.diagDebug("The install ".concat(this._instance.installDone ? "has already" : "has not yet", " been sent"));
746
- if (this._instance.sleep) return [2 /*return*/];
747
- if (!!this._instance.installDone) return [3 /*break*/, 6];
748
- return [4 /*yield*/, this.performInstall()];
1060
+ if (_a.sent()) instance.installDone = false;
1061
+ if (instance.invalidatedBool) return [2 /*return*/];
1062
+ Log.diagDebug("The install ".concat(instance.installDone ? "has already" : "has not yet", " been sent"));
1063
+ if (instance.sleep) return [2 /*return*/];
1064
+ if (!!instance.installDone) return [3 /*break*/, 6];
1065
+ return [4 /*yield*/, this.performInstall(instance)];
749
1066
  case 5:
750
1067
  _a.sent();
751
1068
  _a.label = 6;
752
1069
  case 6:
753
- if (!(this._instance.kvinitDone && this._instance.installDone)) return [3 /*break*/, 8];
754
- return [4 /*yield*/, this._jobQueue.start(this._instance)];
1070
+ if (instance.invalidatedBool) return [2 /*return*/];
1071
+ if (!(instance.kvinitDone && instance.installDone)) return [3 /*break*/, 8];
1072
+ return [4 /*yield*/, jobQueue.start(instance)];
755
1073
  case 7:
756
1074
  _a.sent();
757
1075
  _a.label = 8;
@@ -761,46 +1079,49 @@ var Kochava = /** @class */function () {
761
1079
  });
762
1080
  });
763
1081
  };
764
- Kochava.prototype.performNewKvinit = function () {
1082
+ Kochava.prototype.performNewKvinit = function (instance) {
765
1083
  return __awaiter(this, void 0, void 0, function () {
766
1084
  var _a, newKvinit, newKvinitStr;
767
1085
  return __generator(this, function (_b) {
768
1086
  switch (_b.label) {
769
1087
  case 0:
770
- _a = this._instance;
771
- return [4 /*yield*/, Kvinit.send(this._instance, this._instance.retryWaterfall)];
1088
+ _a = instance;
1089
+ return [4 /*yield*/, Kvinit.send(instance, instance.retryWaterfall)];
772
1090
  case 1:
773
1091
  _a.kvinitDone = _b.sent();
774
- return [4 /*yield*/, updatePersistedValue(PersistKey.KvinitSentDate, String(utils.getCurrTimeSec()), this._instance.useCookies)];
1092
+ if (instance.invalidatedBool) return [2 /*return*/];
1093
+ return [4 /*yield*/, updatePersistedValue(PersistKey.KvinitSentDate, String(utils.getCurrTimeSec()), instance.useCookies)];
775
1094
  case 2:
776
1095
  _b.sent();
1096
+ if (instance.invalidatedBool) return [2 /*return*/];
777
1097
  newKvinit = {};
778
- return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.LastKvinit, this._instance.useCookies)];
1098
+ return [4 /*yield*/, readAndUpdatePersistedValue(PersistKey.LastKvinit, instance.useCookies)];
779
1099
  case 3:
780
1100
  newKvinitStr = _b.sent();
1101
+ if (instance.invalidatedBool) return [2 /*return*/];
781
1102
  if (newKvinitStr) {
782
1103
  newKvinit = JSON.parse(newKvinitStr);
783
1104
  }
784
- return [4 /*yield*/, Kvinit.applyKvinitResp(this._instance, newKvinit)];
1105
+ return [4 /*yield*/, Kvinit.applyKvinitResp(instance, newKvinit)];
785
1106
  case 4:
786
1107
  _b.sent();
787
- Log.trace("KochavaConfig after new Kvinit:", JSON.parse(JSON.stringify(this._instance.kochavaConfig)));
1108
+ Log.trace("KochavaConfig after new Kvinit:", JSON.parse(JSON.stringify(instance.kochavaConfig)));
788
1109
  return [2 /*return*/];
789
1110
  }
790
1111
  });
791
1112
  });
792
1113
  };
793
- Kochava.prototype.checkResendId = function () {
1114
+ Kochava.prototype.checkResendId = function (instance) {
794
1115
  return __awaiter(this, void 0, void 0, function () {
795
1116
  var resendId, needsNewInstall;
796
1117
  return __generator(this, function (_a) {
797
1118
  switch (_a.label) {
798
1119
  case 0:
799
1120
  resendId = "";
800
- if (this._instance.kochavaConfig.install) {
801
- resendId = this._instance.kochavaConfig.install.resend_id;
1121
+ if (instance.kochavaConfig.install) {
1122
+ resendId = instance.kochavaConfig.install.resend_id;
802
1123
  }
803
- return [4 /*yield*/, checkInstallIdChange(resendId, this._instance.useCookies)];
1124
+ return [4 /*yield*/, checkInstallIdChange(resendId, instance.useCookies)];
804
1125
  case 1:
805
1126
  needsNewInstall = _a.sent();
806
1127
  if (needsNewInstall) {
@@ -811,46 +1132,54 @@ var Kochava = /** @class */function () {
811
1132
  });
812
1133
  });
813
1134
  };
814
- Kochava.prototype.performInstall = function () {
1135
+ Kochava.prototype.performInstall = function (instance) {
815
1136
  return __awaiter(this, void 0, void 0, function () {
816
- var request, _a;
1137
+ var jobQueue, request, _a;
817
1138
  return __generator(this, function (_b) {
818
1139
  switch (_b.label) {
819
1140
  case 0:
820
- return [4 /*yield*/, Install.build(this._instance)];
1141
+ jobQueue = this._jobQueue;
1142
+ return [4 /*yield*/, Install.build(instance)];
821
1143
  case 1:
822
1144
  request = _b.sent();
823
- _a = this._instance;
824
- return [4 /*yield*/, Install.send(this._instance, request)];
1145
+ _a = instance;
1146
+ return [4 /*yield*/, Install.send(instance, request)];
825
1147
  case 2:
826
1148
  _a.installDone = _b.sent();
827
- if (!this._instance.installDone) return [2 /*return*/];
1149
+ if (!instance.installDone) return [2 /*return*/];
1150
+ if (instance.invalidatedBool) return [2 /*return*/];
828
1151
  // If the install succeeded, remove all idLink that were passed to it
829
- Install.onSuccess(this._instance);
830
- return [4 /*yield*/, this._jobQueue.persistIdLinkQueue()];
1152
+ return [4 /*yield*/, Install.onSuccess(instance)];
831
1153
  case 3:
1154
+ // If the install succeeded, remove all idLink that were passed to it
832
1155
  _b.sent();
833
- return [4 /*yield*/, this._jobQueue.persistUpdateQueue()];
1156
+ if (instance.invalidatedBool) return [2 /*return*/];
1157
+ return [4 /*yield*/, jobQueue.persistIdLinkQueue()];
834
1158
  case 4:
835
1159
  _b.sent();
836
- return [4 /*yield*/, this._persistSentCustomDeviceIds()];
1160
+ if (instance.invalidatedBool) return [2 /*return*/];
1161
+ return [4 /*yield*/, jobQueue.persistUpdateQueue()];
837
1162
  case 5:
1163
+ _b.sent();
1164
+ if (instance.invalidatedBool) return [2 /*return*/];
1165
+ return [4 /*yield*/, this._persistSentCustomDeviceIds(instance)];
1166
+ case 6:
838
1167
  _b.sent();
839
1168
  return [2 /*return*/];
840
1169
  }
841
1170
  });
842
1171
  });
843
1172
  };
844
- Kochava.prototype._persistSentCustomDeviceIds = function () {
1173
+ Kochava.prototype._persistSentCustomDeviceIds = function (instance) {
845
1174
  var _a, _b;
846
1175
  return __awaiter(this, void 0, void 0, function () {
847
1176
  var allowed, sentIds;
848
1177
  return __generator(this, function (_c) {
849
1178
  switch (_c.label) {
850
1179
  case 0:
851
- allowed = (_b = (_a = this._instance.kochavaConfig) === null || _a === void 0 ? void 0 : _a.privacy.allow_custom_ids) !== null && _b !== void 0 ? _b : [];
1180
+ allowed = (_b = (_a = instance.kochavaConfig) === null || _a === void 0 ? void 0 : _a.privacy.allow_custom_ids) !== null && _b !== void 0 ? _b : [];
852
1181
  sentIds = {};
853
- this._instance.customValues.forEach(function (cv) {
1182
+ instance.customValues.forEach(function (cv) {
854
1183
  if (!cv.isDeviceId) return;
855
1184
  var key = Object.keys(cv.data)[0];
856
1185
  var keyAllowed = false;