@splitsoftware/splitio-commons 2.0.0-rc.1 → 2.0.0-rc.2

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 CHANGED
@@ -1,6 +1,9 @@
1
1
  2.0.0 (October XX, 2024)
2
2
  - Added support for targeting rules based on large segments.
3
3
  - Added `factory.destroy()` method, which invokes the `destroy` method on all SDK clients created by the factory.
4
+ - Added `factory.getState()` method for standalone server-side SDKs, which returns the rollout plan snapshot from the storage.
5
+ - Added `preloadedData` configuration option for standalone client-side SDKs, which allows preloading the SDK storage with a snapshot of the rollout plan.
6
+ - Updated internal storage factory to emit the SDK_READY_FROM_CACHE event when it corresponds, to clean up the initialization flow.
4
7
  - Updated the handling of timers and async operations inside an `init` factory method to enable lazy initialization of the SDK in standalone mode. This update is intended for the React SDK.
5
8
  - Bugfixing - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
6
9
  - BREAKING CHANGES:
@@ -45,8 +45,10 @@ function sdkFactory(params) {
45
45
  readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED);
46
46
  readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
47
47
  },
48
+ onReadyFromCacheCb: function () {
49
+ readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
50
+ }
48
51
  });
49
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
50
52
  var clients = {};
51
53
  var telemetryTracker = (0, telemetryTracker_1.telemetryTrackerFactory)(storage.telemetry, platform.now);
52
54
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
@@ -1,51 +1,113 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dataLoaderFactory = void 0;
4
- var browser_1 = require("../utils/constants/browser");
3
+ exports.getSnapshot = exports.loadData = void 0;
4
+ var sets_1 = require("../utils/lang/sets");
5
+ var key_1 = require("../utils/key");
5
6
  /**
6
- * Factory of client-side storage loader
7
+ * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
8
+ * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
7
9
  *
8
- * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
9
- * and extended with a `mySegmentsData` property.
10
- * @returns function to preload the storage
10
+ * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader and extended with a `mySegmentsData` property.
11
+ * @param storage object containing `splits` and `segments` cache (client-side variant)
12
+ * @param userKey user key (matching key) of the provided MySegmentsCache
13
+ *
14
+ * @TODO extend to load largeSegments
15
+ * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
16
+ * @TODO add logs, and input validation in this module, in favor of size reduction.
17
+ * @TODO unit tests
11
18
  */
