@splitsoftware/splitio-commons 2.4.2-rc.3 → 2.5.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +3 -11
- package/cjs/evaluator/convertions/index.js +9 -1
- package/cjs/evaluator/matchersTransform/index.js +2 -3
- package/cjs/sdkClient/sdkClientMethodCS.js +5 -1
- package/cjs/sdkFactory/index.js +8 -2
- package/cjs/storages/getRolloutPlan.js +69 -0
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +21 -17
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +37 -33
- package/cjs/storages/inLocalStorage/index.js +13 -31
- package/cjs/storages/inLocalStorage/validateCache.js +25 -30
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -0
- package/cjs/storages/setRolloutPlan.js +66 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +0 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +0 -2
- package/cjs/sync/syncManagerOnline.js +24 -28
- package/cjs/utils/env/isLocalStorageAvailable.js +5 -28
- package/cjs/utils/inputValidation/index.js +1 -3
- package/cjs/utils/settingsValidation/index.js +4 -0
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/evaluator/convertions/index.js +7 -0
- package/esm/evaluator/matchersTransform/index.js +3 -4
- package/esm/sdkClient/sdkClientMethodCS.js +5 -1
- package/esm/sdkFactory/index.js +8 -2
- package/esm/storages/getRolloutPlan.js +65 -0
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +21 -17
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +37 -33
- package/esm/storages/inLocalStorage/index.js +14 -32
- package/esm/storages/inLocalStorage/validateCache.js +25 -30
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +4 -0
- package/esm/storages/setRolloutPlan.js +61 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +0 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +0 -2
- package/esm/sync/syncManagerOnline.js +24 -28
- package/esm/utils/env/isLocalStorageAvailable.js +3 -24
- package/esm/utils/inputValidation/index.js +0 -1
- package/esm/utils/settingsValidation/index.js +4 -0
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/evaluator/convertions/index.ts +10 -0
- package/src/evaluator/matchersTransform/index.ts +3 -4
- package/src/sdkClient/sdkClientMethodCS.ts +7 -1
- package/src/sdkFactory/index.ts +12 -4
- package/src/storages/getRolloutPlan.ts +72 -0
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +17 -18
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +22 -19
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +37 -34
- package/src/storages/inLocalStorage/index.ts +16 -37
- package/src/storages/inLocalStorage/validateCache.ts +25 -31
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +4 -0
- package/src/storages/setRolloutPlan.ts +71 -0
- package/src/storages/types.ts +25 -23
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +2 -1
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +0 -2
- package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -3
- package/src/sync/syncManagerOnline.ts +22 -27
- package/src/types.ts +2 -35
- package/src/utils/env/isLocalStorageAvailable.ts +3 -24
- package/src/utils/inputValidation/index.ts +0 -1
- package/src/utils/settingsValidation/index.ts +4 -0
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +36 -44
- package/cjs/storages/dataLoader.js +0 -50
- package/cjs/storages/inLocalStorage/storageAdapter.js +0 -54
- package/cjs/utils/inputValidation/preloadedData.js +0 -59
- package/esm/storages/dataLoader.js +0 -46
- package/esm/storages/inLocalStorage/storageAdapter.js +0 -50
- package/esm/utils/inputValidation/preloadedData.js +0 -55
- package/src/storages/dataLoader.ts +0 -55
- package/src/storages/inLocalStorage/storageAdapter.ts +0 -62
- package/src/utils/inputValidation/preloadedData.ts +0 -57
|
@@ -4,20 +4,19 @@ 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) {
|
|
8
8
|
var _this = _super.call(this) || this;
|
|
9
9
|
_this.log = log;
|
|
10
10
|
_this.keys = keys;
|
|
11
|
-
_this.storage = storage;
|
|
12
11
|
return _this;
|
|
13
12
|
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
14
13
|
}
|
|
15
14
|
MySegmentsCacheInLocal.prototype.addSegment = function (name) {
|
|
16
15
|
var segmentKey = this.keys.buildSegmentNameKey(name);
|
|
17
16
|
try {
|
|
18
|
-
if (
|
|
17
|
+
if (localStorage.getItem(segmentKey) === DEFINED)
|
|
19
18
|
return false;
|
|
20
|
-
|
|
19
|
+
localStorage.setItem(segmentKey, DEFINED);
|
|
21
20
|
return true;
|
|
22
21
|
}
|
|
23
22
|
catch (e) {
|
|
@@ -28,9 +27,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
28
27
|
MySegmentsCacheInLocal.prototype.removeSegment = function (name) {
|
|
29
28
|
var segmentKey = this.keys.buildSegmentNameKey(name);
|
|
30
29
|
try {
|
|
31
|
-
if (
|
|
30
|
+
if (localStorage.getItem(segmentKey) !== DEFINED)
|
|
32
31
|
return false;
|
|
33
|
-
|
|
32
|
+
localStorage.removeItem(segmentKey);
|
|
34
33
|
return true;
|
|
35
34
|
}
|
|
36
35
|
catch (e) {
|
|
@@ -39,16 +38,17 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
39
38
|
}
|
|
40
39
|
};
|
|
41
40
|
MySegmentsCacheInLocal.prototype.isInSegment = function (name) {
|
|
42
|
-
return
|
|
41
|
+
return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
|
|
43
42
|
};
|
|
44
43
|
MySegmentsCacheInLocal.prototype.getRegisteredSegments = function () {
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
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);
|
|
48
48
|
if (segmentName)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
accum.push(segmentName);
|
|
50
|
+
return accum;
|
|
51
|
+
}, []);
|
|
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
|
-
|
|
59
|
+
localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
60
60
|
else
|
|
61
|
-
|
|
61
|
+
localStorage.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 =
|
|
69
|
+
var value = localStorage.getItem(this.keys.buildTillKey());
|
|
70
70
|
if (value !== null) {
|
|
71
71
|
value = parseInt(value, 10);
|
|
72
72
|
return isNaNNumber(value) ? n : value;
|
|
@@ -3,15 +3,14 @@ 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) {
|
|
7
7
|
this.keys = keys;
|
|
8
8
|
this.log = settings.log;
|
|
9
|
-
this.storage = storage;
|
|
10
9
|
}
|
|
11
10
|
RBSegmentsCacheInLocal.prototype.clear = function () {
|
|
12
11
|
var _this = this;
|
|
13
12
|
this.getNames().forEach(function (name) { return _this.remove(name); });
|
|
14
|
-
|
|
13
|
+
localStorage.removeItem(this.keys.buildRBSegmentsTillKey());
|
|
15
14
|
};
|
|
16
15
|
RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
17
16
|
var _this = this;
|
|
@@ -21,8 +20,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
21
20
|
};
|
|
22
21
|
RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
23
22
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
localStorage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
24
|
+
localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
26
25
|
}
|
|
27
26
|
catch (e) {
|
|
28
27
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -30,19 +29,20 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
30
29
|
};
|
|
31
30
|
RBSegmentsCacheInLocal.prototype.updateSegmentCount = function (diff) {
|
|
32
31
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
33
|
-
var count = toNumber(
|
|
32
|
+
var count = toNumber(localStorage.getItem(segmentsCountKey)) + diff;
|
|
33
|
+
// @ts-expect-error
|
|
34
34
|
if (count > 0)
|
|
35
|
-
|
|
35
|
+
localStorage.setItem(segmentsCountKey, count);
|
|
36
36
|
else
|
|
37
|
-
|
|
37
|
+
localStorage.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
|
|
44
|
-
var previous =
|
|
45
|
-
|
|
43
|
+
var rbSegmentFromLocalStorage = localStorage.getItem(rbSegmentKey);
|
|
44
|
+
var previous = rbSegmentFromLocalStorage ? JSON.parse(rbSegmentFromLocalStorage) : null;
|
|
45
|
+
localStorage.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
|
-
|
|
65
|
+
localStorage.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 =
|
|
76
|
+
var len = localStorage.length;
|
|
77
77
|
var accum = [];
|
|
78
78
|
var cur = 0;
|
|
79
79
|
while (cur < len) {
|
|
80
|
-
var key =
|
|
80
|
+
var key = localStorage.key(cur);
|
|
81
81
|
if (key != null && this.keys.isRBSegmentKey(key))
|
|
82
82
|
accum.push(this.keys.extractKey(key));
|
|
83
83
|
cur++;
|
|
@@ -85,9 +85,13 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
85
85
|
return accum;
|
|
86
86
|
};
|
|
87
87
|
RBSegmentsCacheInLocal.prototype.get = function (name) {
|
|
88
|
-
var item =
|
|
88
|
+
var item = localStorage.getItem(this.keys.buildRBSegmentKey(name));
|
|
89
89
|
return item && JSON.parse(item);
|
|
90
90
|
};
|
|
91
|
+
RBSegmentsCacheInLocal.prototype.getAll = function () {
|
|
92
|
+
var _this = this;
|
|
93
|
+
return this.getNames().map(function (key) { return _this.get(key); });
|
|
94
|
+
};
|
|
91
95
|
RBSegmentsCacheInLocal.prototype.contains = function (names) {
|
|
92
96
|
var namesArray = setToArray(names);
|
|
93
97
|
var namesInStorage = this.getNames();
|
|
@@ -95,7 +99,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
95
99
|
};
|
|
96
100
|
RBSegmentsCacheInLocal.prototype.getChangeNumber = function () {
|
|
97
101
|
var n = -1;
|
|
98
|
-
var value =
|
|
102
|
+
var value = localStorage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
99
103
|
if (value !== null) {
|
|
100
104
|
value = parseInt(value, 10);
|
|
101
105
|
return isNaNNumber(value) ? n : value;
|
|
@@ -103,7 +107,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
103
107
|
return n;
|
|
104
108
|
};
|
|
105
109
|
RBSegmentsCacheInLocal.prototype.usesSegments = function () {
|
|
106
|
-
var storedCount =
|
|
110
|
+
var storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
107
111
|
var splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
108
112
|
return isFiniteNumber(splitsWithSegmentsCount) ?
|
|
109
113
|
splitsWithSegmentsCount > 0 :
|
|
@@ -3,22 +3,25 @@ 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
|
+
*/
|
|
6
9
|
var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
7
10
|
__extends(SplitsCacheInLocal, _super);
|
|
8
|
-
function SplitsCacheInLocal(settings, keys
|
|
11
|
+
function SplitsCacheInLocal(settings, keys) {
|
|
9
12
|
var _this = _super.call(this) || this;
|
|
10
13
|
_this.keys = keys;
|
|
11
14
|
_this.log = settings.log;
|
|
12
15
|
_this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
|
|
13
|
-
_this.storage = storage;
|
|
14
16
|
return _this;
|
|
15
17
|
}
|
|
16
18
|
SplitsCacheInLocal.prototype._decrementCount = function (key) {
|
|
17
|
-
var count = toNumber(
|
|
19
|
+
var count = toNumber(localStorage.getItem(key)) - 1;
|
|
20
|
+
// @ts-expect-error
|
|
18
21
|
if (count > 0)
|
|
19
|
-
|
|
22
|
+
localStorage.setItem(key, count);
|
|
20
23
|
else
|
|
21
|
-
|
|
24
|
+
localStorage.removeItem(key);
|
|
22
25
|
};
|
|
23
26
|
SplitsCacheInLocal.prototype._decrementCounts = function (split) {
|
|
24
27
|
try {
|
|
@@ -36,10 +39,12 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
36
39
|
SplitsCacheInLocal.prototype._incrementCounts = function (split) {
|
|
37
40
|
try {
|
|
38
41
|
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
39
|
-
|
|
42
|
+
// @ts-expect-error
|
|
43
|
+
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
40
44
|
if (usesSegments(split)) {
|
|
41
45
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
42
|
-
|
|
46
|
+
// @ts-expect-error
|
|
47
|
+
localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
catch (e) {
|
|
@@ -51,18 +56,17 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
51
56
|
* We cannot simply call `localStorage.clear()` since that implies removing user items from the storage.
|
|
52
57
|
*/
|
|
53
58
|
SplitsCacheInLocal.prototype.clear = function () {
|
|
54
|
-
var _this = this;
|
|
55
59
|
// collect item keys
|
|
56
|
-
var len =
|
|
60
|
+
var len = localStorage.length;
|
|
57
61
|
var accum = [];
|
|
58
62
|
for (var cur = 0; cur < len; cur++) {
|
|
59
|
-
var key =
|
|
63
|
+
var key = localStorage.key(cur);
|
|
60
64
|
if (key != null && this.keys.isSplitsCacheKey(key))
|
|
61
65
|
accum.push(key);
|
|
62
66
|
}
|
|
63
67
|
// remove items
|
|
64
68
|
accum.forEach(function (key) {
|
|
65
|
-
|
|
69
|
+
localStorage.removeItem(key);
|
|
66
70
|
});
|
|
67
71
|
this.hasSync = false;
|
|
68
72
|
};
|
|
@@ -70,13 +74,13 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
70
74
|
try {
|
|
71
75
|
var name_1 = split.name;
|
|
72
76
|
var splitKey = this.keys.buildSplitKey(name_1);
|
|
73
|
-
var
|
|
74
|
-
var previousSplit =
|
|
77
|
+
var splitFromLocalStorage = localStorage.getItem(splitKey);
|
|
78
|
+
var previousSplit = splitFromLocalStorage ? JSON.parse(splitFromLocalStorage) : null;
|
|
75
79
|
if (previousSplit) {
|
|
76
80
|
this._decrementCounts(previousSplit);
|
|
77
81
|
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
78
82
|
}
|
|
79
|
-
|
|
83
|
+
localStorage.setItem(splitKey, JSON.stringify(split));
|
|
80
84
|
this._incrementCounts(split);
|
|
81
85
|
this.addToFlagSets(split);
|
|
82
86
|
return true;
|
|
@@ -91,7 +95,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
91
95
|
var split = this.getSplit(name);
|
|
92
96
|
if (!split)
|
|
93
97
|
return false;
|
|
94
|
-
|
|
98
|
+
localStorage.removeItem(this.keys.buildSplitKey(name));
|
|
95
99
|
this._decrementCounts(split);
|
|
96
100
|
this.removeFromFlagSets(split.name, split.sets);
|
|
97
101
|
return true;
|
|
@@ -102,14 +106,14 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
102
106
|
}
|
|
103
107
|
};
|
|
104
108
|
SplitsCacheInLocal.prototype.getSplit = function (name) {
|
|
105
|
-
var item =
|
|
109
|
+
var item = localStorage.getItem(this.keys.buildSplitKey(name));
|
|
106
110
|
return item && JSON.parse(item);
|
|
107
111
|
};
|
|
108
112
|
SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
109
113
|
try {
|
|
110
|
-
|
|
114
|
+
localStorage.setItem(this.keys.buildSplitsTillKey(), changeNumber + '');
|
|
111
115
|
// update "last updated" timestamp with current time
|
|
112
|
-
|
|
116
|
+
localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
113
117
|
this.hasSync = true;
|
|
114
118
|
return true;
|
|
115
119
|
}
|
|
@@ -120,7 +124,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
120
124
|
};
|
|
121
125
|
SplitsCacheInLocal.prototype.getChangeNumber = function () {
|
|
122
126
|
var n = -1;
|
|
123
|
-
var value =
|
|
127
|
+
var value = localStorage.getItem(this.keys.buildSplitsTillKey());
|
|
124
128
|
if (value !== null) {
|
|
125
129
|
value = parseInt(value, 10);
|
|
126
130
|
return isNaNNumber(value) ? n : value;
|
|
@@ -128,11 +132,11 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
128
132
|
return n;
|
|
129
133
|
};
|
|
130
134
|
SplitsCacheInLocal.prototype.getSplitNames = function () {
|
|
131
|
-
var len =
|
|
135
|
+
var len = localStorage.length;
|
|
132
136
|
var accum = [];
|
|
133
137
|
var cur = 0;
|
|
134
138
|
while (cur < len) {
|
|
135
|
-
var key =
|
|
139
|
+
var key = localStorage.key(cur);
|
|
136
140
|
if (key != null && this.keys.isSplitKey(key))
|
|
137
141
|
accum.push(this.keys.extractKey(key));
|
|
138
142
|
cur++;
|
|
@@ -140,14 +144,14 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
140
144
|
return accum;
|
|
141
145
|
};
|
|
142
146
|
SplitsCacheInLocal.prototype.trafficTypeExists = function (trafficType) {
|
|
143
|
-
var ttCount = toNumber(
|
|
147
|
+
var ttCount = toNumber(localStorage.getItem(this.keys.buildTrafficTypeKey(trafficType)));
|
|
144
148
|
return isFiniteNumber(ttCount) && ttCount > 0;
|
|
145
149
|
};
|
|
146
150
|
SplitsCacheInLocal.prototype.usesSegments = function () {
|
|
147
151
|
// If cache hasn't been synchronized with the cloud, assume we need them.
|
|
148
152
|
if (!this.hasSync)
|
|
149
153
|
return true;
|
|
150
|
-
var storedCount =
|
|
154
|
+
var storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
151
155
|
var splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
152
156
|
return isFiniteNumber(splitsWithSegmentsCount) ?
|
|
153
157
|
splitsWithSegmentsCount > 0 :
|
|
@@ -157,8 +161,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
157
161
|
var _this = this;
|
|
158
162
|
return flagSets.map(function (flagSet) {
|
|
159
163
|
var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
|
|
160
|
-
var
|
|
161
|
-
return new Set(
|
|
164
|
+
var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
|
|
165
|
+
return new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
|
|
162
166
|
});
|
|
163
167
|
};
|
|
164
168
|
SplitsCacheInLocal.prototype.addToFlagSets = function (featureFlag) {
|
|
@@ -169,10 +173,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
169
173
|
if (_this.flagSetsFilter.length > 0 && !_this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === featureFlagSet; }))
|
|
170
174
|
return;
|
|
171
175
|
var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
|
|
172
|
-
var
|
|
173
|
-
var flagSetCache = new Set(
|
|
176
|
+
var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
|
|
177
|
+
var flagSetCache = new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
|
|
174
178
|
flagSetCache.add(featureFlag.name);
|
|
175
|
-
|
|
179
|
+
localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
|
|
176
180
|
});
|
|
177
181
|
};
|
|
178
182
|
SplitsCacheInLocal.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
|
|
@@ -185,16 +189,16 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
185
189
|
};
|
|
186
190
|
SplitsCacheInLocal.prototype.removeNames = function (flagSetName, featureFlagName) {
|
|
187
191
|
var flagSetKey = this.keys.buildFlagSetKey(flagSetName);
|
|
188
|
-
var
|
|
189
|
-
if (!
|
|
192
|
+
var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
|
|
193
|
+
if (!flagSetFromLocalStorage)
|
|
190
194
|
return;
|
|
191
|
-
var flagSetCache = new Set(JSON.parse(
|
|
195
|
+
var flagSetCache = new Set(JSON.parse(flagSetFromLocalStorage));
|
|
192
196
|
flagSetCache.delete(featureFlagName);
|
|
193
197
|
if (flagSetCache.size === 0) {
|
|
194
|
-
|
|
198
|
+
localStorage.removeItem(flagSetKey);
|
|
195
199
|
return;
|
|
196
200
|
}
|
|
197
|
-
|
|
201
|
+
localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
|
|
198
202
|
};
|
|
199
203
|
return SplitsCacheInLocal;
|
|
200
204
|
}(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
|
|
6
|
+
import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
|
|
7
7
|
import { SplitsCacheInLocal } from './SplitsCacheInLocal';
|
|
8
8
|
import { RBSegmentsCacheInLocal } from './RBSegmentsCacheInLocal';
|
|
9
9
|
import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
@@ -14,20 +14,6 @@ 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
|
-
}
|
|
31
17
|
/**
|
|
32
18
|
* InLocal storage factory for standalone client-side SplitFactory
|
|
33
19
|
*/
|
|
@@ -35,17 +21,18 @@ export function InLocalStorage(options) {
|
|
|
35
21
|
if (options === void 0) { options = {}; }
|
|
36
22
|
var prefix = validatePrefix(options.prefix);
|
|
37
23
|
function InLocalStorageCSFactory(params) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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');
|
|
41
27
|
return InMemoryStorageCSFactory(params);
|
|
28
|
+
}
|
|
29
|
+
var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize;
|
|
42
30
|
var matchingKey = getMatching(settings.core.key);
|
|
43
31
|
var keys = new KeyBuilderCS(prefix, matchingKey);
|
|
44
|
-
var splits = new SplitsCacheInLocal(settings, keys
|
|
45
|
-
var rbSegments = new RBSegmentsCacheInLocal(settings, keys
|
|
46
|
-
var segments = new MySegmentsCacheInLocal(log, keys
|
|
47
|
-
var largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)
|
|
48
|
-
var validateCachePromise;
|
|
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));
|
|
49
36
|
return {
|
|
50
37
|
splits: splits,
|
|
51
38
|
rbSegments: rbSegments,
|
|
@@ -57,21 +44,16 @@ export function InLocalStorage(options) {
|
|
|
57
44
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
58
45
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
59
46
|
validateCache: function () {
|
|
60
|
-
return
|
|
61
|
-
},
|
|
62
|
-
save: function () {
|
|
63
|
-
return storage.save && storage.save();
|
|
64
|
-
},
|
|
65
|
-
destroy: function () {
|
|
66
|
-
return storage.whenSaved && storage.whenSaved();
|
|
47
|
+
return validateCache(options, settings, keys, splits, rbSegments, segments, largeSegments);
|
|
67
48
|
},
|
|
49
|
+
destroy: function () { },
|
|
68
50
|
// When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
|
|
69
51
|
shared: function (matchingKey) {
|
|
70
52
|
return {
|
|
71
53
|
splits: this.splits,
|
|
72
54
|
rbSegments: this.rbSegments,
|
|
73
|
-
segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)
|
|
74
|
-
largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)
|
|
55
|
+
segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
|
|
56
|
+
largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
|
|
75
57
|
impressions: this.impressions,
|
|
76
58
|
impressionCounts: this.impressionCounts,
|
|
77
59
|
events: this.events,
|
|
@@ -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,
|
|
12
|
-
var log = settings.log;
|
|
11
|
+
function validateExpiration(options, settings, keys, currentTimestamp, isThereCache) {
|
|
12
|
+
var log = settings.log, initialRolloutPlan = settings.initialRolloutPlan;
|
|
13
13
|
// Check expiration
|
|
14
|
-
var lastUpdatedTimestamp = parseInt(
|
|
14
|
+
var lastUpdatedTimestamp = parseInt(localStorage.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,16 +22,16 @@ function validateExpiration(options, storage, settings, keys, currentTimestamp,
|
|
|
22
22
|
}
|
|
23
23
|
// Check hash
|
|
24
24
|
var storageHashKey = keys.buildHashKey();
|
|
25
|
-
var storageHash =
|
|
25
|
+
var storageHash = localStorage.getItem(storageHashKey);
|
|
26
26
|
var currentStorageHash = getStorageHash(settings);
|
|
27
27
|
if (storageHash !== currentStorageHash) {
|
|
28
28
|
try {
|
|
29
|
-
|
|
29
|
+
localStorage.setItem(storageHashKey, currentStorageHash);
|
|
30
30
|
}
|
|
31
31
|
catch (e) {
|
|
32
32
|
log.error(LOG_PREFIX + e);
|
|
33
33
|
}
|
|
34
|
-
if (isThereCache) {
|
|
34
|
+
if (isThereCache && !initialRolloutPlan) {
|
|
35
35
|
log.info(LOG_PREFIX + 'SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
@@ -39,7 +39,7 @@ function validateExpiration(options, storage, settings, keys, currentTimestamp,
|
|
|
39
39
|
}
|
|
40
40
|
// Clear on init
|
|
41
41
|
if (options.clearOnInit) {
|
|
42
|
-
var lastClearTimestamp = parseInt(
|
|
42
|
+
var lastClearTimestamp = parseInt(localStorage.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,28 +54,23 @@ function validateExpiration(options, storage, settings, keys, currentTimestamp,
|
|
|
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,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
settings.log.error(LOG_PREFIX + e);
|
|
72
|
-
}
|
|
73
|
-
// Persist clear
|
|
74
|
-
if (storage.save)
|
|
75
|
-
storage.save();
|
|
76
|
-
return false;
|
|
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);
|
|
77
71
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Check if ready from cache
|
|
75
|
+
return isThereCache;
|
|
81
76
|
}
|
|
@@ -42,6 +42,10 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
42
42
|
RBSegmentsCacheInMemory.prototype.get = function (name) {
|
|
43
43
|
return this.cache[name] || null;
|
|
44
44
|
};
|
|
45
|
+
RBSegmentsCacheInMemory.prototype.getAll = function () {
|
|
46
|
+
var _this = this;
|
|
47
|
+
return this.getNames().map(function (key) { return _this.get(key); });
|
|
48
|
+
};
|
|
45
49
|
RBSegmentsCacheInMemory.prototype.contains = function (names) {
|
|
46
50
|
var namesArray = setToArray(names);
|
|
47
51
|
var namesInStorage = this.getNames();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isObject } from '../utils/lang';
|
|
2
|
+
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
3
|
+
/**
|
|
4
|
+
* Validates if the given rollout plan is valid.
|
|
5
|
+
*/
|
|
6
|
+
export function validateRolloutPlan(log, settings) {
|
|
7
|
+
var mode = settings.mode, initialRolloutPlan = settings.initialRolloutPlan;
|
|
8
|
+
if (isConsumerMode(mode)) {
|
|
9
|
+
log.warn('storage: initial rollout plan is ignored in consumer mode');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (isObject(initialRolloutPlan) && isObject(initialRolloutPlan.splitChanges))
|
|
13
|
+
return initialRolloutPlan;
|
|
14
|
+
log.error('storage: invalid rollout plan provided');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Sets the given synchronous storage with the provided rollout plan snapshot.
|
|
19
|
+
* If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
|
|
20
|
+
* Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
|
|
21
|
+
*/
|
|
22
|
+
export function setRolloutPlan(log, rolloutPlan, storage, matchingKey) {
|
|
23
|
+
var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
|
|
24
|
+
var _a = rolloutPlan.splitChanges, ff = _a.ff, rbs = _a.rbs;
|
|
25
|
+
log.debug("storage: set feature flags and segments" + (matchingKey ? " for key " + matchingKey : ''));
|
|
26
|
+
if (splits && ff) {
|
|
27
|
+
splits.clear();
|
|
28
|
+
splits.update(ff.d, [], ff.t);
|
|
29
|
+
}
|
|
30
|
+
if (rbSegments && rbs) {
|
|
31
|
+
rbSegments.clear();
|
|
32
|
+
rbSegments.update(rbs.d, [], rbs.t);
|
|
33
|
+
}
|
|
34
|
+
var segmentChanges = rolloutPlan.segmentChanges;
|
|
35
|
+
if (matchingKey) { // add memberships data (client-side)
|
|
36
|
+
var memberships = rolloutPlan.memberships && rolloutPlan.memberships[matchingKey];
|
|
37
|
+
if (!memberships && segmentChanges) {
|
|
38
|
+
memberships = {
|
|
39
|
+
ms: {
|
|
40
|
+
k: segmentChanges.filter(function (segment) {
|
|
41
|
+
return segment.added.indexOf(matchingKey) > -1;
|
|
42
|
+
}).map(function (segment) { return ({ n: segment.name }); })
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (memberships) {
|
|
47
|
+
if (memberships.ms)
|
|
48
|
+
segments.resetSegments(memberships.ms);
|
|
49
|
+
if (memberships.ls && largeSegments)
|
|
50
|
+
largeSegments.resetSegments(memberships.ls);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else { // add segments data (server-side)
|
|
54
|
+
if (segmentChanges) {
|
|
55
|
+
segments.clear();
|
|
56
|
+
segmentChanges.forEach(function (segment) {
|
|
57
|
+
segments.update(segment.name, segment.added, segment.removed, segment.till);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -42,9 +42,10 @@ export function fromObjectUpdaterFactory(splitsParser, storage, readiness, setti
|
|
|
42
42
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
43
43
|
if (startingUp) {
|
|
44
44
|
startingUp = false;
|
|
45
|
-
|
|
45
|
+
var isCacheLoaded_1 = storage.validateCache ? storage.validateCache() : false;
|
|
46
|
+
Promise.resolve().then(function () {
|
|
46
47
|
// Emits SDK_READY_FROM_CACHE
|
|
47
|
-
if (
|
|
48
|
+
if (isCacheLoaded_1)
|
|
48
49
|
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
49
50
|
// Emits SDK_READY
|
|
50
51
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|