@splitsoftware/splitio-commons 2.5.0-rc.1 → 2.5.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +13 -1
- package/cjs/storages/getRolloutPlan.js +1 -1
- 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/getRolloutPlan.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/getRolloutPlan.ts +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 +38 -1
|
@@ -34,6 +34,8 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
|
|
|
34
34
|
shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
|
|
35
35
|
shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
|
|
36
36
|
}
|
|
37
|
+
if (storage.save)
|
|
38
|
+
storage.save();
|
|
37
39
|
// Notify update if required
|
|
38
40
|
if ((0, AbstractSplitsCacheSync_1.usesSegmentsSync)(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
39
41
|
readyOnAlreadyExistentState = false;
|
|
@@ -154,6 +154,8 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
|
|
|
154
154
|
segments.registerSegments((0, sets_1.setToArray)(usedSegments))
|
|
155
155
|
]).then(function (_a) {
|
|
156
156
|
var ffChanged = _a[0], rbsChanged = _a[1];
|
|
157
|
+
if (storage.save)
|
|
158
|
+
storage.save();
|
|
157
159
|
if (splitsEventEmitter) {
|
|
158
160
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
159
161
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
@@ -69,35 +69,39 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
69
69
|
*/
|
|
70
70
|
start: function () {
|
|
71
71
|
running = true;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
// @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
|
|
73
|
+
submitterManager.start(!(0, consent_1.isConsentGranted)(settings));
|
|
74
|
+
return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
|
|
75
|
+
if (!running)
|
|
76
|
+
return;
|
|
77
|
+
if (startFirstTime) {
|
|
78
|
+
// Emits SDK_READY_FROM_CACHE
|
|
79
|
+
if (isCacheLoaded)
|
|
80
|
+
readiness.splits.emit(constants_4.SDK_SPLITS_CACHE_LOADED);
|
|
81
|
+
}
|
|
82
|
+
// start syncing splits and segments
|
|
83
|
+
if (pollingManager) {
|
|
84
|
+
// If synchronization is disabled pushManager and pollingManager should not start
|
|
85
|
+
if (syncEnabled) {
|
|
86
|
+
if (pushManager) {
|
|
87
|
+
// Doesn't call `syncAll` when the syncManager is resuming
|
|
88
|
+
if (startFirstTime) {
|
|
89
|
+
pollingManager.syncAll();
|
|
90
|
+
}
|
|
91
|
+
pushManager.start();
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
pollingManager.start();
|
|
85
95
|
}
|
|
86
|
-
pushManager.start();
|
|
87
96
|
}
|
|
88
97
|
else {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
else {
|
|
93
|
-
if (startFirstTime) {
|
|
94
|
-
pollingManager.syncAll();
|
|
98
|
+
if (startFirstTime) {
|
|
99
|
+
pollingManager.syncAll();
|
|
100
|
+
}
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
submitterManager.start(!(0, consent_1.isConsentGranted)(settings));
|
|
100
|
-
startFirstTime = false;
|
|
103
|
+
startFirstTime = false;
|
|
104
|
+
});
|
|
101
105
|
},
|
|
102
106
|
/**
|
|
103
107
|
* Method used to stop/pause the syncManager.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isLocalStorageAvailable = void 0;
|
|
3
|
+
exports.isWebStorage = exports.isValidStorageWrapper = exports.isLocalStorageAvailable = void 0;
|
|
4
4
|
/* eslint-disable no-undef */
|
|
5
5
|
function isLocalStorageAvailable() {
|
|
6
6
|
var mod = '__SPLITSOFTWARE__';
|
|
@@ -14,3 +14,24 @@ function isLocalStorageAvailable() {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
exports.isLocalStorageAvailable = isLocalStorageAvailable;
|
|
17
|
+
function isValidStorageWrapper(wrapper) {
|
|
18
|
+
return wrapper !== null &&
|
|
19
|
+
typeof wrapper === 'object' &&
|
|
20
|
+
typeof wrapper.setItem === 'function' &&
|
|
21
|
+
typeof wrapper.getItem === 'function' &&
|
|
22
|
+
typeof wrapper.removeItem === 'function';
|
|
23
|
+
}
|
|
24
|
+
exports.isValidStorageWrapper = isValidStorageWrapper;
|
|
25
|
+
function isWebStorage(wrapper) {
|
|
26
|
+
if (typeof wrapper.length === 'number') {
|
|
27
|
+
try {
|
|
28
|
+
wrapper.key(0);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
exports.isWebStorage = isWebStorage;
|
|
@@ -6,7 +6,7 @@ var constants_1 = require("../../../logger/constants");
|
|
|
6
6
|
var constants_2 = require("../../../utils/constants");
|
|
7
7
|
function __InLocalStorageMockFactory(params) {
|
|
8
8
|
var result = (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
|
|
9
|
-
result.validateCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
|
|
9
|
+
result.validateCache = function () { return Promise.resolve(true); }; // to emit SDK_READY_FROM_CACHE
|
|
10
10
|
return result;
|
|
11
11
|
}
|
|
12
12
|
exports.__InLocalStorageMockFactory = __InLocalStorageMockFactory;
|
|
@@ -7,7 +7,7 @@ export function getRolloutPlan(log, storage, options) {
|
|
|
7
7
|
if (options === void 0) { options = {}; }
|
|
8
8
|
var keys = options.keys, exposeSegments = options.exposeSegments;
|
|
9
9
|
var splits = storage.splits, segments = storage.segments, rbSegments = storage.rbSegments;
|
|
10
|
-
log.debug("storage: get feature flags" + (keys ? ", and memberships for keys " + keys : '') + (exposeSegments ? ', and segments' : ''));
|
|
10
|
+
log.debug("storage: get feature flags" + (keys ? ", and memberships for keys: " + keys : '') + (exposeSegments ? ', and segments' : ''));
|
|
11
11
|
return {
|
|
12
12
|
splitChanges: {
|
|
13
13
|
ff: {
|
|
@@ -4,19 +4,20 @@ import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
|
|
|
4
4
|
import { LOG_PREFIX, DEFINED } from './constants';
|
|
5
5
|
var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
6
6
|
__extends(MySegmentsCacheInLocal, _super);
|
|
7
|
-
function MySegmentsCacheInLocal(log, keys) {
|
|
7
|
+
function MySegmentsCacheInLocal(log, keys, storage) {
|
|
8
8
|
var _this = _super.call(this) || this;
|
|
9
9
|
_this.log = log;
|
|
10
10
|
_this.keys = keys;
|
|
11
|
+
_this.storage = storage;
|
|
11
12
|
return _this;
|
|
12
13
|
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
13
14
|
}
|
|
14
15
|
MySegmentsCacheInLocal.prototype.addSegment = function (name) {
|
|
15
16
|
var segmentKey = this.keys.buildSegmentNameKey(name);
|
|
16
17
|
try {
|
|
17
|
-
if (
|
|
18
|
+
if (this.storage.getItem(segmentKey) === DEFINED)
|
|
18
19
|
return false;
|
|
19
|
-
|
|
20
|
+
this.storage.setItem(segmentKey, DEFINED);
|
|
20
21
|
return true;
|
|
21
22
|
}
|
|
22
23
|
catch (e) {
|
|
@@ -27,9 +28,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
27
28
|
MySegmentsCacheInLocal.prototype.removeSegment = function (name) {
|
|
28
29
|
var segmentKey = this.keys.buildSegmentNameKey(name);
|
|
29
30
|
try {
|
|
30
|
-
if (
|
|
31
|
+
if (this.storage.getItem(segmentKey) !== DEFINED)
|
|
31
32
|
return false;
|
|
32
|
-
|
|
33
|
+
this.storage.removeItem(segmentKey);
|
|
33
34
|
return true;
|
|
34
35
|
}
|
|
35
36
|
catch (e) {
|
|
@@ -38,17 +39,16 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
41
|
MySegmentsCacheInLocal.prototype.isInSegment = function (name) {
|
|
41
|
-
return
|
|
42
|
+
return this.storage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
|
|
42
43
|
};
|
|
43
44
|
MySegmentsCacheInLocal.prototype.getRegisteredSegments = function () {
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
var segmentName = _this.keys.extractSegmentName(key);
|
|
45
|
+
var registeredSegments = [];
|
|
46
|
+
for (var i = 0, len = this.storage.length; i < len; i++) {
|
|
47
|
+
var segmentName = this.keys.extractSegmentName(this.storage.key(i));
|
|
48
48
|
if (segmentName)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
registeredSegments.push(segmentName);
|
|
50
|
+
}
|
|
51
|
+
return registeredSegments;
|
|
52
52
|
};
|
|
53
53
|
MySegmentsCacheInLocal.prototype.getKeysCount = function () {
|
|
54
54
|
return 1;
|
|
@@ -56,9 +56,9 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
56
56
|
MySegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
57
57
|
try {
|
|
58
58
|
if (changeNumber)
|
|
59
|
-
|
|
59
|
+
this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
60
60
|
else
|
|
61
|
-
|
|
61
|
+
this.storage.removeItem(this.keys.buildTillKey());
|
|
62
62
|
}
|
|
63
63
|
catch (e) {
|
|
64
64
|
this.log.error(e);
|
|
@@ -66,7 +66,7 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
|
|
|
66
66
|
};
|
|
67
67
|
MySegmentsCacheInLocal.prototype.getChangeNumber = function () {
|
|
68
68
|
var n = -1;
|
|
69
|
-
var value =
|
|
69
|
+
var value = this.storage.getItem(this.keys.buildTillKey());
|
|
70
70
|
if (value !== null) {
|
|
71
71
|
value = parseInt(value, 10);
|
|
72
72
|
return isNaNNumber(value) ? n : value;
|
|
@@ -3,14 +3,15 @@ import { setToArray } from '../../utils/lang/sets';
|
|
|
3
3
|
import { usesSegments } from '../AbstractSplitsCacheSync';
|
|
4
4
|
import { LOG_PREFIX } from './constants';
|
|
5
5
|
var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
6
|
-
function RBSegmentsCacheInLocal(settings, keys) {
|
|
6
|
+
function RBSegmentsCacheInLocal(settings, keys, storage) {
|
|
7
7
|
this.keys = keys;
|
|
8
8
|
this.log = settings.log;
|
|
9
|
+
this.storage = storage;
|
|
9
10
|
}
|
|
10
11
|
RBSegmentsCacheInLocal.prototype.clear = function () {
|
|
11
12
|
var _this = this;
|
|
12
13
|
this.getNames().forEach(function (name) { return _this.remove(name); });
|
|
13
|
-
|
|
14
|
+
this.storage.removeItem(this.keys.buildRBSegmentsTillKey());
|
|
14
15
|
};
|
|
15
16
|
RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
16
17
|
var _this = this;
|
|
@@ -20,8 +21,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
20
21
|
};
|
|
21
22
|
RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
22
23
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
25
|
+
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
25
26
|
}
|
|
26
27
|
catch (e) {
|
|
27
28
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -29,20 +30,19 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
29
30
|
};
|
|
30
31
|
RBSegmentsCacheInLocal.prototype.updateSegmentCount = function (diff) {
|
|
31
32
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
32
|
-
var count = toNumber(
|
|
33
|
-
// @ts-expect-error
|
|
33
|
+
var count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
|
|
34
34
|
if (count > 0)
|
|
35
|
-
|
|
35
|
+
this.storage.setItem(segmentsCountKey, count + '');
|
|
36
36
|
else
|
|
37
|
-
|
|
37
|
+
this.storage.removeItem(segmentsCountKey);
|
|
38
38
|
};
|
|
39
39
|
RBSegmentsCacheInLocal.prototype.add = function (rbSegment) {
|
|
40
40
|
try {
|
|
41
41
|
var name_1 = rbSegment.name;
|
|
42
42
|
var rbSegmentKey = this.keys.buildRBSegmentKey(name_1);
|
|
43
|
-
var
|
|
44
|
-
var previous =
|
|
45
|
-
|
|
43
|
+
var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
44
|
+
var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
45
|
+
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
46
46
|
var usesSegmentsDiff = 0;
|
|
47
47
|
if (previous && usesSegments(previous))
|
|
48
48
|
usesSegmentsDiff--;
|
|
@@ -62,7 +62,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
62
62
|
var rbSegment = this.get(name);
|
|
63
63
|
if (!rbSegment)
|
|
64
64
|
return false;
|
|
65
|
-
|
|
65
|
+
this.storage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
66
66
|
if (usesSegments(rbSegment))
|
|
67
67
|
this.updateSegmentCount(-1);
|
|
68
68
|
return true;
|
|
@@ -73,11 +73,11 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
RBSegmentsCacheInLocal.prototype.getNames = function () {
|
|
76
|
-
var len =
|
|
76
|
+
var len = this.storage.length;
|
|
77
77
|
var accum = [];
|
|
78
78
|
var cur = 0;
|
|
79
79
|
while (cur < len) {
|
|
80
|
-
var key =
|
|
80
|
+
var key = this.storage.key(cur);
|
|
81
81
|
if (key != null && this.keys.isRBSegmentKey(key))
|
|
82
82
|
accum.push(this.keys.extractKey(key));
|
|
83
83
|
cur++;
|
|
@@ -85,7 +85,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
85
85
|
return accum;
|
|
86
86
|
};
|
|
87
87
|
RBSegmentsCacheInLocal.prototype.get = function (name) {
|
|
88
|
-
var item =
|
|
88
|
+
var item = this.storage.getItem(this.keys.buildRBSegmentKey(name));
|
|
89
89
|
return item && JSON.parse(item);
|
|
90
90
|
};
|
|
91
91
|
RBSegmentsCacheInLocal.prototype.getAll = function () {
|
|
@@ -99,7 +99,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
99
99
|
};
|
|
100
100
|
RBSegmentsCacheInLocal.prototype.getChangeNumber = function () {
|
|
101
101
|
var n = -1;
|
|
102
|
-
var value =
|
|
102
|
+
var value = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
103
103
|
if (value !== null) {
|
|
104
104
|
value = parseInt(value, 10);
|
|
105
105
|
return isNaNNumber(value) ? n : value;
|
|
@@ -107,7 +107,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
107
107
|
return n;
|
|
108
108
|
};
|
|
109
109
|
RBSegmentsCacheInLocal.prototype.usesSegments = function () {
|
|
110
|
-
var storedCount =
|
|
110
|
+
var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
111
111
|
var splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
112
112
|
return isFiniteNumber(splitsWithSegmentsCount) ?
|
|
113
113
|
splitsWithSegmentsCount > 0 :
|
|
@@ -3,25 +3,22 @@ import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSyn
|
|
|
3
3
|
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
|
|
4
4
|
import { LOG_PREFIX } from './constants';
|
|
5
5
|
import { setToArray } from '../../utils/lang/sets';
|
|
6
|
-
/**
|
|
7
|
-
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
|
|
8
|
-
*/
|
|
9
6
|
var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
10
7
|
__extends(SplitsCacheInLocal, _super);
|
|
11
|
-
function SplitsCacheInLocal(settings, keys) {
|
|
8
|
+
function SplitsCacheInLocal(settings, keys, storage) {
|
|
12
9
|
var _this = _super.call(this) || this;
|
|
13
10
|
_this.keys = keys;
|
|
14
11
|
_this.log = settings.log;
|
|
15
12
|
_this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
|
|
13
|
+
_this.storage = storage;
|
|
16
14
|
return _this;
|
|
17
15
|
}
|
|
18
16
|
SplitsCacheInLocal.prototype._decrementCount = function (key) {
|
|
19
|
-
var count = toNumber(
|
|
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,21 @@ export function InLocalStorage(options) {
|
|
|
44
57
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
45
58
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
46
59
|
validateCache: function () {
|
|
47
|
-
return validateCache(options, settings, keys, splits, rbSegments, segments, largeSegments);
|
|
60
|
+
return validateCachePromise || (validateCachePromise = validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments));
|
|
61
|
+
},
|
|
62
|
+
save: function () {
|
|
63
|
+
return storage.save && storage.save();
|
|
64
|
+
},
|
|
65
|
+
destroy: function () {
|
|
66
|
+
return storage.whenSaved && storage.whenSaved();
|
|
48
67
|
},
|
|
49
|
-
destroy: function () { },
|
|
50
68
|
// When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
|
|
51
69
|
shared: function (matchingKey) {
|
|
52
70
|
return {
|
|
53
71
|
splits: this.splits,
|
|
54
72
|
rbSegments: this.rbSegments,
|
|
55
|
-
segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
|
|
56
|
-
largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
|
|
73
|
+
segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey), storage),
|
|
74
|
+
largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage),
|
|
57
75
|
impressions: this.impressions,
|
|
58
76
|
impressionCounts: this.impressionCounts,
|
|
59
77
|
events: this.events,
|