12
- function dataLoaderFactory(preloadedData) {
13
- /**
14
- * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
15
- * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
16
- *
17
- * @param storage object containing `splits` and `segments` cache (client-side variant)
18
- * @param userId user key string of the provided MySegmentsCache
19
- *
20
- * @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
21
- * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
22
- */
23
- return function loadData(storage, userId) {
24
- // Do not load data if current preloadedData is empty
25
- if (Object.keys(preloadedData).length === 0)
26
- return;
27
- var _a = preloadedData.lastUpdated, lastUpdated = _a === void 0 ? -1 : _a, _b = preloadedData.segmentsData, segmentsData = _b === void 0 ? {} : _b, _c = preloadedData.since, since = _c === void 0 ? -1 : _c, _d = preloadedData.splitsData, splitsData = _d === void 0 ? {} : _d;
19
+ function loadData(preloadedData, storage, matchingKey) {
20
+ // Do not load data if current preloadedData is empty
21
+ if (Object.keys(preloadedData).length === 0)
22
+ return;
23
+ var _a = preloadedData.segmentsData, segmentsData = _a === void 0 ? {} : _a, _b = preloadedData.since, since = _b === void 0 ? -1 : _b, _c = preloadedData.splitsData, splitsData = _c === void 0 ? [] : _c;
24
+ if (storage.splits) {
28
25
  var storedSince = storage.splits.getChangeNumber();
29
- var expirationTimestamp = Date.now() - browser_1.DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
30
- // Do not load data if current localStorage data is more recent,
31
- // or if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
32
- if (storedSince > since || lastUpdated < expirationTimestamp)
26
+ // Do not load data if current data is more recent
27
+ if (storedSince > since)
33
28
  return;
34
29
  // cleaning up the localStorage data, since some cached splits might need be part of the preloaded data
35
30
  storage.splits.clear();
36
31
  storage.splits.setChangeNumber(since);
37
32
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
38
- storage.splits.addSplits(Object.keys(splitsData).map(function (splitName) { return JSON.parse(splitsData[splitName]); }));
39
- // add mySegments data
40
- var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
41
- if (!mySegmentsData) {
42
- // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
43
- mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
44
- var userIds = JSON.parse(segmentsData[segmentName]).added;
45
- return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
46
- });
33
+ storage.splits.addSplits(splitsData.map(function (split) { return ([split.name, split]); }));
34
+ }
35
+ if (matchingKey) { // add mySegments data (client-side)
36
+ var membershipsData = preloadedData.membershipsData && preloadedData.membershipsData[matchingKey];
37
+ if (!membershipsData && segmentsData) {
38
+ membershipsData = {
39
+ ms: {
40
+ k: Object.keys(segmentsData).filter(function (segmentName) {
41
+ var segmentKeys = segmentsData[segmentName];
42
+ return segmentKeys.indexOf(matchingKey) > -1;
43
+ }).map(function (segmentName) { return ({ n: segmentName }); })
44
+ }
45
+ };
47
46
  }
48
- storage.segments.resetSegments({ k: mySegmentsData.map(function (s) { return ({ n: s }); }) });
47
+ if (membershipsData) {
48
+ if (membershipsData.ms)
49
+ storage.segments.resetSegments(membershipsData.ms);
50
+ if (membershipsData.ls && storage.largeSegments)
51
+ storage.largeSegments.resetSegments(membershipsData.ls);
52
+ }
53
+ }
54
+ else { // add segments data (server-side)
55
+ Object.keys(segmentsData).forEach(function (segmentName) {
56
+ var segmentKeys = segmentsData[segmentName];
57
+ storage.segments.update(segmentName, segmentKeys, [], -1);
58
+ });
59
+ }
60
+ }
61
+ exports.loadData = loadData;
62
+ function getSnapshot(storage, userKeys) {
63
+ return {
64
+ // lastUpdated: Date.now(),
65
+ since: storage.splits.getChangeNumber(),
66
+ splitsData: storage.splits.getAll(),
67
+ segmentsData: userKeys ?
68
+ undefined : // @ts-ignore accessing private prop
69
+ Object.keys(storage.segments.segmentCache).reduce(function (prev, cur) {
70
+ prev[cur] = (0, sets_1.setToArray)(storage.segments.segmentCache[cur]);
71
+ return prev;
72
+ }, {}),
73
+ membershipsData: userKeys ?
74
+ userKeys.reduce(function (prev, userKey) {
75
+ if (storage.shared) {
76
+ // Client-side segments
77
+ // @ts-ignore accessing private prop
78
+ var sharedStorage = storage.shared(userKey);
79
+ prev[(0, key_1.getMatching)(userKey)] = {
80
+ ms: {
81
+ // @ts-ignore accessing private prop
82
+ k: Object.keys(sharedStorage.segments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
83
+ // cn: sharedStorage.segments.getChangeNumber()
84
+ },
85
+ ls: sharedStorage.largeSegments ? {
86
+ // @ts-ignore accessing private prop
87
+ k: Object.keys(sharedStorage.largeSegments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
88
+ // cn: sharedStorage.largeSegments.getChangeNumber()
89
+ } : undefined
90
+ };
91
+ }
92
+ else {
93
+ prev[(0, key_1.getMatching)(userKey)] = {
94
+ ms: {
95
+ // Server-side segments
96
+ // @ts-ignore accessing private prop
97
+ k: Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
98
+ return storage.segments.segmentCache[segmentName].has(userKey) ?
99
+ prev.concat({ n: segmentName }) :
100
+ prev;
101
+ }, [])
102
+ },
103
+ ls: {
104
+ k: []
105
+ }
106
+ };
107
+ }
108
+ return prev;
109
+ }, {}) :
110
+ undefined
49
111
  };
50
112
  }
51
- exports.dataLoaderFactory = dataLoaderFactory;
113
+ exports.getSnapshot = getSnapshot;
@@ -9,13 +9,15 @@ 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");
12
14
  /**
13
15
  * InMemory storage factory for standalone client-side SplitFactory
14
16
  *
15
17
  * @param params parameters required by EventsCacheSync
16
18
  */
17
19
  function InMemoryStorageCSFactory(params) {
18
- var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
20
+ var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation, preloadedData = _a.preloadedData, onReadyFromCacheCb = params.onReadyFromCacheCb;
19
21
  var splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
20
22
  var segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
21
23
  var largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
@@ -39,11 +41,16 @@ function InMemoryStorageCSFactory(params) {
39
41
  this.uniqueKeys && this.uniqueKeys.clear();
40
42
  },
41
43
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
42
- shared: function () {
44
+ shared: function (matchingKey) {
45
+ var segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
46
+ var largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
47
+ if (preloadedData) {
48
+ (0, dataLoader_1.loadData)(preloadedData, { segments: segments, largeSegments: largeSegments }, matchingKey);
49
+ }
43
50
  return {
44
51
  splits: this.splits,
45
- segments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
46
- largeSegments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
52
+ segments: segments,
53
+ largeSegments: largeSegments,
47
54
  impressions: this.impressions,
48
55
  impressionCounts: this.impressionCounts,
49
56
  events: this.events,
@@ -68,6 +75,11 @@ function InMemoryStorageCSFactory(params) {
68
75
  if (storage.uniqueKeys)
69
76
  storage.uniqueKeys.track = noopTrack;
70
77
  }
78
+ if (preloadedData) {
79
+ (0, dataLoader_1.loadData)(preloadedData, storage, (0, key_1.getMatching)(params.settings.core.key));
80
+ if (splits.getChangeNumber() > -1)
81
+ onReadyFromCacheCb();
82
+ }
71
83
  return storage;
72
84
  }
73
85
  exports.InMemoryStorageCSFactory = InMemoryStorageCSFactory;
@@ -26,7 +26,7 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
26
26
  segmentChangesFetcher(since, segmentName, noCache, till).then(function (changes) {
27
27
  return Promise.all(changes.map(function (x) {
28
28
  log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
29
- return segments.update(x.name, x.added, x.removed, x.till);
29
+ return segments.update(segmentName, x.added, x.removed, x.till);
30
30
  })).then(function (updates) {
31
31
  return updates.some(function (update) { return update; });
32
32
  });
@@ -5,7 +5,7 @@ import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
5
5
  import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
6
6
  import { createLoggerAPI } from '../logger/sdkLogger';
7
7
  import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
8
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
8
+ import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
9
9
  import { objectAssign } from '../utils/lang/objectAssign';
10
10
  import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
11
11
  import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
@@ -42,8 +42,10 @@ export function sdkFactory(params) {
42
42
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
43
43
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
44
44
  },
45
+ onReadyFromCacheCb: function () {
46
+ readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
47
+ }
45
48
  });
46
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
47
49
  var clients = {};
48
50
  var telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
49
51
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
@@ -1,47 +1,108 @@
1
- import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../utils/constants/browser';
1
+ import { setToArray } from '../utils/lang/sets';
2
+ import { getMatching } from '../utils/key';
2
3
  /**
3
- * Factory of client-side storage loader
4
+ * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
5
+ * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
4
6
  *
5
- * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
6
- * and extended with a `mySegmentsData` property.
7
- * @returns function to preload the storage
7
+ * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader and extended with a `mySegmentsData` property.
8
+ * @param storage object containing `splits` and `segments` cache (client-side variant)
9
+ * @param userKey user key (matching key) of the provided MySegmentsCache
10
+ *
11
+ * @TODO extend to load largeSegments
12
+ * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
13
+ * @TODO add logs, and input validation in this module, in favor of size reduction.
14
+ * @TODO unit tests
8
15
  */
9
- export function dataLoaderFactory(preloadedData) {
10
- /**
11
- * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
12
- * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
13
- *
14
- * @param storage object containing `splits` and `segments` cache (client-side variant)
15
- * @param userId user key string of the provided MySegmentsCache
16
- *
17
- * @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
18
- * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
19
- */
20
- return function loadData(storage, userId) {
21
- // Do not load data if current preloadedData is empty
22
- if (Object.keys(preloadedData).length === 0)
23
- return;
24
- var _a = preloadedData.lastUpdated, lastUpdated = _a === void 0 ? -1 : _a, _b = preloadedData.segmentsData, segmentsData = _b === void 0 ? {} : _b, _c = preloadedData.since, since = _c === void 0 ? -1 : _c, _d = preloadedData.splitsData, splitsData = _d === void 0 ? {} : _d;
16
+ export function loadData(preloadedData, storage, matchingKey) {
17
+ // Do not load data if current preloadedData is empty
18
+ if (Object.keys(preloadedData).length === 0)
19
+ return;
20
+ var _a = preloadedData.segmentsData, segmentsData = _a === void 0 ? {} : _a, _b = preloadedData.since, since = _b === void 0 ? -1 : _b, _c = preloadedData.splitsData, splitsData = _c === void 0 ? [] : _c;
21
+ if (storage.splits) {
25
22
  var storedSince = storage.splits.getChangeNumber();
26
- var expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
27
- // Do not load data if current localStorage data is more recent,
28
- // or if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
29
- if (storedSince > since || lastUpdated < expirationTimestamp)
23
+ // Do not load data if current data is more recent
24
+ if (storedSince > since)
30
25
  return;
31
26
  // cleaning up the localStorage data, since some cached splits might need be part of the preloaded data
32
27
  storage.splits.clear();
33
28
  storage.splits.setChangeNumber(since);
34
29
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
35
- storage.splits.addSplits(Object.keys(splitsData).map(function (splitName) { return JSON.parse(splitsData[splitName]); }));
36
- // add mySegments data
37
- var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
38
- if (!mySegmentsData) {
39
- // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
40
- mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
41
- var userIds = JSON.parse(segmentsData[segmentName]).added;
42
- return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
43
- });
30
+ storage.splits.addSplits(splitsData.map(function (split) { return ([split.name, split]); }));
31
+ }
32
+ if (matchingKey) { // add mySegments data (client-side)
33
+ var membershipsData = preloadedData.membershipsData && preloadedData.membershipsData[matchingKey];
34
+ if (!membershipsData && segmentsData) {
35
+ membershipsData = {
36
+ ms: {
37
+ k: Object.keys(segmentsData).filter(function (segmentName) {
38
+ var segmentKeys = segmentsData[segmentName];
39
+ return segmentKeys.indexOf(matchingKey) > -1;
40
+ }).map(function (segmentName) { return ({ n: segmentName }); })
41
+ }
42
+ };
44
43
  }
45
- storage.segments.resetSegments({ k: mySegmentsData.map(function (s) { return ({ n: s }); }) });
44
+ if (membershipsData) {
45
+ if (membershipsData.ms)
46
+ storage.segments.resetSegments(membershipsData.ms);
47
+ if (membershipsData.ls && storage.largeSegments)
48
+ storage.largeSegments.resetSegments(membershipsData.ls);
49
+ }
50
+ }
51
+ else { // add segments data (server-side)
52
+ Object.keys(segmentsData).forEach(function (segmentName) {
53
+ var segmentKeys = segmentsData[segmentName];
54
+ storage.segments.update(segmentName, segmentKeys, [], -1);
55
+ });
56
+ }
57
+ }
58
+ export function getSnapshot(storage, userKeys) {
59
+ return {
60
+ // lastUpdated: Date.now(),
61
+ since: storage.splits.getChangeNumber(),
62
+ splitsData: storage.splits.getAll(),
63
+ segmentsData: userKeys ?
64
+ undefined : // @ts-ignore accessing private prop
65
+ Object.keys(storage.segments.segmentCache).reduce(function (prev, cur) {
66
+ prev[cur] = setToArray(storage.segments.segmentCache[cur]);
67
+ return prev;
68
+ }, {}),
69
+ membershipsData: userKeys ?
70
+ userKeys.reduce(function (prev, userKey) {
71
+ if (storage.shared) {
72
+ // Client-side segments
73
+ // @ts-ignore accessing private prop
74
+ var sharedStorage = storage.shared(userKey);
75
+ prev[getMatching(userKey)] = {
76
+ ms: {
77
+ // @ts-ignore accessing private prop
78
+ k: Object.keys(sharedStorage.segments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
79
+ // cn: sharedStorage.segments.getChangeNumber()
80
+ },
81
+ ls: sharedStorage.largeSegments ? {
82
+ // @ts-ignore accessing private prop
83
+ k: Object.keys(sharedStorage.largeSegments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
84
+ // cn: sharedStorage.largeSegments.getChangeNumber()
85
+ } : undefined
86
+ };
87
+ }
88
+ else {
89
+ prev[getMatching(userKey)] = {
90
+ ms: {
91
+ // Server-side segments
92
+ // @ts-ignore accessing private prop
93
+ k: Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
94
+ return storage.segments.segmentCache[segmentName].has(userKey) ?
95
+ prev.concat({ n: segmentName }) :
96
+ prev;
97
+ }, [])
98
+ },
99
+ ls: {
100
+ k: []
101
+ }
102
+ };
103
+ }
104
+ return prev;
105
+ }, {}) :
106
+ undefined
46
107
  };
47
108
  }
@@ -6,13 +6,15 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
6
6
  import { DEBUG, LOCALHOST_MODE, NONE, 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 { loadData } from '../dataLoader';
9
11
  /**
10
12
  * InMemory storage factory for standalone client-side SplitFactory
11
13
  *
12
14
  * @param params parameters required by EventsCacheSync
13
15
  */
14
16
  export function InMemoryStorageCSFactory(params) {
15
- var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
17
+ var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation, preloadedData = _a.preloadedData, onReadyFromCacheCb = params.onReadyFromCacheCb;
16
18
  var splits = new SplitsCacheInMemory(__splitFiltersValidation);
17
19
  var segments = new MySegmentsCacheInMemory();
18
20
  var largeSegments = new MySegmentsCacheInMemory();
@@ -36,11 +38,16 @@ export function InMemoryStorageCSFactory(params) {
36
38
  this.uniqueKeys && this.uniqueKeys.clear();
37
39
  },
38
40
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
39
- shared: function () {
41
+ shared: function (matchingKey) {
42
+ var segments = new MySegmentsCacheInMemory();
43
+ var largeSegments = new MySegmentsCacheInMemory();
44
+ if (preloadedData) {
45
+ loadData(preloadedData, { segments: segments, largeSegments: largeSegments }, matchingKey);
46
+ }
40
47
  return {
41
48
  splits: this.splits,
42
- segments: new MySegmentsCacheInMemory(),
43
- largeSegments: new MySegmentsCacheInMemory(),
49
+ segments: segments,
50
+ largeSegments: largeSegments,
44
51
  impressions: this.impressions,
45
52
  impressionCounts: this.impressionCounts,
46
53
  events: this.events,
@@ -65,6 +72,11 @@ export function InMemoryStorageCSFactory(params) {
65
72
  if (storage.uniqueKeys)
66
73
  storage.uniqueKeys.track = noopTrack;
67
74
  }
75
+ if (preloadedData) {
76
+ loadData(preloadedData, storage, getMatching(params.settings.core.key));
77
+ if (splits.getChangeNumber() > -1)
78
+ onReadyFromCacheCb();
79
+ }
68
80
  return storage;
69
81
  }
70
82
  InMemoryStorageCSFactory.type = STORAGE_MEMORY;
@@ -23,7 +23,7 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
23
23
  segmentChangesFetcher(since, segmentName, noCache, till).then(function (changes) {
24
24
  return Promise.all(changes.map(function (x) {
25
25
  log.debug(LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
26
- return segments.update(x.name, x.added, x.removed, x.till);
26
+ return segments.update(segmentName, x.added, x.removed, x.till);
27
27
  })).then(function (updates) {
28
28
  return updates.some(function (update) { return update; });
29
29
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.0.0-rc.1",
3
+ "version": "2.0.0-rc.2",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -7,7 +7,7 @@ import { IBasicClient, SplitIO } from '../types';
7
7
  import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
8
8
  import { createLoggerAPI } from '../logger/sdkLogger';
9
9
  import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
10
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10
+ import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
11
11
  import { objectAssign } from '../utils/lang/objectAssign';
12
12
  import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
13
13
  import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
@@ -43,7 +43,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
43
43
 
44
44
  const storage = storageFactory({
45
45
  settings,
46
- onReadyCb: (error) => {
46
+ onReadyCb(error) {
47
47
  if (error) {
48
48
  // If storage fails to connect, SDK_READY_TIMED_OUT event is emitted immediately. Review when timeout and non-recoverable errors are reworked
49
49
  readiness.timeout();
@@ -52,8 +52,11 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
52
52
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
53
53
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
54
54
  },
55
+ onReadyFromCacheCb() {
56
+ readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
57
+ }
55
58
  });
56
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
59
+
57
60
  const clients: Record<string, IBasicClient> = {};
58
61
  const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
59
62
  const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
@@ -1,55 +1,114 @@
1
1
  import { SplitIO } from '../types';
2
- import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../utils/constants/browser';
3
- import { DataLoader, ISegmentsCacheSync, ISplitsCacheSync } from './types';
2
+ import { ISegmentsCacheSync, ISplitsCacheSync, IStorageSync } from './types';
3
+ import { setToArray } from '../utils/lang/sets';
4
+ import { getMatching } from '../utils/key';
5
+ import { IMembershipsResponse, IMySegmentsResponse } from '../dtos/types';
4
6
 
5
7
  /**
6
- * Factory of client-side storage loader
8
+ * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
9
+ * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
7
10
  *
8
- * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
9
- * and extended with a `mySegmentsData` property.
10
- * @returns function to preload the storage
11
+ * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader and extended with a `mySegmentsData` property.
12
+ * @param storage object containing `splits` and `segments` cache (client-side variant)
13
+ * @param userKey user key (matching key) of the provided MySegmentsCache
14
+ *
15
+ * @TODO extend to load largeSegments
16
+ * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
17
+ * @TODO add logs, and input validation in this module, in favor of size reduction.
18
+ * @TODO unit tests
11
19
  */
12
- export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader {
13
-
14
- /**
15
- * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
16
- * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
17
- *
18
- * @param storage object containing `splits` and `segments` cache (client-side variant)
19
- * @param userId user key string of the provided MySegmentsCache
20
- *
21
- * @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
22
- * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
23
- */
24
- return function loadData(storage: { splits: ISplitsCacheSync, segments: ISegmentsCacheSync }, userId: string) {
25
- // Do not load data if current preloadedData is empty
26
- if (Object.keys(preloadedData).length === 0) return;
27
-
28
- const { lastUpdated = -1, segmentsData = {}, since = -1, splitsData = {} } = preloadedData;
20
+ export function loadData(preloadedData: SplitIO.PreloadedData, storage: { splits?: ISplitsCacheSync, segments: ISegmentsCacheSync, largeSegments?: ISegmentsCacheSync }, matchingKey?: string) {
21
+ // Do not load data if current preloadedData is empty
22
+ if (Object.keys(preloadedData).length === 0) return;
23
+
24
+ const { segmentsData = {}, since = -1, splitsData = [] } = preloadedData;
29
25
 
26
+ if (storage.splits) {
30
27
  const storedSince = storage.splits.getChangeNumber();
31
- const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
32
28
 
33
- // Do not load data if current localStorage data is more recent,
34
- // or if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
35
- if (storedSince > since || lastUpdated < expirationTimestamp) return;
29
+ // Do not load data if current data is more recent
30
+ if (storedSince > since) return;
36
31
 
37
32
  // cleaning up the localStorage data, since some cached splits might need be part of the preloaded data
38
33
  storage.splits.clear();
39
34
  storage.splits.setChangeNumber(since);
40
35
 
41
36
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
42
- storage.splits.addSplits(Object.keys(splitsData).map(splitName => JSON.parse(splitsData[splitName])));
43
-
44
- // add mySegments data
45
- let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
46
- if (!mySegmentsData) {
47
- // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
48
- mySegmentsData = Object.keys(segmentsData).filter(segmentName => {
49
- const userIds = JSON.parse(segmentsData[segmentName]).added;
50
- return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
51
- });
37
+ storage.splits.addSplits(splitsData.map(split => ([split.name, split])));
38
+ }
39
+
40
+ if (matchingKey) { // add mySegments data (client-side)
41
+ let membershipsData = preloadedData.membershipsData && preloadedData.membershipsData[matchingKey];
42
+ if (!membershipsData && segmentsData) {
43
+ membershipsData = {
44
+ ms: {
45
+ k: Object.keys(segmentsData).filter(segmentName => {
46
+ const segmentKeys = segmentsData[segmentName];
47
+ return segmentKeys.indexOf(matchingKey) > -1;
48
+ }).map(segmentName => ({ n: segmentName }))
49
+ }
50
+ };
51
+ }
52
+ if (membershipsData) {
53
+ if (membershipsData.ms) storage.segments.resetSegments(membershipsData.ms);
54
+ if (membershipsData.ls && storage.largeSegments) storage.largeSegments.resetSegments(membershipsData.ls);
52
55
  }
53
- storage.segments.resetSegments({ k: mySegmentsData.map(s => ({ n: s })) });
56
+
57
+ } else { // add segments data (server-side)
58
+ Object.keys(segmentsData).forEach(segmentName => {
59
+ const segmentKeys = segmentsData[segmentName];
60
+ storage.segments.update(segmentName, segmentKeys, [], -1);
61
+ });
62
+ }
63
+ }
64
+
65
+ export function getSnapshot(storage: IStorageSync, userKeys?: SplitIO.SplitKey[]): SplitIO.PreloadedData {
66
+ return {
67
+ // lastUpdated: Date.now(),
68
+ since: storage.splits.getChangeNumber(),
69
+ splitsData: storage.splits.getAll(),
70
+ segmentsData: userKeys ?
71
+ undefined : // @ts-ignore accessing private prop
72
+ Object.keys(storage.segments.segmentCache).reduce((prev, cur) => { // @ts-ignore accessing private prop
73
+ prev[cur] = setToArray(storage.segments.segmentCache[cur] as Set<string>);
74
+ return prev;
75
+ }, {}),
76
+ membershipsData: userKeys ?
77
+ userKeys.reduce<Record<string, IMembershipsResponse>>((prev, userKey) => {
78
+ if (storage.shared) {
79
+ // Client-side segments
80
+ // @ts-ignore accessing private prop
81
+ const sharedStorage = storage.shared(userKey);
82
+ prev[getMatching(userKey)] = {
83
+ ms: {
84
+ // @ts-ignore accessing private prop
85
+ k: Object.keys(sharedStorage.segments.segmentCache).map(segmentName => ({ n: segmentName })),
86
+ // cn: sharedStorage.segments.getChangeNumber()
87
+ },
88
+ ls: sharedStorage.largeSegments ? {
89
+ // @ts-ignore accessing private prop
90
+ k: Object.keys(sharedStorage.largeSegments.segmentCache).map(segmentName => ({ n: segmentName })),
91
+ // cn: sharedStorage.largeSegments.getChangeNumber()
92
+ } : undefined
93
+ };
94
+ } else {
95
+ prev[getMatching(userKey)] = {
96
+ ms: {
97
+ // Server-side segments
98
+ // @ts-ignore accessing private prop
99
+ k: Object.keys(storage.segments.segmentCache).reduce<IMySegmentsResponse['k']>((prev, segmentName) => { // @ts-ignore accessing private prop
100
+ return storage.segments.segmentCache[segmentName].has(userKey) ?
101
+ prev!.concat({ n: segmentName }) :
102
+ prev;
103
+ }, [])
104
+ },
105
+ ls: {
106
+ k: []
107
+ }
108
+ };
109
+ }
110
+ return prev;
111
+ }, {}) :
112
+ undefined
54
113
  };
55
114
  }
@@ -7,6 +7,8 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
10
+ import { getMatching } from '../../utils/key';
11
+ import { loadData } from '../dataLoader';
10
12
 
11
13
  /**
12
14
  * InMemory storage factory for standalone client-side SplitFactory
@@ -14,7 +16,7 @@ import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
14
16
  * @param params parameters required by EventsCacheSync
15
17
  */
16
18
  export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
17
- const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
19
+ const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation }, preloadedData }, onReadyFromCacheCb } = params;
18
20
 
19
21
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
20
22
  const segments = new MySegmentsCacheInMemory();
@@ -42,11 +44,18 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
42
44
  },
43
45
 
44
46
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
45
- shared() {
47
+ shared(matchingKey: string) {
48
+ const segments = new MySegmentsCacheInMemory();
49
+ const largeSegments = new MySegmentsCacheInMemory();
50
+
51
+ if (preloadedData) {
52
+ loadData(preloadedData, { segments, largeSegments }, matchingKey);
53
+ }
54
+
46
55
  return {
47
56
  splits: this.splits,
48
- segments: new MySegmentsCacheInMemory(),
49
- largeSegments: new MySegmentsCacheInMemory(),
57
+ segments,
58
+ largeSegments,
50
59
  impressions: this.impressions,
51
60
  impressionCounts: this.impressionCounts,
52
61
  events: this.events,
@@ -72,6 +81,12 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
72
81
  if (storage.uniqueKeys) storage.uniqueKeys.track = noopTrack;
73
82
  }
74
83
 
84
+
85
+ if (preloadedData) {
86
+ loadData(preloadedData, storage, getMatching(params.settings.core.key));
87
+ if (splits.getChangeNumber() > -1) onReadyFromCacheCb();
88
+ }
89
+
75
90
  return storage;
76
91
  }
77
92
 
@@ -488,8 +488,6 @@ export interface IStorageAsync extends IStorageBase<
488
488
 
489
489
  /** StorageFactory */
490
490
 
491
- export type DataLoader = (storage: IStorageSync, matchingKey: string) => void
492
-
493
491
  export interface IStorageFactoryParams {
494
492
  settings: ISettings,
495
493
  /**
@@ -497,6 +495,10 @@ export interface IStorageFactoryParams {
497
495
  * It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
498
496
  */
499
497
  onReadyCb: (error?: any) => void,
498
+ /**
499
+ * It is meant for emitting SDK_READY_FROM_CACHE event in standalone mode with preloaded data
500
+ */
501
+ onReadyFromCacheCb: () => void,
500
502
  }
501
503
 
502
504
  export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';
@@ -38,7 +38,7 @@ export function segmentChangesUpdaterFactory(
38
38
  segmentChangesFetcher(since, segmentName, noCache, till).then((changes) => {
39
39
  return Promise.all(changes.map(x => {
40
40
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
41
- return segments.update(x.name, x.added, x.removed, x.till);
41
+ return segments.update(segmentName, x.added, x.removed, x.till);
42
42
  })).then((updates) => {
43
43
  return updates.some(update => update);
44
44
  });
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ISplitFiltersValidation } from './dtos/types';
1
+ import { IMembershipsResponse, ISplit, ISplitFiltersValidation } from './dtos/types';
2
2
  import { IIntegration, IIntegrationFactoryParams } from './integrations/types';
3
3
  import { ILogger } from './logger/types';
4
4
  import { ISdkFactoryContext } from './sdkFactory/types';
@@ -97,6 +97,7 @@ export interface ISettings {
97
97
  eventsFirstPushWindow: number
98
98
  },
99
99
  readonly storage: IStorageSyncFactory | IStorageAsyncFactory,
100
+ readonly preloadedData?: SplitIO.PreloadedData,
100
101
  readonly integrations: Array<{
101
102
  readonly type: string,
102
103
  (params: IIntegrationFactoryParams): IIntegration | void
@@ -770,31 +771,31 @@ export namespace SplitIO {
770
771
  * If this value is older than 10 days ago (expiration time policy), the data is not used to update the storage content.
771
772
  * @TODO configurable expiration time policy?
772
773
  */
773
- lastUpdated: number,
774
+ // lastUpdated: number,
774
775
  /**
775
776
  * Change number of the preloaded data.
776
777
  * If this value is older than the current changeNumber at the storage, the data is not used to update the storage content.
777
778
  */
778
779
  since: number,
779
780
  /**
780
- * Map of feature flags to their stringified definitions.
781
+ * List of feature flag definitions.
782
+ * @TODO rename to flags
781
783
  */
782
- splitsData: {
783
- [splitName: string]: string
784
- },
784
+ splitsData: ISplit[],
785
785
  /**
786
- * Optional map of user keys to their list of segments.
787
- * @TODO remove when releasing first version
786
+ * Optional map of user keys to their memberships.
787
+ * @TODO rename to memberships
788
788
  */
789
- mySegmentsData?: {
790
- [key: string]: string[]
789
+ membershipsData?: {
790
+ [key: string]: IMembershipsResponse
791
791
  },
792
792
  /**
793
- * Optional map of segments to their stringified definitions.
794
- * This property is ignored if `mySegmentsData` was provided.
793
+ * Optional map of segments to their list of keys.
794
+ * This property is ignored if `membershipsData` was provided.
795
+ * @TODO rename to segments
795
796
  */
796
797
  segmentsData?: {
797
- [segmentName: string]: string
798
+ [segmentName: string]: string[]
798
799
  },
799
800
  }
800
801
  /**
@@ -1,10 +1,21 @@
1
1
  import { SplitIO } from '../types';
2
- import { DataLoader } from './types';
2
+ import { ISegmentsCacheSync, ISplitsCacheSync, IStorageSync } from './types';
3
3
  /**
4
- * Factory of client-side storage loader
4
+ * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
5
+ * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
5
6
  *
6
- * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
7
- * and extended with a `mySegmentsData` property.
8
- * @returns function to preload the storage
7
+ * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader and extended with a `mySegmentsData` property.
8
+ * @param storage object containing `splits` and `segments` cache (client-side variant)
9
+ * @param userKey user key (matching key) of the provided MySegmentsCache
10
+ *
11
+ * @TODO extend to load largeSegments
12
+ * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
13
+ * @TODO add logs, and input validation in this module, in favor of size reduction.
14
+ * @TODO unit tests
9
15
  */
10
- export declare function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader;
16
+ export declare function loadData(preloadedData: SplitIO.PreloadedData, storage: {
17
+ splits?: ISplitsCacheSync;
18
+ segments: ISegmentsCacheSync;
19
+ largeSegments?: ISegmentsCacheSync;
20
+ }, matchingKey?: string): void;
21
+ export declare function getSnapshot(storage: IStorageSync, userKeys?: SplitIO.SplitKey[]): SplitIO.PreloadedData;
@@ -387,7 +387,6 @@ export interface IStorageSync extends IStorageBase<ISplitsCacheSync, ISegmentsCa
387
387
  export interface IStorageAsync extends IStorageBase<ISplitsCacheAsync, ISegmentsCacheAsync, IImpressionsCacheAsync | IImpressionsCacheSync, IImpressionCountsCacheBase, IEventsCacheAsync | IEventsCacheSync, ITelemetryCacheAsync | ITelemetryCacheSync, IUniqueKeysCacheBase> {
388
388
  }
389
389
  /** StorageFactory */
390
- export declare type DataLoader = (storage: IStorageSync, matchingKey: string) => void;
391
390
  export interface IStorageFactoryParams {
392
391
  settings: ISettings;
393
392
  /**
@@ -395,6 +394,10 @@ export interface IStorageFactoryParams {
395
394
  * It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
396
395
  */
397
396
  onReadyCb: (error?: any) => void;
397
+ /**
398
+ * It is meant for emitting SDK_READY_FROM_CACHE event in standalone mode with preloaded data
399
+ */
400
+ onReadyFromCacheCb: () => void;
398
401
  }
399
402
  export declare type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';
400
403
  export declare type IStorageSyncFactory = {
package/types/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ISplitFiltersValidation } from './dtos/types';
1
+ import { IMembershipsResponse, ISplit, ISplitFiltersValidation } from './dtos/types';
2
2
  import { IIntegration, IIntegrationFactoryParams } from './integrations/types';
3
3
  import { ILogger } from './logger/types';
4
4
  import { ISdkFactoryContext } from './sdkFactory/types';
@@ -91,6 +91,7 @@ export interface ISettings {
91
91
  eventsFirstPushWindow: number;
92
92
  };
93
93
  readonly storage: IStorageSyncFactory | IStorageAsyncFactory;
94
+ readonly preloadedData?: SplitIO.PreloadedData;
94
95
  readonly integrations: Array<{
95
96
  readonly type: string;
96
97
  (params: IIntegrationFactoryParams): IIntegration | void;
@@ -769,31 +770,30 @@ export declare namespace SplitIO {
769
770
  * If this value is older than 10 days ago (expiration time policy), the data is not used to update the storage content.
770
771
  * @TODO configurable expiration time policy?
771
772
  */
772
- lastUpdated: number;
773
773
  /**
774
774
  * Change number of the preloaded data.
775
775
  * If this value is older than the current changeNumber at the storage, the data is not used to update the storage content.
776
776
  */
777
777
  since: number;
778
778
  /**
779
- * Map of feature flags to their stringified definitions.
779
+ * List of feature flag definitions.
780
+ * @TODO rename to flags
780
781
  */
781
- splitsData: {
782
- [splitName: string]: string;
783
- };
782
+ splitsData: ISplit[];
784
783
  /**
785
- * Optional map of user keys to their list of segments.
786
- * @TODO remove when releasing first version
784
+ * Optional map of user keys to their memberships.
785
+ * @TODO rename to memberships
787
786
  */
788
- mySegmentsData?: {
789
- [key: string]: string[];
787
+ membershipsData?: {
788
+ [key: string]: IMembershipsResponse;
790
789
  };
791
790
  /**
792
- * Optional map of segments to their stringified definitions.
793
- * This property is ignored if `mySegmentsData` was provided.
791
+ * Optional map of segments to their list of keys.
792
+ * This property is ignored if `membershipsData` was provided.
793
+ * @TODO rename to segments
794
794
  */
795
795
  segmentsData?: {
796
- [segmentName: string]: string;
796
+ [segmentName: string]: string[];
797
797
  };
798
798
  }
799
799
  /**