@splitsoftware/splitio-commons 2.0.3-rc.0 → 2.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGES.txt +5 -2
  2. package/README.md +2 -2
  3. package/cjs/evaluator/index.js +0 -2
  4. package/cjs/evaluator/matchers/large_segment.js +0 -6
  5. package/cjs/evaluator/matchers/segment.js +0 -6
  6. package/cjs/listeners/browser.js +6 -4
  7. package/cjs/listeners/node.js +2 -2
  8. package/cjs/readiness/readinessManager.js +6 -0
  9. package/cjs/sdkClient/client.js +13 -13
  10. package/cjs/sdkClient/sdkClient.js +1 -1
  11. package/cjs/sdkFactory/index.js +14 -9
  12. package/cjs/sdkManager/index.js +1 -2
  13. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
  14. package/cjs/storages/AbstractSplitsCacheSync.js +0 -7
  15. package/cjs/storages/KeyBuilderCS.js +3 -0
  16. package/cjs/storages/dataLoader.js +3 -2
  17. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
  18. package/cjs/storages/inLocalStorage/index.js +8 -7
  19. package/cjs/storages/inLocalStorage/validateCache.js +79 -0
  20. package/cjs/storages/inMemory/InMemoryStorage.js +3 -3
  21. package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -4
  22. package/cjs/storages/inRedis/SplitsCacheInRedis.js +1 -1
  23. package/cjs/storages/inRedis/index.js +13 -9
  24. package/cjs/storages/pluggable/index.js +21 -16
  25. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  26. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -10
  27. package/cjs/sync/streaming/pushManager.js +8 -6
  28. package/cjs/sync/submitters/impressionCountsSubmitter.js +4 -2
  29. package/cjs/sync/submitters/submitterManager.js +6 -3
  30. package/cjs/sync/syncManagerOnline.js +10 -4
  31. package/cjs/trackers/eventTracker.js +1 -1
  32. package/cjs/trackers/impressionsTracker.js +19 -18
  33. package/cjs/trackers/strategy/strategyDebug.js +11 -4
  34. package/cjs/trackers/strategy/strategyNone.js +16 -11
  35. package/cjs/trackers/strategy/strategyOptimized.js +21 -11
  36. package/cjs/utils/settingsValidation/index.js +1 -1
  37. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  38. package/esm/evaluator/index.js +0 -2
  39. package/esm/evaluator/matchers/large_segment.js +0 -6
  40. package/esm/evaluator/matchers/segment.js +0 -6
  41. package/esm/listeners/browser.js +3 -1
  42. package/esm/listeners/node.js +2 -2
  43. package/esm/readiness/readinessManager.js +6 -0
  44. package/esm/sdkClient/client.js +13 -13
  45. package/esm/sdkClient/sdkClient.js +1 -1
  46. package/esm/sdkFactory/index.js +15 -10
  47. package/esm/sdkManager/index.js +1 -2
  48. package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
  49. package/esm/storages/AbstractSplitsCacheSync.js +0 -7
  50. package/esm/storages/KeyBuilderCS.js +3 -0
  51. package/esm/storages/dataLoader.js +2 -1
  52. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
  53. package/esm/storages/inLocalStorage/index.js +9 -8
  54. package/esm/storages/inLocalStorage/validateCache.js +75 -0
  55. package/esm/storages/inMemory/InMemoryStorage.js +4 -4
  56. package/esm/storages/inMemory/InMemoryStorageCS.js +4 -5
  57. package/esm/storages/inRedis/SplitsCacheInRedis.js +1 -1
  58. package/esm/storages/inRedis/index.js +14 -10
  59. package/esm/storages/pluggable/index.js +22 -17
  60. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  61. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -11
  62. package/esm/sync/streaming/pushManager.js +8 -6
  63. package/esm/sync/submitters/impressionCountsSubmitter.js +4 -2
  64. package/esm/sync/submitters/submitterManager.js +6 -3
  65. package/esm/sync/syncManagerOnline.js +10 -4
  66. package/esm/trackers/eventTracker.js +1 -1
  67. package/esm/trackers/impressionsTracker.js +19 -18
  68. package/esm/trackers/strategy/strategyDebug.js +11 -4
  69. package/esm/trackers/strategy/strategyNone.js +16 -11
  70. package/esm/trackers/strategy/strategyOptimized.js +21 -11
  71. package/esm/utils/settingsValidation/index.js +1 -1
  72. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  73. package/package.json +1 -1
  74. package/src/dtos/types.ts +1 -2
  75. package/src/evaluator/index.ts +0 -2
  76. package/src/evaluator/matchers/large_segment.ts +0 -7
  77. package/src/evaluator/matchers/segment.ts +0 -7
  78. package/src/evaluator/types.ts +1 -1
  79. package/src/listeners/browser.ts +3 -1
  80. package/src/listeners/node.ts +2 -2
  81. package/src/readiness/readinessManager.ts +5 -0
  82. package/src/sdkClient/client.ts +11 -11
  83. package/src/sdkClient/sdkClient.ts +1 -1
  84. package/src/sdkFactory/index.ts +16 -11
  85. package/src/sdkFactory/types.ts +1 -1
  86. package/src/sdkManager/index.ts +1 -2
  87. package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
  88. package/src/storages/AbstractSplitsCacheSync.ts +0 -8
  89. package/src/storages/KeyBuilderCS.ts +4 -0
  90. package/src/storages/dataLoader.ts +3 -1
  91. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -66
  92. package/src/storages/inLocalStorage/index.ts +12 -13
  93. package/src/storages/inLocalStorage/validateCache.ts +91 -0
  94. package/src/storages/inMemory/InMemoryStorage.ts +4 -4
  95. package/src/storages/inMemory/InMemoryStorageCS.ts +4 -5
  96. package/src/storages/inRedis/SplitsCacheInRedis.ts +1 -1
  97. package/src/storages/inRedis/index.ts +10 -10
  98. package/src/storages/pluggable/index.ts +22 -17
  99. package/src/storages/types.ts +3 -6
  100. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
  101. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -11
  102. package/src/sync/streaming/pushManager.ts +8 -6
  103. package/src/sync/submitters/impressionCountsSubmitter.ts +4 -2
  104. package/src/sync/submitters/submitterManager.ts +4 -3
  105. package/src/sync/submitters/uniqueKeysSubmitter.ts +3 -2
  106. package/src/sync/syncManagerOnline.ts +11 -5
  107. package/src/trackers/eventTracker.ts +1 -1
  108. package/src/trackers/impressionsTracker.ts +19 -18
  109. package/src/trackers/strategy/strategyDebug.ts +11 -4
  110. package/src/trackers/strategy/strategyNone.ts +17 -11
  111. package/src/trackers/strategy/strategyOptimized.ts +20 -10
  112. package/src/trackers/types.ts +8 -2
  113. package/src/utils/lang/index.ts +1 -1
  114. package/src/utils/settingsValidation/index.ts +1 -1
  115. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  116. package/types/index.d.ts +1 -1
  117. package/types/splitio.d.ts +26 -6
  118. package/cjs/utils/constants/browser.js +0 -5
  119. package/esm/utils/constants/browser.js +0 -2
  120. package/src/utils/constants/browser.ts +0 -2
