@splitsoftware/splitio-commons 2.5.0-rc.1 → 2.5.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGES.txt +13 -1
  2. package/cjs/storages/getRolloutPlan.js +1 -1
  3. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
  4. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +17 -17
  5. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
  6. package/cjs/storages/inLocalStorage/index.js +31 -13
  7. package/cjs/storages/inLocalStorage/storageAdapter.js +54 -0
  8. package/cjs/storages/inLocalStorage/validateCache.js +28 -23
  9. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  10. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -0
  11. package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -0
  12. package/cjs/sync/syncManagerOnline.js +28 -24
  13. package/cjs/utils/env/isLocalStorageAvailable.js +22 -1
  14. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  15. package/esm/storages/getRolloutPlan.js +1 -1
  16. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
  17. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +17 -17
  18. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
  19. package/esm/storages/inLocalStorage/index.js +32 -14
  20. package/esm/storages/inLocalStorage/storageAdapter.js +50 -0
  21. package/esm/storages/inLocalStorage/validateCache.js +28 -23
  22. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  23. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -0
  24. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -0
  25. package/esm/sync/syncManagerOnline.js +28 -24
  26. package/esm/utils/env/isLocalStorageAvailable.js +19 -0
  27. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  28. package/package.json +1 -1
  29. package/src/storages/getRolloutPlan.ts +1 -1
  30. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +18 -17
  31. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +19 -18
  32. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +34 -37
  33. package/src/storages/inLocalStorage/index.ts +37 -16
  34. package/src/storages/inLocalStorage/storageAdapter.ts +62 -0
  35. package/src/storages/inLocalStorage/validateCache.ts +29 -23
  36. package/src/storages/types.ts +19 -1
  37. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +1 -2
  38. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -0
  39. package/src/sync/polling/updaters/splitChangesUpdater.ts +3 -1
  40. package/src/sync/syncManagerOnline.ts +27 -22
  41. package/src/utils/env/isLocalStorageAvailable.ts +20 -0
  42. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  43. package/types/splitio.d.ts +38 -1
@@ -34,6 +34,8 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
34
34
  shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
35
35
  shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
36
36
  }
37
+ if (storage.save)
38
+ storage.save();
37
39
  // Notify update if required
