@splitsoftware/splitio-commons 2.4.2-rc.1 → 2.4.2-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +13 -1
- package/cjs/logger/messages/error.js +1 -1
- package/cjs/sdkClient/sdkClient.js +0 -1
- package/cjs/sdkFactory/index.js +3 -10
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +5 -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/pollingManagerCS.js +5 -4
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +5 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -1
- package/cjs/sync/syncManagerOnline.js +31 -26
- package/cjs/trackers/impressionsTracker.js +4 -4
- package/cjs/utils/env/isLocalStorageAvailable.js +28 -5
- package/cjs/utils/settingsValidation/splitFilters.js +0 -6
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/logger/messages/error.js +1 -1
- package/esm/sdkClient/sdkClient.js +0 -1
- package/esm/sdkFactory/index.js +3 -10
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/AbstractSplitsCacheSync.js +3 -0
- 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/pollingManagerCS.js +5 -4
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +5 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -1
- package/esm/sync/syncManagerOnline.js +31 -26
- package/esm/trackers/impressionsTracker.js +4 -4
- package/esm/utils/env/isLocalStorageAvailable.js +24 -3
- package/esm/utils/settingsValidation/splitFilters.js +0 -6
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/logger/messages/error.ts +1 -1
- package/src/sdkClient/sdkClient.ts +0 -1
- package/src/sdkFactory/index.ts +3 -13
- package/src/services/splitHttpClient.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +5 -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/pollingManagerCS.ts +5 -4
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +5 -2
- package/src/sync/polling/updaters/splitChangesUpdater.ts +4 -2
- package/src/sync/syncManagerOnline.ts +30 -24
- package/src/trackers/impressionsTracker.ts +3 -3
- package/src/utils/env/isLocalStorageAvailable.ts +24 -3
- package/src/utils/settingsValidation/splitFilters.ts +0 -6
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +72 -16
package/CHANGES.txt
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
2.5.0 (August XX, 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.4.1 (June 3, 2025)
|
|
2
5
|
- Bugfix - Improved the Proxy fallback to flag spec version 1.2 to handle cases where the Proxy does not return an end-of-stream marker in 400 status code responses.
|
|
3
6
|
|
|
@@ -9,7 +12,7 @@
|
|
|
9
12
|
- Updated the Redis storage to:
|
|
10
13
|
- Avoid lazy require of the `ioredis` dependency when the SDK is initialized, and
|
|
11
14
|
- Flag the SDK as ready from cache immediately to allow queueing feature flag evaluations before SDK_READY event is emitted (Reverted in v1.7.0).
|
|
12
|
-
- Bugfix - Enhanced HTTP client module to implement timeouts for failing requests that might otherwise remain pending indefinitely on some Fetch API implementations.
|
|
15
|
+
- 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.
|
|
13
16
|
|
|
14
17
|
2.2.0 (March 28, 2025)
|
|
15
18
|
- Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
|
|
@@ -47,6 +50,15 @@
|
|
|
47
50
|
- 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.
|
|
48
51
|
- Removed the `sync.localhostMode` configuration option to plug the LocalhostMode module.
|
|
49
52
|
|
|
53
|
+
1.17.1 (July 25, 2025)
|
|
54
|
+
- Updated the Redis storage to avoid lazy require of the `ioredis` dependency when the SDK is initialized.
|
|
55
|
+
- Updated some transitive dependencies for vulnerability fixes.
|
|
56
|
+
- 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.
|
|
57
|
+
- Bugfix - Properly handle rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
|
|
58
|
+
- 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).
|
|
59
|
+
- 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.
|
|
60
|
+
- Bugfix - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
|
|
61
|
+
|
|
50
62
|
1.17.0 (September 6, 2024)
|
|
51
63
|
- Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance SDK HTTP request Headers for Authorization Frameworks.
|
|
52
64
|
- Added `isTimedout` and `lastUpdate` properties to IStatusInterface to keep track of the timestamp of the last SDK event, used on React and Redux SDKs.
|
|
@@ -17,7 +17,7 @@ exports.codesError = [
|
|
|
17
17
|
[c.ERROR_SYNC_OFFLINE_LOADING, c.LOG_PREFIX_SYNC_OFFLINE + 'There was an issue loading the mock feature flags data. No changes will be applied to the current cache. %s'],
|
|
18
18
|
[c.ERROR_STREAMING_SSE, c.LOG_PREFIX_SYNC_STREAMING + 'Failed to connect or error on streaming connection, with error message: %s'],
|
|
19
19
|
[c.ERROR_STREAMING_AUTH, c.LOG_PREFIX_SYNC_STREAMING + 'Failed to authenticate for streaming. Error: %s.'],
|
|
20
|
-
[c.ERROR_HTTP, '
|
|
20
|
+
[c.ERROR_HTTP, 'HTTP request failed with %s. URL: %s. Message: %s'],
|
|
21
21
|
// client status
|
|
22
22
|
[c.ERROR_CLIENT_LISTENER, 'A listener was added for %s on the SDK, which has already fired and won\'t be emitted again. The callback won\'t be executed.'],
|
|
23
23
|
[c.ERROR_CLIENT_DESTROYED, '%s: Client has already been destroyed - no calls possible.'],
|
|
@@ -38,7 +38,6 @@ function sdkClientFactory(params, isSharedClient) {
|
|
|
38
38
|
(0, clientInputValidation_1.clientInputValidationDecorator)(settings, (0, client_1.clientFactory)(params), sdkReadinessManager.readinessManager),
|
|
39
39
|
// Sdk destroy
|
|
40
40
|
{
|
|
41
|
-
__ctx: params,
|
|
42
41
|
flush: function () {
|
|
43
42
|
// @TODO define cooldown time
|
|
44
43
|
return __cooldown(__flush, COOLDOWN_TIME_IN_MILLIS);
|
package/cjs/sdkFactory/index.js
CHANGED
|
@@ -87,7 +87,8 @@ function sdkFactory(params) {
|
|
|
87
87
|
initCallbacks.length = 0;
|
|
88
88
|
}
|
|
89
89
|
log.info(constants_1.NEW_FACTORY);
|
|
90
|
-
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
return (0, objectAssign_1.objectAssign)({
|
|
91
92
|
// Split evaluation and event tracking engine
|
|
92
93
|
client: clientMethod,
|
|
93
94
|
// Manager API to explore available information
|
|
@@ -101,15 +102,7 @@ function sdkFactory(params) {
|
|
|
101
102
|
destroy: function () {
|
|
102
103
|
hasInit = false;
|
|
103
104
|
return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
|
|
104
|
-
}
|
|
105
|
-
__ctx: ctx
|
|
105
|
+
}
|
|
106
106
|
}, extraProps && extraProps(ctx), lazyInit ? { init: init } : init());
|
|
107
|
-
// append factory to global
|
|
108
|
-
if (typeof window === 'object') { // @ts-ignore
|
|
109
|
-
// eslint-disable-next-line no-undef
|
|
110
|
-
(window.__HARNESS_FME__ = window.__HARNESS_FME__ || []).push(factory);
|
|
111
|
-
}
|
|
112
|
-
// @ts-ignore
|
|
113
|
-
return factory;
|
|
114
107
|
}
|
|
115
108
|
exports.sdkFactory = sdkFactory;
|
|
@@ -69,7 +69,7 @@ function splitHttpClientFactory(settings, _a) {
|
|
|
69
69
|
msg = error.message || 'Network Error';
|
|
70
70
|
}
|
|
71
71
|
if (!resp || resp.status !== 403) { // 403's log we'll be handled somewhere else.
|
|
72
|
-
log[logErrorsAsInfo ? 'info' : 'error'](constants_1.ERROR_HTTP, [resp ? resp.status : '
|
|
72
|
+
log[logErrorsAsInfo ? 'info' : 'error'](constants_1.ERROR_HTTP, [resp ? 'status code ' + resp.status : 'no status code', url, msg]);
|
|
73
73
|
}
|
|
74
74
|
var networkError = new Error(msg);
|
|
75
75
|
// passes `undefined` as statusCode if not an HTTP error (resp === undefined)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.usesSegments = exports.AbstractSplitsCacheSync = void 0;
|
|
3
|
+
exports.usesSegmentsSync = exports.usesSegments = exports.AbstractSplitsCacheSync = void 0;
|
|
4
4
|
var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
5
5
|
var constants_1 = require("../utils/constants");
|
|
6
6
|
/**
|
|
@@ -69,3 +69,7 @@ function usesSegments(ruleEntity) {
|
|
|
69
69
|
return false;
|
|
70
70
|
}
|
|
71
71
|
exports.usesSegments = usesSegments;
|
|
72
|
+
function usesSegmentsSync(storage) {
|
|
73
|
+
return storage.splits.usesSegments() || storage.rbSegments.usesSegments();
|
|
74
|
+
}
|
|
75
|
+
exports.usesSegmentsSync = usesSegmentsSync;
|
|
@@ -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.contains = function (names) {
|
|
@@ -98,7 +98,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
98
98
|
};
|
|
99
99
|
RBSegmentsCacheInLocal.prototype.getChangeNumber = function () {
|
|
100
100
|
var n = -1;
|
|
101
|
-
var value =
|
|
101
|
+
var value = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
102
102
|
if (value !== null) {
|
|
103
103
|
value = parseInt(value, 10);
|
|
104
104
|
return (0, lang_1.isNaNNumber)(value) ? n : value;
|
|
@@ -106,7 +106,7 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
106
106
|
return n;
|
|
107
107
|
};
|
|
108
108
|
RBSegmentsCacheInLocal.prototype.usesSegments = function () {
|
|
109
|
-
var storedCount =
|
|
109
|
+
var storedCount = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
110
110
|
var splitsWithSegmentsCount = storedCount === null ? 0 : (0, lang_1.toNumber)(storedCount);
|
|
111
111
|
return (0, lang_1.isFiniteNumber)(splitsWithSegmentsCount) ?
|
|
112
112
|
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;
|