@splitsoftware/splitio-commons 2.0.0-rc.0 → 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.
Files changed (114) hide show
  1. package/CHANGES.txt +5 -1
  2. package/cjs/evaluator/Engine.js +1 -1
  3. package/cjs/evaluator/index.js +1 -1
  4. package/cjs/readiness/readinessManager.js +13 -2
  5. package/cjs/sdkClient/sdkClientMethodCS.js +0 -1
  6. package/cjs/sdkFactory/index.js +29 -9
  7. package/cjs/storages/{AbstractSegmentsCacheSync.js → AbstractMySegmentsCacheSync.js} +15 -17
  8. package/cjs/storages/dataLoader.js +99 -37
  9. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +5 -5
  10. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +3 -2
  11. package/cjs/storages/inLocalStorage/index.js +1 -1
  12. package/cjs/storages/inMemory/InMemoryStorageCS.js +18 -6
  13. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -5
  14. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +13 -27
  15. package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -1
  16. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +2 -1
  17. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +2 -1
  18. package/cjs/storages/inRedis/RedisAdapter.js +2 -1
  19. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +13 -19
  20. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +2 -1
  21. package/cjs/storages/pluggable/SegmentsCachePluggable.js +11 -32
  22. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +2 -1
  23. package/cjs/storages/pluggable/inMemoryWrapper.js +2 -1
  24. package/cjs/sync/offline/syncManagerOffline.js +18 -11
  25. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +12 -28
  26. package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -1
  27. package/cjs/sync/syncManagerOnline.js +20 -21
  28. package/cjs/trackers/eventTracker.js +12 -10
  29. package/cjs/trackers/impressionsTracker.js +16 -14
  30. package/cjs/trackers/uniqueKeysTracker.js +5 -3
  31. package/cjs/utils/lang/sets.js +12 -2
  32. package/esm/evaluator/Engine.js +1 -1
  33. package/esm/evaluator/index.js +2 -2
  34. package/esm/readiness/readinessManager.js +13 -2
  35. package/esm/sdkClient/sdkClientMethodCS.js +0 -1
  36. package/esm/sdkFactory/index.js +30 -10
  37. package/esm/storages/{AbstractSegmentsCacheSync.js → AbstractMySegmentsCacheSync.js} +14 -16
  38. package/esm/storages/dataLoader.js +96 -35
  39. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +5 -5
  40. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +3 -2
  41. package/esm/storages/inLocalStorage/index.js +1 -1
  42. package/esm/storages/inMemory/InMemoryStorageCS.js +18 -6
  43. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -5
  44. package/esm/storages/inMemory/SegmentsCacheInMemory.js +13 -27
  45. package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -1
  46. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +2 -1
  47. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +2 -1
  48. package/esm/storages/inRedis/RedisAdapter.js +2 -1
  49. package/esm/storages/inRedis/SegmentsCacheInRedis.js +13 -19
  50. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +2 -1
  51. package/esm/storages/pluggable/SegmentsCachePluggable.js +11 -32
  52. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +2 -1
  53. package/esm/storages/pluggable/inMemoryWrapper.js +2 -1
  54. package/esm/sync/offline/syncManagerOffline.js +18 -11
  55. package/esm/sync/polling/updaters/segmentChangesUpdater.js +12 -28
  56. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -1
  57. package/esm/sync/syncManagerOnline.js +20 -21
  58. package/esm/trackers/eventTracker.js +12 -10
  59. package/esm/trackers/impressionsTracker.js +16 -14
  60. package/esm/trackers/uniqueKeysTracker.js +5 -3
  61. package/esm/utils/lang/sets.js +10 -1
  62. package/package.json +1 -1
  63. package/src/evaluator/Engine.ts +1 -1
  64. package/src/evaluator/index.ts +2 -2
  65. package/src/readiness/readinessManager.ts +12 -3
  66. package/src/readiness/types.ts +3 -0
  67. package/src/sdkClient/sdkClientMethodCS.ts +0 -2
  68. package/src/sdkFactory/index.ts +34 -12
  69. package/src/sdkFactory/types.ts +2 -0
  70. package/src/storages/{AbstractSegmentsCacheSync.ts → AbstractMySegmentsCacheSync.ts} +13 -28
  71. package/src/storages/dataLoader.ts +97 -38
  72. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +5 -5
  73. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +3 -2
  74. package/src/storages/inLocalStorage/index.ts +1 -1
  75. package/src/storages/inMemory/InMemoryStorageCS.ts +21 -6
  76. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +5 -5
  77. package/src/storages/inMemory/SegmentsCacheInMemory.ts +12 -26
  78. package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -1
  79. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +2 -1
  80. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +2 -1
  81. package/src/storages/inRedis/RedisAdapter.ts +2 -1
  82. package/src/storages/inRedis/SegmentsCacheInRedis.ts +13 -22
  83. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +2 -1
  84. package/src/storages/pluggable/SegmentsCachePluggable.ts +11 -35
  85. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +2 -1
  86. package/src/storages/pluggable/inMemoryWrapper.ts +2 -1
  87. package/src/storages/types.ts +7 -11
  88. package/src/sync/offline/syncManagerOffline.ts +21 -13
  89. package/src/sync/polling/updaters/segmentChangesUpdater.ts +13 -29
  90. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -1
  91. package/src/sync/syncManagerOnline.ts +17 -17
  92. package/src/sync/types.ts +1 -1
  93. package/src/trackers/eventTracker.ts +11 -8
  94. package/src/trackers/impressionsTracker.ts +13 -10
  95. package/src/trackers/types.ts +1 -0
  96. package/src/trackers/uniqueKeysTracker.ts +6 -4
  97. package/src/types.ts +14 -13
  98. package/src/utils/lang/sets.ts +11 -1
  99. package/types/readiness/types.d.ts +3 -0
  100. package/types/sdkFactory/types.d.ts +1 -0
  101. package/types/storages/dataLoader.d.ts +17 -6
  102. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +5 -5
  103. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +5 -5
  104. package/types/storages/inMemory/SegmentsCacheInMemory.d.ts +5 -7
  105. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +0 -1
  106. package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +6 -3
  107. package/types/storages/pluggable/SegmentsCachePluggable.d.ts +4 -16
  108. package/types/storages/types.d.ts +7 -10
  109. package/types/sync/types.d.ts +1 -1
  110. package/types/trackers/eventTracker.d.ts +1 -1
  111. package/types/trackers/impressionsTracker.d.ts +1 -1
  112. package/types/trackers/types.d.ts +1 -0
  113. package/types/types.d.ts +13 -13
  114. package/types/utils/lang/sets.d.ts +1 -0
