@splitsoftware/splitio-commons 1.17.1-rc.0 → 1.17.1-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.
Files changed (61) hide show
  1. package/CHANGES.txt +3 -0
  2. package/cjs/sdkClient/identity.js +7 -0
  3. package/cjs/sdkClient/sdkClient.js +5 -5
  4. package/cjs/sdkClient/sdkClientMethod.js +3 -1
  5. package/cjs/sdkClient/sdkClientMethodCS.js +8 -13
  6. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +8 -13
  7. package/cjs/sdkFactory/index.js +8 -2
  8. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
  9. package/cjs/storages/AbstractSplitsCacheSync.js +0 -7
  10. package/cjs/storages/dataLoader.js +65 -32
  11. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -9
  12. package/cjs/storages/inLocalStorage/index.js +4 -1
  13. package/cjs/storages/inMemory/InMemoryStorageCS.js +16 -4
  14. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -7
  15. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -10
  16. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -12
  17. package/esm/sdkClient/identity.js +3 -0
  18. package/esm/sdkClient/sdkClient.js +5 -5
  19. package/esm/sdkClient/sdkClientMethod.js +3 -1
  20. package/esm/sdkClient/sdkClientMethodCS.js +6 -11
  21. package/esm/sdkClient/sdkClientMethodCSWithTT.js +6 -11
  22. package/esm/sdkFactory/index.js +9 -3
  23. package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
  24. package/esm/storages/AbstractSplitsCacheSync.js +0 -7
  25. package/esm/storages/dataLoader.js +62 -30
  26. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -9
  27. package/esm/storages/inLocalStorage/index.js +5 -2
  28. package/esm/storages/inMemory/InMemoryStorageCS.js +16 -4
  29. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -8
  30. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -11
  31. package/esm/utils/settingsValidation/storage/storageCS.js +0 -10
  32. package/package.json +1 -1
  33. package/src/sdkClient/identity.ts +5 -0
  34. package/src/sdkClient/sdkClient.ts +5 -5
  35. package/src/sdkClient/sdkClientMethod.ts +4 -1
  36. package/src/sdkClient/sdkClientMethodCS.ts +6 -12
  37. package/src/sdkClient/sdkClientMethodCSWithTT.ts +6 -12
  38. package/src/sdkFactory/index.ts +11 -4
  39. package/src/sdkFactory/types.ts +2 -1
  40. package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
  41. package/src/storages/AbstractSplitsCacheSync.ts +0 -8
  42. package/src/storages/dataLoader.ts +63 -32
  43. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -10
  44. package/src/storages/inLocalStorage/index.ts +6 -2
  45. package/src/storages/inMemory/InMemoryStorageCS.ts +19 -4
  46. package/src/storages/types.ts +1 -6
  47. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +3 -7
  48. package/src/sync/polling/updaters/splitChangesUpdater.ts +3 -11
  49. package/src/types.ts +16 -9
  50. package/src/utils/settingsValidation/storage/storageCS.ts +0 -13
  51. package/types/sdkClient/identity.d.ts +0 -4
  52. package/types/sdkClient/sdkClientMethod.d.ts +1 -1
  53. package/types/sdkFactory/types.d.ts +2 -1
  54. package/types/storages/AbstractMySegmentsCacheSync.d.ts +39 -0
  55. package/types/storages/AbstractSplitsCacheAsync.d.ts +0 -5
  56. package/types/storages/AbstractSplitsCacheSync.d.ts +0 -5
  57. package/types/storages/dataLoader.d.ts +17 -6
  58. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +0 -6
  59. package/types/storages/types.d.ts +1 -4
  60. package/types/types.d.ts +15 -9
  61. package/types/utils/settingsValidation/storage/storageCS.d.ts +0 -5
@@ -6,23 +6,19 @@ import { sdkClientFactory } from './sdkClient';
6
6
  import { objectAssign } from '../utils/lang/objectAssign';
7
7
  import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
8
8
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
9
- function buildInstanceId(key, trafficType) {
10
- // @ts-ignore
11
- return (key.matchingKey ? key.matchingKey : key) + "-" + (key.bucketingKey ? key.bucketingKey : key) + "-" + (trafficType !== undefined ? trafficType : '');
12
- }
9
+ import { buildInstanceId } from './identity';
13
10
  /**
14
11
  * Factory of client method for the client-side (browser) variant of the Isomorphic JS SDK,
15
12
  * where clients can have a bound TT for the track method, which is provided via the settings
16
13
  * (default client) or the client method (shared clients).
17
14
  */
