@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.
- package/CHANGES.txt +13 -1
- package/cjs/logger/messages/error.js +1 -1
- package/cjs/sdkClient/sdkClient.js +0 -1
- package/cjs/sdkFactory/index.js +3 -10
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/AbstractMySegmentsCacheSync.js +31 -23
- package/cjs/storages/AbstractSplitsCacheSync.js +8 -3
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +20 -19
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/cjs/storages/inLocalStorage/index.js +28 -13
- package/cjs/storages/inLocalStorage/storageAdapter.js +48 -0
- package/cjs/storages/inLocalStorage/validateCache.js +25 -23
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/pollingManagerCS.js +5 -4
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/cjs/sync/syncManagerOnline.js +31 -26
- package/cjs/trackers/impressionsTracker.js +4 -4
- package/cjs/utils/env/isLocalStorageAvailable.js +28 -5
- package/cjs/utils/settingsValidation/splitFilters.js +0 -6
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/logger/messages/error.js +1 -1
- package/esm/sdkClient/sdkClient.js +0 -1
- package/esm/sdkFactory/index.js +3 -10
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/AbstractMySegmentsCacheSync.js +31 -23
- package/esm/storages/AbstractSplitsCacheSync.js +6 -2
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +20 -19
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/esm/storages/inLocalStorage/index.js +29 -14
- package/esm/storages/inLocalStorage/storageAdapter.js +44 -0
- package/esm/storages/inLocalStorage/validateCache.js +25 -23
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/pollingManagerCS.js +5 -4
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/esm/sync/syncManagerOnline.js +31 -26
- package/esm/trackers/impressionsTracker.js +4 -4
- package/esm/utils/env/isLocalStorageAvailable.js +24 -3
- package/esm/utils/settingsValidation/splitFilters.js +0 -6
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/logger/messages/error.ts +1 -1
- package/src/sdkClient/sdkClient.ts +0 -1
- package/src/sdkFactory/index.ts +3 -13
- package/src/services/splitHttpClient.ts +1 -1
- package/src/storages/AbstractMySegmentsCacheSync.ts +26 -20
- package/src/storages/AbstractSplitsCacheSync.ts +8 -3
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +18 -17
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +22 -20
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +34 -37
- package/src/storages/inLocalStorage/index.ts +33 -16
- package/src/storages/inLocalStorage/storageAdapter.ts +50 -0
- package/src/storages/inLocalStorage/validateCache.ts +26 -23
- package/src/storages/types.ts +17 -1
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +1 -2
- package/src/sync/polling/pollingManagerCS.ts +5 -4
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -2
- package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -1
- package/src/sync/syncManagerOnline.ts +30 -24
- package/src/trackers/impressionsTracker.ts +3 -3
- package/src/utils/env/isLocalStorageAvailable.ts +24 -3
- package/src/utils/settingsValidation/splitFilters.ts +0 -6
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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(
|
|
33
|
-
// @ts-expect-error
|
|
34
|
+
var count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
|
|
34
35
|
if (count > 0)
|
|
35
|
-
|
|
36
|
+
this.storage.setItem(segmentsCountKey, count + '');
|
|
36
37
|
else
|
|
37
|
-
|
|
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
|
|
44
|
-
var previous =
|
|
45
|
-
|
|
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
|
-
|
|
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 =
|
|
77
|
+
var len = this.storage.length;
|
|
77
78
|
var accum = [];
|
|
78
79
|
var cur = 0;
|
|
79
80
|
while (cur < len) {
|
|
80
|
-
var key =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
20
|
-
// @ts-expect-error
|
|
17
|
+
var count = toNumber(this.storage.getItem(key)) - 1;
|
|
21
18
|
if (count > 0)
|
|
22
|
-
|
|
19
|
+
this.storage.setItem(key, count + '');
|
|
23
20
|
else
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
56
|
+
var len = this.storage.length;
|
|
61
57
|
var accum = [];
|
|
62
58
|
for (var cur = 0; cur < len; cur++) {
|
|
63
|
-
var key =
|
|
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
|
-
|
|
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
|
|
78
|
-
var previousSplit =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
110
|
+
this.storage.setItem(this.keys.buildSplitsTillKey(), changeNumber + '');
|
|
115
111
|
// update "last updated" timestamp with current time
|
|
116
|
-
|
|
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 =
|
|
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 =
|
|
131
|
+
var len = this.storage.length;
|
|
136
132
|
var accum = [];
|
|
137
133
|
var cur = 0;
|
|
138
134
|
while (cur < len) {
|
|
139
|
-
var key =
|
|
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(
|
|
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 =
|
|
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
|
|
165
|
-
return new Set(
|
|
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
|
|
177
|
-
var flagSetCache = new Set(
|
|
172
|
+
var flagSetFromStorage = _this.storage.getItem(flagSetKey);
|
|
173
|
+
var flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
|
|
178
174
|
flagSetCache.add(featureFlag.name);
|
|
179
|
-
|
|
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
|
|
193
|
-
if (!
|
|
188
|
+
var flagSetFromStorage = this.storage.getItem(flagSetKey);
|
|
189
|
+
if (!flagSetFromStorage)
|
|
194
190
|
return;
|
|
195
|
-
var flagSetCache = new Set(JSON.parse(
|
|
191
|
+
var flagSetCache = new Set(JSON.parse(flagSetFromStorage));
|
|
196
192
|
flagSetCache.delete(featureFlagName);
|
|
197
193
|
if (flagSetCache.size === 0) {
|
|
198
|
-
|
|
194
|
+
this.storage.removeItem(flagSetKey);
|
|
199
195
|
return;
|
|
200
196
|
}
|
|
201
|
-
|
|
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(
|
|
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 =
|
|
25
|
+
var storageHash = storage.getItem(storageHashKey);
|
|
26
26
|
var currentStorageHash = getStorageHash(settings);
|
|
27
27
|
if (storageHash !== currentStorageHash) {
|
|
28
28
|
try {
|
|
29
|
-
|
|
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(
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
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() && !
|
|
50
|
+
if (!readiness.isReady() && !usesSegmentsSync(storage))
|
|
50
51
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
51
52
|
}
|
|
52
|
-
if (!
|
|
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 (
|
|
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
|
|
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 ((
|
|
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
|
|
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
|