@splitsoftware/splitio-commons 2.4.2-rc.1 → 2.4.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.
Files changed (67) hide show
  1. package/CHANGES.txt +13 -1
  2. package/cjs/logger/messages/error.js +1 -1
  3. package/cjs/sdkClient/sdkClient.js +0 -1
  4. package/cjs/sdkFactory/index.js +3 -10
  5. package/cjs/services/splitHttpClient.js +1 -1
  6. package/cjs/storages/AbstractMySegmentsCacheSync.js +31 -23
  7. package/cjs/storages/AbstractSplitsCacheSync.js +8 -3
  8. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
  9. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +20 -19
  10. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
  11. package/cjs/storages/inLocalStorage/index.js +28 -13
  12. package/cjs/storages/inLocalStorage/storageAdapter.js +48 -0
  13. package/cjs/storages/inLocalStorage/validateCache.js +25 -23
  14. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  15. package/cjs/sync/polling/pollingManagerCS.js +5 -4
  16. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -2
  17. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
  18. package/cjs/sync/syncManagerOnline.js +31 -26
  19. package/cjs/trackers/impressionsTracker.js +4 -4
  20. package/cjs/utils/env/isLocalStorageAvailable.js +28 -5
  21. package/cjs/utils/settingsValidation/splitFilters.js +0 -6
  22. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  23. package/esm/logger/messages/error.js +1 -1
  24. package/esm/sdkClient/sdkClient.js +0 -1
  25. package/esm/sdkFactory/index.js +3 -10
  26. package/esm/services/splitHttpClient.js +1 -1
  27. package/esm/storages/AbstractMySegmentsCacheSync.js +31 -23
  28. package/esm/storages/AbstractSplitsCacheSync.js +6 -2
  29. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
  30. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +20 -19
  31. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
  32. package/esm/storages/inLocalStorage/index.js +29 -14
  33. package/esm/storages/inLocalStorage/storageAdapter.js +44 -0
  34. package/esm/storages/inLocalStorage/validateCache.js +25 -23
  35. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  36. package/esm/sync/polling/pollingManagerCS.js +5 -4
  37. package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -2
  38. package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
  39. package/esm/sync/syncManagerOnline.js +31 -26
  40. package/esm/trackers/impressionsTracker.js +4 -4
  41. package/esm/utils/env/isLocalStorageAvailable.js +24 -3
  42. package/esm/utils/settingsValidation/splitFilters.js +0 -6
  43. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  44. package/package.json +1 -1
  45. package/src/logger/messages/error.ts +1 -1
  46. package/src/sdkClient/sdkClient.ts +0 -1
  47. package/src/sdkFactory/index.ts +3 -13
  48. package/src/services/splitHttpClient.ts +1 -1
  49. package/src/storages/AbstractMySegmentsCacheSync.ts +26 -20
  50. package/src/storages/AbstractSplitsCacheSync.ts +8 -3
  51. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +18 -17
  52. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +22 -20
  53. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +34 -37
  54. package/src/storages/inLocalStorage/index.ts +33 -16
  55. package/src/storages/inLocalStorage/storageAdapter.ts +50 -0
  56. package/src/storages/inLocalStorage/validateCache.ts +26 -23
  57. package/src/storages/types.ts +17 -1
  58. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +1 -2
  59. package/src/sync/polling/pollingManagerCS.ts +5 -4
  60. package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -2
  61. package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -1
  62. package/src/sync/syncManagerOnline.ts +30 -24
  63. package/src/trackers/impressionsTracker.ts +3 -3
  64. package/src/utils/env/isLocalStorageAvailable.ts +24 -3
  65. package/src/utils/settingsValidation/splitFilters.ts +0 -6
  66. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  67. package/types/splitio.d.ts +57 -16
@@ -3,25 +3,27 @@ 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;
17
- this.setChangeNumber(changeNumber);
18
18
  var updated = toAdd.map(function (toAdd) { return _this.add(toAdd); }).some(function (result) { return result; });
19
- return toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
19
+ updated = toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
20
+ this.setChangeNumber(changeNumber);
21
+ return updated;
20
22
  };
21
23
  RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
22
24
  try {
23
- localStorage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
24
- localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
25
+ this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
26
+ this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
25
27
  }
26
28
  catch (e) {
27
29
  this.log.error(LOG_PREFIX + e);
@@ -29,20 +31,19 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
29
31
  };