package/CHANGES.txt CHANGED
@@ -1,15 +1,19 @@
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.
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.
4
8
  - Bugfixing - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
5
9
  - BREAKING CHANGES:
6
10
  - Updated default flag spec version to 1.2.
7
11
  - Removed `/mySegments` endpoint from SplitAPI module, as it is replaced by `/memberships` endpoint.
8
12
  - Removed support for MY_SEGMENTS_UPDATE and MY_SEGMENTS_UPDATE_V2 notification types, as they are replaced by MEMBERSHIPS_MS_UPDATE and MEMBERSHIPS_LS_UPDATE notification types.
9
13
  - Removed the deprecated `GOOGLE_ANALYTICS_TO_SPLIT` and `SPLIT_TO_GOOGLE_ANALYTICS` integrations.
10
- - Removed internal ponyfills for `Map`, `Set` and `Array.from` global objects, dropping support for IE and other outdated browsers. The SDK now requires the runtime environment to support these features natively or to provide a polyfill.
11
14
  - Removed the migration logic for the old format of MySegments keys in LocalStorage introduced in JavaScript SDK v10.17.3.
12
15
  - Removed the `sdkClientMethodCSWithTT` function, which handled the logic to bound an optional traffic type to SDK clients. Client-side SDK implementations must use `sdkClientMethodCS` module, which, unlike the previous function, does not allow passing a traffic type but simplifies the SDK API.
16
+ - Removed internal ponyfills for `Map` and `Set` global objects, dropping support for IE and other outdated browsers. The SDK now requires the runtime environment to support these features natively or to provide a polyfill.
13
17
 
14
18
  1.17.0 (September 6, 2024)
15
19
  - Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance SDK HTTP request Headers for Authorization Frameworks.
