@splitsoftware/splitio-commons 2.5.0-rc.0 → 2.5.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.
Files changed (41) hide show
  1. package/CHANGES.txt +3 -3
  2. package/cjs/evaluator/convertions/index.js +9 -1
  3. package/cjs/evaluator/matchersTransform/index.js +2 -3
  4. package/cjs/sdkClient/sdkClientMethodCS.js +5 -1
  5. package/cjs/sdkFactory/index.js +8 -2
  6. package/cjs/storages/getRolloutPlan.js +69 -0
  7. package/cjs/storages/inLocalStorage/validateCache.js +2 -2
  8. package/cjs/storages/inMemory/InMemoryStorageCS.js +14 -32
  9. package/cjs/storages/setRolloutPlan.js +66 -0
  10. package/cjs/utils/inputValidation/index.js +1 -3
  11. package/cjs/utils/settingsValidation/index.js +4 -0
  12. package/esm/evaluator/convertions/index.js +7 -0
  13. package/esm/evaluator/matchersTransform/index.js +3 -4
  14. package/esm/sdkClient/sdkClientMethodCS.js +5 -1
  15. package/esm/sdkFactory/index.js +8 -2
  16. package/esm/storages/getRolloutPlan.js +65 -0
  17. package/esm/storages/inLocalStorage/validateCache.js +2 -2
  18. package/esm/storages/inMemory/InMemoryStorageCS.js +14 -32
  19. package/esm/storages/setRolloutPlan.js +61 -0
  20. package/esm/utils/inputValidation/index.js +0 -1
  21. package/esm/utils/settingsValidation/index.js +4 -0
  22. package/package.json +1 -1
  23. package/src/evaluator/convertions/index.ts +10 -0
  24. package/src/evaluator/matchersTransform/index.ts +3 -4
  25. package/src/sdkClient/sdkClientMethodCS.ts +7 -1
  26. package/src/sdkFactory/index.ts +9 -2
  27. package/src/storages/getRolloutPlan.ts +72 -0
  28. package/src/storages/inLocalStorage/validateCache.ts +2 -2
  29. package/src/storages/inMemory/InMemoryStorageCS.ts +14 -37
  30. package/src/storages/setRolloutPlan.ts +71 -0
  31. package/src/storages/types.ts +20 -2
  32. package/src/types.ts +2 -0
  33. package/src/utils/inputValidation/index.ts +0 -1
  34. package/src/utils/settingsValidation/index.ts +4 -0
  35. package/types/splitio.d.ts +30 -35
  36. package/cjs/storages/dataLoader.js +0 -109
  37. package/cjs/utils/inputValidation/preloadedData.js +0 -59
  38. package/esm/storages/dataLoader.js +0 -104
  39. package/esm/utils/inputValidation/preloadedData.js +0 -55
  40. package/src/storages/dataLoader.ts +0 -113
  41. package/src/utils/inputValidation/preloadedData.ts +0 -57
package/CHANGES.txt CHANGED
@@ -1,6 +1,6 @@
1
- 2.5.0 (August XX, 2025)
2
- - Added `factory.getCache()` method for standalone server-side SDKs, which returns the rollout plan snapshot from the storage.
3
- - Added `preloadedData` configuration option for standalone client-side SDKs, which allows preloading the SDK storage with a snapshot of the rollout plan.
1
+ 2.5.0 (September 10, 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 = undefined;
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.start = (0, convertions_1.zeroSinceSS)(value.start);
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
@@ -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
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
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, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, __splitFiltersValidation = _a.sync.__splitFiltersValidation, preloadedData = _a.preloadedData, onReadyFromCacheCb = params.onReadyFromCacheCb;
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 (matchingKey) {
40
- if (!storages[matchingKey]) {
41
- var segments_1 = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
42
- var largeSegments_1 = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
43
- if (preloadedData) {
44
- (0, dataLoader_1.setCache)(log, preloadedData, { segments: segments_1, largeSegments: largeSegments_1 }, matchingKey);
45
- }
46
- storages[matchingKey] = {
47
- splits: this.splits,
48
- rbSegments: this.rbSegments,
49
- segments: segments_1,
50
- largeSegments: largeSegments_1,
51
- impressions: this.impressions,
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.validatePreloadedData = 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;
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 = undefined;
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.start = zeroSinceSS(value.start);
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
@@ -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
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
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, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, __splitFiltersValidation = _a.sync.__splitFiltersValidation, preloadedData = _a.preloadedData, onReadyFromCacheCb = params.onReadyFromCacheCb;
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 (matchingKey) {
37
- if (!storages[matchingKey]) {
38
- var segments_1 = new MySegmentsCacheInMemory();
39
- var largeSegments_1 = new MySegmentsCacheInMemory();
40
- if (preloadedData) {
41
- setCache(log, preloadedData, { segments: segments_1, largeSegments: largeSegments_1 }, matchingKey);
42
- }
43
- storages[matchingKey] = {
44
- splits: this.splits,
45
- rbSegments: this.rbSegments,
46
- segments: segments_1,
47
- largeSegments: largeSegments_1,
48
- impressions: this.impressions,
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;