@@ -1,5 +1,5 @@
1
1
  import { timeout } from '../../../utils/promise/timeout';
2
- import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
2
+ import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
3
3
  import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
4
4
  import { startsWith } from '../../../utils/lang';
5
5
  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);
@@ -306,12 +306,14 @@ export function pushManagerFactory(params, pollingManager) {
306
306
  // Reconnects in case of a new client.
307
307
  // Run in next event-loop cycle to save authentication calls
308
308
  // in case multiple clients are created in the current cycle.
309
- setTimeout(function checkForReconnect() {
310
- if (connectForNewClient) {
311
- connectForNewClient = false;
312
- connectPush();
313
- }
314
- }, 0);
309
+ if (this.isRunning()) {
310
+ setTimeout(function checkForReconnect() {
311
+ if (connectForNewClient) {
312
+ connectForNewClient = false;
313
+ connectPush();
314
+ }
315
+ }, 0);
316
+ }
315
317
  }
316
318
  },
317
319
  // [Only for client-side]
@@ -26,6 +26,8 @@ var IMPRESSIONS_COUNT_RATE = 1800000; // 30 minutes
26
26
  */
27
27
  export function impressionCountsSubmitterFactory(params) {
28
28
  var log = params.settings.log, postTestImpressionsCount = params.splitApi.postTestImpressionsCount, impressionCounts = params.storage.impressionCounts;
29
- // retry impressions counts only once.
30
- return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
29
+ if (impressionCounts) {
30
+ // retry impressions counts only once.
31
+ return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
32
+ }
31
33
  }
