@splitsoftware/splitio-commons 2.1.1-rc.0 → 2.1.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 (187) hide show
  1. package/CHANGES.txt +7 -0
  2. package/README.md +0 -1
  3. package/cjs/evaluator/combiners/and.js +6 -2
  4. package/cjs/evaluator/combiners/ifelseif.js +6 -6
  5. package/cjs/evaluator/condition/index.js +5 -6
  6. package/cjs/evaluator/index.js +7 -7
  7. package/cjs/evaluator/matchers/index.js +1 -3
  8. package/cjs/evaluator/matchers/matcherTypes.js +0 -1
  9. package/cjs/evaluator/matchersTransform/index.js +0 -4
  10. package/cjs/evaluator/parser/index.js +2 -2
  11. package/cjs/evaluator/value/sanitize.js +0 -1
  12. package/cjs/logger/constants.js +3 -4
  13. package/cjs/logger/messages/debug.js +2 -3
  14. package/cjs/logger/messages/error.js +1 -1
  15. package/cjs/logger/messages/warn.js +2 -2
  16. package/cjs/readiness/readinessManager.js +6 -0
  17. package/cjs/sdkClient/client.js +29 -19
  18. package/cjs/sdkClient/clientAttributesDecoration.js +19 -25
  19. package/cjs/sdkClient/clientInputValidation.js +28 -26
  20. package/cjs/services/splitApi.js +2 -2
  21. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
  22. package/cjs/storages/AbstractSplitsCacheSync.js +2 -12
  23. package/cjs/storages/KeyBuilder.js +0 -9
  24. package/cjs/storages/KeyBuilderCS.js +4 -4
  25. package/cjs/storages/KeyBuilderSS.js +0 -3
  26. package/cjs/storages/dataLoader.js +3 -2
  27. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
  28. package/cjs/storages/inLocalStorage/index.js +5 -7
  29. package/cjs/storages/inLocalStorage/validateCache.js +79 -0
  30. package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
  31. package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -4
  32. package/cjs/storages/inRedis/index.js +0 -2
  33. package/cjs/storages/pluggable/index.js +2 -3
  34. package/cjs/storages/utils.js +1 -0
  35. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  36. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  37. package/cjs/sync/polling/pollingManagerCS.js +7 -7
  38. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  39. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  40. package/cjs/sync/polling/updaters/splitChangesUpdater.js +33 -60
  41. package/cjs/sync/streaming/SSEHandler/index.js +0 -1
  42. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +77 -106
  43. package/cjs/sync/streaming/constants.js +1 -2
  44. package/cjs/sync/streaming/pushManager.js +16 -3
  45. package/cjs/sync/submitters/impressionsSubmitter.js +3 -2
  46. package/cjs/sync/syncManagerOnline.js +10 -5
  47. package/cjs/trackers/strategy/strategyDebug.js +2 -0
  48. package/cjs/trackers/strategy/strategyOptimized.js +3 -0
  49. package/cjs/utils/constants/index.js +2 -3
  50. package/cjs/utils/inputValidation/eventProperties.js +12 -1
  51. package/cjs/utils/inputValidation/index.js +3 -1
  52. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  53. package/esm/evaluator/combiners/and.js +6 -2
  54. package/esm/evaluator/combiners/ifelseif.js +7 -7
  55. package/esm/evaluator/condition/index.js +5 -6
  56. package/esm/evaluator/index.js +7 -7
  57. package/esm/evaluator/matchers/index.js +1 -3
  58. package/esm/evaluator/matchers/matcherTypes.js +0 -1
  59. package/esm/evaluator/matchersTransform/index.js +0 -4
  60. package/esm/evaluator/parser/index.js +2 -2
  61. package/esm/evaluator/value/sanitize.js +0 -1
  62. package/esm/logger/constants.js +0 -1
  63. package/esm/logger/messages/debug.js +2 -3
  64. package/esm/logger/messages/error.js +1 -1
  65. package/esm/logger/messages/warn.js +2 -2
  66. package/esm/readiness/readinessManager.js +6 -0
  67. package/esm/sdkClient/client.js +29 -19
  68. package/esm/sdkClient/clientAttributesDecoration.js +19 -25
  69. package/esm/sdkClient/clientInputValidation.js +29 -27
  70. package/esm/services/splitApi.js +2 -2
  71. package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
  72. package/esm/storages/AbstractSplitsCacheSync.js +2 -12
  73. package/esm/storages/KeyBuilder.js +0 -9
  74. package/esm/storages/KeyBuilderCS.js +4 -4
  75. package/esm/storages/KeyBuilderSS.js +0 -3
  76. package/esm/storages/dataLoader.js +2 -1
  77. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
  78. package/esm/storages/inLocalStorage/index.js +5 -7
  79. package/esm/storages/inLocalStorage/validateCache.js +75 -0
  80. package/esm/storages/inMemory/InMemoryStorage.js +0 -3
  81. package/esm/storages/inMemory/InMemoryStorageCS.js +0 -4
  82. package/esm/storages/inRedis/index.js +0 -2
  83. package/esm/storages/pluggable/index.js +2 -3
  84. package/esm/storages/utils.js +1 -0
  85. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  86. package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  87. package/esm/sync/polling/pollingManagerCS.js +7 -7
  88. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  89. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  90. package/esm/sync/polling/updaters/splitChangesUpdater.js +34 -61
  91. package/esm/sync/streaming/SSEHandler/index.js +1 -2
  92. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +73 -102
  93. package/esm/sync/streaming/constants.js +0 -1
  94. package/esm/sync/streaming/pushManager.js +19 -6
  95. package/esm/sync/submitters/impressionsSubmitter.js +3 -2
  96. package/esm/sync/syncManagerOnline.js +10 -5
  97. package/esm/trackers/strategy/strategyDebug.js +2 -0
  98. package/esm/trackers/strategy/strategyOptimized.js +3 -0
  99. package/esm/utils/constants/index.js +1 -2
  100. package/esm/utils/inputValidation/eventProperties.js +10 -0
  101. package/esm/utils/inputValidation/index.js +1 -0
  102. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  103. package/package.json +1 -1
  104. package/src/dtos/types.ts +8 -32
  105. package/src/evaluator/Engine.ts +1 -1
  106. package/src/evaluator/combiners/and.ts +4 -5
  107. package/src/evaluator/combiners/ifelseif.ts +9 -7
  108. package/src/evaluator/condition/engineUtils.ts +1 -1
  109. package/src/evaluator/condition/index.ts +12 -12
  110. package/src/evaluator/index.ts +7 -7
  111. package/src/evaluator/matchers/index.ts +1 -3
  112. package/src/evaluator/matchers/matcherTypes.ts +0 -1
  113. package/src/evaluator/matchersTransform/index.ts +0 -3
  114. package/src/evaluator/parser/index.ts +3 -3
  115. package/src/evaluator/types.ts +2 -2
  116. package/src/evaluator/value/index.ts +2 -2
  117. package/src/evaluator/value/sanitize.ts +4 -5
  118. package/src/logger/constants.ts +0 -1
  119. package/src/logger/messages/debug.ts +2 -3
  120. package/src/logger/messages/error.ts +1 -1
  121. package/src/logger/messages/warn.ts +2 -2
  122. package/src/readiness/readinessManager.ts +5 -0
  123. package/src/sdkClient/client.ts +31 -21
  124. package/src/sdkClient/clientAttributesDecoration.ts +20 -27
  125. package/src/sdkClient/clientInputValidation.ts +30 -27
  126. package/src/sdkManager/index.ts +1 -1
  127. package/src/services/splitApi.ts +2 -2
  128. package/src/services/types.ts +1 -1
  129. package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
  130. package/src/storages/AbstractSplitsCacheSync.ts +3 -14
  131. package/src/storages/KeyBuilder.ts +0 -12
  132. package/src/storages/KeyBuilderCS.ts +5 -5
  133. package/src/storages/KeyBuilderSS.ts +0 -4
  134. package/src/storages/dataLoader.ts +3 -1
  135. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +26 -87
  136. package/src/storages/inLocalStorage/index.ts +8 -12
  137. package/src/storages/inLocalStorage/validateCache.ts +91 -0
  138. package/src/storages/inMemory/InMemoryStorage.ts +0 -3
  139. package/src/storages/inMemory/InMemoryStorageCS.ts +0 -4
  140. package/src/storages/inRedis/index.ts +0 -2
  141. package/src/storages/pluggable/index.ts +2 -3
  142. package/src/storages/types.ts +2 -37
  143. package/src/storages/utils.ts +1 -0
  144. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
  145. package/src/sync/polling/fetchers/splitChangesFetcher.ts +1 -2
  146. package/src/sync/polling/fetchers/types.ts +0 -1
  147. package/src/sync/polling/pollingManagerCS.ts +7 -7
  148. package/src/sync/polling/types.ts +2 -2
  149. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
  150. package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
  151. package/src/sync/polling/updaters/splitChangesUpdater.ts +43 -71
  152. package/src/sync/streaming/SSEHandler/index.ts +1 -2
  153. package/src/sync/streaming/SSEHandler/types.ts +2 -2
  154. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +68 -98
  155. package/src/sync/streaming/constants.ts +0 -1
  156. package/src/sync/streaming/parseUtils.ts +2 -2
  157. package/src/sync/streaming/pushManager.ts +18 -6
  158. package/src/sync/streaming/types.ts +2 -3
  159. package/src/sync/submitters/impressionsSubmitter.ts +3 -2
  160. package/src/sync/submitters/types.ts +23 -33
  161. package/src/sync/syncManagerOnline.ts +11 -5
  162. package/src/trackers/strategy/strategyDebug.ts +2 -0
  163. package/src/trackers/strategy/strategyOptimized.ts +3 -0
  164. package/src/utils/constants/index.ts +1 -2
  165. package/src/utils/inputValidation/eventProperties.ts +10 -0
  166. package/src/utils/inputValidation/index.ts +1 -0
  167. package/src/utils/lang/index.ts +2 -2
  168. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  169. package/types/splitio.d.ts +128 -36
  170. package/cjs/evaluator/matchers/rbsegment.js +0 -43
  171. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -117
  172. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +0 -61
  173. package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +0 -64
  174. package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +0 -64
  175. package/cjs/utils/constants/browser.js +0 -5
  176. package/esm/evaluator/matchers/rbsegment.js +0 -39
  177. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -114
  178. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +0 -58
  179. package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +0 -61
  180. package/esm/storages/pluggable/RBSegmentsCachePluggable.js +0 -61
  181. package/esm/utils/constants/browser.js +0 -2
  182. package/src/evaluator/matchers/rbsegment.ts +0 -61
  183. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +0 -136
  184. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +0 -68
  185. package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +0 -79
  186. package/src/storages/pluggable/RBSegmentsCachePluggable.ts +0 -76
  187. package/src/utils/constants/browser.ts +0 -2