30
32
  RBSegmentsCacheInLocal.prototype.updateSegmentCount = function (diff) {
31
33
  var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
32
- var count = toNumber(localStorage.getItem(segmentsCountKey)) + diff;
33
- // @ts-expect-error
34
+ var count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
34
35
  if (count > 0)
35
- localStorage.setItem(segmentsCountKey, count);
36
+ this.storage.setItem(segmentsCountKey, count + '');
36
37
  else
37
- localStorage.removeItem(segmentsCountKey);
38
+ this.storage.removeItem(segmentsCountKey);
38
39
  };
39
40
  RBSegmentsCacheInLocal.prototype.add = function (rbSegment) {
40
41
  try {
41
42
  var name_1 = rbSegment.name;
42
43
  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));
44
+ var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
45
+ var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
46
+ this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
46
47
  var usesSegmentsDiff = 0;
47
48
  if (previous && usesSegments(previous))
48
49
  usesSegmentsDiff--;
@@ -62,7 +63,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
62
63
  var rbSegment = this.get(name);
63
64
  if (!rbSegment)
64
65
  return false;
65
- localStorage.removeItem(this.keys.buildRBSegmentKey(name));
66
+ this.storage.removeItem(this.keys.buildRBSegmentKey(name));
66
67
  if (usesSegments(rbSegment))
67
68
  this.updateSegmentCount(-1);
68
69
  return true;
@@ -73,11 +74,11 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
73
74
  }
74
75
  };