@@ -17,7 +17,7 @@ var Engine = /** @class */ (function () {
17
17
  function Engine(baseInfo, evaluator) {
18
18
  this.baseInfo = baseInfo;
19
19
  this.evaluator = evaluator;
20
- // in case we don't have a default treatment in the instanciation, use 'control'
20
+ // in case we don't have a default treatment in the instantiation, use 'control'
21
21
  if (typeof this.baseInfo.defaultTreatment !== 'string') {
22
22
  this.baseInfo.defaultTreatment = constants_1.CONTROL;
23
23
  }
@@ -70,7 +70,7 @@ function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, storage, met
70
70
  }
71
71
  }
72
72
  return featureFlags.size ?
73
- evaluateFeatures(log, key, Array.from(featureFlags), attributes, storage) :
73
+ evaluateFeatures(log, key, (0, sets_1.setToArray)(featureFlags), attributes, storage) :
74
74
  {};
75
75
  }
76
76
  // get features by flag sets
@@ -7,6 +7,8 @@ function splitsEventEmitterFactory(EventEmitter) {
7
7
  var splitsEventEmitter = (0, objectAssign_1.objectAssign)(new EventEmitter(), {
8
8
  splitsArrived: false,
9
9
  splitsCacheLoaded: false,
10
+ initialized: false,
11
+ initCallbacks: []
10
12
  });
11
13
  // `isSplitKill` condition avoids an edge-case of wrongly emitting SDK_READY if:
12
14
  // - `/memberships` fetch and SPLIT_KILL occurs before `/splitChanges` fetch, and
@@ -46,7 +48,7 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
46
48
  // emit SDK_READY_TIMED_OUT
47
49
  var hasTimedout = false;
48
50
  function timeout() {
49
- if (hasTimedout)
51
+ if (hasTimedout || isReady)
50
52
  return;
51
53
  hasTimedout = true;
52
54
  syncLastUpdate();
@@ -54,7 +56,10 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
54
56
  }
55
57
  var readyTimeoutId;
56
58
  if (readyTimeout > 0) {
57
- readyTimeoutId = setTimeout(timeout, readyTimeout);
59
+ if (splits.initialized)
60
+ readyTimeoutId = setTimeout(timeout, readyTimeout);
61
+ else
62
+ splits.initCallbacks.push(function () { readyTimeoutId = setTimeout(timeout, readyTimeout); });
58
63
  }
59
64
  // emit SDK_READY and SDK_UPDATE
60
65
  var isReady = false;
@@ -116,6 +121,12 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
116
121
  // Called on 403 error (client-side SDK key on server-side), to set the SDK as destroyed for
117
122
  // tracking and evaluations, while keeping event listeners to emit SDK_READY_TIMED_OUT event
118
123
  setDestroyed: function () { isDestroyed = true; },
124
+ init: function () {
125
+ if (splits.initialized)
126
+ return;
127
+ splits.initialized = true;
128
+ splits.initCallbacks.forEach(function (cb) { return cb(); });
129
+ },
119
130
  destroy: function () {
120
131
  isDestroyed = true;
121
132
  syncLastUpdate();
@@ -55,7 +55,6 @@ function sdkClientMethodCSFactory(params) {
55
55
  storage: sharedStorage || storage,
56
56
  syncManager: sharedSyncManager,
57
57
  }), true), validKey);
58
- sharedSyncManager && sharedSyncManager.start();
59
58
  log.info(constants_1.NEW_SHARED_CLIENT);
60
59
  }