@@ -0,0 +1,91 @@
1
+ import { ISettings } from '../../types';
2
+ import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
3
+ import { getStorageHash } from '../KeyBuilder';
4
+ import { LOG_PREFIX } from './constants';
5
+ import type { SplitsCacheInLocal } from './SplitsCacheInLocal';
6
+ import type { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
7
+ import { KeyBuilderCS } from '../KeyBuilderCS';
8
+ import SplitIO from '../../../types/splitio';
9
+
10
+ const DEFAULT_CACHE_EXPIRATION_IN_DAYS = 10;
11
+ const MILLIS_IN_A_DAY = 86400000;
12
+
13
+ /**
14
+ * Validates if cache should be cleared and sets the cache `hash` if needed.
15
+ *
16
+ * @returns `true` if cache should be cleared, `false` otherwise
17
+ */
18
+ function validateExpiration(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, currentTimestamp: number, isThereCache: boolean) {
19
+ const { log } = settings;
20
+
21
+ // Check expiration
22
+ const lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()) as string, 10);
23
+ if (!isNaNNumber(lastUpdatedTimestamp)) {
24
+ const cacheExpirationInDays = isFiniteNumber(options.expirationDays) && options.expirationDays >= 1 ? options.expirationDays : DEFAULT_CACHE_EXPIRATION_IN_DAYS;
25
+ const expirationTimestamp = currentTimestamp - MILLIS_IN_A_DAY * cacheExpirationInDays;
26
+ if (lastUpdatedTimestamp < expirationTimestamp) {
27
+ log.info(LOG_PREFIX + 'Cache expired more than ' + cacheExpirationInDays + ' days ago. Cleaning up cache');
28
+ return true;
29
+ }
30
+ }
31
+
32
+ // Check hash
33
+ const storageHashKey = keys.buildHashKey();
34
+ const storageHash = localStorage.getItem(storageHashKey);
35
+ const currentStorageHash = getStorageHash(settings);
36
+
37
+ if (storageHash !== currentStorageHash) {
38
+ try {
39
+ localStorage.setItem(storageHashKey, currentStorageHash);
40
+ } catch (e) {
41
+ log.error(LOG_PREFIX + e);
42
+ }
43
+ if (isThereCache) {
44
+ log.info(LOG_PREFIX + 'SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');
45
+ return true;
46
+ }
47
+ return false; // No cache to clear
48
+ }
49
+
50
+ // Clear on init
51
+ if (options.clearOnInit) {
52
+ const lastClearTimestamp = parseInt(localStorage.getItem(keys.buildLastClear()) as string, 10);
53
+
54
+ if (isNaNNumber(lastClearTimestamp) || lastClearTimestamp < currentTimestamp - MILLIS_IN_A_DAY) {
55
+ log.info(LOG_PREFIX + 'clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
56
+ return true;
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Clean cache if:
63
+ * - it has expired, i.e., its `lastUpdated` timestamp is older than the given `expirationTimestamp`
64
+ * - its hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
65
+ * - `clearOnInit` was set and cache was not cleared in the last 24 hours
66
+ *
67
+ * @returns `true` if cache is ready to be used, `false` otherwise (cache was cleared or there is no cache)
68
+ */
69
+ export function validateCache(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, splits: SplitsCacheInLocal, segments: MySegmentsCacheInLocal, largeSegments: MySegmentsCacheInLocal): boolean {
70
+
71
+ const currentTimestamp = Date.now();
72
+ const isThereCache = splits.getChangeNumber() > -1;
73
+
74
+ if (validateExpiration(options, settings, keys, currentTimestamp, isThereCache)) {
75
+ splits.clear();
76
+ segments.clear();
77
+ largeSegments.clear();
78
+
79
+ // Update last clear timestamp
80
+ try {
81
+ localStorage.setItem(keys.buildLastClear(), currentTimestamp + '');
82
+ } catch (e) {
83
+ settings.log.error(LOG_PREFIX + e);
84
+ }
85
+
86
+ return false;
87
+ }
88
+
89
+ // Check if ready from cache
90
+ return isThereCache;
91
+ }
@@ -7,7 +7,6 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
10
- import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
11
10
 
12
11
  /**
13
12
  * InMemory storage factory for standalone server-side SplitFactory
@@ -18,12 +17,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
18
17
  const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
19
18
 
20
19
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
21
- const rbSegments = new RBSegmentsCacheInMemory();
22
20
  const segments = new SegmentsCacheInMemory();
23
21
 
24
22
  const storage = {
25
23
  splits,
26
- rbSegments,
27
24
  segments,
28
25
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
29
26
  impressionCounts: new ImpressionCountsCacheInMemory(),
@@ -7,7 +7,6 @@ import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
10
- import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
11
10
 
12
11
  /**
13
12
  * InMemory storage factory for standalone client-side SplitFactory
@@ -18,13 +17,11 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
18
17
  const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
19
18
 
20
19
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
21
- const rbSegments = new RBSegmentsCacheInMemory();
22
20
  const segments = new MySegmentsCacheInMemory();
23
21
  const largeSegments = new MySegmentsCacheInMemory();
24
22
 
25
23
  const storage = {
26
24
  splits,
27
- rbSegments,
28
25
  segments,
29
26
  largeSegments,
30
27
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
@@ -39,7 +36,6 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
39
36
  shared() {
40
37
  return {
41
38
  splits: this.splits,
42
- rbSegments: this.rbSegments,
43
39
  segments: new MySegmentsCacheInMemory(),
44
40
  largeSegments: new MySegmentsCacheInMemory(),
45
41
  impressions: this.impressions,
@@ -11,7 +11,6 @@ import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
11
11
  import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
12
12
  import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
13
13
  import { metadataBuilder } from '../utils';
14
- import { RBSegmentsCacheInRedis } from './RBSegmentsCacheInRedis';
15
14
 
16
15
  export interface InRedisStorageOptions {
17
16
  prefix?: string
@@ -51,7 +50,6 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
51
50
 
52
51
  return {
53
52
  splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
54
- rbSegments: new RBSegmentsCacheInRedis(log, keys, redisClient),
55
53
  segments: new SegmentsCacheInRedis(log, keys, redisClient),
56
54
  impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
57
55
  impressionCounts: impressionCountsCache,
@@ -20,7 +20,6 @@ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
20
20
  import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
21
21
  import { metadataBuilder } from '../utils';
22
22
  import { LOG_PREFIX } from '../pluggable/constants';
23
- import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
24
23
 
25
24
  const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
26
25
  const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
@@ -89,7 +88,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
89
88
  // Connects to wrapper and emits SDK_READY event on main client
90
89
  const connectPromise = wrapper.connect().then(() => {
91
90
  if (isSynchronizer) {
92
- // In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
91
+ // @TODO reuse InLocalStorage::validateCache logic
92
+ // In standalone or producer mode, clear storage if SDK key, flags filter criteria or flags spec version was modified
93
93
  return wrapper.get(keys.buildHashKey()).then((hash) => {
94
94
  const currentHash = getStorageHash(settings);
95
95
  if (hash !== currentHash) {
@@ -117,7 +117,6 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
117
117
 
118
118
  return {
119
119
  splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
120
- rbSegments: new RBSegmentsCachePluggable(log, keys, wrapper),
121
120
  segments: new SegmentsCachePluggable(log, keys, wrapper),
122
121
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
123
122
  impressionCounts: impressionCountsCache,
@@ -1,5 +1,5 @@
1
1
  import SplitIO from '../../types/splitio';
2
- import { MaybeThenable, ISplit, IRBSegment, IMySegmentsResponse } from '../dtos/types';
2
+ import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
3
3
  import { MySegmentsData } from '../sync/polling/types';
4
4
  import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types';
5
5
  import { ISettings } from '../types';
@@ -189,8 +189,6 @@ export interface ISplitsCacheBase {
189
189
  // only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
190
190
  usesSegments(): MaybeThenable<boolean>,
191
191
  clear(): MaybeThenable<boolean | void>,
192
- // should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
193
- checkCache(): MaybeThenable<boolean>,
194
192
  killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
195
193
  getNamesByFlagSets(flagSets: string[]): MaybeThenable<Set<string>[]>
196
194
  }
@@ -205,7 +203,6 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
205
203
  trafficTypeExists(trafficType: string): boolean,
206
204
  usesSegments(): boolean,
207
205
  clear(): void,
208
- checkCache(): boolean,
209
206
  killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
210
207
  getNamesByFlagSets(flagSets: string[]): Set<string>[]
211
208
  }
@@ -220,39 +217,10 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
220
217
  trafficTypeExists(trafficType: string): Promise<boolean>,
221
218
  usesSegments(): Promise<boolean>,
222
219
  clear(): Promise<boolean | void>,
223
- checkCache(): Promise<boolean>,
224
220
  killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
225
221
  getNamesByFlagSets(flagSets: string[]): Promise<Set<string>[]>
226
222
  }
227
223
 
228
- /** Rule-Based Segments cache */
229
-
230
- export interface IRBSegmentsCacheBase {
231
- update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): MaybeThenable<boolean>,
232
- get(name: string): MaybeThenable<IRBSegment | null>,
233
- getChangeNumber(): MaybeThenable<number>,
234
- clear(): MaybeThenable<boolean | void>,
235
- contains(names: Set<string>): MaybeThenable<boolean>,
236
- }
237
-
238
- export interface IRBSegmentsCacheSync extends IRBSegmentsCacheBase {
239
- update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean,
240
- get(name: string): IRBSegment | null,
241
- getChangeNumber(): number,
242
- clear(): void,
243
- contains(names: Set<string>): boolean,
244
- // Used only for smart pausing in client-side standalone. Returns true if the storage contains a RBSegment using segments or large segments matchers
245
- usesSegments(): boolean,
246
- }
247
-
248
- export interface IRBSegmentsCacheAsync extends IRBSegmentsCacheBase {
249
- update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): Promise<boolean>,
250
- get(name: string): Promise<IRBSegment | null>,
251
- getChangeNumber(): Promise<number>,
252
- clear(): Promise<boolean | void>,
253
- contains(names: Set<string>): Promise<boolean>,
254
- }
255
-
256
224
  /** Segments cache */
257
225
 
258
226
  export interface ISegmentsCacheBase {
@@ -451,7 +419,6 @@ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync,
451
419
 
452
420
  export interface IStorageBase<
453
421
  TSplitsCache extends ISplitsCacheBase = ISplitsCacheBase,
454
- TRBSegmentsCache extends IRBSegmentsCacheBase = IRBSegmentsCacheBase,
455
422
  TSegmentsCache extends ISegmentsCacheBase = ISegmentsCacheBase,
456
423
  TImpressionsCache extends IImpressionsCacheBase = IImpressionsCacheBase,
457
424
  TImpressionsCountCache extends IImpressionCountsCacheBase = IImpressionCountsCacheBase,
@@ -460,7 +427,6 @@ export interface IStorageBase<
460
427
  TUniqueKeysCache extends IUniqueKeysCacheBase = IUniqueKeysCacheBase
461
428
  > {
462
429
  splits: TSplitsCache,
463
- rbSegments: TRBSegmentsCache,
464
430
  segments: TSegmentsCache,
465
431
  impressions: TImpressionsCache,
466
432
  impressionCounts: TImpressionsCountCache,
@@ -473,7 +439,6 @@ export interface IStorageBase<
473
439
 
474
440
  export interface IStorageSync extends IStorageBase<
475
441
  ISplitsCacheSync,
476
- IRBSegmentsCacheSync,
477
442
  ISegmentsCacheSync,
478
443
  IImpressionsCacheSync,
479
444
  IImpressionCountsCacheSync,
@@ -482,12 +447,12 @@ export interface IStorageSync extends IStorageBase<
482
447
  IUniqueKeysCacheSync
483
448
  > {
484
449
  // Defined in client-side
450
+ validateCache?: () => boolean, // @TODO support async
485
451
  largeSegments?: ISegmentsCacheSync,
486
452
  }
487
453
 
488
454
  export interface IStorageAsync extends IStorageBase<
489
455
  ISplitsCacheAsync,
490
- IRBSegmentsCacheAsync,
491
456
  ISegmentsCacheAsync,
492
457
  IImpressionsCacheAsync | IImpressionsCacheSync,
493
458
  IImpressionCountsCacheBase,
@@ -30,6 +30,7 @@ export function impressionsToJSON(impressions: SplitIO.ImpressionDTO[], metadata
30
30
  c: impression.changeNumber,
31
31
  m: impression.time,
32
32
  pt: impression.pt,
33
+ properties: impression.properties
33
34
  }
34
35
  };
35
36
 
@@ -1,6 +1,6 @@
1
1
  import { forOwn } from '../../../utils/lang';
2
2
  import { IReadinessManager } from '../../../readiness/types';
3
- import { ISplitsCacheSync } from '../../../storages/types';
3
+ import { IStorageSync } from '../../../storages/types';
4
4
  import { ISplitsParser } from '../splitsParser/types';
5
5
  import { ISplit, ISplitPartial } from '../../../dtos/types';
6
6
  import { syncTaskFactory } from '../../syncTask';
@@ -15,7 +15,7 @@ import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/c
15
15
  */
16
16
  export function fromObjectUpdaterFactory(
17
17
  splitsParser: ISplitsParser,
18
- storage: { splits: ISplitsCacheSync },
18
+ storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
19
19
  readiness: IReadinessManager,
20
20
  settings: ISettings,
21
21
  ): () => Promise<boolean> {
@@ -59,9 +59,10 @@ export function fromObjectUpdaterFactory(
59
59
 
60
60
  if (startingUp) {
61
61
  startingUp = false;
62
- Promise.resolve(splitsCache.checkCache()).then(cacheReady => {
62
+ const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
63
+ Promise.resolve().then(() => {
63
64
  // Emits SDK_READY_FROM_CACHE
64
- if (cacheReady) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
65
+ if (isCacheLoaded) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
65
66
  // Emits SDK_READY
66
67
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
67
68
  });
@@ -79,7 +80,7 @@ export function fromObjectUpdaterFactory(
79
80
  */
80
81
  export function fromObjectSyncTaskFactory(
81
82
  splitsParser: ISplitsParser,
82
- storage: { splits: ISplitsCacheSync },
83
+ storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
83
84
  readiness: IReadinessManager,
84
85
  settings: ISettings
85
86
  ): ISyncTask<[], boolean> {
@@ -11,12 +11,11 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
11
11
  since: number,
12
12
  noCache?: boolean,
13
13
  till?: number,
14
- rbSince?: number,
15
14
  // Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
16
15
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
17
16
  ) {
18
17
 
19
- let splitsPromise = fetchSplitChanges(since, noCache, till, rbSince);
18
+ let splitsPromise = fetchSplitChanges(since, noCache, till);
20
19
  if (decorator) splitsPromise = decorator(splitsPromise);
21
20
 
22
21
  return splitsPromise.then(resp => resp.json());
@@ -5,7 +5,6 @@ export type ISplitChangesFetcher = (
5
5
  since: number,
6
6
  noCache?: boolean,
7
7
  till?: number,
8
- rbSince?: number,
9
8
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
10
9
  ) => Promise<ISplitChangesResponse>
11
10
 
@@ -43,10 +43,10 @@ export function pollingManagerCSFactory(
43
43
  // smart pausing
44
44
  readiness.splits.on(SDK_SPLITS_ARRIVED, () => {
45
45
  if (!splitsSyncTask.isRunning()) return; // noop if not doing polling
46
- const usingSegments = storage.splits.usesSegments() || storage.rbSegments.usesSegments();
47
- if (usingSegments !== mySegmentsSyncTask.isRunning()) {
48
- log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
49
- if (usingSegments) {
46
+ const splitsHaveSegments = storage.splits.usesSegments();
47
+ if (splitsHaveSegments !== mySegmentsSyncTask.isRunning()) {
48
+ log.info(POLLING_SMART_PAUSING, [splitsHaveSegments ? 'ON' : 'OFF']);
49
+ if (splitsHaveSegments) {
50
50
  startMySegmentsSyncTasks();
51
51
  } else {
52
52
  stopMySegmentsSyncTasks();
@@ -59,9 +59,9 @@ export function pollingManagerCSFactory(
59
59
 
60
60
  // smart ready
61
61
  function smartReady() {
62
- if (!readiness.isReady() && !storage.splits.usesSegments() && !storage.rbSegments.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
62
+ if (!readiness.isReady() && !storage.splits.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
63
63
  }
64
- if (!storage.splits.usesSegments() && !storage.rbSegments.usesSegments()) setTimeout(smartReady, 0);
64
+ if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
65
65
  else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
66
66
 
67
67
  mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
@@ -77,7 +77,7 @@ export function pollingManagerCSFactory(
77
77
  log.info(POLLING_START);
78
78
 
79
79
  splitsSyncTask.start();
80
- if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) startMySegmentsSyncTasks();
80
+ if (storage.splits.usesSegments()) startMySegmentsSyncTasks();
81
81
  },
82
82
 
83
83
  // Stop periodic fetching (polling)
@@ -1,10 +1,10 @@
1
- import { IRBSegment, ISplit } from '../../dtos/types';
1
+ import { ISplit } from '../../dtos/types';
2
2
  import { IReadinessManager } from '../../readiness/types';
3
3
  import { IStorageSync } from '../../storages/types';
4
4
  import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../streaming/types';
5
5
  import { ITask, ISyncTask } from '../types';
6
6
 
7
- export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit | IRBSegment, changeNumber: number }], boolean> { }
7
+ export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }], boolean> { }
8
8
 
9
9
  export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
10
10
 
@@ -27,7 +27,7 @@ export function mySegmentsUpdaterFactory(
27
27
  matchingKey: string
28
28
  ): IMySegmentsUpdater {
29
29
 
30
- const { splits, rbSegments, segments, largeSegments } = storage;
30
+ const { splits, segments, largeSegments } = storage;
31
31
  let readyOnAlreadyExistentState = true;
32
32
  let startingUp = true;
33
33
 
@@ -51,7 +51,7 @@ export function mySegmentsUpdaterFactory(
51
51
  }
52
52
 
53
53
  // Notify update if required
54
- if ((splits.usesSegments() || rbSegments.usesSegments()) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
54
+ if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
55
55
  readyOnAlreadyExistentState = false;
56
56
  segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
57
57
  }
@@ -51,7 +51,7 @@ export function segmentChangesUpdaterFactory(
51
51
  * Returned promise will not be rejected.
52
52
  *
53
53
  * @param fetchOnlyNew - if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
54
- * This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE or RB_SEGMENT_UPDATE notifications.
54
+ * This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE notifications.
55
55
  * @param segmentName - segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
56
56
  * @param noCache - true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
57
57
  * @param till - till target for the provided segmentName, for CDN bypass.
@@ -1,18 +1,16 @@
1
1
  import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types';
2
2
  import { ISplitChangesFetcher } from '../fetchers/types';
3
- import { IRBSegment, ISplit, ISplitChangesResponse, ISplitFiltersValidation, MaybeThenable } from '../../../dtos/types';
3
+ import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
4
4
  import { ISplitsEventEmitter } from '../../../readiness/types';
5
5
  import { timeout } from '../../../utils/promise/timeout';
6
- import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
6
+ import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
7
7
  import { ILogger } from '../../../logger/types';
8
- import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
8
+ import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
9
9
  import { startsWith } from '../../../utils/lang';
10
- import { IN_RULE_BASED_SEGMENT, IN_SEGMENT } from '../../../utils/constants';
10
+ import { IN_SEGMENT } from '../../../utils/constants';
11
11
  import { setToArray } from '../../../utils/lang/sets';
12
- import { SPLIT_UPDATE } from '../../streaming/constants';
13
12
 
14
- export type InstantUpdate = { payload: ISplit | IRBSegment, changeNumber: number, type: string };
15
- type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
13
+ type ISplitChangesUpdater = (noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) => Promise<boolean>
16
14
 
17
15
  // Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
18
16
  // Returns a promise that could be rejected.
@@ -29,24 +27,24 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
29
27
  * Collect segments from a raw split definition.
30
28
  * Exported for testing purposes.
31
29
  */
32
- export function parseSegments(ruleEntity: ISplit | IRBSegment, matcherType: typeof IN_SEGMENT | typeof IN_RULE_BASED_SEGMENT = IN_SEGMENT): Set<string> {
33
- const { conditions, excluded } = ruleEntity as IRBSegment;
34
- const segments = new Set<string>(excluded && excluded.segments);
30
+ export function parseSegments({ conditions }: ISplit): Set<string> {
31
+ let segments = new Set<string>();
35
32
 
36
33
  for (let i = 0; i < conditions.length; i++) {
37
34
  const matchers = conditions[i].matcherGroup.matchers;
38
35
 
39
36
  matchers.forEach(matcher => {
40
- if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
37
+ if (matcher.matcherType === IN_SEGMENT) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
41
38
  });
42
39
  }
43
40
 
44
41
  return segments;
45
42
  }
46
43
 
47
- interface ISplitMutations<T extends ISplit | IRBSegment> {
48
- added: T[],
49
- removed: T[]
44
+ interface ISplitMutations {
45
+ added: ISplit[],
46
+ removed: ISplit[],
47
+ segments: string[]
50
48
  }
51
49
 
52
50
  /**
@@ -75,21 +73,25 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
75
73
  * i.e., an object with added splits, removed splits and used segments.
76
74
  * Exported for testing purposes.
77
75
  */
78
- export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>, segments: Set<string>, filters?: ISplitFiltersValidation): ISplitMutations<T> {
76
+ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersValidation): ISplitMutations {
77
+ const segments = new Set<string>();
78
+ const computed = entries.reduce((accum, split) => {
79
+ if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
80
+ accum.added.push(split);
79
81
 
80
- return rules.reduce((accum, ruleEntity) => {
81
- if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
82
- accum.added.push(ruleEntity);
83
-
84
- parseSegments(ruleEntity).forEach((segmentName: string) => {
82
+ parseSegments(split).forEach((segmentName: string) => {
85
83
  segments.add(segmentName);
86
84
  });
87
85
  } else {
88
- accum.removed.push(ruleEntity);
86
+ accum.removed.push(split);
89
87
  }
90
88
 
91
89
  return accum;
92
- }, { added: [], removed: [] } as ISplitMutations<T>);
90
+ }, { added: [], removed: [], segments: [] } as ISplitMutations);
91
+
92
+ computed.segments = setToArray(segments);
93
+
94
+ return computed;
93
95
  }
94
96
 
95
97
  /**
@@ -109,14 +111,14 @@ export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>,
109
111
  export function splitChangesUpdaterFactory(
110
112
  log: ILogger,
111
113
  splitChangesFetcher: ISplitChangesFetcher,
112
- storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments'>,
114
+ storage: Pick<IStorageBase, 'splits' | 'segments'>,
113
115
  splitFiltersValidation: ISplitFiltersValidation,
114
116
  splitsEventEmitter?: ISplitsEventEmitter,
115
117
  requestTimeoutBeforeReady: number = 0,
116
118
  retriesOnFailureBeforeReady: number = 0,
117
119
  isClientSide?: boolean
118
- ): SplitChangesUpdater {
119
- const { splits, rbSegments, segments } = storage;
120
+ ): ISplitChangesUpdater {
121
+ const { splits, segments } = storage;
120
122
 
121
123
  let startingUp = true;
122
124
 
@@ -133,53 +135,32 @@ export function splitChangesUpdaterFactory(
133
135
  * @param noCache - true to revalidate data to fetch
134
136
  * @param till - query param to bypass CDN requests
135
137
  */
136
- return function splitChangesUpdater(noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) {
138
+ return function splitChangesUpdater(noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) {
137
139
 
138
140
  /**
139
141
  * @param since - current changeNumber at splitsCache
140
142
  * @param retry - current number of retry attempts
141
143
  */
142
- function _splitChangesUpdater(sinces: [number, number], retry = 0): Promise<boolean> {
143
- const [since, rbSince] = sinces;
144
- log.debug(SYNC_SPLITS_FETCH, sinces);
145
- const fetcherPromise = Promise.resolve(
146
- instantUpdate ?
147
- instantUpdate.type === SPLIT_UPDATE ?
148
- // IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
149
- Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
150
- return contains ?
151
- { ff: { d: [instantUpdate.payload as ISplit], t: instantUpdate.changeNumber } } :
152
- splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
153
- }) :
154
- { rbs: { d: [instantUpdate.payload as IRBSegment], t: instantUpdate.changeNumber } } :
155
- splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
144
+ function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
145
+ log.debug(SYNC_SPLITS_FETCH, [since]);
146
+ return Promise.resolve(splitUpdateNotification ?
147
+ { splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
148
+ splitChangesFetcher(since, noCache, till, _promiseDecorator)
156
149
  )
157
150
  .then((splitChanges: ISplitChangesResponse) => {
158
151
  startingUp = false;
159
152
 
160
- const usedSegments = new Set<string>();
161
-
162
- let ffUpdate: MaybeThenable<boolean> = false;
163
- if (splitChanges.ff) {
164
- const { added, removed } = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation);
165
- log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
166
- ffUpdate = splits.update(added, removed, splitChanges.ff.t);
167
- }
153
+ const mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
168
154
 
169
- let rbsUpdate: MaybeThenable<boolean> = false;
170
- if (splitChanges.rbs) {
171
- const { added, removed } = computeMutation(splitChanges.rbs.d, usedSegments);
172
- log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
173
- rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
174
- }
155
+ log.debug(SYNC_SPLITS_UPDATE, [mutation.added.length, mutation.removed.length, mutation.segments.length]);
175
156
 
176
- return Promise.all([ffUpdate, rbsUpdate,
177
- // @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
178
- segments.registerSegments(setToArray(usedSegments))
179
- ]).then(([ffChanged, rbsChanged]) => {
157
+ return Promise.all([
158
+ splits.update(mutation.added, mutation.removed, splitChanges.till),
159
+ segments.registerSegments(mutation.segments)
160
+ ]).then(([isThereUpdate]) => {
180
161
  if (splitsEventEmitter) {
181
162
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
182
- return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
163
+ return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && isThereUpdate && (isClientSide || checkAllSegmentsExist(segments))))
183
164
  .catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
184
165
  .then(emitSplitsArrivedEvent => {
185
166
  // emit SDK events
@@ -196,24 +177,15 @@ export function splitChangesUpdaterFactory(
196
177
  if (startingUp && retriesOnFailureBeforeReady > retry) {
197
178
  retry += 1;
198
179
  log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
199
- return _splitChangesUpdater(sinces, retry);
180
+ return _splitChangesUpdater(since, retry);
200
181
  } else {
201
182
  startingUp = false;
202
183
  }
203
184
  return false;
204
185
  });
205
-
206
- // After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
207
- // Wrapping in a promise since checkCache can be async.
208
- if (splitsEventEmitter && startingUp) {
209
- Promise.resolve(splits.checkCache()).then(isCacheReady => {
210
- if (isCacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
211
- });
212
- }
213
- return fetcherPromise;
214
186
  }
215
187
 
216
- // `getChangeNumber` never rejects or throws error
217
- return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
188
+ let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
189
+ return sincePromise.then(_splitChangesUpdater);
218
190
  };
219
191
  }