@@ -6,11 +6,14 @@ import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
6
6
  export function submitterManagerFactory(params) {
7
7
  var submitters = [
8
8
  impressionsSubmitterFactory(params),
9
- eventsSubmitterFactory(params),
10
- impressionCountsSubmitterFactory(params),
11
- uniqueKeysSubmitterFactory(params)
9
+ eventsSubmitterFactory(params)
12
10
  ];
11
+ var impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
12
+ if (impressionCountsSubmitter)
13
+ submitters.push(impressionCountsSubmitter);
13
14
  var telemetrySubmitter = telemetrySubmitterFactory(params);
15
+ if (params.storage.uniqueKeys)
16
+ submitters.push(uniqueKeysSubmitterFactory(params));
14
17
  return {
15
18
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
16
19
  start: function (onlyTelemetry) {
@@ -3,6 +3,7 @@ import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
3
3
  import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
4
4
  import { isConsentGranted } from '../consent';
5
5
  import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
6
+ import { SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
6
7
  /**
7
8
  * Online SyncManager factory.
8
9
  * Can be used for server-side API, and client-side API with or without multiple clients.
@@ -16,7 +17,7 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
16
17
  * SyncManager factory for modular SDK
17
18
  */
18
19
  return function (params) {
19
- var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker;
20
+ var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker, storage = params.storage, readiness = params.readiness;
20
21
  /** Polling Manager */
21
22
  var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
22
23
  /** Push Manager */
@@ -64,6 +65,11 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
64
65
  */
65
66
  start: function () {
66
67
  running = true;
68
+ if (startFirstTime) {
69
+ var isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
70
+ if (isCacheLoaded)
71
+ Promise.resolve().then(function () { readiness.splits.emit(SDK_SPLITS_CACHE_LOADED); });
72
+ }
67
73
  // start syncing splits and segments
68
74
  if (pollingManager) {
69
75
  // If synchronization is disabled pushManager and pollingManager should not start
@@ -72,7 +78,6 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
72
78
  // Doesn't call `syncAll` when the syncManager is resuming
73
79
  if (startFirstTime) {
74
80
  pollingManager.syncAll();
75
- startFirstTime = false;
76
81
  }
77
82
  pushManager.start();
78
83
  }
@@ -83,12 +88,12 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
83
88
  else {
84
89
  if (startFirstTime) {
85
90
  pollingManager.syncAll();
86
- startFirstTime = false;
87
91
  }
88
92
  }
89
93
  }
90
94
  // start periodic data recording (events, impressions, telemetry).
91
95
  submitterManager.start(!isConsentGranted(settings));
96
+ startFirstTime = false;
92
97
  },
93
98
  /**
94
99
  * Method used to stop/pause the syncManager.
@@ -115,6 +120,8 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
115
120
  if (!pollingManager)
116
121
  return;
117
122
  var mySegmentsSyncTask = pollingManager.add(matchingKey, readinessManager, storage);
123
+ if (syncEnabled && pushManager)
124
+ pushManager.add(matchingKey, mySegmentsSyncTask);
118
125
  if (running) {
119
126
  if (syncEnabled) {
120
127
  if (pushManager) {
@@ -128,7 +135,6 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
128
135
  // of segments since `syncAll` was already executed when starting the main client
129
136
  mySegmentsSyncTask.execute();
130
137
  }
131
- pushManager.add(matchingKey, mySegmentsSyncTask);
132
138
  }
133
139
  else {
134
140
  if (storage.splits.usesSegments())
@@ -22,7 +22,7 @@ export function eventTrackerFactory(settings, eventsCache, whenInit, integration
22
22
  whenInit(function () {
23
23
  // Wrap in a timeout because we don't want it to be blocking.
24
24
  setTimeout(function () {
25
- // copy of event, to avoid unexpected behaviour if modified by integrations
25
+ // copy of event, to avoid unexpected behavior if modified by integrations
26
26
  var eventDataCopy = objectAssign({}, eventData);
27
27
  if (properties)
28
28
  eventDataCopy.properties = objectAssign({}, properties);
@@ -4,37 +4,38 @@ import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIO
4
4
  import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
5
5
  /**
6
6
  * Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
7
+ *
8
+ * @param impressionsCache - cache to save impressions
9
+ * @param metadata - runtime metadata (ip, hostname and version)
10
+ * @param impressionListener - optional impression listener
11
+ * @param integrationsManager - optional integrations manager
12
+ * @param strategy - strategy for impressions tracking.
7
13
  */
8
- export function impressionsTrackerFactory(settings, impressionsCache, noneStrategy, strategy, whenInit, integrationsManager, telemetryCache) {
14
+ export function impressionsTrackerFactory(settings, impressionsCache, strategy, whenInit, integrationsManager, telemetryCache) {
9
15
  var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
10
16
  return {
11
17
  track: function (impressions, attributes) {
12
18
  if (settings.userConsent === CONSENT_DECLINED)
13
19
  return;
14
- var impressionsToStore = impressions.filter(function (_a) {
15
- var impression = _a[0], track = _a[1];
16
- return track === false ?
17
- noneStrategy.process(impression) :
18
- strategy.process(impression);
19
- });
20
- var impressionsLength = impressions.length;
21
- var impressionsToStoreLength = impressionsToStore.length;
22
- if (impressionsToStoreLength) {
23
- var res = impressionsCache.track(impressionsToStore.map(function (item) { return item[0]; }));
20
+ var impressionsCount = impressions.length;
21
+ var _a = strategy.process(impressions), impressionsToStore = _a.impressionsToStore, impressionsToListener = _a.impressionsToListener, deduped = _a.deduped;
22
+ var impressionsToListenerCount = impressionsToListener.length;
23
+ if (impressionsToStore.length > 0) {
24
+ var res = impressionsCache.track(impressionsToStore);
24
25
  // If we're on an async storage, handle error and log it.
25
26
  if (thenable(res)) {
26
27
  res.then(function () {
27
- log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsLength]);
28
+ log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
28
29
  }).catch(function (err) {
29
- log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsLength, err]);
30
+ log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
30
31
  });
31
32
  }
32
33
  else {
33
34
  // Record when impressionsCache is sync only (standalone mode)
34
35
  // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
35
36
  if (telemetryCache) {
36
- telemetryCache.recordImpressionStats(QUEUED, impressionsToStoreLength);
37
- telemetryCache.recordImpressionStats(DEDUPED, impressionsLength - impressionsToStoreLength);
37
+ telemetryCache.recordImpressionStats(QUEUED, impressionsToStore.length);
38
+ telemetryCache.recordImpressionStats(DEDUPED, deduped);
38
39
  }
39
40
  }
40
41
  }
@@ -42,8 +43,8 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
42
43
  if (impressionListener || integrationsManager) {
43
44
  var _loop_1 = function (i) {
44
45
  var impressionData = {
45
- // copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
46
- impression: objectAssign({}, impressions[i][0]),
46
+ // copy of impression, to avoid unexpected behavior if modified by integrations or impressionListener
47
+ impression: objectAssign({}, impressionsToListener[i]),
47
48
  attributes: attributes,
48
49
  ip: ip,
49
50
  hostname: hostname,
@@ -65,7 +66,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
65
66
  });
66
67
  });
67
68
  };
68
- for (var i = 0; i < impressionsLength; i++) {
69
+ for (var i = 0; i < impressionsToListenerCount; i++) {
69
70
  _loop_1(i);
70
71
  }
71
72
  }
@@ -2,13 +2,20 @@
2
2
  * Debug strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
3
3
  *
4
4
  * @param impressionsObserver - impression observer. Previous time (pt property) is included in impression instances
5
- * @returns Debug strategy
5
+ * @returns IStrategyResult
6
6
  */
7
7
  export function strategyDebugFactory(impressionsObserver) {
8
8
  return {
9
- process: function (impression) {
10
- impression.pt = impressionsObserver.testAndSet(impression);
11
- return true;
9
+ process: function (impressions) {
10
+ impressions.forEach(function (impression) {
11
+ // Adds previous time if it is enabled
12
+ impression.pt = impressionsObserver.testAndSet(impression);
13
+ });
14
+ return {
15
+ impressionsToStore: impressions,
16
+ impressionsToListener: impressions,
17
+ deduped: 0
18
+ };
12
19
  }
13
20
  };
14
21
  }
@@ -1,20 +1,25 @@
1
1
  /**
2
2
  * None strategy for impressions tracker.
3
3
  *
4
- * @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
4
+ * @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
5
5
  * @param uniqueKeysTracker - unique keys tracker in charge of tracking the unique keys per split.
6
- * @returns None strategy
6
+ * @returns IStrategyResult
7
7
  */
8
- export function strategyNoneFactory(impressionCounts, uniqueKeysTracker) {
8
+ export function strategyNoneFactory(impressionsCounter, uniqueKeysTracker) {
9
9
  return {
10
- process: function (impression) {
11
- var now = Date.now();
12
- // Increments impression counter per featureName
13
- impressionCounts.track(impression.feature, now, 1);
14
- // Keep track by unique key
15
- uniqueKeysTracker.track(impression.keyName, impression.feature);
16
- // Do not store impressions
17
- return false;
10
+ process: function (impressions) {
11
+ impressions.forEach(function (impression) {
12
+ var now = Date.now();
13
+ // Increments impression counter per featureName
14
+ impressionsCounter.track(impression.feature, now, 1);
15
+ // Keep track by unique key
16
+ uniqueKeysTracker.track(impression.keyName, impression.feature);
17
+ });
18
+ return {
19
+ impressionsToStore: [],
20
+ impressionsToListener: impressions,
21
+ deduped: 0
22
+ };
18
23
  }
19
24
  };
20
25
  }
@@ -3,19 +3,29 @@ import { truncateTimeFrame } from '../../utils/time';
3
3
  * Optimized strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
4
4
  *
5
5
  * @param impressionsObserver - impression observer. previous time (pt property) is included in impression instances
6
- * @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
7
- * @returns Optimized strategy
6
+ * @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
7
+ * @returns IStrategyResult
8
8
  */
9
- export function strategyOptimizedFactory(impressionsObserver, impressionCounts) {
9
+ export function strategyOptimizedFactory(impressionsObserver, impressionsCounter) {
10
10
  return {
11
- process: function (impression) {
12
- impression.pt = impressionsObserver.testAndSet(impression);
13
- var now = Date.now();
14
- // Increments impression counter per featureName
15
- if (impression.pt)
16
- impressionCounts.track(impression.feature, now, 1);
17
- // Checks if the impression should be added in queue to be sent
18
- return (!impression.pt || impression.pt < truncateTimeFrame(now)) ? true : false;
11
+ process: function (impressions) {
12
+ var impressionsToStore = [];
13
+ impressions.forEach(function (impression) {
14
+ impression.pt = impressionsObserver.testAndSet(impression);
15
+ var now = Date.now();
16
+ // Increments impression counter per featureName
17
+ if (impression.pt)
18
+ impressionsCounter.track(impression.feature, now, 1);
19
+ // Checks if the impression should be added in queue to be sent
20
+ if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
21
+ impressionsToStore.push(impression);
22
+ }
23
+ });
24
+ return {
25
+ impressionsToStore: impressionsToStore,
26
+ impressionsToListener: impressions,
27
+ deduped: impressions.length - impressionsToStore.length
28
+ };
19
29
  }
20
30
  };
21
31
  }
@@ -136,7 +136,7 @@ export function settingsValidation(config, validationParams) {
136
136
  withDefaults.core.key = 'localhost_key';
137
137
  }
138
138
  else {
139
- // Keeping same behaviour than JS SDK: if settings key or TT are invalid,
139
+ // Keeping same behavior than JS SDK: if settings key or TT are invalid,
140
140
  // `false` value is used as bound key/TT of the default client, which leads to some issues.
141
141
  // @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
142
142
  withDefaults.core.key = validateKey(log, maybeKey, LOG_PREFIX_CLIENT_INSTANTIATION);
@@ -3,7 +3,7 @@ import { ERROR_STORAGE_INVALID } from '../../../logger/constants';
3
3
  import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants';
4
4
  export function __InLocalStorageMockFactory(params) {
5
5
  var result = InMemoryStorageCSFactory(params);
6
- result.splits.checkCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
6
+ result.validateCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
7
7
  return result;
8
8
  }
9
9
  __InLocalStorageMockFactory.type = STORAGE_MEMORY;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.0.3-rc.0",
3
+ "version": "2.1.0-rc.1",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
package/src/dtos/types.ts CHANGED
@@ -208,8 +208,7 @@ export interface ISplit {
208
208
  configurations?: {
209
209
  [treatmentName: string]: string
210
210
  },
211
- sets?: string[],
212
- trackImpressions?: boolean
211
+ sets?: string[]
213
212
  }
214
213
 
215
214
  // Split definition used in offline mode
@@ -156,14 +156,12 @@ function getEvaluation(
156
156
  return evaluation.then(result => {
157
157
  result.changeNumber = split.getChangeNumber();
158
158
  result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
159
- result.track = splitJSON.trackImpressions;
160
159
 
161
160
  return result;
162
161
  });
163
162
  } else {
164
163
  evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
165
164
  evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
166
- evaluation.track = splitJSON.trackImpressions;
167
165
  }
168
166
  }
169
167
 
@@ -1,18 +1,11 @@
1
1
  import { MaybeThenable } from '../../dtos/types';
2
2
  import { ISegmentsCacheBase } from '../../storages/types';
3
- import { thenable } from '../../utils/promise/thenable';
4
3
 
5
4
  export function largeSegmentMatcherContext(largeSegmentName: string, storage: { largeSegments?: ISegmentsCacheBase }) {
6
5
 
7
6
  return function largeSegmentMatcher(key: string): MaybeThenable<boolean> {
8
7
  const isInLargeSegment = storage.largeSegments ? storage.largeSegments.isInSegment(largeSegmentName, key) : false;
9
8
 
10
- if (thenable(isInLargeSegment)) {
11
- isInLargeSegment.then(result => {
12
- return result;
13
- });
14
- }
15
-
16
9
  return isInLargeSegment;
17
10
  };
18
11
  }
@@ -1,18 +1,11 @@
1
1
  import { MaybeThenable } from '../../dtos/types';
2
2
  import { ISegmentsCacheBase } from '../../storages/types';
3
- import { thenable } from '../../utils/promise/thenable';
4
3
 
5
4
  export function segmentMatcherContext(segmentName: string, storage: { segments: ISegmentsCacheBase }) {
6
5
 
7
6
  return function segmentMatcher(key: string): MaybeThenable<boolean> {
8
7
  const isInSegment = storage.segments.isInSegment(segmentName, key);
9
8
 
10
- if (thenable(isInSegment)) {
11
- isInSegment.then(result => {
12
- return result;
13
- });
14
- }
15
-
16
9
  return isInSegment;
17
10
  };
18
11
  }
@@ -25,7 +25,7 @@ export interface IEvaluation {
25
25
  config?: string | null
26
26
  }
27
27
 
28
- export type IEvaluationResult = IEvaluation & { treatment: string; track?: boolean }
28
+ export type IEvaluationResult = IEvaluation & { treatment: string }
29
29
 
30
30
  export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
31
31
 
@@ -8,6 +8,7 @@ import { IResponse, ISplitApi } from '../services/types';
8
8
  import { ISettings } from '../types';
9
9
  import SplitIO from '../../types/splitio';
10
10
  import { ImpressionsPayload } from '../sync/submitters/types';
11
+ import { OPTIMIZED, DEBUG, NONE } from '../utils/constants';
11
12
  import { objectAssign } from '../utils/lang/objectAssign';
12
13
  import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
13
14
  import { ISyncManager } from '../sync/types';
@@ -77,9 +78,10 @@ export class BrowserSignalListener implements ISignalListener {
77
78
 
78
79
  // Flush impressions & events data if there is user consent
79
80
  if (isConsentGranted(this.settings)) {
81
+ const sim = this.settings.sync.impressionsMode;
80
82
  const extraMetadata = {
81
83
  // sim stands for Sync/Split Impressions Mode
82
- sim: this.settings.sync.impressionsMode
84
+ sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
83
85
  };
84
86
 
85
87
  this._flushData(events + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
@@ -56,7 +56,7 @@ export class NodeSignalListener implements ISignalListener {
56
56
  // Cleaned up, remove handlers.
57
57
  this.stop();
58
58
 
59
- // This handler prevented the default behaviour, start again.
59
+ // This handler prevented the default behavior, start again.
60
60
  // eslint-disable-next-line no-undef
61
61
  process.kill(process.pid, SIGTERM);
62
62
  };
@@ -72,7 +72,7 @@ export class NodeSignalListener implements ISignalListener {
72
72
  }
73
73
 
74
74
  if (thenable(handlerResult)) {
75
- // Always exit, even with errors. The promise is returned for UT purposses.
75
+ // Always exit, even with errors. The promise is returned for UT purposes.
76
76
  return handlerResult.then(wrapUp).catch(wrapUp);
77
77
  } else {
78
78
  wrapUp();
@@ -3,6 +3,7 @@ import { ISettings } from '../types';
3
3
  import SplitIO from '../../types/splitio';
4
4
  import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
5
5
  import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';
6
+ import { STORAGE_LOCALSTORAGE } from '../utils/constants';
6
7
 
7
8
  function splitsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): ISplitsEventEmitter {
8
9
  const splitsEventEmitter = objectAssign(new EventEmitter(), {
@@ -114,6 +115,10 @@ export function readinessManagerFactory(
114
115
  isReady = true;
115
116
  try {
116
117
  syncLastUpdate();
118
+ if (!isReadyFromCache && settings.storage?.type === STORAGE_LOCALSTORAGE) {
119
+ isReadyFromCache = true;
120
+ gate.emit(SDK_READY_FROM_CACHE);
121
+ }
117
122
  gate.emit(SDK_READY);
118
123
  } catch (e) {
119
124
  // throws user callback exceptions in next tick
@@ -34,11 +34,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
34
34
  const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
35
35
 
36
36
  const wrapUp = (evaluationResult: IEvaluationResult) => {
37
- const queue: [impression: SplitIO.ImpressionDTO, track?: boolean][] = [];
37
+ const queue: SplitIO.ImpressionDTO[] = [];
38
38
  const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, methodName, queue);
39
39
  impressionsTracker.track(queue, attributes);
40
40
 
41
- stopTelemetryTracker(queue[0] && queue[0][0].label);
41
+ stopTelemetryTracker(queue[0] && queue[0].label);
42
42
  return treatment;
43
43
  };
44
44
 
@@ -59,14 +59,14 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
59
59
  const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
60
60
 
61
61
  const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
62
- const queue: [impression: SplitIO.ImpressionDTO, track?: boolean][] = [];
62
+ const queue: SplitIO.ImpressionDTO[] = [];
63
63
  const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
64
64
  Object.keys(evaluationResults).forEach(featureFlagName => {
65
65
  treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
66
66
  });
67
67
  impressionsTracker.track(queue, attributes);
68
68
 
69
- stopTelemetryTracker(queue[0] && queue[0][0].label);
69
+ stopTelemetryTracker(queue[0] && queue[0].label);
70
70
  return treatments;
71
71
  };
72
72
 
@@ -87,7 +87,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
87
87
  const stopTelemetryTracker = telemetryTracker.trackEval(method);
88
88
 
89
89
  const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
90
- const queue: [impression: SplitIO.ImpressionDTO, track?: boolean][] = [];
90
+ const queue: SplitIO.ImpressionDTO[] = [];
91
91
  const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
92
92
  const evaluations = evaluationResults;
93
93
  Object.keys(evaluations).forEach(featureFlagName => {
@@ -95,7 +95,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
95
95
  });
96
96
  impressionsTracker.track(queue, attributes);
97
97
 
98
- stopTelemetryTracker(queue[0] && queue[0][0].label);
98
+ stopTelemetryTracker(queue[0] && queue[0].label);
99
99
  return treatments;
100
100
  };
101
101
 
@@ -128,25 +128,25 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
128
128
  attributes: SplitIO.Attributes | undefined,
129
129
  withConfig: boolean,
130
130
  invokingMethodName: string,
131
- queue: [impression: SplitIO.ImpressionDTO, track?: boolean][]
131
+ queue: SplitIO.ImpressionDTO[]
132
132
  ): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
133
133
  const matchingKey = getMatching(key);
134
134
  const bucketingKey = getBucketing(key);
135
135
 
136
- const { treatment, label, changeNumber, config = null, track } = evaluation;
136
+ const { treatment, label, changeNumber, config = null } = evaluation;
137
137
  log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
138
138
 
139
139
  if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
140
140
  log.info(IMPRESSION_QUEUEING);
141
- queue.push([{
141
+ queue.push({
142
142
  feature: featureFlagName,
143
143
  keyName: matchingKey,
144
144
  treatment,
145
145
  time: Date.now(),
146
146
  bucketingKey,
147
147
  label,
148
- changeNumber: changeNumber as number,
149
- }, track]);
148
+ changeNumber: changeNumber as number
149
+ });
150
150
  }
151
151
 
152
152
  if (withConfig) {
@@ -61,7 +61,7 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
61
61
  releaseApiKey(settings.core.authorizationKey);
62
62
  telemetryTracker.sessionLength();
63
63
  signalListener && signalListener.stop();
64
- uniqueKeysTracker.stop();
64
+ uniqueKeysTracker && uniqueKeysTracker.stop();
65
65
  }
66
66
 
67
67
  // Stop background jobs