75
76
  RBSegmentsCacheInLocal.prototype.getNames = function () {
76
- var len = localStorage.length;
77
+ var len = this.storage.length;
77
78
  var accum = [];
78
79
  var cur = 0;
79
80
  while (cur < len) {
80
- var key = localStorage.key(cur);
81
+ var key = this.storage.key(cur);
81
82
  if (key != null && this.keys.isRBSegmentKey(key))
82
83
  accum.push(this.keys.extractKey(key));
83
84
  cur++;
@@ -85,7 +86,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
85
86
  return accum;
86
87
  };
87
88
  RBSegmentsCacheInLocal.prototype.get = function (name) {
88
- var item = localStorage.getItem(this.keys.buildRBSegmentKey(name));
89
+ var item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
89
90
  return item && JSON.parse(item);
90
91
  };
91
92
  RBSegmentsCacheInLocal.prototype.contains = function (names) {
@@ -95,7 +96,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
95
96
  };
96
97
  RBSegmentsCacheInLocal.prototype.getChangeNumber = function () {
97
98
  var n = -1;
98
- var value = localStorage.getItem(this.keys.buildRBSegmentsTillKey());
99
+ var value = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
99
100
  if (value !== null) {
100
101
  value = parseInt(value, 10);
101
102
  return isNaNNumber(value) ? n : value;
@@ -103,7 +104,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
103
104
  return n;
104
105
  };
105
106
  RBSegmentsCacheInLocal.prototype.usesSegments = function () {
106
- var storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
107
+ var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
107
108
  var splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
108
109
  return isFiniteNumber(splitsWithSegmentsCount) ?
109
110
  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,18 @@ 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
+ destroy: function () {
63
+ return storage.save && storage.save();
48
64
  },
49
- destroy: function () { },
50
65
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
51
66
  shared: function (matchingKey) {
52
67
  return {
53
68
  splits: this.splits,
54
69
  rbSegments: this.rbSegments,
55
- segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
56
- largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
70
+ segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey), storage),
71
+ largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage),
57
72
  impressions: this.impressions,
58
73
  impressionCounts: this.impressionCounts,
59
74
  events: this.events,
@@ -0,0 +1,44 @@
1
+ import { LOG_PREFIX } from './constants';
2
+ function isTillKey(key) {
3
+ return key.endsWith('.till');
4
+ }
5
+ export function storageAdapter(log, prefix, wrapper) {
6
+ var cache = {};
7
+ var connectPromise;
8
+ var disconnectPromise = Promise.resolve();
9
+ return {
10
+ load: function () {
11
+ return connectPromise || (connectPromise = Promise.resolve(wrapper.getItem(prefix)).then(function (storedCache) {
12
+ cache = JSON.parse(storedCache || '{}');
13
+ }).catch(function (e) {
14
+ log.error(LOG_PREFIX + 'Rejected promise calling storage getItem, with error: ' + e);
15
+ }));
16
+ },
17
+ save: function () {
18
+ return disconnectPromise = disconnectPromise.then(function () {
19
+ return Promise.resolve(wrapper.setItem(prefix, JSON.stringify(cache))).catch(function (e) {
20
+ log.error(LOG_PREFIX + 'Rejected promise calling storage setItem, with error: ' + e);
21
+ });
22
+ });
23
+ },
24
+ get length() {
25
+ return Object.keys(cache).length;
26
+ },
27
+ getItem: function (key) {
28
+ return cache[key] || null;
29
+ },
30
+ key: function (index) {
31
+ return Object.keys(cache)[index] || null;
32
+ },
33
+ removeItem: function (key) {
34
+ delete cache[key];
35
+ if (isTillKey(key))
36
+ this.save();
37
+ },
38
+ setItem: function (key, value) {
39
+ cache[key] = value;
40
+ if (isTillKey(key))
41
+ this.save();
42
+ }
43
+ };
44
+ }
@@ -8,10 +8,10 @@ var MILLIS_IN_A_DAY = 86400000;
8
8
  *
9
9
  * @returns `true` if cache should be cleared, `false` otherwise
10
10
  */
11
- function validateExpiration(options, settings, keys, currentTimestamp, isThereCache) {
11
+ function validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache) {
12
12
  var log = settings.log;
13
13
  // Check expiration
14
- var lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()), 10);
14
+ var lastUpdatedTimestamp = parseInt(storage.getItem(keys.buildLastUpdatedKey()), 10);
15
15
  if (!isNaNNumber(lastUpdatedTimestamp)) {
16
16
  var cacheExpirationInDays = isFiniteNumber(options.expirationDays) && options.expirationDays >= 1 ? options.expirationDays : DEFAULT_CACHE_EXPIRATION_IN_DAYS;
17
17
  var expirationTimestamp = currentTimestamp - MILLIS_IN_A_DAY * cacheExpirationInDays;
@@ -22,11 +22,11 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
22
22
  }
23
23
  // Check hash
24
24
  var storageHashKey = keys.buildHashKey();
25
- var storageHash = localStorage.getItem(storageHashKey);
25
+ var storageHash = storage.getItem(storageHashKey);
26
26
  var currentStorageHash = getStorageHash(settings);
27
27
  if (storageHash !== currentStorageHash) {
28
28
  try {
29
- localStorage.setItem(storageHashKey, currentStorageHash);
29
+ storage.setItem(storageHashKey, currentStorageHash);
30
30
  }
31
31
  catch (e) {
32
32
  log.error(LOG_PREFIX + e);
@@ -39,7 +39,7 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
39
39
  }
40
40
  // Clear on init
41
41
  if (options.clearOnInit) {
42
- var lastClearTimestamp = parseInt(localStorage.getItem(keys.buildLastClear()), 10);
42
+ var lastClearTimestamp = parseInt(storage.getItem(keys.buildLastClear()), 10);
43
43
  if (isNaNNumber(lastClearTimestamp) || lastClearTimestamp < currentTimestamp - MILLIS_IN_A_DAY) {
44
44
  log.info(LOG_PREFIX + 'clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
45
45
  return true;
@@ -54,23 +54,25 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
54
54
  *
55
55
  * @returns `true` if cache is ready to be used, `false` otherwise (cache was cleared or there is no cache)
56
56
  */
57
- export function validateCache(options, settings, keys, splits, rbSegments, segments, largeSegments) {
58
- var currentTimestamp = Date.now();
59
- var isThereCache = splits.getChangeNumber() > -1;
60
- if (validateExpiration(options, settings, keys, currentTimestamp, isThereCache)) {
61
- splits.clear();
62
- rbSegments.clear();
63
- segments.clear();
64
- largeSegments.clear();
65
- // Update last clear timestamp
66
- try {
67
- localStorage.setItem(keys.buildLastClear(), currentTimestamp + '');
68
- }
69
- catch (e) {
70
- settings.log.error(LOG_PREFIX + e);
57
+ export function validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments) {
58
+ return Promise.resolve(storage.load && storage.load()).then(function () {
59
+ var currentTimestamp = Date.now();
60
+ var isThereCache = splits.getChangeNumber() > -1;
61
+ if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
62
+ splits.clear();
63
+ rbSegments.clear();
64
+ segments.clear();
65
+ largeSegments.clear();
66
+ // Update last clear timestamp
67
+ try {
68
+ storage.setItem(keys.buildLastClear(), currentTimestamp + '');
69
+ }
70
+ catch (e) {
71
+ settings.log.error(LOG_PREFIX + e);
72
+ }
73
+ return false;
71
74
  }
72
- return false;
73
- }
74
- // Check if ready from cache
75
- return isThereCache;
75
+ // Check if ready from cache
76
+ return isThereCache;
77
+ });
76
78
  }
@@ -42,10 +42,9 @@ export function fromObjectUpdaterFactory(splitsParser, storage, readiness, setti
42
42
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
43
43
  if (startingUp) {
44
44
  startingUp = false;
45
- var isCacheLoaded_1 = storage.validateCache ? storage.validateCache() : false;
46
- Promise.resolve().then(function () {
45
+ Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
47
46
  // Emits SDK_READY_FROM_CACHE
48
- if (isCacheLoaded_1)
47
+ if (isCacheLoaded)
49
48
  readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
50
49
  // Emits SDK_READY
51
50
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
@@ -4,6 +4,7 @@ import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask';
4
4
  import { getMatching } from '../../utils/key';
5
5
  import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants';
6
6
  import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger/constants';
7
+ import { usesSegmentsSync } from '../../storages/AbstractSplitsCacheSync';
7
8
  /**
8
9
  * Expose start / stop mechanism for polling data from services.
9
10
  * For client-side API with multiple clients.
@@ -31,7 +32,7 @@ export function pollingManagerCSFactory(params) {
31
32
  readiness.splits.on(SDK_SPLITS_ARRIVED, function () {
32
33
  if (!splitsSyncTask.isRunning())
33
34
  return; // noop if not doing polling
34
- var usingSegments = storage.splits.usesSegments() || storage.rbSegments.usesSegments();
35
+ var usingSegments = usesSegmentsSync(storage);
35
36
  if (usingSegments !== mySegmentsSyncTask.isRunning()) {
36
37
  log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
37
38
  if (usingSegments) {
@@ -46,10 +47,10 @@ export function pollingManagerCSFactory(params) {
46
47
  var mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMemberships, storage, readiness, settings, matchingKey);
47
48
  // smart ready
48
49
  function smartReady() {
49
- if (!readiness.isReady() && !storage.splits.usesSegments() && !storage.rbSegments.usesSegments())
50
+ if (!readiness.isReady() && !usesSegmentsSync(storage))
50
51
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
51
52
  }
52
- if (!storage.splits.usesSegments() && !storage.rbSegments.usesSegments())
53
+ if (!usesSegmentsSync(storage))
53
54
  setTimeout(smartReady, 0);
54
55
  else
55
56
  readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
@@ -63,7 +64,7 @@ export function pollingManagerCSFactory(params) {
63
64
  start: function () {
64
65
  log.info(POLLING_START);
65
66
  splitsSyncTask.start();
66
- if (storage.splits.usesSegments() || storage.rbSegments.usesSegments())
67
+ if (usesSegmentsSync(storage))
67
68
  startMySegmentsSyncTasks();
68
69
  },
69
70
  // Stop periodic fetching (polling)
@@ -2,6 +2,7 @@ import { timeout } from '../../../utils/promise/timeout';
2
2
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
3
3
  import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
4
4
  import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
5
+ import { usesSegmentsSync } from '../../../storages/AbstractSplitsCacheSync';
5
6
  /**
6
7
  * factory of MySegments updater, a task that:
7
8
  * - fetches mySegments using `mySegmentsFetcher`
@@ -9,7 +10,7 @@ import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
9
10
  * - uses `segmentsEventEmitter` to emit events related to segments data updates
10
11
  */
11
12
  export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) {
12
- var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
13
+ var segments = storage.segments, largeSegments = storage.largeSegments;
13
14
  var readyOnAlreadyExistentState = true;
14
15
  var startingUp = true;
15
16
  /** timeout and telemetry decorator for `splitChangesFetcher` promise */
@@ -31,7 +32,7 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmen
31
32
  shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
32
33
  }
33
34
  // Notify update if required
34
- if ((splits.usesSegments() || rbSegments.usesSegments()) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
35
+ if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
35
36
  readyOnAlreadyExistentState = false;
36
37
  segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
37
38
  }
@@ -42,7 +42,7 @@ export function parseSegments(ruleEntity, matcherType) {
42
42
  }
43
43
  /**
44
44
  * If there are defined filters and one feature flag doesn't match with them, its status is changed to 'ARCHIVE' to avoid storing it
45
- * If there are set filter defined, names filter is ignored
45
+ * If there is `bySet` filter, `byName` and `byPrefix` filters are ignored
46
46
  *
47
47
  * @param featureFlag - feature flag to be evaluated
48
48
  * @param filters - splitFiltersValidation bySet | byName