@splitsoftware/splitio-commons 2.5.0 → 2.5.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +12 -0
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +17 -17
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/cjs/storages/inLocalStorage/index.js +31 -13
- package/cjs/storages/inLocalStorage/storageAdapter.js +54 -0
- package/cjs/storages/inLocalStorage/validateCache.js +28 -23
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -0
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -0
- package/cjs/sync/syncManagerOnline.js +28 -24
- package/cjs/utils/env/isLocalStorageAvailable.js +22 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +17 -17
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +33 -37
- package/esm/storages/inLocalStorage/index.js +32 -14
- package/esm/storages/inLocalStorage/storageAdapter.js +50 -0
- package/esm/storages/inLocalStorage/validateCache.js +28 -23
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -0
- package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -0
- package/esm/sync/syncManagerOnline.js +28 -24
- package/esm/utils/env/isLocalStorageAvailable.js +19 -0
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +18 -17
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +19 -18
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +34 -37
- package/src/storages/inLocalStorage/index.ts +37 -16
- package/src/storages/inLocalStorage/storageAdapter.ts +62 -0
- package/src/storages/inLocalStorage/validateCache.ts +29 -23
- package/src/storages/types.ts +19 -1
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +1 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -0
- package/src/sync/polling/updaters/splitChangesUpdater.ts +3 -1
- package/src/sync/syncManagerOnline.ts +27 -22
- package/src/utils/env/isLocalStorageAvailable.ts +20 -0
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +30 -0
package/CHANGES.txt
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
2.6.0 (September 19, 2025)
|
|
2
|
+
- Added `storage.wrapper` configuration option to allow the SDK to use a custom storage wrapper for the storage type `LOCALSTORAGE`. Default value is `window.localStorage`.
|
|
3
|
+
|
|
1
4
|
2.5.0 (September 10, 2025)
|
|
2
5
|
- Added `factory.getRolloutPlan()` method for standalone server-side SDKs, which returns the rollout plan snapshot from the storage.
|
|
3
6
|
- Added `initialRolloutPlan` configuration option for standalone client-side SDKs, which allows preloading the SDK storage with a snapshot of the rollout plan.
|
|
@@ -51,6 +54,15 @@
|
|
|
51
54
|
- Removed internal ponyfills for `Map` and `Set` global objects, dropping support for IE and other outdated browsers. The SDK now requires the runtime environment to support these features natively or to provide a polyfill.
|
|
52
55
|
- Removed the `sync.localhostMode` configuration option to plug the LocalhostMode module.
|
|
53
56
|
|
|
57
|
+
1.17.1 (July 25, 2025)
|
|
58
|
+
- Updated the Redis storage to avoid lazy require of the `ioredis` dependency when the SDK is initialized.
|
|
59
|
+
- Updated some transitive dependencies for vulnerability fixes.
|
|
60
|
+
- Bugfix - Enhanced HTTP client module to implement timeouts for failing requests that might otherwise remain pending indefinitely on some Fetch API implementations, pausing the SDK synchronization process.
|
|
61
|
+
- Bugfix - Properly handle rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
|
|
62
|
+
- Bugfix - Sanitize the `SplitSDKMachineName` header value to avoid exceptions on HTTP/S requests when it contains non ISO-8859-1 characters (Related to issue https://github.com/splitio/javascript-client/issues/847).
|
|
63
|
+
- Bugfix - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification.
|
|
64
|
+
- Bugfix - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
|
|
65
|
+
|
|
54
66
|
1.17.0 (September 6, 2024)
|
|
55
67
|
- Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance SDK HTTP request Headers for Authorization Frameworks.
|
|
56
68
|
- Added `isTimedout` and `lastUpdate` properties to IStatusInterface to keep track of the timestamp of the last SDK event, used on React and Redux SDKs.
|
|
@@ -7,19 +7,20 @@ var AbstractMySegmentsCacheSync_1 = require("../AbstractMySegmentsCacheSync");
|
|
|
7
7
|
var constants_1 = require("./constants");
|
|
8
8
|
var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
9
9
|
(0, tslib_1.__extends)(MySegmentsCacheInLocal, _super);
|
|
10
|
-
function MySegmentsCacheInLocal(log, keys) {
|
|
10
|
+
function MySegmentsCacheInLocal(log, keys, storage) {
|
|
11
11
|
var _this = _super.call(this) || this;
|
|
12
12
|
_this.log = log;
|
|
13
13
|
_this.keys = keys;
|
|
14
|
+
_this.storage = storage;
|
|
14
15
|
return _this;
|
|
15
16
|
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
16
17
|
}
|
|
17
18
|
MySegmentsCacheInLocal.prototype.addSegment = function (name) {
|
|
18
19
|
var segmentKey = this.keys.buildSegmentNameKey(name);
|
|
19
20
|
try {
|
|
20
|
-
if (
|
|
21
|
+
if (this.storage.getItem(segmentKey) === constants_1.DEFINED)
|
|
21
22
|
return false;
|
|
22
|
-
|
|
23
|
+
this.storage.setItem(segmentKey, constants_1.DEFINED);
|
|
23
24
|
return true;
|
|
24
25
|
}
|
|
25
26
|
catch (e) {
|
|
@@ -30,9 +31,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
30
31
|
MySegmentsCacheInLocal.prototype.removeSegment = function (name) {
|
|
31
32
|
var segmentKey = this.keys.buildSegmentNameKey(name);
|
|
32
33
|
try {
|
|
33
|
-
if (
|
|
34
|
+
if (this.storage.getItem(segmentKey) !== constants_1.DEFINED)
|
|
34
35
|
return false;
|
|
35
|
-
|
|
36
|
+
this.storage.removeItem(segmentKey);
|
|
36
37
|
return true;
|
|
37
38
|
}
|
|
38
39
|
catch (e) {
|
|
@@ -41,17 +42,16 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
44
|
MySegmentsCacheInLocal.prototype.isInSegment = function (name) {
|
|
44
|
-
return
|
|
45
|
+
return this.storage.getItem(this.keys.buildSegmentNameKey(name)) === constants_1.DEFINED;
|
|
45
46
|
};
|
|
46
47
|
MySegmentsCacheInLocal.prototype.getRegisteredSegments = function () {
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var segmentName = _this.keys.extractSegmentName(key);
|
|
48
|
+
var registeredSegments = [];
|
|
49
|
+
for (var i = 0, len = this.storage.length; i < len; i++) {
|
|
50
|
+
var segmentName = this.keys.extractSegmentName(this.storage.key(i));
|
|
51
51
|
if (segmentName)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
registeredSegments.push(segmentName);
|
|
53
|
+
}
|
|
54
|
+
return registeredSegments;
|
|
55
55
|
};
|
|
56
56
|
MySegmentsCacheInLocal.prototype.getKeysCount = function () {
|
|
57
57
|
return 1;
|
|
@@ -59,9 +59,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
59
59
|
MySegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
60
60
|
try {
|
|
61
61
|
if (changeNumber)
|
|
62
|
-
|
|
62
|
+
this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
63
63
|
else
|
|
64
|
-
|
|
64
|
+
this.storage.removeItem(this.keys.buildTillKey());
|
|
65
65
|
}
|
|
66
66
|
catch (e) {
|
|
67
67
|
this.log.error(e);
|
|
@@ -69,7 +69,7 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
69
69
|
};
|
|
70
70
|
MySegmentsCacheInLocal.prototype.getChangeNumber = function () {
|
|
71
71
|
var n = -1;
|
|
72
|
-
var value =
|
|
72
|
+
var value = this.storage.getItem(this.keys.buildTillKey());
|
|
73
73
|
if (value !== null) {
|
|
74
74
|
value = parseInt(value, 10);
|
|
75
75
|
return (0, lang_1.isNaNNumber)(value) ? n : value;
|
|
@@ -6,14 +6,15 @@ var sets_1 = require("../../utils/lang/sets");
|
|
|
6
6
|
var AbstractSplitsCacheSync_1 = require("../AbstractSplitsCacheSync");
|
|
7
7
|
var constants_1 = require("./constants");
|
|
8
8
|
var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
9
|
-
function RBSegmentsCacheInLocal(settings, keys) {
|
|
9
|
+
function RBSegmentsCacheInLocal(settings, keys, storage) {
|
|
10
10
|
this.keys = keys;
|
|
11
11
|
this.log = settings.log;
|
|
12
|
+
this.storage = storage;
|
|
12
13
|
}
|
|
13
14
|
RBSegmentsCacheInLocal.prototype.clear = function () {
|
|
14
15
|
var _this = this;
|
|
15
16
|
this.getNames().forEach(function (name) { return _this.remove(name); });
|
|
16
|
-
|
|
17
|
+
this.storage.removeItem(this.keys.buildRBSegmentsTillKey());
|
|
17
18
|
};
|
|
18
19
|
RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
19
20
|
var _this = this;
|
|
@@ -23,8 +24,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
23
24
|
};
|
|
24
25
|
RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
25
26
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
28
|
+
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
28
29
|
}
|
|
29
30
|
catch (e) {
|
|
30
31
|
this.log.error(constants_1.LOG_PREFIX + e);
|
|
@@ -32,20 +33,19 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
32
33
|
};
|
|
33
34
|
RBSegmentsCacheInLocal.prototype.updateSegmentCount = function (diff) {
|
|
34
35
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
35
|
-
var count = (0, lang_1.toNumber)(
|
|
36
|
-
// @ts-expect-error
|
|
36
|
+
var count = (0, lang_1.toNumber)(this.storage.getItem(segmentsCountKey)) + diff;
|
|
37
37
|
if (count > 0)
|
|
38
|
-
|
|
38
|
+
this.storage.setItem(segmentsCountKey, count + '');
|
|
39
39
|
else
|
|
40
|
-
|
|
40
|
+
this.storage.removeItem(segmentsCountKey);
|
|
41
41
|
};
|
|
42
42
|
RBSegmentsCacheInLocal.prototype.add = function (rbSegment) {
|
|
43
43
|
try {
|
|
44
44
|
var name_1 = rbSegment.name;
|
|
45
45
|
var rbSegmentKey = this.keys.buildRBSegmentKey(name_1);
|
|
46
|
-
var
|
|
47
|
-
var previous =
|
|
48
|
-
|
|
46
|
+
var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
47
|
+
var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
48
|
+
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
49
49
|
var usesSegmentsDiff = 0;
|
|
50
50
|
if (previous && (0, AbstractSplitsCacheSync_1.usesSegments)(previous))
|
|
51
51
|
usesSegmentsDiff--;
|
|
@@ -65,7 +65,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
65
65
|
var rbSegment = this.get(name);
|
|
66
66
|
if (!rbSegment)
|
|
67
67
|
return false;
|
|
68
|
-
|
|
68
|
+
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
69
69
|
if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
|
|
70
70
|
this.updateSegmentCount(-1);
|
|
71
71
|
return true;
|
|
@@ -76,11 +76,11 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
RBSegmentsCacheInLocal.prototype.getNames = function () {
|
|
79
|
-
var len =
|
|
79
|
+
var len = this.storage.length;
|
|
80
80
|
var accum = [];
|
|
81
81
|
var cur = 0;
|
|
82
82
|
while (cur < len) {
|
|
83
|
-
var key =
|
|
83
|
+
var key = this.storage.key(cur);
|
|
84
84
|
if (key != null && this.keys.isRBSegmentKey(key))
|
|
85
85
|
accum.push(this.keys.extractKey(key));
|
|
86
86
|
cur++;
|
|
@@ -88,7 +88,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
88
88
|
return accum;
|
|
89
89
|
};
|
|
90
90
|
RBSegmentsCacheInLocal.prototype.get = function (name) {
|
|
91
|
-
var item =
|
|
91
|
+
var item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
|
|
92
92
|
return item && JSON.parse(item);
|
|
93
93
|
};
|
|
94
94
|
RBSegmentsCacheInLocal.prototype.getAll = function () {
|
|
@@ -102,7 +102,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
102
102
|
};
|
|
103
103
|
RBSegmentsCacheInLocal.prototype.getChangeNumber = function () {
|
|
104
104
|
var n = -1;
|
|
105
|
-
var value =
|
|
105
|
+
var value = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
106
106
|
if (value !== null) {
|
|
107
107
|
value = parseInt(value, 10);
|
|
108
108
|
return (0, lang_1.isNaNNumber)(value) ? n : value;
|
|
@@ -110,7 +110,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
110
110
|
return n;
|
|
111
111
|
};
|
|
112
112
|
RBSegmentsCacheInLocal.prototype.usesSegments = function () {
|
|
113
|
-
var storedCount =
|
|
113
|
+
var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
114
114
|
var splitsWithSegmentsCount = storedCount === null ? 0 : (0, lang_1.toNumber)(storedCount);
|
|
115
115
|
return (0, lang_1.isFiniteNumber)(splitsWithSegmentsCount) ?
|
|
116
116
|
splitsWithSegmentsCount > 0 :
|
|
@@ -6,25 +6,22 @@ var AbstractSplitsCacheSync_1 = require("../AbstractSplitsCacheSync");
|
|
|
6
6
|
var lang_1 = require("../../utils/lang");
|
|
7
7
|
var constants_1 = require("./constants");
|
|
8
8
|
var sets_1 = require("../../utils/lang/sets");
|
|
9
|
-
/**
|
|
10
|
-
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
|
|
11
|
-
*/
|
|
12
9
|
var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
13
10
|
(0, tslib_1.__extends)(SplitsCacheInLocal, _super);
|
|
14
|
-
function SplitsCacheInLocal(settings, keys) {
|
|
11
|
+
function SplitsCacheInLocal(settings, keys, storage) {
|
|
15
12
|
var _this = _super.call(this) || this;
|
|
16
13
|
_this.keys = keys;
|
|
17
14
|
_this.log = settings.log;
|
|
18
15
|
_this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
|
|
16
|
+
_this.storage = storage;
|
|
19
17
|
return _this;
|
|
20
18
|
}
|
|
21
19
|
SplitsCacheInLocal.prototype._decrementCount = function (key) {
|
|
22
|
-
var count = (0, lang_1.toNumber)(
|
|
23
|
-
// @ts-expect-error
|
|
20
|
+
var count = (0, lang_1.toNumber)(this.storage.getItem(key)) - 1;
|
|
24
21
|
if (count > 0)
|
|
25
|
-
|
|
22
|
+
this.storage.setItem(key, count + '');
|
|
26
23
|
else
|
|
27
|
-
|
|
24
|
+
this.storage.removeItem(key);
|
|
28
25
|
};
|
|
29
26
|
SplitsCacheInLocal.prototype._decrementCounts = function (split) {
|
|
30
27
|
try {
|
|
@@ -42,12 +39,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
42
39
|
SplitsCacheInLocal.prototype._incrementCounts = function (split) {
|
|
43
40
|
try {
|
|
44
41
|
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
45
|
-
|
|
46
|
-
localStorage.setItem(ttKey, (0, lang_1.toNumber)(localStorage.getItem(ttKey)) + 1);
|
|
42
|
+
this.storage.setItem(ttKey, ((0, lang_1.toNumber)(this.storage.getItem(ttKey)) + 1) + '');
|
|
47
43
|
if ((0, AbstractSplitsCacheSync_1.usesSegments)(split)) {
|
|
48
44
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
49
|
-
|
|
50
|
-
localStorage.setItem(segmentsCountKey, (0, lang_1.toNumber)(localStorage.getItem(segmentsCountKey)) + 1);
|
|
45
|
+
this.storage.setItem(segmentsCountKey, ((0, lang_1.toNumber)(this.storage.getItem(segmentsCountKey)) + 1) + '');
|
|
51
46
|
}
|
|
52
47
|
}
|
|
53
48
|
catch (e) {
|
|
@@ -59,17 +54,18 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
59
54
|
* We cannot simply call `localStorage.clear()` since that implies removing user items from the storage.
|
|
60
55
|
*/
|
|
61
56
|
SplitsCacheInLocal.prototype.clear = function () {
|
|
57
|
+
var _this = this;
|
|
62
58
|
// collect item keys
|
|
63
|
-
var len =
|
|
59
|
+
var len = this.storage.length;
|
|
64
60
|
var accum = [];
|
|
65
61
|
for (var cur = 0; cur < len; cur++) {
|
|
66
|
-
var key =
|
|
62
|
+
var key = this.storage.key(cur);
|
|
67
63
|
if (key != null && this.keys.isSplitsCacheKey(key))
|
|
68
64
|
accum.push(key);
|
|
69
65
|
}
|
|
70
66
|
// remove items
|
|
71
67
|
accum.forEach(function (key) {
|
|
72
|
-
|
|
68
|
+
_this.storage.removeItem(key);
|
|
73
69
|
});
|
|
74
70
|
this.hasSync = false;
|
|
75
71
|
};
|
|
@@ -77,13 +73,13 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
77
73
|
try {
|
|
78
74
|
var name_1 = split.name;
|
|
79
75
|
var splitKey = this.keys.buildSplitKey(name_1);
|
|
80
|
-
var
|
|
81
|
-
var previousSplit =
|
|
76
|
+
var splitFromStorage = this.storage.getItem(splitKey);
|
|
77
|
+
var previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
|
|
82
78
|
if (previousSplit) {
|
|
83
79
|
this._decrementCounts(previousSplit);
|
|
84
80
|
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
85
81
|
}
|
|
86
|
-
|
|
82
|
+
this.storage.setItem(splitKey, JSON.stringify(split));
|
|
87
83
|
this._incrementCounts(split);
|
|
88
84
|
this.addToFlagSets(split);
|
|
89
85
|
return true;
|
|
@@ -98,7 +94,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
98
94
|
var split = this.getSplit(name);
|
|
99
95
|
if (!split)
|
|
100
96
|
return false;
|
|
101
|
-
|
|
97
|
+
this.storage.removeItem(this.keys.buildSplitKey(name));
|
|
102
98
|
this._decrementCounts(split);
|
|
103
99
|
this.removeFromFlagSets(split.name, split.sets);
|
|
104
100
|
return true;
|
|
@@ -109,14 +105,14 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
109
105
|
}
|
|
110
106
|
};
|
|
111
107
|
SplitsCacheInLocal.prototype.getSplit = function (name) {
|
|
112
|
-
var item =
|
|
108
|
+
var item = this.storage.getItem(this.keys.buildSplitKey(name));
|
|
113
109
|
return item && JSON.parse(item);
|
|
114
110
|
};
|
|
115
111
|
SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
116
112
|
try {
|
|
117
|
-
|
|
113
|
+
this.storage.setItem(this.keys.buildSplitsTillKey(), changeNumber + '');
|
|
118
114
|
// update "last updated" timestamp with current time
|
|
119
|
-
|
|
115
|
+
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
120
116
|
this.hasSync = true;
|
|
121
117
|
return true;
|
|
122
118
|
}
|
|
@@ -127,7 +123,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
127
123
|
};
|
|
128
124
|
SplitsCacheInLocal.prototype.getChangeNumber = function () {
|
|
129
125
|
var n = -1;
|
|
130
|
-
var value =
|
|
126
|
+
var value = this.storage.getItem(this.keys.buildSplitsTillKey());
|
|
131
127
|
if (value !== null) {
|
|
132
128
|
value = parseInt(value, 10);
|
|
133
129
|
return (0, lang_1.isNaNNumber)(value) ? n : value;
|
|
@@ -135,11 +131,11 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
135
131
|
return n;
|
|
136
132
|
};
|
|
137
133
|
SplitsCacheInLocal.prototype.getSplitNames = function () {
|
|
138
|
-
var len =
|
|
134
|
+
var len = this.storage.length;
|
|
139
135
|
var accum = [];
|
|
140
136
|
var cur = 0;
|
|
141
137
|
while (cur < len) {
|
|
142
|
-
var key =
|
|
138
|
+
var key = this.storage.key(cur);
|
|
143
139
|
if (key != null && this.keys.isSplitKey(key))
|
|
144
140
|
accum.push(this.keys.extractKey(key));
|
|
145
141
|
cur++;
|
|
@@ -147,14 +143,14 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
147
143
|
return accum;
|
|
148
144
|
};
|
|
149
145
|
SplitsCacheInLocal.prototype.trafficTypeExists = function (trafficType) {
|
|
150
|
-
var ttCount = (0, lang_1.toNumber)(
|
|
146
|
+
var ttCount = (0, lang_1.toNumber)(this.storage.getItem(this.keys.buildTrafficTypeKey(trafficType)));
|
|
151
147
|
return (0, lang_1.isFiniteNumber)(ttCount) && ttCount > 0;
|
|
152
148
|
};
|
|
153
149
|
SplitsCacheInLocal.prototype.usesSegments = function () {
|
|
154
150
|
// If cache hasn't been synchronized with the cloud, assume we need them.
|
|
155
151
|
if (!this.hasSync)
|
|
156
152
|
return true;
|
|
157
|
-
var storedCount =
|
|
153
|
+
var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
158
154
|
var splitsWithSegmentsCount = storedCount === null ? 0 : (0, lang_1.toNumber)(storedCount);
|
|
159
155
|
return (0, lang_1.isFiniteNumber)(splitsWithSegmentsCount) ?
|
|
160
156
|
splitsWithSegmentsCount > 0 :
|
|
@@ -164,8 +160,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
164
160
|
var _this = this;
|
|
165
161
|
return flagSets.map(function (flagSet) {
|
|
166
162
|
var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
|
|
167
|
-
var
|
|
168
|
-
return new Set(
|
|
163
|
+
var flagSetFromStorage = _this.storage.getItem(flagSetKey);
|
|
164
|
+
return new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
|
|
169
165
|
});
|
|
170
166
|
};
|
|
171
167
|
SplitsCacheInLocal.prototype.addToFlagSets = function (featureFlag) {
|
|
@@ -176,10 +172,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
176
172
|
if (_this.flagSetsFilter.length > 0 && !_this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === featureFlagSet; }))
|
|
177
173
|
return;
|
|
178
174
|
var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
|
|
179
|
-
var
|
|
180
|
-
var flagSetCache = new Set(
|
|
175
|
+
var flagSetFromStorage = _this.storage.getItem(flagSetKey);
|
|
176
|
+
var flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
|
|
181
177
|
flagSetCache.add(featureFlag.name);
|
|
182
|
-
|
|
178
|
+
_this.storage.setItem(flagSetKey, JSON.stringify((0, sets_1.setToArray)(flagSetCache)));
|
|
183
179
|
});
|
|
184
180
|
};
|
|
185
181
|
SplitsCacheInLocal.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
|
|
@@ -192,16 +188,16 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
192
188
|
};
|
|
193
189
|
SplitsCacheInLocal.prototype.removeNames = function (flagSetName, featureFlagName) {
|
|
194
190
|
var flagSetKey = this.keys.buildFlagSetKey(flagSetName);
|
|
195
|
-
var
|
|
196
|
-
if (!
|
|
191
|
+
var flagSetFromStorage = this.storage.getItem(flagSetKey);
|
|
192
|
+
if (!flagSetFromStorage)
|
|
197
193
|
return;
|
|
198
|
-
var flagSetCache = new Set(JSON.parse(
|
|
194
|
+
var flagSetCache = new Set(JSON.parse(flagSetFromStorage));
|
|
199
195
|
flagSetCache.delete(featureFlagName);
|
|
200
196
|
if (flagSetCache.size === 0) {
|
|
201
|
-
|
|
197
|
+
this.storage.removeItem(flagSetKey);
|
|
202
198
|
return;
|
|
203
199
|
}
|
|
204
|
-
|
|
200
|
+
this.storage.setItem(flagSetKey, JSON.stringify((0, sets_1.setToArray)(flagSetCache)));
|
|
205
201
|
};
|
|
206
202
|
return SplitsCacheInLocal;
|
|
207
203
|
}(AbstractSplitsCacheSync_1.AbstractSplitsCacheSync));
|
|
@@ -17,6 +17,20 @@ var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
|
|
|
17
17
|
var UniqueKeysCacheInMemoryCS_1 = require("../inMemory/UniqueKeysCacheInMemoryCS");
|
|
18
18
|
var key_1 = require("../../utils/key");
|
|
19
19
|
var validateCache_1 = require("./validateCache");
|
|
20
|
+
var storageAdapter_1 = require("./storageAdapter");
|
|
21
|
+
function validateStorage(log, prefix, wrapper) {
|
|
22
|
+
if (wrapper) {
|
|
23
|
+
if ((0, isLocalStorageAvailable_1.isValidStorageWrapper)(wrapper)) {
|
|
24
|
+
return (0, isLocalStorageAvailable_1.isWebStorage)(wrapper) ?
|
|
25
|
+
wrapper : // localStorage and sessionStorage don't need adapter
|
|
26
|
+
(0, storageAdapter_1.storageAdapter)(log, prefix, wrapper);
|
|
27
|
+
}
|
|
28
|
+
log.warn(constants_1.LOG_PREFIX + 'Invalid storage provided. Falling back to LocalStorage API');
|
|
29
|
+
}
|
|
30
|
+
if ((0, isLocalStorageAvailable_1.isLocalStorageAvailable)())
|
|
31
|
+
return localStorage;
|
|
32
|
+
log.warn(constants_1.LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
|
|
33
|
+
}
|
|
20
34
|
/**
|
|
21
35
|
* InLocal storage factory for standalone client-side SplitFactory
|
|
22
36
|
*/
|
|
@@ -24,18 +38,17 @@ function InLocalStorage(options) {
|
|
|
24
38
|
if (options === void 0) { options = {}; }
|
|
25
39
|
var prefix = (0, KeyBuilder_1.validatePrefix)(options.prefix);
|
|
26
40
|
function InLocalStorageCSFactory(params) {
|
|
27
|
-
// Fallback to InMemoryStorage if LocalStorage API is not available
|
|
28
|
-
if (!(0, isLocalStorageAvailable_1.isLocalStorageAvailable)()) {
|
|
29
|
-
params.settings.log.warn(constants_1.LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
|
|
30
|
-
return (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
|
|
31
|
-
}
|
|
32
41
|
var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize;
|
|
42
|
+
var storage = validateStorage(log, prefix, options.wrapper);
|
|
43
|
+
if (!storage)
|
|
44
|
+
return (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
|
|
33
45
|
var matchingKey = (0, key_1.getMatching)(settings.core.key);
|
|
34
46
|
var keys = new KeyBuilderCS_1.KeyBuilderCS(prefix, matchingKey);
|
|
35
|
-
var splits = new SplitsCacheInLocal_1.SplitsCacheInLocal(settings, keys);
|
|
36
|
-
var rbSegments = new RBSegmentsCacheInLocal_1.RBSegmentsCacheInLocal(settings, keys);
|
|
37
|
-
var segments = new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, keys);
|
|
38
|
-
var largeSegments = new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, (0, KeyBuilderCS_1.myLargeSegmentsKeyBuilder)(prefix, matchingKey));
|
|
47
|
+
var splits = new SplitsCacheInLocal_1.SplitsCacheInLocal(settings, keys, storage);
|
|
48
|
+
var rbSegments = new RBSegmentsCacheInLocal_1.RBSegmentsCacheInLocal(settings, keys, storage);
|
|
49
|
+
var segments = new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, keys, storage);
|
|
50
|
+
var largeSegments = new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, (0, KeyBuilderCS_1.myLargeSegmentsKeyBuilder)(prefix, matchingKey), storage);
|
|
51
|
+
var validateCachePromise;
|
|
39
52
|
return {
|
|
40
53
|
splits: splits,
|
|
41
54
|
rbSegments: rbSegments,
|
|
@@ -47,16 +60,21 @@ function InLocalStorage(options) {
|
|
|
47
60
|
telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
|
|
48
61
|
uniqueKeys: new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS(),
|
|
49
62
|
validateCache: function () {
|
|
50
|
-
return (0, validateCache_1.validateCache)(options, settings, keys, splits, rbSegments, segments, largeSegments);
|
|
63
|
+
return validateCachePromise || (validateCachePromise = (0, validateCache_1.validateCache)(options, storage, settings, keys, splits, rbSegments, segments, largeSegments));
|
|
64
|
+
},
|
|
65
|
+
save: function () {
|
|
66
|
+
return storage.save && storage.save();
|
|
67
|
+
},
|
|
68
|
+
destroy: function () {
|
|
69
|
+
return storage.whenSaved && storage.whenSaved();
|
|
51
70
|
},
|
|
52
|
-
destroy: function () { },
|
|
53
71
|
// When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
|
|
54
72
|
shared: function (matchingKey) {
|
|
55
73
|
return {
|
|
56
74
|
splits: this.splits,
|
|
57
75
|
rbSegments: this.rbSegments,
|
|
58
|
-
segments: new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, new KeyBuilderCS_1.KeyBuilderCS(prefix, matchingKey)),
|
|
59
|
-
largeSegments: new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, (0, KeyBuilderCS_1.myLargeSegmentsKeyBuilder)(prefix, matchingKey)),
|
|
76
|
+
segments: new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, new KeyBuilderCS_1.KeyBuilderCS(prefix, matchingKey), storage),
|
|
77
|
+
largeSegments: new MySegmentsCacheInLocal_1.MySegmentsCacheInLocal(log, (0, KeyBuilderCS_1.myLargeSegmentsKeyBuilder)(prefix, matchingKey), storage),
|
|
60
78
|
impressions: this.impressions,
|
|
61
79
|
impressionCounts: this.impressionCounts,
|
|
62
80
|
events: this.events,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.storageAdapter = void 0;
|
|
4
|
+
var constants_1 = require("./constants");
|
|
5
|
+
function storageAdapter(log, prefix, wrapper) {
|
|
6
|
+
var keys = [];
|
|
7
|
+
var cache = {};
|
|
8
|
+
var loadPromise;
|
|
9
|
+
var savePromise = Promise.resolve();
|
|
10
|
+
return {
|
|
11
|
+
load: function () {
|
|
12
|
+
return loadPromise || (loadPromise = Promise.resolve().then(function () {
|
|
13
|
+
return wrapper.getItem(prefix);
|
|
14
|
+
}).then(function (storedCache) {
|
|
15
|
+
cache = JSON.parse(storedCache || '{}');
|
|
16
|
+
keys = Object.keys(cache);
|
|
17
|
+
}).catch(function (e) {
|
|
18
|
+
log.error(constants_1.LOG_PREFIX + 'Rejected promise calling wrapper `getItem` method, with error: ' + e);
|
|
19
|
+
}));
|
|
20
|
+
},
|
|
21
|
+
save: function () {
|
|
22
|
+
return savePromise = savePromise.then(function () {
|
|
23
|
+
return Promise.resolve(wrapper.setItem(prefix, JSON.stringify(cache)));
|
|
24
|
+
}).catch(function (e) {
|
|
25
|
+
log.error(constants_1.LOG_PREFIX + 'Rejected promise calling wrapper `setItem` method, with error: ' + e);
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
whenSaved: function () {
|
|
29
|
+
return savePromise;
|
|
30
|
+
},
|
|
31
|
+
get length() {
|
|
32
|
+
return keys.length;
|
|
33
|
+
},
|
|
34
|
+
getItem: function (key) {
|
|
35
|
+
return cache[key] || null;
|
|
36
|
+
},
|
|
37
|
+
key: function (index) {
|
|
38
|
+
return keys[index] || null;
|
|
39
|
+
},
|
|
40
|
+
removeItem: function (key) {
|
|
41
|
+
var index = keys.indexOf(key);
|
|
42
|
+
if (index === -1)
|
|
43
|
+
return;
|
|
44
|
+
keys.splice(index, 1);
|
|
45
|
+
delete cache[key];
|
|
46
|
+
},
|
|
47
|
+
setItem: function (key, value) {
|
|
48
|
+
if (keys.indexOf(key) === -1)
|
|
49
|
+
keys.push(key);
|
|
50
|
+
cache[key] = value;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
exports.storageAdapter = storageAdapter;
|
|
@@ -11,10 +11,10 @@ var MILLIS_IN_A_DAY = 86400000;
|
|
|
11
11
|
*
|
|
12
12
|
* @returns `true` if cache should be cleared, `false` otherwise
|
|
13
13
|
*/
|
|
14
|
-
function validateExpiration(options, settings, keys, currentTimestamp, isThereCache) {
|
|
14
|
+
function validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache) {
|
|
15
15
|
var log = settings.log, initialRolloutPlan = settings.initialRolloutPlan;
|
|
16
16
|
// Check expiration
|
|
17
|
-
var lastUpdatedTimestamp = parseInt(
|
|
17
|
+
var lastUpdatedTimestamp = parseInt(storage.getItem(keys.buildLastUpdatedKey()), 10);
|
|
18
18
|
if (!(0, lang_1.isNaNNumber)(lastUpdatedTimestamp)) {
|
|
19
19
|
var cacheExpirationInDays = (0, lang_1.isFiniteNumber)(options.expirationDays) && options.expirationDays >= 1 ? options.expirationDays : DEFAULT_CACHE_EXPIRATION_IN_DAYS;
|
|
20
20
|
var expirationTimestamp = currentTimestamp - MILLIS_IN_A_DAY * cacheExpirationInDays;
|
|
@@ -25,11 +25,11 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
25
25
|
}
|
|
26
26
|
// Check hash
|
|
27
27
|
var storageHashKey = keys.buildHashKey();
|
|
28
|
-
var storageHash =
|
|
28
|
+
var storageHash = storage.getItem(storageHashKey);
|
|
29
29
|
var currentStorageHash = (0, KeyBuilder_1.getStorageHash)(settings);
|
|
30
30
|
if (storageHash !== currentStorageHash) {
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
storage.setItem(storageHashKey, currentStorageHash);
|
|
33
33
|
}
|
|
34
34
|
catch (e) {
|
|
35
35
|
log.error(constants_1.LOG_PREFIX + e);
|
|
@@ -42,7 +42,7 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
42
42
|
}
|
|
43
43
|
// Clear on init
|
|
44
44
|
if (options.clearOnInit) {
|
|
45
|
-
var lastClearTimestamp = parseInt(
|
|
45
|
+
var lastClearTimestamp = parseInt(storage.getItem(keys.buildLastClear()), 10);
|
|
46
46
|
if ((0, lang_1.isNaNNumber)(lastClearTimestamp) || lastClearTimestamp < currentTimestamp - MILLIS_IN_A_DAY) {
|
|
47
47
|
log.info(constants_1.LOG_PREFIX + 'clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
|
|
48
48
|
return true;
|
|
@@ -57,24 +57,29 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
57
57
|
*
|
|
58
58
|
* @returns `true` if cache is ready to be used, `false` otherwise (cache was cleared or there is no cache)
|
|
59
59
|
*/
|
|
60
|
-
function validateCache(options, settings, keys, splits, rbSegments, segments, largeSegments) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
function validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments) {
|
|
61
|
+
return Promise.resolve(storage.load && storage.load()).then(function () {
|
|
62
|
+
var currentTimestamp = Date.now();
|
|
63
|
+
var isThereCache = splits.getChangeNumber() > -1;
|
|
64
|
+
if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
|
|
65
|
+
splits.clear();
|
|
66
|
+
rbSegments.clear();
|
|
67
|
+
segments.clear();
|
|
68
|
+
largeSegments.clear();
|
|
69
|
+
// Update last clear timestamp
|
|
70
|
+
try {
|
|
71
|
+
storage.setItem(keys.buildLastClear(), currentTimestamp + '');
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
settings.log.error(constants_1.LOG_PREFIX + e);
|
|
75
|
+
}
|
|
76
|
+
// Persist clear
|
|
77
|
+
if (storage.save)
|
|
78
|
+
storage.save();
|
|
79
|
+
return false;
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return isThereCache;
|
|
81
|
+
// Check if ready from cache
|
|
82
|
+
return isThereCache;
|
|
83
|
+
});
|
|
79
84
|
}
|
|
80
85
|
exports.validateCache = validateCache;
|
|
@@ -45,10 +45,9 @@ function fromObjectUpdaterFactory(splitsParser, storage, readiness, settings) {
|
|
|
45
45
|
readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED);
|
|
46
46
|
if (startingUp) {
|
|
47
47
|
startingUp = false;
|
|
48
|
-
|
|
49
|
-
Promise.resolve().then(function () {
|
|
48
|
+
Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
|
|
50
49
|
// Emits SDK_READY_FROM_CACHE
|
|
51
|
-
if (
|
|
50
|
+
if (isCacheLoaded)
|
|
52
51
|
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
53
52
|
// Emits SDK_READY
|
|
54
53
|
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
|
|
@@ -34,6 +34,8 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
|
|
|
34
34
|
shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
|
|
35
35
|
shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
|
|
36
36
|
}
|
|
37
|
+
if (storage.save)
|
|
38
|
+
storage.save();
|
|
37
39
|
// Notify update if required
|
|
38
40
|
if ((0, AbstractSplitsCacheSync_1.usesSegmentsSync)(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
39
41
|
readyOnAlreadyExistentState = false;
|