@splitsoftware/splitio-commons 2.5.0-rc.0 → 2.5.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +3 -3
- package/cjs/evaluator/convertions/index.js +9 -1
- package/cjs/evaluator/matchersTransform/index.js +2 -3
- package/cjs/sdkClient/sdkClientMethodCS.js +5 -1
- package/cjs/sdkFactory/index.js +8 -2
- package/cjs/storages/getRolloutPlan.js +69 -0
- package/cjs/storages/inLocalStorage/validateCache.js +2 -2
- package/cjs/storages/inMemory/InMemoryStorageCS.js +14 -32
- package/cjs/storages/setRolloutPlan.js +66 -0
- package/cjs/utils/inputValidation/index.js +1 -3
- package/cjs/utils/settingsValidation/index.js +4 -0
- package/esm/evaluator/convertions/index.js +7 -0
- package/esm/evaluator/matchersTransform/index.js +3 -4
- package/esm/sdkClient/sdkClientMethodCS.js +5 -1
- package/esm/sdkFactory/index.js +8 -2
- package/esm/storages/getRolloutPlan.js +65 -0
- package/esm/storages/inLocalStorage/validateCache.js +2 -2
- package/esm/storages/inMemory/InMemoryStorageCS.js +14 -32
- package/esm/storages/setRolloutPlan.js +61 -0
- package/esm/utils/inputValidation/index.js +0 -1
- package/esm/utils/settingsValidation/index.js +4 -0
- package/package.json +1 -1
- package/src/evaluator/convertions/index.ts +10 -0
- package/src/evaluator/matchersTransform/index.ts +3 -4
- package/src/sdkClient/sdkClientMethodCS.ts +7 -1
- package/src/sdkFactory/index.ts +9 -2
- package/src/storages/getRolloutPlan.ts +72 -0
- package/src/storages/inLocalStorage/validateCache.ts +2 -2
- package/src/storages/inMemory/InMemoryStorageCS.ts +14 -37
- package/src/storages/setRolloutPlan.ts +71 -0
- package/src/storages/types.ts +20 -2
- package/src/types.ts +2 -0
- package/src/utils/inputValidation/index.ts +0 -1
- package/src/utils/settingsValidation/index.ts +4 -0
- package/types/splitio.d.ts +22 -34
- package/cjs/storages/dataLoader.js +0 -109
- package/cjs/utils/inputValidation/preloadedData.js +0 -59
- package/esm/storages/dataLoader.js +0 -104
- package/esm/utils/inputValidation/preloadedData.js +0 -55
- package/src/storages/dataLoader.ts +0 -113
- package/src/utils/inputValidation/preloadedData.ts +0 -57
package/CHANGES.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
2.5.0 (
|
|
2
|
-
- Added `factory.
|
|
3
|
-
- Added `
|
|
1
|
+
2.5.0 (September 9, 2025)
|
|
2
|
+
- Added `factory.getRolloutPlan()` method for standalone server-side SDKs, which returns the rollout plan snapshot from the storage.
|
|
3
|
+
- Added `initialRolloutPlan` configuration option for standalone client-side SDKs, which allows preloading the SDK storage with a snapshot of the rollout plan.
|
|
4
4
|
|
|
5
5
|
2.4.1 (June 3, 2025)
|
|
6
6
|
- 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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.zeroSinceSS = exports.zeroSinceHH = void 0;
|
|
3
|
+
exports.betweenDateTimeTransform = exports.zeroSinceSS = exports.zeroSinceHH = void 0;
|
|
4
4
|
function zeroSinceHH(millisSinceEpoch) {
|
|
5
5
|
return new Date(millisSinceEpoch).setUTCHours(0, 0, 0, 0);
|
|
6
6
|
}
|
|
@@ -9,3 +9,11 @@ function zeroSinceSS(millisSinceEpoch) {
|
|
|
9
9
|
return new Date(millisSinceEpoch).setUTCSeconds(0, 0);
|
|
10
10
|
}
|
|
11
11
|
exports.zeroSinceSS = zeroSinceSS;
|
|
12
|
+
function betweenDateTimeTransform(betweenMatcherData) {
|
|
13
|
+
return {
|
|
14
|
+
dataType: betweenMatcherData.dataType,
|
|
15
|
+
start: zeroSinceSS(betweenMatcherData.start),
|
|
16
|
+
end: zeroSinceSS(betweenMatcherData.end)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
exports.betweenDateTimeTransform = betweenDateTimeTransform;
|
|
@@ -17,7 +17,7 @@ function matchersTransform(matchers) {
|
|
|
17
17
|
var type = (0, matcherTypes_1.matcherTypesMapper)(matcherType);
|
|
18
18
|
// As default input data type we use string (even for ALL_KEYS)
|
|
19
19
|
var dataType = matcherTypes_1.matcherDataTypes.STRING;
|
|
20
|
-
var value
|
|
20
|
+
var value;
|
|
21
21
|
if (type === matcherTypes_1.matcherTypes.IN_SEGMENT) {
|
|
22
22
|
value = (0, segment_1.segmentTransform)(userDefinedSegmentMatcherData);
|
|
23
23
|
}
|
|
@@ -45,8 +45,7 @@ function matchersTransform(matchers) {
|
|
|
45
45
|
value = betweenMatcherData;
|
|
46
46
|
dataType = matcherTypes_1.matcherDataTypes.NUMBER;
|
|
47
47
|
if (value.dataType === 'DATETIME') {
|
|
48
|
-
value
|
|
49
|
-
value.end = (0, convertions_1.zeroSinceSS)(value.end);
|
|
48
|
+
value = (0, convertions_1.betweenDateTimeTransform)(value);
|
|
50
49
|
dataType = matcherTypes_1.matcherDataTypes.DATETIME;
|
|
51
50
|
}
|
|
52
51
|
}
|
|
@@ -9,12 +9,13 @@ var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
|
9
9
|
var constants_1 = require("../logger/constants");
|
|
10
10
|
var constants_2 = require("../readiness/constants");
|
|
11
11
|
var identity_1 = require("./identity");
|
|
12
|
+
var setRolloutPlan_1 = require("../storages/setRolloutPlan");
|
|
12
13
|
/**
|
|
13
14
|
* Factory of client method for the client-side API variant where TT is ignored.
|
|
14
15
|
* Therefore, clients don't have a bound TT for the track method.
|
|
15
16
|
*/
|
|
16
17
|
function sdkClientMethodCSFactory(params) {
|
|
17
|
-
var clients = params.clients, storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, key = _a.core.key, log = _a.log;
|
|
18
|
+
var clients = params.clients, storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, key = _a.core.key, log = _a.log, initialRolloutPlan = _a.initialRolloutPlan;
|
|
18
19
|
var mainClientInstance = (0, clientCS_1.clientCSDecorator)(log, (0, sdkClient_1.sdkClientFactory)(params), key);
|
|
19
20
|
var parsedDefaultKey = (0, key_2.keyParser)(key);
|
|
20
21
|
var defaultInstanceId = (0, identity_1.buildInstanceId)(parsedDefaultKey);
|
|
@@ -42,6 +43,9 @@ function sdkClientMethodCSFactory(params) {
|
|
|
42
43
|
// Emit SDK_READY in consumer mode for shared clients
|
|
43
44
|
sharedSdkReadiness_1.readinessManager.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
|
|
44
45
|
});
|
|
46
|
+
if (sharedStorage && initialRolloutPlan) {
|
|
47
|
+
(0, setRolloutPlan_1.setRolloutPlan)(log, initialRolloutPlan, { segments: sharedStorage.segments, largeSegments: sharedStorage.largeSegments }, matchingKey);
|
|
48
|
+
}
|
|
45
49
|
// 3 possibilities:
|
|
46
50
|
// - Standalone mode: both syncManager and sharedSyncManager are defined
|
|
47
51
|
// - Consumer mode: both syncManager and sharedSyncManager are undefined
|
package/cjs/sdkFactory/index.js
CHANGED
|
@@ -15,12 +15,14 @@ var strategyOptimized_1 = require("../trackers/strategy/strategyOptimized");
|
|
|
15
15
|
var strategyNone_1 = require("../trackers/strategy/strategyNone");
|
|
16
16
|
var uniqueKeysTracker_1 = require("../trackers/uniqueKeysTracker");
|
|
17
17
|
var constants_3 = require("../utils/constants");
|
|
18
|
+
var setRolloutPlan_1 = require("../storages/setRolloutPlan");
|
|
19
|
+
var key_1 = require("../utils/key");
|
|
18
20
|
/**
|
|
19
21
|
* Modular SDK factory
|
|
20
22
|
*/
|
|
21
23
|
function sdkFactory(params) {
|
|
22
24
|
var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory, splitApiFactory = params.splitApiFactory, extraProps = params.extraProps, syncManagerFactory = params.syncManagerFactory, SignalListener = params.SignalListener, impressionsObserverFactory = params.impressionsObserverFactory, integrationsManagerFactory = params.integrationsManagerFactory, sdkManagerFactory = params.sdkManagerFactory, sdkClientMethodFactory = params.sdkClientMethodFactory, filterAdapterFactory = params.filterAdapterFactory, lazyInit = params.lazyInit;
|
|
23
|
-
var log = settings.log, impressionsMode = settings.sync.impressionsMode;
|
|
25
|
+
var log = settings.log, impressionsMode = settings.sync.impressionsMode, initialRolloutPlan = settings.initialRolloutPlan, key = settings.core.key;
|
|
24
26
|
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
|
|
25
27
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
26
28
|
// initialization
|
|
@@ -49,7 +51,11 @@ function sdkFactory(params) {
|
|
|
49
51
|
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
50
52
|
}
|
|
51
53
|
});
|
|
52
|
-
|
|
54
|
+
if (initialRolloutPlan) {
|
|
55
|
+
(0, setRolloutPlan_1.setRolloutPlan)(log, initialRolloutPlan, storage, key && (0, key_1.getMatching)(key));
|
|
56
|
+
if (storage.splits.getChangeNumber() > -1)
|
|
57
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
58
|
+
}
|
|
53
59
|
var clients = {};
|
|
54
60
|
var telemetryTracker = (0, telemetryTracker_1.telemetryTrackerFactory)(storage.telemetry, platform.now);
|
|
55
61
|
var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRolloutPlan = void 0;
|
|
4
|
+
var sets_1 = require("../utils/lang/sets");
|
|
5
|
+
var key_1 = require("../utils/key");
|
|
6
|
+
/**
|
|
7
|
+
* Gets the rollout plan snapshot from the given synchronous storage.
|
|
8
|
+
*/
|
|
9
|
+
function getRolloutPlan(log, storage, options) {
|
|
10
|
+
if (options === void 0) { options = {}; }
|
|
11
|
+
var keys = options.keys, exposeSegments = options.exposeSegments;
|
|
12
|
+
var splits = storage.splits, segments = storage.segments, rbSegments = storage.rbSegments;
|
|
13
|
+
log.debug("storage: get feature flags" + (keys ? ", and memberships for keys " + keys : '') + (exposeSegments ? ', and segments' : ''));
|
|
14
|
+
return {
|
|
15
|
+
splitChanges: {
|
|
16
|
+
ff: {
|
|
17
|
+
t: splits.getChangeNumber(),
|
|
18
|
+
s: -1,
|
|
19
|
+
d: splits.getAll(),
|
|
20
|
+
},
|
|
21
|
+
rbs: {
|
|
22
|
+
t: rbSegments.getChangeNumber(),
|
|
23
|
+
s: -1,
|
|
24
|
+
d: rbSegments.getAll(),
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
segmentChanges: exposeSegments ? // @ts-ignore accessing private prop
|
|
28
|
+
Object.keys(segments.segmentCache).map(function (segmentName) { return ({
|
|
29
|
+
name: segmentName,
|
|
30
|
+
added: (0, sets_1.setToArray)(segments.segmentCache[segmentName]),
|
|
31
|
+
removed: [],
|
|
32
|
+
since: -1,
|
|
33
|
+
till: segments.getChangeNumber(segmentName)
|
|
34
|
+
}); }) :
|
|
35
|
+
undefined,
|
|
36
|
+
memberships: keys ?
|
|
37
|
+
keys.reduce(function (prev, key) {
|
|
38
|
+
var matchingKey = (0, key_1.getMatching)(key);
|
|
39
|
+
if (storage.shared) { // Client-side segments
|
|
40
|
+
var sharedStorage = storage.shared(matchingKey);
|
|
41
|
+
prev[matchingKey] = {
|
|
42
|
+
ms: {
|
|
43
|
+
k: Object.keys(sharedStorage.segments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
44
|
+
},
|
|
45
|
+
ls: sharedStorage.largeSegments ? {
|
|
46
|
+
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
47
|
+
} : undefined
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else { // Server-side segments
|
|
51
|
+
prev[matchingKey] = {
|
|
52
|
+
ms: {
|
|
53
|
+
k: Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
|
|
54
|
+
return storage.segments.segmentCache[segmentName].has(matchingKey) ?
|
|
55
|
+
prev.concat({ n: segmentName }) :
|
|
56
|
+
prev;
|
|
57
|
+
}, [])
|
|
58
|
+
},
|
|
59
|
+
ls: {
|
|
60
|
+
k: []
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return prev;
|
|
65
|
+
}, {}) :
|
|
66
|
+
undefined
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
exports.getRolloutPlan = getRolloutPlan;
|
|
@@ -12,7 +12,7 @@ var MILLIS_IN_A_DAY = 86400000;
|
|
|
12
12
|
* @returns `true` if cache should be cleared, `false` otherwise
|
|
13
13
|
*/
|
|
14
14
|
function validateExpiration(options, settings, keys, currentTimestamp, isThereCache) {
|
|
15
|
-
var log = settings.log;
|
|
15
|
+
var log = settings.log, initialRolloutPlan = settings.initialRolloutPlan;
|
|
16
16
|
// Check expiration
|
|
17
17
|
var lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()), 10);
|
|
18
18
|
if (!(0, lang_1.isNaNNumber)(lastUpdatedTimestamp)) {
|
|
@@ -34,7 +34,7 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
34
34
|
catch (e) {
|
|
35
35
|
log.error(constants_1.LOG_PREFIX + e);
|
|
36
36
|
}
|
|
37
|
-
if (isThereCache) {
|
|
37
|
+
if (isThereCache && !initialRolloutPlan) {
|
|
38
38
|
log.info(constants_1.LOG_PREFIX + 'SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');
|
|
39
39
|
return true;
|
|
40
40
|
}
|
|
@@ -9,8 +9,6 @@ var ImpressionCountsCacheInMemory_1 = require("./ImpressionCountsCacheInMemory")
|
|
|
9
9
|
var constants_1 = require("../../utils/constants");
|
|
10
10
|
var TelemetryCacheInMemory_1 = require("./TelemetryCacheInMemory");
|
|
11
11
|
var UniqueKeysCacheInMemoryCS_1 = require("./UniqueKeysCacheInMemoryCS");
|
|
12
|
-
var key_1 = require("../../utils/key");
|
|
13
|
-
var dataLoader_1 = require("../dataLoader");
|
|
14
12
|
var RBSegmentsCacheInMemory_1 = require("./RBSegmentsCacheInMemory");
|
|
15
13
|
/**
|
|
16
14
|
* InMemory storage factory for standalone client-side SplitFactory
|
|
@@ -18,8 +16,7 @@ var RBSegmentsCacheInMemory_1 = require("./RBSegmentsCacheInMemory");
|
|
|
18
16
|
* @param params - parameters required by EventsCacheSync
|
|
19
17
|
*/
|
|
20
18
|
function InMemoryStorageCSFactory(params) {
|
|
21
|
-
var _a = params.settings,
|
|
22
|
-
var storages = {};
|
|
19
|
+
var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, __splitFiltersValidation = _a.sync.__splitFiltersValidation;
|
|
23
20
|
var splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
|
|
24
21
|
var rbSegments = new RBSegmentsCacheInMemory_1.RBSegmentsCacheInMemory();
|
|
25
22
|
var segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
|
|
@@ -36,27 +33,19 @@ function InMemoryStorageCSFactory(params) {
|
|
|
36
33
|
uniqueKeys: new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS(),
|
|
37
34
|
destroy: function () { },
|
|
38
35
|
// When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
|
|
39
|
-
shared: function (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
impressionCounts: this.impressionCounts,
|
|
53
|
-
events: this.events,
|
|
54
|
-
telemetry: this.telemetry,
|
|
55
|
-
uniqueKeys: this.uniqueKeys,
|
|
56
|
-
destroy: function () { }
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return storages[matchingKey];
|
|
36
|
+
shared: function () {
|
|
37
|
+
return {
|
|
38
|
+
splits: this.splits,
|
|
39
|
+
rbSegments: this.rbSegments,
|
|
40
|
+
segments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
|
|
41
|
+
largeSegments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
|
|
42
|
+
impressions: this.impressions,
|
|
43
|
+
impressionCounts: this.impressionCounts,
|
|
44
|
+
events: this.events,
|
|
45
|
+
telemetry: this.telemetry,
|
|
46
|
+
uniqueKeys: this.uniqueKeys,
|
|
47
|
+
destroy: function () { }
|
|
48
|
+
};
|
|
60
49
|
},
|
|
61
50
|
};
|
|
62
51
|
// @TODO revisit storage logic in localhost mode
|
|
@@ -68,13 +57,6 @@ function InMemoryStorageCSFactory(params) {
|
|
|
68
57
|
storage.impressionCounts.track = noopTrack;
|
|
69
58
|
storage.uniqueKeys.track = noopTrack;
|
|
70
59
|
}
|
|
71
|
-
var matchingKey = (0, key_1.getMatching)(params.settings.core.key);
|
|
72
|
-
storages[matchingKey] = storage;
|
|
73
|
-
if (preloadedData) {
|
|
74
|
-
(0, dataLoader_1.setCache)(log, preloadedData, storage, matchingKey);
|
|
75
|
-
if (splits.getChangeNumber() > -1)
|
|
76
|
-
onReadyFromCacheCb();
|
|
77
|
-
}
|
|
78
60
|
return storage;
|
|
79
61
|
}
|
|
80
62
|
exports.InMemoryStorageCSFactory = InMemoryStorageCSFactory;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setRolloutPlan = exports.validateRolloutPlan = void 0;
|
|
4
|
+
var lang_1 = require("../utils/lang");
|
|
5
|
+
var mode_1 = require("../utils/settingsValidation/mode");
|
|
6
|
+
/**
|
|
7
|
+
* Validates if the given rollout plan is valid.
|
|
8
|
+
*/
|
|
9
|
+
function validateRolloutPlan(log, settings) {
|
|
10
|
+
var mode = settings.mode, initialRolloutPlan = settings.initialRolloutPlan;
|
|
11
|
+
if ((0, mode_1.isConsumerMode)(mode)) {
|
|
12
|
+
log.warn('storage: initial rollout plan is ignored in consumer mode');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if ((0, lang_1.isObject)(initialRolloutPlan) && (0, lang_1.isObject)(initialRolloutPlan.splitChanges))
|
|
16
|
+
return initialRolloutPlan;
|
|
17
|
+
log.error('storage: invalid rollout plan provided');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
exports.validateRolloutPlan = validateRolloutPlan;
|
|
21
|
+
/**
|
|
22
|
+
* Sets the given synchronous storage with the provided rollout plan snapshot.
|
|
23
|
+
* If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
|
|
24
|
+
* Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
|
|
25
|
+
*/
|
|
26
|
+
function setRolloutPlan(log, rolloutPlan, storage, matchingKey) {
|
|
27
|
+
var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
|
|
28
|
+
var _a = rolloutPlan.splitChanges, ff = _a.ff, rbs = _a.rbs;
|
|
29
|
+
log.debug("storage: set feature flags and segments" + (matchingKey ? " for key " + matchingKey : ''));
|
|
30
|
+
if (splits && ff) {
|
|
31
|
+
splits.clear();
|
|
32
|
+
splits.update(ff.d, [], ff.t);
|
|
33
|
+
}
|
|
34
|
+
if (rbSegments && rbs) {
|
|
35
|
+
rbSegments.clear();
|
|
36
|
+
rbSegments.update(rbs.d, [], rbs.t);
|
|
37
|
+
}
|
|
38
|
+
var segmentChanges = rolloutPlan.segmentChanges;
|
|
39
|
+
if (matchingKey) { // add memberships data (client-side)
|
|
40
|
+
var memberships = rolloutPlan.memberships && rolloutPlan.memberships[matchingKey];
|
|
41
|
+
if (!memberships && segmentChanges) {
|
|
42
|
+
memberships = {
|
|
43
|
+
ms: {
|
|
44
|
+
k: segmentChanges.filter(function (segment) {
|
|
45
|
+
return segment.added.indexOf(matchingKey) > -1;
|
|
46
|
+
}).map(function (segment) { return ({ n: segment.name }); })
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (memberships) {
|
|
51
|
+
if (memberships.ms)
|
|
52
|
+
segments.resetSegments(memberships.ms);
|
|
53
|
+
if (memberships.ls && largeSegments)
|
|
54
|
+
largeSegments.resetSegments(memberships.ls);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else { // add segments data (server-side)
|
|
58
|
+
if (segmentChanges) {
|
|
59
|
+
segments.clear();
|
|
60
|
+
segmentChanges.forEach(function (segment) {
|
|
61
|
+
segments.update(segment.name, segment.added, segment.removed, segment.till);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.setRolloutPlan = setRolloutPlan;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateEvaluationOptions = exports.
|
|
3
|
+
exports.validateEvaluationOptions = exports.validateTrafficTypeExistence = exports.validateSplitExistence = exports.validateIfOperational = exports.validateIfNotDestroyed = exports.validateTrafficType = exports.validateSplits = exports.validateSplit = exports.validateKey = exports.validateEventProperties = exports.validateEventValue = exports.validateEvent = exports.validateAttributes = exports.releaseApiKey = exports.validateAndTrackApiKey = exports.validateApiKey = void 0;
|
|
4
4
|
var apiKey_1 = require("./apiKey");
|
|
5
5
|
Object.defineProperty(exports, "validateApiKey", { enumerable: true, get: function () { return apiKey_1.validateApiKey; } });
|
|
6
6
|
Object.defineProperty(exports, "validateAndTrackApiKey", { enumerable: true, get: function () { return apiKey_1.validateAndTrackApiKey; } });
|
|
@@ -28,7 +28,5 @@ var splitExistence_1 = require("./splitExistence");
|
|
|
28
28
|
Object.defineProperty(exports, "validateSplitExistence", { enumerable: true, get: function () { return splitExistence_1.validateSplitExistence; } });
|
|
29
29
|
var trafficTypeExistence_1 = require("./trafficTypeExistence");
|
|
30
30
|
Object.defineProperty(exports, "validateTrafficTypeExistence", { enumerable: true, get: function () { return trafficTypeExistence_1.validateTrafficTypeExistence; } });
|
|
31
|
-
var preloadedData_1 = require("./preloadedData");
|
|
32
|
-
Object.defineProperty(exports, "validatePreloadedData", { enumerable: true, get: function () { return preloadedData_1.validatePreloadedData; } });
|
|
33
31
|
var eventProperties_2 = require("./eventProperties");
|
|
34
32
|
Object.defineProperty(exports, "validateEvaluationOptions", { enumerable: true, get: function () { return eventProperties_2.validateEvaluationOptions; } });
|
|
@@ -8,6 +8,7 @@ var constants_1 = require("../constants");
|
|
|
8
8
|
var impressionsMode_1 = require("./impressionsMode");
|
|
9
9
|
var key_1 = require("../inputValidation/key");
|
|
10
10
|
var constants_2 = require("../../logger/constants");
|
|
11
|
+
var setRolloutPlan_1 = require("../../storages/setRolloutPlan");
|
|
11
12
|
// Exported for telemetry
|
|
12
13
|
exports.base = {
|
|
13
14
|
// Define which kind of object you want to retrieve from SplitFactory
|
|
@@ -131,6 +132,9 @@ function settingsValidation(config, validationParams) {
|
|
|
131
132
|
// @ts-ignore, modify readonly prop
|
|
132
133
|
if (storage)
|
|
133
134
|
withDefaults.storage = storage(withDefaults);
|
|
135
|
+
// @ts-ignore, modify readonly prop
|
|
136
|
+
if (withDefaults.initialRolloutPlan)
|
|
137
|
+
withDefaults.initialRolloutPlan = (0, setRolloutPlan_1.validateRolloutPlan)(log, withDefaults);
|
|
134
138
|
// Validate key and TT (for client-side)
|
|
135
139
|
var maybeKey = withDefaults.core.key;
|
|
136
140
|
if (validationParams.acceptKey) {
|
|
@@ -4,3 +4,10 @@ export function zeroSinceHH(millisSinceEpoch) {
|
|
|
4
4
|
export function zeroSinceSS(millisSinceEpoch) {
|
|
5
5
|
return new Date(millisSinceEpoch).setUTCSeconds(0, 0);
|
|
6
6
|
}
|
|
7
|
+
export function betweenDateTimeTransform(betweenMatcherData) {
|
|
8
|
+
return {
|
|
9
|
+
dataType: betweenMatcherData.dataType,
|
|
10
|
+
start: zeroSinceSS(betweenMatcherData.start),
|
|
11
|
+
end: zeroSinceSS(betweenMatcherData.end)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -3,7 +3,7 @@ import { matcherTypes, matcherTypesMapper, matcherDataTypes } from '../matchers/
|
|
|
3
3
|
import { segmentTransform } from './segment';
|
|
4
4
|
import { whitelistTransform } from './whitelist';
|
|
5
5
|
import { numericTransform } from './unaryNumeric';
|
|
6
|
-
import { zeroSinceHH, zeroSinceSS } from '../convertions';
|
|
6
|
+
import { zeroSinceHH, zeroSinceSS, betweenDateTimeTransform } from '../convertions';
|
|
7
7
|
/**
|
|
8
8
|
* Flat the complex matcherGroup structure into something handy.
|
|
9
9
|
*/
|
|
@@ -14,7 +14,7 @@ export function matchersTransform(matchers) {
|
|
|
14
14
|
var type = matcherTypesMapper(matcherType);
|
|
15
15
|
// As default input data type we use string (even for ALL_KEYS)
|
|
16
16
|
var dataType = matcherDataTypes.STRING;
|
|
17
|
-
var value
|
|
17
|
+
var value;
|
|
18
18
|
if (type === matcherTypes.IN_SEGMENT) {
|
|
19
19
|
value = segmentTransform(userDefinedSegmentMatcherData);
|
|
20
20
|
}
|
|
@@ -42,8 +42,7 @@ export function matchersTransform(matchers) {
|
|
|
42
42
|
value = betweenMatcherData;
|
|
43
43
|
dataType = matcherDataTypes.NUMBER;
|
|
44
44
|
if (value.dataType === 'DATETIME') {
|
|
45
|
-
value
|
|
46
|
-
value.end = zeroSinceSS(value.end);
|
|
45
|
+
value = betweenDateTimeTransform(value);
|
|
47
46
|
dataType = matcherDataTypes.DATETIME;
|
|
48
47
|
}
|
|
49
48
|
}
|
|
@@ -6,12 +6,13 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
6
6
|
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
|
|
7
7
|
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
|
|
8
8
|
import { buildInstanceId } from './identity';
|
|
9
|
+
import { setRolloutPlan } from '../storages/setRolloutPlan';
|
|
9
10
|
/**
|
|
10
11
|
* Factory of client method for the client-side API variant where TT is ignored.
|
|
11
12
|
* Therefore, clients don't have a bound TT for the track method.
|
|
12
13
|
*/
|
|
13
14
|
export function sdkClientMethodCSFactory(params) {
|
|
14
|
-
var clients = params.clients, storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, key = _a.core.key, log = _a.log;
|
|
15
|
+
var clients = params.clients, storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, key = _a.core.key, log = _a.log, initialRolloutPlan = _a.initialRolloutPlan;
|
|
15
16
|
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), key);
|
|
16
17
|
var parsedDefaultKey = keyParser(key);
|
|
17
18
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey);
|
|
@@ -39,6 +40,9 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
39
40
|
// Emit SDK_READY in consumer mode for shared clients
|
|
40
41
|
sharedSdkReadiness_1.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
41
42
|
});
|
|
43
|
+
if (sharedStorage && initialRolloutPlan) {
|
|
44
|
+
setRolloutPlan(log, initialRolloutPlan, { segments: sharedStorage.segments, largeSegments: sharedStorage.largeSegments }, matchingKey);
|
|
45
|
+
}
|
|
42
46
|
// 3 possibilities:
|
|
43
47
|
// - Standalone mode: both syncManager and sharedSyncManager are defined
|
|
44
48
|
// - Consumer mode: both syncManager and sharedSyncManager are undefined
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -12,12 +12,14 @@ import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized
|
|
|
12
12
|
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
|
|
13
13
|
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
|
|
14
14
|
import { DEBUG, OPTIMIZED } from '../utils/constants';
|
|
15
|
+
import { setRolloutPlan } from '../storages/setRolloutPlan';
|
|
16
|
+
import { getMatching } from '../utils/key';
|
|
15
17
|
/**
|
|
16
18
|
* Modular SDK factory
|
|
17
19
|
*/
|
|
18
20
|
export function sdkFactory(params) {
|
|
19
21
|
var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory, splitApiFactory = params.splitApiFactory, extraProps = params.extraProps, syncManagerFactory = params.syncManagerFactory, SignalListener = params.SignalListener, impressionsObserverFactory = params.impressionsObserverFactory, integrationsManagerFactory = params.integrationsManagerFactory, sdkManagerFactory = params.sdkManagerFactory, sdkClientMethodFactory = params.sdkClientMethodFactory, filterAdapterFactory = params.filterAdapterFactory, lazyInit = params.lazyInit;
|
|
20
|
-
var log = settings.log, impressionsMode = settings.sync.impressionsMode;
|
|
22
|
+
var log = settings.log, impressionsMode = settings.sync.impressionsMode, initialRolloutPlan = settings.initialRolloutPlan, key = settings.core.key;
|
|
21
23
|
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
|
|
22
24
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
23
25
|
// initialization
|
|
@@ -46,7 +48,11 @@ export function sdkFactory(params) {
|
|
|
46
48
|
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
47
49
|
}
|
|
48
50
|
});
|
|
49
|
-
|
|
51
|
+
if (initialRolloutPlan) {
|
|
52
|
+
setRolloutPlan(log, initialRolloutPlan, storage, key && getMatching(key));
|
|
53
|
+
if (storage.splits.getChangeNumber() > -1)
|
|
54
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
55
|
+
}
|
|
50
56
|
var clients = {};
|
|
51
57
|
var telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
52
58
|
var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { setToArray } from '../utils/lang/sets';
|
|
2
|
+
import { getMatching } from '../utils/key';
|
|
3
|
+
/**
|
|
4
|
+
* Gets the rollout plan snapshot from the given synchronous storage.
|
|
5
|
+
*/
|
|
6
|
+
export function getRolloutPlan(log, storage, options) {
|
|
7
|
+
if (options === void 0) { options = {}; }
|
|
8
|
+
var keys = options.keys, exposeSegments = options.exposeSegments;
|
|
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' : ''));
|
|
11
|
+
return {
|
|
12
|
+
splitChanges: {
|
|
13
|
+
ff: {
|
|
14
|
+
t: splits.getChangeNumber(),
|
|
15
|
+
s: -1,
|
|
16
|
+
d: splits.getAll(),
|
|
17
|
+
},
|
|
18
|
+
rbs: {
|
|
19
|
+
t: rbSegments.getChangeNumber(),
|
|
20
|
+
s: -1,
|
|
21
|
+
d: rbSegments.getAll(),
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
segmentChanges: exposeSegments ? // @ts-ignore accessing private prop
|
|
25
|
+
Object.keys(segments.segmentCache).map(function (segmentName) { return ({
|
|
26
|
+
name: segmentName,
|
|
27
|
+
added: setToArray(segments.segmentCache[segmentName]),
|
|
28
|
+
removed: [],
|
|
29
|
+
since: -1,
|
|
30
|
+
till: segments.getChangeNumber(segmentName)
|
|
31
|
+
}); }) :
|
|
32
|
+
undefined,
|
|
33
|
+
memberships: keys ?
|
|
34
|
+
keys.reduce(function (prev, key) {
|
|
35
|
+
var matchingKey = getMatching(key);
|
|
36
|
+
if (storage.shared) { // Client-side segments
|
|
37
|
+
var sharedStorage = storage.shared(matchingKey);
|
|
38
|
+
prev[matchingKey] = {
|
|
39
|
+
ms: {
|
|
40
|
+
k: Object.keys(sharedStorage.segments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
41
|
+
},
|
|
42
|
+
ls: sharedStorage.largeSegments ? {
|
|
43
|
+
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
44
|
+
} : undefined
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else { // Server-side segments
|
|
48
|
+
prev[matchingKey] = {
|
|
49
|
+
ms: {
|
|
50
|
+
k: Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
|
|
51
|
+
return storage.segments.segmentCache[segmentName].has(matchingKey) ?
|
|
52
|
+
prev.concat({ n: segmentName }) :
|
|
53
|
+
prev;
|
|
54
|
+
}, [])
|
|
55
|
+
},
|
|
56
|
+
ls: {
|
|
57
|
+
k: []
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return prev;
|
|
62
|
+
}, {}) :
|
|
63
|
+
undefined
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -9,7 +9,7 @@ var MILLIS_IN_A_DAY = 86400000;
|
|
|
9
9
|
* @returns `true` if cache should be cleared, `false` otherwise
|
|
10
10
|
*/
|
|
11
11
|
function validateExpiration(options, settings, keys, currentTimestamp, isThereCache) {
|
|
12
|
-
var log = settings.log;
|
|
12
|
+
var log = settings.log, initialRolloutPlan = settings.initialRolloutPlan;
|
|
13
13
|
// Check expiration
|
|
14
14
|
var lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()), 10);
|
|
15
15
|
if (!isNaNNumber(lastUpdatedTimestamp)) {
|
|
@@ -31,7 +31,7 @@ function validateExpiration(options, settings, keys, currentTimestamp, isThereCa
|
|
|
31
31
|
catch (e) {
|
|
32
32
|
log.error(LOG_PREFIX + e);
|
|
33
33
|
}
|
|
34
|
-
if (isThereCache) {
|
|
34
|
+
if (isThereCache && !initialRolloutPlan) {
|
|
35
35
|
log.info(LOG_PREFIX + 'SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
@@ -6,8 +6,6 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
|
6
6
|
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
7
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
8
8
|
import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
|
|
9
|
-
import { getMatching } from '../../utils/key';
|
|
10
|
-
import { setCache } from '../dataLoader';
|
|
11
9
|
import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
|
|
12
10
|
/**
|
|
13
11
|
* InMemory storage factory for standalone client-side SplitFactory
|
|
@@ -15,8 +13,7 @@ import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
|
|
|
15
13
|
* @param params - parameters required by EventsCacheSync
|
|
16
14
|
*/
|
|
17
15
|
export function InMemoryStorageCSFactory(params) {
|
|
18
|
-
var _a = params.settings,
|
|
19
|
-
var storages = {};
|
|
16
|
+
var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, __splitFiltersValidation = _a.sync.__splitFiltersValidation;
|
|
20
17
|
var splits = new SplitsCacheInMemory(__splitFiltersValidation);
|
|
21
18
|
var rbSegments = new RBSegmentsCacheInMemory();
|
|
22
19
|
var segments = new MySegmentsCacheInMemory();
|
|
@@ -33,27 +30,19 @@ export function InMemoryStorageCSFactory(params) {
|
|
|
33
30
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
34
31
|
destroy: function () { },
|
|
35
32
|
// When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
|
|
36
|
-
shared: function (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
impressionCounts: this.impressionCounts,
|
|
50
|
-
events: this.events,
|
|
51
|
-
telemetry: this.telemetry,
|
|
52
|
-
uniqueKeys: this.uniqueKeys,
|
|
53
|
-
destroy: function () { }
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
return storages[matchingKey];
|
|
33
|
+
shared: function () {
|
|
34
|
+
return {
|
|
35
|
+
splits: this.splits,
|
|
36
|
+
rbSegments: this.rbSegments,
|
|
37
|
+
segments: new MySegmentsCacheInMemory(),
|
|
38
|
+
largeSegments: new MySegmentsCacheInMemory(),
|
|
39
|
+
impressions: this.impressions,
|
|
40
|
+
impressionCounts: this.impressionCounts,
|
|
41
|
+
events: this.events,
|
|
42
|
+
telemetry: this.telemetry,
|
|
43
|
+
uniqueKeys: this.uniqueKeys,
|
|
44
|
+
destroy: function () { }
|
|
45
|
+
};
|
|
57
46
|
},
|
|
58
47
|
};
|
|
59
48
|
// @TODO revisit storage logic in localhost mode
|
|
@@ -65,13 +54,6 @@ export function InMemoryStorageCSFactory(params) {
|
|
|
65
54
|
storage.impressionCounts.track = noopTrack;
|
|
66
55
|
storage.uniqueKeys.track = noopTrack;
|
|
67
56
|
}
|
|
68
|
-
var matchingKey = getMatching(params.settings.core.key);
|
|
69
|
-
storages[matchingKey] = storage;
|
|
70
|
-
if (preloadedData) {
|
|
71
|
-
setCache(log, preloadedData, storage, matchingKey);
|
|
72
|
-
if (splits.getChangeNumber() > -1)
|
|
73
|
-
onReadyFromCacheCb();
|
|
74
|
-
}
|
|
75
57
|
return storage;
|
|
76
58
|
}
|
|
77
59
|
InMemoryStorageCSFactory.type = STORAGE_MEMORY;
|