61
60
  else {
@@ -19,12 +19,19 @@ var constants_3 = require("../utils/constants");
19
19
  * Modular SDK factory
20
20
  */
21
21
  function sdkFactory(params) {
22
- 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;
22
+ 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
23
  var log = settings.log, impressionsMode = settings.sync.impressionsMode;
24
24
  // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
25
25
  // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
26
- // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
27
- (0, apiKey_1.validateAndTrackApiKey)(log, settings.core.authorizationKey);
26
+ // initialization
27
+ var hasInit = false;
28
+ var initCallbacks = [];
29
+ function whenInit(cb) {
30
+ if (hasInit)
31
+ cb();
32
+ else
33
+ initCallbacks.push(cb);
34
+ }
28
35
  var sdkReadinessManager = (0, sdkReadinessManager_1.sdkReadinessManagerFactory)(platform.EventEmitter, settings);
29
36
  var readiness = sdkReadinessManager.readinessManager;
30
37
  var storage = storageFactory({
@@ -38,8 +45,10 @@ function sdkFactory(params) {
38
45
  readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED);
39
46
  readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
40
47
  },
48
+ onReadyFromCacheCb: function () {
49
+ readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
50
+ }
41
51
  });
42
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
43
52
  var clients = {};
44
53
  var telemetryTracker = (0, telemetryTracker_1.telemetryTrackerFactory)(storage.telemetry, platform.now);
45
54
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
@@ -56,8 +65,8 @@ function sdkFactory(params) {
56
65
  default:
57
66
  strategy = (0, strategyDebug_1.strategyDebugFactory)(observer);
58
67
  }
59
- var impressionsTracker = (0, impressionsTracker_1.impressionsTrackerFactory)(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
60
- var eventTracker = (0, eventTracker_1.eventTrackerFactory)(settings, storage.events, integrationsManager, storage.telemetry);
68
+ var impressionsTracker = (0, impressionsTracker_1.impressionsTrackerFactory)(settings, storage.impressions, strategy, whenInit, integrationsManager, storage.telemetry);
69
+ var eventTracker = (0, eventTracker_1.eventTrackerFactory)(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
61
70
  // splitApi is used by SyncManager and Browser signal listener
62
71
  var splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
63
72
  var ctx = { clients: clients, splitApi: splitApi, eventTracker: eventTracker, impressionsTracker: impressionsTracker, telemetryTracker: telemetryTracker, uniqueKeysTracker: uniqueKeysTracker, sdkReadinessManager: sdkReadinessManager, readiness: readiness, settings: settings, storage: storage, platform: platform };
@@ -68,8 +77,19 @@ function sdkFactory(params) {
68
77
  // SDK client and manager
69
78
  var clientMethod = sdkClientMethodFactory(ctx);
70
79
  var managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
71
- syncManager && syncManager.start();
72
- signalListener && signalListener.start();
80
+ function init() {
81
+ if (hasInit)
82
+ return;
83
+ hasInit = true;
84
+ // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
85
+ (0, apiKey_1.validateAndTrackApiKey)(log, settings.core.authorizationKey);
86
+ readiness.init();
87
+ uniqueKeysTracker && uniqueKeysTracker.start();
88
+ syncManager && syncManager.start();
89
+ signalListener && signalListener.start();
90
+ initCallbacks.forEach(function (cb) { return cb(); });
91
+ initCallbacks.length = 0;
92
+ }
73
93
  log.info(constants_1.NEW_FACTORY);
74
94
  // @ts-ignore
75
95
  return (0, objectAssign_1.objectAssign)({
@@ -86,6 +106,6 @@ function sdkFactory(params) {
86
106
  destroy: function () {
87
107
  return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
88
108
  }
89
- }, extraProps && extraProps(ctx));
109
+ }, extraProps && extraProps(ctx), lazyInit ? { init: init } : init());
90
110
  }
91
111
  exports.sdkFactory = sdkFactory;
@@ -1,39 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AbstractSegmentsCacheSync = void 0;
3
+ exports.AbstractMySegmentsCacheSync = void 0;
4
4
  /**
5
5
  * This class provides a skeletal implementation of the ISegmentsCacheSync interface
6
6
  * to minimize the effort required to implement this interface.
7
7
  */
8
- var AbstractSegmentsCacheSync = /** @class */ (function () {
9
- function AbstractSegmentsCacheSync() {
8
+ var AbstractMySegmentsCacheSync = /** @class */ (function () {
9
+ function AbstractMySegmentsCacheSync() {
10
10
  }
11
11
  /**
12
12
  * clear the cache.
13
13
  */
14
- AbstractSegmentsCacheSync.prototype.clear = function () {
14
+ AbstractMySegmentsCacheSync.prototype.clear = function () {
15
15
  this.resetSegments({});
16
16
  };
17
- /**
18
- * For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
19
- * For client-side synchronizer: the method is not used.
20
- */
21
- AbstractSegmentsCacheSync.prototype.registerSegments = function (names) { return false; };
17
+ // No-op. Not used in client-side.
18
+ AbstractMySegmentsCacheSync.prototype.registerSegments = function () { return false; };
19
+ AbstractMySegmentsCacheSync.prototype.update = function () { return false; };
22
20
  /**
23
21
  * For server-side synchronizer: the method is not used.
24
22
  * For client-side synchronizer: it resets or updates the cache.
25
23
  */
26
- AbstractSegmentsCacheSync.prototype.resetSegments = function (segmentsData) {
24
+ AbstractMySegmentsCacheSync.prototype.resetSegments = function (segmentsData) {
27
25
  var _this = this;
28
- this.setChangeNumber(undefined, segmentsData.cn);
26
+ this.setChangeNumber(segmentsData.cn);
29
27
  var _a = segmentsData, added = _a.added, removed = _a.removed;
30
28
  if (added && removed) {
31
29
  var isDiff_1 = false;
32
30
  added.forEach(function (segment) {
33
- isDiff_1 = _this.addToSegment(segment) || isDiff_1;
31
+ isDiff_1 = _this.addSegment(segment) || isDiff_1;
34
32
  });
35
33
  removed.forEach(function (segment) {
36
- isDiff_1 = _this.removeFromSegment(segment) || isDiff_1;
34
+ isDiff_1 = _this.removeSegment(segment) || isDiff_1;
37
35
  });
38
36
  return isDiff_1;
39
37
  }
@@ -50,13 +48,13 @@ var AbstractSegmentsCacheSync = /** @class */ (function () {
50
48
  return false;
51
49
  // Slowest path => add and/or remove segments
52
50
  for (var removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
53
- this.removeFromSegment(storedSegmentKeys[removeIndex]);
51
+ this.removeSegment(storedSegmentKeys[removeIndex]);
54
52
  }
55
53
  for (var addIndex = index; addIndex < names.length; addIndex++) {
56
- this.addToSegment(names[addIndex]);
54
+ this.addSegment(names[addIndex]);
57
55
  }
58
56
  return true;
59
57
  };
60
- return AbstractSegmentsCacheSync;
58
+ return AbstractMySegmentsCacheSync;
61
59
  }());
62
- exports.AbstractSegmentsCacheSync = AbstractSegmentsCacheSync;
60
+ exports.AbstractMySegmentsCacheSync = AbstractMySegmentsCacheSync;
@@ -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;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MySegmentsCacheInLocal = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var lang_1 = require("../../utils/lang");
6
- var AbstractSegmentsCacheSync_1 = require("../AbstractSegmentsCacheSync");
6
+ var AbstractMySegmentsCacheSync_1 = require("../AbstractMySegmentsCacheSync");
7
7
  var constants_1 = require("./constants");
8
8
  var MySegmentsCacheInLocal = /** @class */ (function (_super) {
9
9
  (0, tslib_1.__extends)(MySegmentsCacheInLocal, _super);
@@ -14,7 +14,7 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
14
14
  return _this;
15
15
  // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
16
16
  }
17
- MySegmentsCacheInLocal.prototype.addToSegment = function (name) {
17
+ MySegmentsCacheInLocal.prototype.addSegment = function (name) {
18
18
  var segmentKey = this.keys.buildSegmentNameKey(name);
19
19
  try {
20
20
  if (localStorage.getItem(segmentKey) === constants_1.DEFINED)
@@ -27,7 +27,7 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
27
27
  return false;
28
28
  }
29
29
  };
30
- MySegmentsCacheInLocal.prototype.removeFromSegment = function (name) {
30
+ MySegmentsCacheInLocal.prototype.removeSegment = function (name) {
31
31
  var segmentKey = this.keys.buildSegmentNameKey(name);
32
32
  try {
33
33
  if (localStorage.getItem(segmentKey) !== constants_1.DEFINED)
@@ -56,7 +56,7 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
56
56
  MySegmentsCacheInLocal.prototype.getKeysCount = function () {
57
57
  return 1;
58
58
  };
59
- MySegmentsCacheInLocal.prototype.setChangeNumber = function (name, changeNumber) {
59
+ MySegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
60
60
  try {
61
61
  if (changeNumber)
62
62
  localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
@@ -77,5 +77,5 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
77
77
  return n;
78
78
  };
79
79
  return MySegmentsCacheInLocal;
80
- }(AbstractSegmentsCacheSync_1.AbstractSegmentsCacheSync));
80
+ }(AbstractMySegmentsCacheSync_1.AbstractMySegmentsCacheSync));
81
81
  exports.MySegmentsCacheInLocal = MySegmentsCacheInLocal;
@@ -6,6 +6,7 @@ var AbstractSplitsCacheSync_1 = require("../AbstractSplitsCacheSync");
6
6
  var lang_1 = require("../../utils/lang");
7
7
  var constants_1 = require("./constants");
8
8
  var KeyBuilder_1 = require("../KeyBuilder");
9
+ var sets_1 = require("../../utils/lang/sets");
9
10
  /**
10
11
  * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
11
12
  */
@@ -243,7 +244,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
243
244
  var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
244
245
  var flagSetCache = new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
245
246
  flagSetCache.add(featureFlag.name);
246
- localStorage.setItem(flagSetKey, JSON.stringify(Array.from(flagSetCache)));
247
+ localStorage.setItem(flagSetKey, JSON.stringify((0, sets_1.setToArray)(flagSetCache)));
247
248
  });
248
249
  };
249
250
  SplitsCacheInLocal.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
@@ -265,7 +266,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
265
266
  localStorage.removeItem(flagSetKey);
266
267
  return;
267
268
  }
268
- localStorage.setItem(flagSetKey, JSON.stringify(Array.from(flagSetCache)));
269
+ localStorage.setItem(flagSetKey, JSON.stringify((0, sets_1.setToArray)(flagSetCache)));
269
270
  };
270
271
  return SplitsCacheInLocal;
271
272
  }(AbstractSplitsCacheSync_1.AbstractSplitsCacheSync));
@@ -56,7 +56,7 @@ function InLocalStorage(options) {
56
56
  this.events.clear();
57
57
  (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
58
58
  },
59
- // When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).
59
+ // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
60
60
  shared: function (matchingKey) {
61
61
  return {
62
62
  splits: this.splits,
@@ -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();
@@ -38,12 +40,17 @@ function InMemoryStorageCSFactory(params) {
38
40
  this.events.clear();
39
41
  this.uniqueKeys && this.uniqueKeys.clear();
40
42
  },
41
- // When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
42
- shared: function () {
43
+ // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
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,
@@ -58,7 +65,7 @@ function InMemoryStorageCSFactory(params) {
58
65
  },
59
66
  };
60
67
  // @TODO revisit storage logic in localhost mode
61
- // No tracking data in localhost mode to avoid memory leaks
68
+ // No tracking in localhost mode to avoid memory leaks: https://github.com/splitio/javascript-commons/issues/181
62
69
  if (params.settings.mode === constants_1.LOCALHOST_MODE) {
63
70
  var noopTrack = function () { return true; };
64
71
  storage.impressions.track = noopTrack;
@@ -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;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MySegmentsCacheInMemory = void 0;
4
4
  var tslib_1 = require("tslib");
5
- var AbstractSegmentsCacheSync_1 = require("../AbstractSegmentsCacheSync");
5
+ var AbstractMySegmentsCacheSync_1 = require("../AbstractMySegmentsCacheSync");
6
6
  /**
7
7
  * Default MySegmentsCacheInMemory implementation that stores MySegments in memory.
8
8
  * Supported by all JS runtimes.
@@ -14,13 +14,13 @@ var MySegmentsCacheInMemory = /** @class */ (function (_super) {
14
14
  _this.segmentCache = {};
15
15
  return _this;
16
16
  }
17
- MySegmentsCacheInMemory.prototype.addToSegment = function (name) {
17
+ MySegmentsCacheInMemory.prototype.addSegment = function (name) {
18
18
  if (this.segmentCache[name])
19
19
  return false;
20
20
  this.segmentCache[name] = true;
21
21
  return true;
22
22
  };
23
- MySegmentsCacheInMemory.prototype.removeFromSegment = function (name) {
23
+ MySegmentsCacheInMemory.prototype.removeSegment = function (name) {
24
24
  if (!this.segmentCache[name])
25
25
  return false;
26
26
  delete this.segmentCache[name];
@@ -29,7 +29,7 @@ var MySegmentsCacheInMemory = /** @class */ (function (_super) {
29
29
  MySegmentsCacheInMemory.prototype.isInSegment = function (name) {
30
30
  return this.segmentCache[name] === true;
31
31
  };
32
- MySegmentsCacheInMemory.prototype.setChangeNumber = function (name, changeNumber) {
32
+ MySegmentsCacheInMemory.prototype.setChangeNumber = function (changeNumber) {
33
33
  this.cn = changeNumber;
34
34
  };
35
35
  MySegmentsCacheInMemory.prototype.getChangeNumber = function () {
@@ -42,5 +42,5 @@ var MySegmentsCacheInMemory = /** @class */ (function (_super) {
42
42
  return 1;
43
43
  };
44
44
  return MySegmentsCacheInMemory;
45
- }(AbstractSegmentsCacheSync_1.AbstractSegmentsCacheSync));
45
+ }(AbstractMySegmentsCacheSync_1.AbstractMySegmentsCacheSync));
46
46
  exports.MySegmentsCacheInMemory = MySegmentsCacheInMemory;
@@ -1,34 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SegmentsCacheInMemory = void 0;
4
- var tslib_1 = require("tslib");
5
- var AbstractSegmentsCacheSync_1 = require("../AbstractSegmentsCacheSync");
6
4
  var lang_1 = require("../../utils/lang");
7
5
  /**
8
- * Default ISplitsCacheSync implementation that stores split definitions in memory.
9
- * Supported by all JS runtimes.
6
+ * Default ISplitsCacheSync implementation for server-side that stores segments definitions in memory.
10
7
  */
11
- var SegmentsCacheInMemory = /** @class */ (function (_super) {
12
- (0, tslib_1.__extends)(SegmentsCacheInMemory, _super);
8
+ var SegmentsCacheInMemory = /** @class */ (function () {
13
9
  function SegmentsCacheInMemory() {
14
- var _this = _super !== null && _super.apply(this, arguments) || this;
15
- _this.segmentCache = {};
16
- _this.segmentChangeNumber = {};
17
- return _this;
10
+ this.segmentCache = {};
11
+ this.segmentChangeNumber = {};
18
12
  }
19
- SegmentsCacheInMemory.prototype.addToSegment = function (name, segmentKeys) {
20
- var values = this.segmentCache[name];
21
- var keySet = values ? values : new Set();
22
- segmentKeys.forEach(function (k) { return keySet.add(k); });
23
- this.segmentCache[name] = keySet;
24
- return true;
25
- };
26
- SegmentsCacheInMemory.prototype.removeFromSegment = function (name, segmentKeys) {
27
- var values = this.segmentCache[name];
28
- var keySet = values ? values : new Set();
29
- segmentKeys.forEach(function (k) { return keySet.delete(k); });
13
+ SegmentsCacheInMemory.prototype.update = function (name, addedKeys, removedKeys, changeNumber) {
14
+ var keySet = this.segmentCache[name] || new Set();
15
+ addedKeys.forEach(function (k) { return keySet.add(k); });
16
+ removedKeys.forEach(function (k) { return keySet.delete(k); });
30
17
  this.segmentCache[name] = keySet;
31
- return true;
18
+ this.segmentChangeNumber[name] = changeNumber;
19
+ return addedKeys.length > 0 || removedKeys.length > 0;
32
20
  };
33
21
  SegmentsCacheInMemory.prototype.isInSegment = function (name, key) {
34
22
  var segmentValues = this.segmentCache[name];
@@ -62,14 +50,12 @@ var SegmentsCacheInMemory = /** @class */ (function (_super) {
62
50
  return acum + _this.segmentCache[segmentName].size;
63
51
  }, 0);
64
52
  };
65
- SegmentsCacheInMemory.prototype.setChangeNumber = function (name, changeNumber) {
66
- this.segmentChangeNumber[name] = changeNumber;
67
- return true;
68
- };
69
53
  SegmentsCacheInMemory.prototype.getChangeNumber = function (name) {
70
54
  var value = this.segmentChangeNumber[name];
71
55
  return (0, lang_1.isIntegerNumber)(value) ? value : -1;
72
56
  };
57
+ // No-op. Not used in server-side
58
+ SegmentsCacheInMemory.prototype.resetSegments = function () { return false; };
73
59
  return SegmentsCacheInMemory;
74
- }(AbstractSegmentsCacheSync_1.AbstractSegmentsCacheSync));
60
+ }());
75
61
  exports.SegmentsCacheInMemory = SegmentsCacheInMemory;