18
15
  export function sdkClientMethodCSFactory(params) {
19
- var storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, _b = _a.core, key = _b.key, trafficType = _b.trafficType, log = _a.log;
16
+ var clients = params.clients, storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, _b = _a.core, key = _b.key, trafficType = _b.trafficType, log = _a.log;
20
17
  var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), key, trafficType);
21
18
  var parsedDefaultKey = keyParser(key);
22
19
  var defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
23
20
  // Cache instances created per factory.
24
- var clientInstances = {};
25
- clientInstances[defaultInstanceId] = mainClientInstance;
21
+ clients[defaultInstanceId] = mainClientInstance;
26
22
  return function client(key, trafficType) {
27
23
  if (key === undefined) {
28
24
  log.debug(RETRIEVE_CLIENT_DEFAULT);
@@ -41,7 +37,7 @@ export function sdkClientMethodCSFactory(params) {
41
37
  }
42
38
  }
43
39
  var instanceId = buildInstanceId(validKey, validTrafficType);
44
- if (!clientInstances[instanceId]) {
40
+ if (!clients[instanceId]) {
45
41
  var matchingKey = getMatching(validKey);
46
42
  var sharedSdkReadiness_1 = sdkReadinessManager.shared();
47
43
  var sharedStorage = storage.shared && storage.shared(matchingKey, function (err) {
@@ -60,11 +56,10 @@ export function sdkClientMethodCSFactory(params) {
60
56
  var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
61
57
  // As shared clients reuse all the storage information, we don't need to check here if we
62
58
  // will use offline or online mode. We should stick with the original decision.
63
- clientInstances[instanceId] = clientCSDecorator(log, sdkClientFactory(objectAssign({}, params, {
59
+ clients[instanceId] = clientCSDecorator(log, sdkClientFactory(objectAssign({}, params, {
64
60
  sdkReadinessManager: sharedSdkReadiness_1,
65
61
  storage: sharedStorage || storage,
66
62
  syncManager: sharedSyncManager,
67
- signalListener: undefined, // only the main client "destroy" method stops the signal listener
68
63
  }), true), validKey, validTrafficType);
69
64
  sharedSyncManager && sharedSyncManager.start();
70
65
  log.info(NEW_SHARED_CLIENT);
@@ -72,6 +67,6 @@ export function sdkClientMethodCSFactory(params) {
72
67
  else {
73
68
  log.debug(RETRIEVE_CLIENT_EXISTING);
74
69
  }
75
- return clientInstances[instanceId];
70
+ return clients[instanceId];
76
71
  };
77
72
  }
@@ -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';
@@ -35,8 +35,11 @@ export function sdkFactory(params) {
35
35
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
36
36
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
37
37
  },
38
+ onReadyFromCacheCb: function () {
39
+ readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
40
+ }
38
41
  });
39
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
42
+ var clients = {};
40
43
  var telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
41
44
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
42
45
  var observer = impressionsObserverFactory();
@@ -56,7 +59,7 @@ export function sdkFactory(params) {
56
59
  var eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
57
60
  // splitApi is used by SyncManager and Browser signal listener
58
61
  var splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
59
- var ctx = { splitApi: splitApi, eventTracker: eventTracker, impressionsTracker: impressionsTracker, telemetryTracker: telemetryTracker, uniqueKeysTracker: uniqueKeysTracker, sdkReadinessManager: sdkReadinessManager, readiness: readiness, settings: settings, storage: storage, platform: platform };
62
+ var ctx = { clients: clients, splitApi: splitApi, eventTracker: eventTracker, impressionsTracker: impressionsTracker, telemetryTracker: telemetryTracker, uniqueKeysTracker: uniqueKeysTracker, sdkReadinessManager: sdkReadinessManager, readiness: readiness, settings: settings, storage: storage, platform: platform };
60
63
  var syncManager = syncManagerFactory && syncManagerFactory(ctx);
61
64
  ctx.syncManager = syncManager;
62
65
  var signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
@@ -79,5 +82,8 @@ export function sdkFactory(params) {
79
82
  // Logger wrapper API
80
83
  Logger: createLoggerAPI(log),
81
84
  settings: settings,
85
+ destroy: function () {
86
+ return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
87
+ }
82
88
  }, extraProps && extraProps(ctx));
83
89
  }
@@ -11,13 +11,6 @@ var AbstractSplitsCacheAsync = /** @class */ (function () {
11
11
  AbstractSplitsCacheAsync.prototype.usesSegments = function () {
12
12
  return Promise.resolve(true);
13
13
  };
14
- /**
15
- * Check if the splits information is already stored in cache.
16
- * Noop, just keeping the interface. This is used by client-side implementations only.
17
- */
18
- AbstractSplitsCacheAsync.prototype.checkCache = function () {
19
- return Promise.resolve(false);
20
- };
21
14
  /**
22
15
  * Kill `name` split and set `defaultTreatment` and `changeNumber`.
23
16
  * Used for SPLIT_KILL push notifications.
@@ -27,13 +27,6 @@ var AbstractSplitsCacheSync = /** @class */ (function () {
27
27
  var _this = this;
28
28
  return this.getSplitNames().map(function (key) { return _this.getSplit(key); });
29
29
  };
30
- /**
31
- * Check if the splits information is already stored in cache. This data can be preloaded.
32
- * It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
33
- */
34
- AbstractSplitsCacheSync.prototype.checkCache = function () {
35
- return false;
36
- };
37
30
  /**
38
31
  * Kill `name` split and set `defaultTreatment` and `changeNumber`.
39
32
  * Used for SPLIT_KILL push notifications.
@@ -1,47 +1,79 @@
1
- import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../utils/constants/browser';
1
+ import { setToArray } from '../utils/lang/sets';
2
2
  /**
3
- * Factory of client-side storage loader
3
+ * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
4
+ * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
4
5
  *
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
6
+ * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader and extended with a `mySegmentsData` property.
7
+ * @param storage object containing `splits` and `segments` cache (client-side variant)
8
+ * @param userKey user key (matching key) of the provided MySegmentsCache
9
+ *
10
+ * @TODO extend to load largeSegments
11
+ * @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.
12
+ * @TODO add logs, and input validation in this module, in favor of size reduction.
13
+ * @TODO unit tests
8
14
  */
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;
15
+ export function loadData(preloadedData, storage, userKey) {
16
+ // Do not load data if current preloadedData is empty
17
+ if (Object.keys(preloadedData).length === 0)
18
+ return;
19
+ 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;
20
+ if (storage.splits) {
25
21
  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)
22
+ // Do not load data if current data is more recent
23
+ if (storedSince > since)
30
24
  return;
31
25
  // cleaning up the localStorage data, since some cached splits might need be part of the preloaded data
32
26
  storage.splits.clear();
33
27
  storage.splits.setChangeNumber(since);
34
28
  // 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];
29
+ storage.splits.addSplits(splitsData.map(function (split) { return ([split.name, split]); }));
30
+ }
31
+ if (userKey) { // add mySegments data (client-side)
32
+ var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userKey];
38
33
  if (!mySegmentsData) {
39
34
  // 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
35
  mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
41
- var userIds = JSON.parse(segmentsData[segmentName]).added;
42
- return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
36
+ var userKeys = segmentsData[segmentName];
37
+ return userKeys.indexOf(userKey) > -1;
43
38
  });
44
39
  }
45
40
  storage.segments.resetSegments({ k: mySegmentsData.map(function (s) { return ({ n: s }); }) });
41
+ }
42
+ else { // add segments data (server-side)
43
+ Object.keys(segmentsData).filter(function (segmentName) {
44
+ var userKeys = segmentsData[segmentName];
45
+ storage.segments.addToSegment(segmentName, userKeys);
46
+ });
47
+ }
48
+ }
49
+ export function getSnapshot(storage, userKeys) {
50
+ return {
51
+ // lastUpdated: Date.now(),
52
+ // @ts-ignore accessing private prop
53
+ since: storage.splits.changeNumber,
54
+ splitsData: storage.splits.getAll(),
55
+ segmentsData: userKeys ?
56
+ undefined : // @ts-ignore accessing private prop
57
+ Object.keys(storage.segments.segmentCache).reduce(function (prev, cur) {
58
+ prev[cur] = setToArray(storage.segments.segmentCache[cur]);
59
+ return prev;
60
+ }, {}),
61
+ mySegmentsData: userKeys ?
62
+ userKeys.reduce(function (prev, userKey) {
63
+ // @ts-ignore accessing private prop
64
+ prev[userKey] = storage.shared ?
65
+ // Client-side segments
66
+ // @ts-ignore accessing private prop
67
+ Object.keys(storage.shared(userKey).segments.segmentCache) :
68
+ // Server-side segments
69
+ // @ts-ignore accessing private prop
70
+ Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
71
+ return storage.segments.segmentCache[segmentName].has(userKey) ?
72
+ prev.concat(segmentName) :
73
+ prev;
74
+ }, []);
75
+ return prev;
76
+ }, {}) :
77
+ undefined
46
78
  };
47
79
  }
@@ -183,14 +183,6 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
183
183
  return true;
184
184
  }
185
185
  };
186
- /**
187
- * Check if the splits information is already stored in browser LocalStorage.
188
- * In this function we could add more code to check if the data is valid.
189
- * @override
190
- */
191
- SplitsCacheInLocal.prototype.checkCache = function () {
192
- return this.getChangeNumber() > -1;
193
- };
194
186
  /**
195
187
  * Clean Splits cache if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
196
188
  *
@@ -213,7 +205,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
213
205
  // mark cache to update the new query filter on first successful splits fetch
214
206
  this.updateNewFilter = true;
215
207
  // if there is cache, clear it
216
- if (this.checkCache())
208
+ if (this.getChangeNumber() > -1)
217
209
  this.clear();
218
210
  }
219
211
  catch (e) {
@@ -11,7 +11,7 @@ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
11
11
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
12
12
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
13
13
  import { LOG_PREFIX } from './constants';
14
- import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
14
+ import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
15
15
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
16
16
  import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
17
17
  import { getMatching } from '../../utils/key';
@@ -27,13 +27,16 @@ export function InLocalStorage(options) {
27
27
  params.settings.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
28
28
  return InMemoryStorageCSFactory(params);
29
29
  }
30
- var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
30
+ var onReadyFromCacheCb = params.onReadyFromCacheCb, settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
31
31
  var matchingKey = getMatching(settings.core.key);
32
32
  var keys = new KeyBuilderCS(prefix, matchingKey);
33
33
  var expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
34
34
  var splits = new SplitsCacheInLocal(settings, keys, expirationTimestamp);
35
35
  var segments = new MySegmentsCacheInLocal(log, keys);
36
36
  var largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));
37
+ if (settings.mode === LOCALHOST_MODE || splits.getChangeNumber() > -1) {
38
+ Promise.resolve().then(onReadyFromCacheCb);
39
+ }
37
40
  return {
38
41
  splits: splits,
39
42
  segments: segments,
@@ -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 instanciation 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;
@@ -1,7 +1,7 @@
1
1
  import { forOwn } from '../../../utils/lang';
2
2
  import { syncTaskFactory } from '../../syncTask';
3
3
  import { CONTROL } from '../../../utils/constants';
4
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
4
+ import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
5
5
  import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/constants';
6
6
  /**
7
7
  * Offline equivalent of `splitChangesUpdaterFactory`
@@ -43,13 +43,8 @@ export function fromObjectUpdaterFactory(splitsParser, storage, readiness, setti
43
43
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
44
44
  if (startingUp) {
45
45
  startingUp = false;
46
- Promise.resolve(splitsCache.checkCache()).then(function (cacheReady) {
47
- // Emits SDK_READY_FROM_CACHE
48
- if (cacheReady)
49
- readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
50
- // Emits SDK_READY
51
- readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
52
- });
46
+ // Emits SDK_READY
47
+ readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
53
48
  }
54
49
  return true;
55
50
  });
@@ -1,6 +1,6 @@
1
1
  import { _Set, setToArray } from '../../../utils/lang/sets';
2
2
  import { timeout } from '../../../utils/promise/timeout';
3
- import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
3
+ import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
4
4
  import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
5
5
  import { startsWith } from '../../../utils/lang';
6
6
  import { IN_SEGMENT } from '../../../utils/constants';
@@ -121,7 +121,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, seg
121
121
  function _splitChangesUpdater(since, retry) {
122
122
  if (retry === void 0) { retry = 0; }
123
123
  log.debug(SYNC_SPLITS_FETCH, [since]);
124
- var fetcherPromise = Promise.resolve(splitUpdateNotification ?
124
+ return Promise.resolve(splitUpdateNotification ?
125
125
  { splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
126
126
  splitChangesFetcher(since, noCache, till, _promiseDecorator))
127
127
  .then(function (splitChanges) {
@@ -165,15 +165,6 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, seg
165
165
  }
166
166
  return false;
167
167
  });
168
- // After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
169
- // Wrapping in a promise since checkCache can be async.
170
- if (splitsEventEmitter && startingUp) {
171
- Promise.resolve(splits.checkCache()).then(function (isCacheReady) {
172
- if (isCacheReady)
173
- splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
174
- });
175
- }
176
- return fetcherPromise;
177
168
  }
178
169
  var sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
179
170
  return sincePromise.then(_splitChangesUpdater);
@@ -1,12 +1,6 @@
1
1
  import { InMemoryStorageCSFactory } from '../../../storages/inMemory/InMemoryStorageCS';
2
2
  import { ERROR_STORAGE_INVALID } from '../../../logger/constants';
3
3
  import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants';
4
- export function __InLocalStorageMockFactory(params) {
5
- var result = InMemoryStorageCSFactory(params);
6
- result.splits.checkCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
7
- return result;
8
- }
9
- __InLocalStorageMockFactory.type = STORAGE_MEMORY;
10
4
  /**
11
5
  * This function validates `settings.storage` object
12
6
  *
@@ -23,10 +17,6 @@ export function validateStorageCS(settings) {
23
17
  storage = InMemoryStorageCSFactory;
24
18
  log.error(ERROR_STORAGE_INVALID);
25
19
  }
26
- // In localhost mode with InLocalStorage, fallback to a mock InLocalStorage to emit SDK_READY_FROM_CACHE
27
- if (mode === LOCALHOST_MODE && storage.type === STORAGE_LOCALSTORAGE) {
28
- return __InLocalStorageMockFactory;
29
- }
30
20
  if ([LOCALHOST_MODE, STANDALONE_MODE].indexOf(mode) === -1) {
31
21
  // Consumer modes require an async storage
32
22
  if (storage.type !== STORAGE_PLUGGABLE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.17.1-rc.0",
3
+ "version": "1.17.1-rc.2",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -0,0 +1,5 @@
1
+ import { SplitIO } from '../types';
2
+
3
+ export function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) { // @ts-ignore
4
+ return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType ? trafficType : ''}`;
5
+ }
@@ -66,11 +66,11 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
66
66
  syncManager && syncManager.stop();
67
67
 
68
68
  return __flush().then(() => {
69
- // Cleanup event listeners
70
- signalListener && signalListener.stop();
71
-
72
- // @TODO stop only if last client is destroyed
73
- if (uniqueKeysTracker) uniqueKeysTracker.stop();
69
+ // For main client, cleanup event listeners and scheduled jobs
70
+ if (!isSharedClient) {
71
+ signalListener && signalListener.stop();
72
+ uniqueKeysTracker && uniqueKeysTracker.stop();
73
+ }
74
74
 
75
75
  // Cleanup storage
76
76
  return storage.destroy();
@@ -4,12 +4,15 @@ import { RETRIEVE_CLIENT_DEFAULT } from '../logger/constants';
4
4
  import { ISdkFactoryContext } from '../sdkFactory/types';
5
5
 
6
6
  /**
7
- * Factory of client method for server-side SDKs (ISDK and IAsyncSDK)
7
+ * Factory of client method for server-side SDKs
8
8
  */
9
9
  export function sdkClientMethodFactory(params: ISdkFactoryContext): () => SplitIO.IClient | SplitIO.IAsyncClient {
10
10
  const log = params.settings.log;
11
11
  const clientInstance = sdkClientFactory(params);
12
12
 
13
+ // Only one client in server-side without bound key
14
+ params.clients[''] = clientInstance;
15
+
13
16
  return function client() {
14
17
  if (arguments.length > 0) {
15
18
  throw new Error('Shared Client not supported by the storage mechanism. Create isolated instances instead.');
@@ -8,18 +8,14 @@ import { objectAssign } from '../utils/lang/objectAssign';
8
8
  import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
9
9
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10
10
  import { ISdkFactoryContext } from '../sdkFactory/types';
11
-
12
- function buildInstanceId(key: SplitIO.SplitKey) {
13
- // @ts-ignore
14
- return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-`;
15
- }
11
+ import { buildInstanceId } from './identity';
16
12
 
17
13
  /**
18
14
  * Factory of client method for the client-side API variant where TT is ignored.
19
15
  * Therefore, clients don't have a bound TT for the track method.
20
16
  */
21
17
  export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey) => SplitIO.ICsClient {
22
- const { storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
18
+ const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
23
19
 
24
20
  const mainClientInstance = clientCSDecorator(
25
21
  log,
@@ -31,8 +27,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
31
27
  const defaultInstanceId = buildInstanceId(parsedDefaultKey);
32
28
 
33
29
  // Cache instances created per factory.
34
- const clientInstances: Record<string, SplitIO.ICsClient> = {};
35
- clientInstances[defaultInstanceId] = mainClientInstance;
30
+ clients[defaultInstanceId] = mainClientInstance;
36
31
 
37
32
  return function client(key?: SplitIO.SplitKey) {
38
33
  if (key === undefined) {
@@ -48,7 +43,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
48
43
 
49
44
  const instanceId = buildInstanceId(validKey);
50
45
 
51
- if (!clientInstances[instanceId]) {
46
+ if (!clients[instanceId]) {
52
47
  const matchingKey = getMatching(validKey);
53
48
 
54
49
  const sharedSdkReadiness = sdkReadinessManager.shared();
@@ -70,13 +65,12 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
70
65
 
71
66
  // As shared clients reuse all the storage information, we don't need to check here if we
72
67
  // will use offline or online mode. We should stick with the original decision.
73
- clientInstances[instanceId] = clientCSDecorator(
68
+ clients[instanceId] = clientCSDecorator(
74
69
  log,
75
70
  sdkClientFactory(objectAssign({}, params, {
76
71
  sdkReadinessManager: sharedSdkReadiness,
77
72
  storage: sharedStorage || storage,
78
73
  syncManager: sharedSyncManager,
79
- signalListener: undefined, // only the main client "destroy" method stops the signal listener
80
74
  }), true) as SplitIO.IClient,
81
75
  validKey
82
76
  );
@@ -88,6 +82,6 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
88
82
  log.debug(RETRIEVE_CLIENT_EXISTING);
89
83
  }
90
84
 
91
- return clientInstances[instanceId];
85
+ return clients[instanceId] as SplitIO.ICsClient;
92
86
  };
93
87
  }
@@ -9,11 +9,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
9
9
  import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
10
10
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
11
11
  import { ISdkFactoryContext } from '../sdkFactory/types';
12
-
13
- function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) {
14
- // @ts-ignore
15
- return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType !== undefined ? trafficType : ''}`;
16
- }
12
+ import { buildInstanceId } from './identity';
17
13
 
18
14
  /**
19
15
  * Factory of client method for the client-side (browser) variant of the Isomorphic JS SDK,
@@ -21,7 +17,7 @@ function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) {
21
17
  * (default client) or the client method (shared clients).
22
18
  */
23
19
  export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey, trafficType?: string) => SplitIO.ICsClient {
24
- const { storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, log } } = params;
20
+ const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, log } } = params;
25
21
 
26
22
  const mainClientInstance = clientCSDecorator(
27
23
  log,
@@ -34,8 +30,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
34
30
  const defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
35
31
 
36
32
  // Cache instances created per factory.
37
- const clientInstances: Record<string, SplitIO.ICsClient> = {};
38
- clientInstances[defaultInstanceId] = mainClientInstance;
33
+ clients[defaultInstanceId] = mainClientInstance;
39
34
 
40
35
  return function client(key?: SplitIO.SplitKey, trafficType?: string) {
41
36
  if (key === undefined) {
@@ -58,7 +53,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
58
53
  }
59
54
  const instanceId = buildInstanceId(validKey, validTrafficType);
60
55
 
61
- if (!clientInstances[instanceId]) {
56
+ if (!clients[instanceId]) {
62
57
  const matchingKey = getMatching(validKey);
63
58
 
64
59
  const sharedSdkReadiness = sdkReadinessManager.shared();
@@ -80,13 +75,12 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
80
75
 
81
76
  // As shared clients reuse all the storage information, we don't need to check here if we
82
77
  // will use offline or online mode. We should stick with the original decision.
83
- clientInstances[instanceId] = clientCSDecorator(
78
+ clients[instanceId] = clientCSDecorator(
84
79
  log,
85
80
  sdkClientFactory(objectAssign({}, params, {
86
81
  sdkReadinessManager: sharedSdkReadiness,
87
82
  storage: sharedStorage || storage,
88
83
  syncManager: sharedSyncManager,
89
- signalListener: undefined, // only the main client "destroy" method stops the signal listener
90
84
  }), true) as SplitIO.IClient,
91
85
  validKey,
92
86
  validTrafficType
@@ -99,6 +93,6 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
99
93
  log.debug(RETRIEVE_CLIENT_EXISTING);
100
94
  }
101
95
 
102
- return clientInstances[instanceId];
96
+ return clients[instanceId] as SplitIO.ICsClient;
103
97
  };
104
98
  }