38
40
  if ((0, AbstractSplitsCacheSync_1.usesSegmentsSync)(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
39
41
  readyOnAlreadyExistentState = false;
@@ -154,6 +154,8 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
154
154
  segments.registerSegments((0, sets_1.setToArray)(usedSegments))
155
155
  ]).then(function (_a) {
156
156
  var ffChanged = _a[0], rbsChanged = _a[1];
157
+ if (storage.save)
158
+ storage.save();
157
159
  if (splitsEventEmitter) {
158
160
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
159
161
  return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
@@ -69,35 +69,39 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
69
69
  */
70
70
  start: function () {
71
71
  running = true;
72
- if (startFirstTime) {
73
- var isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
74
- if (isCacheLoaded)
75
- Promise.resolve().then(function () { readiness.splits.emit(constants_4.SDK_SPLITS_CACHE_LOADED); });
76
- }
77
- // start syncing splits and segments
78
- if (pollingManager) {
79
- // If synchronization is disabled pushManager and pollingManager should not start
80
- if (syncEnabled) {
81
- if (pushManager) {
82
- // Doesn't call `syncAll` when the syncManager is resuming
83
- if (startFirstTime) {
84
- pollingManager.syncAll();
72
+ // @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
73
+ submitterManager.start(!(0, consent_1.isConsentGranted)(settings));
74
+ return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
75
+ if (!running)
76
+ return;
77
+ if (startFirstTime) {
78
+ // Emits SDK_READY_FROM_CACHE
79
+ if (isCacheLoaded)
80
+ readiness.splits.emit(constants_4.SDK_SPLITS_CACHE_LOADED);
81
+ }
82
+ // start syncing splits and segments
83
+ if (pollingManager) {
84
+ // If synchronization is disabled pushManager and pollingManager should not start
85
+ if (syncEnabled) {
86
+ if (pushManager) {
87
+ // Doesn't call `syncAll` when the syncManager is resuming
88
+ if (startFirstTime) {
89
+ pollingManager.syncAll();
90
+ }
91
+ pushManager.start();
92
+ }
93
+ else {
94
+ pollingManager.start();
85
95
  }
86
- pushManager.start();
87
96
  }
88
97
  else {
89
- pollingManager.start();
90
- }
91
- }
92
- else {
93
- if (startFirstTime) {
94
- pollingManager.syncAll();
98
+ if (startFirstTime) {
99
+ pollingManager.syncAll();
100
+ }
95
101
  }
96
102
  }
97
- }
98
- // start periodic data recording (events, impressions, telemetry).
99
- submitterManager.start(!(0, consent_1.isConsentGranted)(settings));
100
- startFirstTime = false;
103
+ startFirstTime = false;
104
+ });
101
105
  },
102
106
  /**
103
107
  * Method used to stop/pause the syncManager.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isLocalStorageAvailable = void 0;
3
+ exports.isWebStorage = exports.isValidStorageWrapper = exports.isLocalStorageAvailable = void 0;
4
4
  /* eslint-disable no-undef */
5
5
  function isLocalStorageAvailable() {
6
6
  var mod = '__SPLITSOFTWARE__';
@@ -14,3 +14,24 @@ function isLocalStorageAvailable() {
14
14
  }
15
15
  }
16
16
  exports.isLocalStorageAvailable = isLocalStorageAvailable;
17
+ function isValidStorageWrapper(wrapper) {
18
+ return wrapper !== null &&
19
+ typeof wrapper === 'object' &&
20
+ typeof wrapper.setItem === 'function' &&
21
+ typeof wrapper.getItem === 'function' &&
22
+ typeof wrapper.removeItem === 'function';
23
+ }
24
+ exports.isValidStorageWrapper = isValidStorageWrapper;
25
+ function isWebStorage(wrapper) {
26
+ if (typeof wrapper.length === 'number') {
27
+ try {
28
+ wrapper.key(0);
29
+ return true;
30
+ }
31
+ catch (e) {
32
+ return false;
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ exports.isWebStorage = isWebStorage;
@@ -6,7 +6,7 @@ var constants_1 = require("../../../logger/constants");
6
6
  var constants_2 = require("../../../utils/constants");
7
7
  function __InLocalStorageMockFactory(params) {
8
8
  var result = (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
9
- result.validateCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
9
+ result.validateCache = function () { return Promise.resolve(true); }; // to emit SDK_READY_FROM_CACHE
10
10
  return result;
11
11
  }
12
12
  exports.__InLocalStorageMockFactory = __InLocalStorageMockFactory;
@@ -7,7 +7,7 @@ export function getRolloutPlan(log, storage, options) {
7
7
  if (options === void 0) { options = {}; }
8
8
  var keys = options.keys, exposeSegments = options.exposeSegments;
9
9
  var splits = storage.splits, segments = storage.segments, rbSegments = storage.rbSegments;
10
- log.debug("storage: get feature flags" + (keys ? ", and memberships for keys " + keys : '') + (exposeSegments ? ', and segments' : ''));
10
+ log.debug("storage: get feature flags" + (keys ? ", and memberships for keys: " + keys : '') + (exposeSegments ? ', and segments' : ''));
11
11
  return {
12
12
  splitChanges: {
13
13
  ff: {
@@ -4,19 +4,20 @@ import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
4
4
  import { LOG_PREFIX, DEFINED } from './constants';
5
5
  var MySegmentsCacheInLocal = /** @class */ (function (_super) {
6
6
  __extends(MySegmentsCacheInLocal, _super);
7
- function MySegmentsCacheInLocal(log, keys) {
7
+ function MySegmentsCacheInLocal(log, keys, storage) {
8
8
  var _this = _super.call(this) || this;
9
9
  _this.log = log;
10
10
  _this.keys = keys;
11
+ _this.storage = storage;
11
12
  return _this;
12
13
  // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
13
14
  }
14
15
  MySegmentsCacheInLocal.prototype.addSegment = function (name) {
15
16
  var segmentKey = this.keys.buildSegmentNameKey(name);
16
17
  try {
17
- if (localStorage.getItem(segmentKey) === DEFINED)
18
+ if (this.storage.getItem(segmentKey) === DEFINED)
18
19
  return false;
19
- localStorage.setItem(segmentKey, DEFINED);
20
+ this.storage.setItem(segmentKey, DEFINED);
20
21
  return true;
21
22
  }
22
23
  catch (e) {
@@ -27,9 +28,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
27
28
  MySegmentsCacheInLocal.prototype.removeSegment = function (name) {
28
29
  var segmentKey = this.keys.buildSegmentNameKey(name);
29
30
  try {
30
- if (localStorage.getItem(segmentKey) !== DEFINED)
31
+ if (this.storage.getItem(segmentKey) !== DEFINED)
31
32
  return false;
32
- localStorage.removeItem(segmentKey);
33
+ this.storage.removeItem(segmentKey);
33
34
  return true;
34
35
  }
35
36
  catch (e) {
@@ -38,17 +39,16 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
38
39
  }
39
40
  };
40
41
  MySegmentsCacheInLocal.prototype.isInSegment = function (name) {
41
- return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
42
+ return this.storage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
42
43
  };
43
44
  MySegmentsCacheInLocal.prototype.getRegisteredSegments = function () {
44
- var _this = this;
45
- // Scan current values from localStorage
46
- return Object.keys(localStorage).reduce(function (accum, key) {
47
- var segmentName = _this.keys.extractSegmentName(key);
45
+ var registeredSegments = [];
46
+ for (var i = 0, len = this.storage.length; i < len; i++) {
47
+ var segmentName = this.keys.extractSegmentName(this.storage.key(i));
48
48
  if (segmentName)
49
- accum.push(segmentName);
50
- return accum;
51
- }, []);
49
+ registeredSegments.push(segmentName);
50
+ }
51
+ return registeredSegments;
52
52
  };
53
53
  MySegmentsCacheInLocal.prototype.getKeysCount = function () {
54
54
  return 1;
@@ -56,9 +56,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
56
56
  MySegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
57
57
  try {
58
58
  if (changeNumber)
59
- localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
59
+ this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
60
60
  else
61
- localStorage.removeItem(this.keys.buildTillKey());
61
+ this.storage.removeItem(this.keys.buildTillKey());
62
62
  }
63
63
  catch (e) {
64
64
  this.log.error(e);
@@ -66,7 +66,7 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
66
66
  };
67
67
  MySegmentsCacheInLocal.prototype.getChangeNumber = function () {
68
68
  var n = -1;
69
- var value = localStorage.getItem(this.keys.buildTillKey());
69
+ var value = this.storage.getItem(this.keys.buildTillKey());
70
70
  if (value !== null) {
71
71
  value = parseInt(value, 10);
72
72
  return isNaNNumber(value) ? n : value;
@@ -3,14 +3,15 @@ import { setToArray } from '../../utils/lang/sets';
3
3
  import { usesSegments } from '../AbstractSplitsCacheSync';
4
4
  import { LOG_PREFIX } from './constants';
5
5
  var RBSegmentsCacheInLocal = /** @class */ (function () {
6
- function RBSegmentsCacheInLocal(settings, keys) {
6
+ function RBSegmentsCacheInLocal(settings, keys, storage) {
7
7
  this.keys = keys;
8
8
  this.log = settings.log;
9
+ this.storage = storage;
9
10
  }
10
11
  RBSegmentsCacheInLocal.prototype.clear = function () {
11
12
  var _this = this;
12
13
  this.getNames().forEach(function (name) { return _this.remove(name); });
13
- localStorage.removeItem(this.keys.buildRBSegmentsTillKey());
14
+ this.storage.removeItem(this.keys.buildRBSegmentsTillKey());
14
15
  };
15
16
  RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
16
17
  var _this = this;
@@ -20,8 +21,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
20
21
  };
21
22
  RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
22
23
  try {
23
- localStorage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
24
- localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
24
+ this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
25
+ this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
25
26
  }
26
27
  catch (e) {
27
28
  this.log.error(LOG_PREFIX + e);
@@ -29,20 +30,19 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
29
30
  };
30
31
  RBSegmentsCacheInLocal.prototype.updateSegmentCount = function (diff) {
31
32
  var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
32
- var count = toNumber(localStorage.getItem(segmentsCountKey)) + diff;
33
- // @ts-expect-error
33
+ var count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
34
34
  if (count > 0)
35
- localStorage.setItem(segmentsCountKey, count);
35
+ this.storage.setItem(segmentsCountKey, count + '');
36
36
  else
37
- localStorage.removeItem(segmentsCountKey);
37
+ this.storage.removeItem(segmentsCountKey);
38
38
  };
39
39
  RBSegmentsCacheInLocal.prototype.add = function (rbSegment) {
40
40
  try {
41
41
  var name_1 = rbSegment.name;
42
42
  var rbSegmentKey = this.keys.buildRBSegmentKey(name_1);
43
- var rbSegmentFromLocalStorage = localStorage.getItem(rbSegmentKey);
44
- var previous = rbSegmentFromLocalStorage ? JSON.parse(rbSegmentFromLocalStorage) : null;
45
- localStorage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
43
+ var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
44
+ var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
45
+ this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
46
46
  var usesSegmentsDiff = 0;
47
47
  if (previous && usesSegments(previous))
48
48
  usesSegmentsDiff--;
@@ -62,7 +62,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
62
62
  var rbSegment = this.get(name);
63
63
  if (!rbSegment)
64
64
  return false;
65
- localStorage.removeItem(this.keys.buildRBSegmentKey(name));
65
+ this.storage.removeItem(this.keys.buildRBSegmentKey(name));
66
66
  if (usesSegments(rbSegment))
67
67
  this.updateSegmentCount(-1);
68
68
  return true;
@@ -73,11 +73,11 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
73
73
  }
74
74
  };
75
75
  RBSegmentsCacheInLocal.prototype.getNames = function () {
76
- var len = localStorage.length;
76
+ var len = this.storage.length;
77
77
  var accum = [];
78
78
  var cur = 0;
79
79
  while (cur < len) {
80
- var key = localStorage.key(cur);
80
+ var key = this.storage.key(cur);
81
81
  if (key != null && this.keys.isRBSegmentKey(key))
82
82
  accum.push(this.keys.extractKey(key));
83
83
  cur++;
@@ -85,7 +85,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
85
85
  return accum;
86
86
  };
87
87
  RBSegmentsCacheInLocal.prototype.get = function (name) {
88
- var item = localStorage.getItem(this.keys.buildRBSegmentKey(name));
88
+ var item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
89
89
  return item && JSON.parse(item);
90
90
  };
91
91
  RBSegmentsCacheInLocal.prototype.getAll = function () {
@@ -99,7 +99,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
99
99
  };
100
100
  RBSegmentsCacheInLocal.prototype.getChangeNumber = function () {
101
101
  var n = -1;
102
- var value = localStorage.getItem(this.keys.buildRBSegmentsTillKey());
102
+ var value = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
103
103
  if (value !== null) {
104
104
  value = parseInt(value, 10);
105
105
  return isNaNNumber(value) ? n : value;
@@ -107,7 +107,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
107
107
  return n;
108
108
  };
109
109
  RBSegmentsCacheInLocal.prototype.usesSegments = function () {
110
- var storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
110
+ var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
111
111
  var splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
112
112
  return isFiniteNumber(splitsWithSegmentsCount) ?
113
113
  splitsWithSegmentsCount > 0 :
@@ -3,25 +3,22 @@ import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSyn
3
3
  import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
4
4
  import { LOG_PREFIX } from './constants';
5
5
  import { setToArray } from '../../utils/lang/sets';
6
- /**
7
- * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
8
- */
9
6
  var SplitsCacheInLocal = /** @class */ (function (_super) {
10
7
  __extends(SplitsCacheInLocal, _super);
11
- function SplitsCacheInLocal(settings, keys) {
8
+ function SplitsCacheInLocal(settings, keys, storage) {
12
9
  var _this = _super.call(this) || this;
13
10
  _this.keys = keys;
14
11
  _this.log = settings.log;
15
12
  _this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
13
+ _this.storage = storage;
16
14
  return _this;
17
15
  }
18
16
  SplitsCacheInLocal.prototype._decrementCount = function (key) {
19
- var count = toNumber(localStorage.getItem(key)) - 1;
20
- // @ts-expect-error
17
+ var count = toNumber(this.storage.getItem(key)) - 1;
21
18
  if (count > 0)
22
- localStorage.setItem(key, count);
19
+ this.storage.setItem(key, count + '');
23
20
  else
24
- localStorage.removeItem(key);
21
+ this.storage.removeItem(key);
25
22
  };
26
23
  SplitsCacheInLocal.prototype._decrementCounts = function (split) {
27
24
  try {
@@ -39,12 +36,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
39
36
  SplitsCacheInLocal.prototype._incrementCounts = function (split) {
40
37
  try {
41
38
  var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
42
- // @ts-expect-error
43
- localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
39
+ this.storage.setItem(ttKey, (toNumber(this.storage.getItem(ttKey)) + 1) + '');
44
40
  if (usesSegments(split)) {
45
41
  var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
46
- // @ts-expect-error
47
- localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
42
+ this.storage.setItem(segmentsCountKey, (toNumber(this.storage.getItem(segmentsCountKey)) + 1) + '');
48
43
  }
49
44
  }
50
45
  catch (e) {
@@ -56,17 +51,18 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
56
51
  * We cannot simply call `localStorage.clear()` since that implies removing user items from the storage.
57
52
  */
58
53
  SplitsCacheInLocal.prototype.clear = function () {
54
+ var _this = this;
59
55
  // collect item keys
60
- var len = localStorage.length;
56
+ var len = this.storage.length;
61
57
  var accum = [];
62
58
  for (var cur = 0; cur < len; cur++) {
63
- var key = localStorage.key(cur);
59
+ var key = this.storage.key(cur);
64
60
  if (key != null && this.keys.isSplitsCacheKey(key))
65
61
  accum.push(key);
66
62
  }
67
63
  // remove items
68
64
  accum.forEach(function (key) {
69
- localStorage.removeItem(key);
65
+ _this.storage.removeItem(key);
70
66
  });
71
67
  this.hasSync = false;
72
68
  };
@@ -74,13 +70,13 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
74
70
  try {
75
71
  var name_1 = split.name;
76
72
  var splitKey = this.keys.buildSplitKey(name_1);
77
- var splitFromLocalStorage = localStorage.getItem(splitKey);
78
- var previousSplit = splitFromLocalStorage ? JSON.parse(splitFromLocalStorage) : null;
73
+ var splitFromStorage = this.storage.getItem(splitKey);
74
+ var previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
79
75
  if (previousSplit) {
80
76
  this._decrementCounts(previousSplit);
81
77
  this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
82
78
  }
83
- localStorage.setItem(splitKey, JSON.stringify(split));
79
+ this.storage.setItem(splitKey, JSON.stringify(split));
84
80
  this._incrementCounts(split);
85
81
  this.addToFlagSets(split);
86
82
  return true;
@@ -95,7 +91,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
95
91
  var split = this.getSplit(name);
96
92
  if (!split)
97
93
  return false;
98
- localStorage.removeItem(this.keys.buildSplitKey(name));
94
+ this.storage.removeItem(this.keys.buildSplitKey(name));
99
95
  this._decrementCounts(split);
100
96
  this.removeFromFlagSets(split.name, split.sets);
101
97
  return true;
@@ -106,14 +102,14 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
106
102
  }
107
103
  };
108
104
  SplitsCacheInLocal.prototype.getSplit = function (name) {
109
- var item = localStorage.getItem(this.keys.buildSplitKey(name));
105
+ var item = this.storage.getItem(this.keys.buildSplitKey(name));
110
106
  return item && JSON.parse(item);
111
107
  };
112
108
  SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
113
109
  try {
114
- localStorage.setItem(this.keys.buildSplitsTillKey(), changeNumber + '');
110
+ this.storage.setItem(this.keys.buildSplitsTillKey(), changeNumber + '');
115
111
  // update "last updated" timestamp with current time
116
- localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
112
+ this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
117
113
  this.hasSync = true;
118
114
  return true;
119
115
  }
@@ -124,7 +120,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
124
120
  };
125
121
  SplitsCacheInLocal.prototype.getChangeNumber = function () {
126
122
  var n = -1;
127
- var value = localStorage.getItem(this.keys.buildSplitsTillKey());
123
+ var value = this.storage.getItem(this.keys.buildSplitsTillKey());
128
124
  if (value !== null) {
129
125
  value = parseInt(value, 10);
130
126
  return isNaNNumber(value) ? n : value;
@@ -132,11 +128,11 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
132
128
  return n;
133
129
  };
134
130
  SplitsCacheInLocal.prototype.getSplitNames = function () {
135
- var len = localStorage.length;
131
+ var len = this.storage.length;
136
132
  var accum = [];
137
133
  var cur = 0;
138
134
  while (cur < len) {
139
- var key = localStorage.key(cur);
135
+ var key = this.storage.key(cur);
140
136
  if (key != null && this.keys.isSplitKey(key))
141
137
  accum.push(this.keys.extractKey(key));
142
138
  cur++;
@@ -144,14 +140,14 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
144
140
  return accum;
145
141
  };
146
142
  SplitsCacheInLocal.prototype.trafficTypeExists = function (trafficType) {
147
- var ttCount = toNumber(localStorage.getItem(this.keys.buildTrafficTypeKey(trafficType)));
143
+ var ttCount = toNumber(this.storage.getItem(this.keys.buildTrafficTypeKey(trafficType)));
148
144
  return isFiniteNumber(ttCount) && ttCount > 0;
149
145
  };
150
146
  SplitsCacheInLocal.prototype.usesSegments = function () {
151
147
  // If cache hasn't been synchronized with the cloud, assume we need them.
152
148
  if (!this.hasSync)
153
149
  return true;
154
- var storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
150
+ var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
155
151
  var splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
156
152
  return isFiniteNumber(splitsWithSegmentsCount) ?
157
153
  splitsWithSegmentsCount > 0 :
@@ -161,8 +157,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
161
157
  var _this = this;
162
158
  return flagSets.map(function (flagSet) {
163
159
  var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
164
- var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
165
- return new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
160
+ var flagSetFromStorage = _this.storage.getItem(flagSetKey);
161
+ return new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
166
162
  });
167
163
  };
168
164
  SplitsCacheInLocal.prototype.addToFlagSets = function (featureFlag) {
@@ -173,10 +169,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
173
169
  if (_this.flagSetsFilter.length > 0 && !_this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === featureFlagSet; }))
174
170
  return;
175
171
  var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
176
- var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
177
- var flagSetCache = new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
172
+ var flagSetFromStorage = _this.storage.getItem(flagSetKey);
173
+ var flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
178
174
  flagSetCache.add(featureFlag.name);
179
- localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
175
+ _this.storage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
180
176
  });
181
177
  };
182
178
  SplitsCacheInLocal.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
@@ -189,16 +185,16 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
189
185
  };
190
186
  SplitsCacheInLocal.prototype.removeNames = function (flagSetName, featureFlagName) {
191
187
  var flagSetKey = this.keys.buildFlagSetKey(flagSetName);
192
- var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
193
- if (!flagSetFromLocalStorage)
188
+ var flagSetFromStorage = this.storage.getItem(flagSetKey);
189
+ if (!flagSetFromStorage)
194
190
  return;
195
- var flagSetCache = new Set(JSON.parse(flagSetFromLocalStorage));
191
+ var flagSetCache = new Set(JSON.parse(flagSetFromStorage));
196
192
  flagSetCache.delete(featureFlagName);
197
193
  if (flagSetCache.size === 0) {
198
- localStorage.removeItem(flagSetKey);
194
+ this.storage.removeItem(flagSetKey);
199
195
  return;
200
196
  }
201
- localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
197
+ this.storage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
202
198
  };
203
199
  return SplitsCacheInLocal;
204
200
  }(AbstractSplitsCacheSync));
@@ -3,7 +3,7 @@ import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCache
3
3
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
4
4
  import { validatePrefix } from '../KeyBuilder';
5
5
  import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
6
- import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
6
+ import { isLocalStorageAvailable, isValidStorageWrapper, isWebStorage } from '../../utils/env/isLocalStorageAvailable';
7
7
  import { SplitsCacheInLocal } from './SplitsCacheInLocal';
8
8
  import { RBSegmentsCacheInLocal } from './RBSegmentsCacheInLocal';
9
9
  import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
@@ -14,6 +14,20 @@ import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/Telem
14
14
  import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
15
15
  import { getMatching } from '../../utils/key';
16
16
  import { validateCache } from './validateCache';
17
+ import { storageAdapter } from './storageAdapter';
18
+ function validateStorage(log, prefix, wrapper) {
19
+ if (wrapper) {
20
+ if (isValidStorageWrapper(wrapper)) {
21
+ return isWebStorage(wrapper) ?
22
+ wrapper : // localStorage and sessionStorage don't need adapter
23
+ storageAdapter(log, prefix, wrapper);
24
+ }
25
+ log.warn(LOG_PREFIX + 'Invalid storage provided. Falling back to LocalStorage API');
26
+ }
27
+ if (isLocalStorageAvailable())
28
+ return localStorage;
29
+ log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
30
+ }
17
31
  /**
18
32
  * InLocal storage factory for standalone client-side SplitFactory
19
33
  */
@@ -21,18 +35,17 @@ export function InLocalStorage(options) {
21
35
  if (options === void 0) { options = {}; }
22
36
  var prefix = validatePrefix(options.prefix);
23
37
  function InLocalStorageCSFactory(params) {
24
- // Fallback to InMemoryStorage if LocalStorage API is not available
25
- if (!isLocalStorageAvailable()) {
26
- params.settings.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
27
- return InMemoryStorageCSFactory(params);
28
- }
29
38
  var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize;
39
+ var storage = validateStorage(log, prefix, options.wrapper);
40
+ if (!storage)
41
+ return InMemoryStorageCSFactory(params);
30
42
  var matchingKey = getMatching(settings.core.key);
31
43
  var keys = new KeyBuilderCS(prefix, matchingKey);
32
- var splits = new SplitsCacheInLocal(settings, keys);
33
- var rbSegments = new RBSegmentsCacheInLocal(settings, keys);
34
- var segments = new MySegmentsCacheInLocal(log, keys);
35
- var largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));
44
+ var splits = new SplitsCacheInLocal(settings, keys, storage);
45
+ var rbSegments = new RBSegmentsCacheInLocal(settings, keys, storage);
46
+ var segments = new MySegmentsCacheInLocal(log, keys, storage);
47
+ var largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage);
48
+ var validateCachePromise;
36
49
  return {
37
50
  splits: splits,
38
51
  rbSegments: rbSegments,
@@ -44,16 +57,21 @@ export function InLocalStorage(options) {
44
57
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
45
58
  uniqueKeys: new UniqueKeysCacheInMemoryCS(),
46
59
  validateCache: function () {
47
- return validateCache(options, settings, keys, splits, rbSegments, segments, largeSegments);
60
+ return validateCachePromise || (validateCachePromise = validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments));
61
+ },
62
+ save: function () {
63
+ return storage.save && storage.save();
64
+ },
65
+ destroy: function () {
66
+ return storage.whenSaved && storage.whenSaved();
48
67
  },
49
- destroy: function () { },
50
68
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
51
69
  shared: function (matchingKey) {
52
70
  return {
53
71
  splits: this.splits,
54
72
  rbSegments: this.rbSegments,
55
- segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
56
- largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
73
+ segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey), storage),
74
+ largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage),
57
75
  impressions: this.impressions,
58
76
  impressionCounts: this.impressionCounts,
59
77
  events: this.events,