@splitsoftware/splitio-commons 0.1.1-canary.6 → 0.1.1-rc.18

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 (269) hide show
  1. package/cjs/evaluator/matchers/matcherTypes.js +4 -4
  2. package/cjs/evaluator/matchersTransform/index.js +11 -11
  3. package/cjs/evaluator/value/sanitize.js +6 -6
  4. package/cjs/listeners/browser.js +5 -2
  5. package/cjs/listeners/node.js +9 -2
  6. package/cjs/logger/constants.js +3 -1
  7. package/cjs/logger/messages/error.js +3 -2
  8. package/cjs/logger/messages/info.js +2 -2
  9. package/cjs/logger/messages/warn.js +2 -1
  10. package/cjs/readiness/readinessManager.js +10 -7
  11. package/cjs/sdkFactory/index.js +1 -1
  12. package/cjs/services/splitApi.js +9 -1
  13. package/cjs/services/splitHttpClient.js +5 -4
  14. package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
  15. package/cjs/storages/inLocalStorage/index.js +5 -2
  16. package/cjs/storages/inMemory/InMemoryStorage.js +2 -0
  17. package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -0
  18. package/cjs/storages/inRedis/SplitsCacheInRedis.js +6 -2
  19. package/cjs/storages/inRedis/index.js +5 -2
  20. package/cjs/storages/pluggable/SplitsCachePluggable.js +6 -2
  21. package/cjs/storages/pluggable/inMemoryWrapper.js +6 -7
  22. package/cjs/storages/pluggable/index.js +5 -2
  23. package/cjs/storages/pluggable/wrapperAdapter.js +0 -1
  24. package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +92 -89
  25. package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +45 -42
  26. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +14 -4
  27. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +0 -8
  28. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +30 -10
  29. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +2 -4
  30. package/cjs/sync/streaming/SSEClient/index.js +38 -20
  31. package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
  32. package/cjs/sync/streaming/SSEHandler/NotificationParser.js +4 -1
  33. package/cjs/sync/streaming/SSEHandler/index.js +8 -9
  34. package/cjs/sync/streaming/SSEHandler/types.js +14 -0
  35. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
  36. package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
  37. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
  38. package/cjs/sync/streaming/constants.js +3 -1
  39. package/cjs/sync/streaming/mySegmentsV2utils.js +75 -0
  40. package/cjs/sync/streaming/pushManager.js +148 -40
  41. package/cjs/sync/submitters/metricsSyncTask.js +1 -1
  42. package/cjs/sync/submitters/submitterSyncTask.js +2 -2
  43. package/cjs/sync/syncManagerFromFile.js +15 -0
  44. package/cjs/sync/syncManagerFromObject.js +14 -0
  45. package/cjs/sync/syncManagerOffline.js +3 -3
  46. package/cjs/sync/syncManagerOnline.js +18 -5
  47. package/cjs/sync/syncTask.js +1 -1
  48. package/cjs/trackers/impressionObserver/ImpressionObserver.js +0 -2
  49. package/cjs/trackers/impressionObserver/buildKey.js +3 -9
  50. package/cjs/trackers/impressionObserver/impressionObserverCS.js +2 -2
  51. package/cjs/trackers/impressionObserver/impressionObserverSS.js +3 -3
  52. package/cjs/utils/constants/index.js +4 -1
  53. package/cjs/utils/decompress/index.js +427 -0
  54. package/cjs/utils/murmur3/{commons.js → common.js} +2 -6
  55. package/cjs/utils/murmur3/murmur3.js +11 -12
  56. package/cjs/utils/murmur3/murmur3_128.js +7 -142
  57. package/cjs/utils/murmur3/murmur3_128_x86.js +154 -0
  58. package/cjs/utils/murmur3/murmur3_64.js +36 -0
  59. package/cjs/utils/murmur3/utfx.js +100 -106
  60. package/cjs/utils/promise/wrapper.js +14 -11
  61. package/cjs/utils/settingsValidation/index.js +5 -2
  62. package/cjs/utils/settingsValidation/localhost/index.js +20 -0
  63. package/cjs/utils/settingsValidation/splitFilters.js +0 -1
  64. package/cjs/utils/settingsValidation/storage/storageCS.js +18 -8
  65. package/cjs/utils/settingsValidation/url.js +1 -1
  66. package/esm/evaluator/matchers/matcherTypes.js +2 -2
  67. package/esm/evaluator/matchersTransform/index.js +12 -12
  68. package/esm/evaluator/value/sanitize.js +7 -7
  69. package/esm/listeners/browser.js +5 -2
  70. package/esm/listeners/node.js +9 -2
  71. package/esm/logger/constants.js +2 -0
  72. package/esm/logger/messages/error.js +3 -2
  73. package/esm/logger/messages/info.js +2 -2
  74. package/esm/logger/messages/warn.js +2 -1
  75. package/esm/readiness/readinessManager.js +10 -7
  76. package/esm/sdkFactory/index.js +1 -1
  77. package/esm/services/splitApi.js +9 -1
  78. package/esm/services/splitHttpClient.js +5 -4
  79. package/esm/storages/AbstractSplitsCacheSync.js +1 -1
  80. package/esm/storages/inLocalStorage/index.js +5 -2
  81. package/esm/storages/inMemory/InMemoryStorage.js +2 -0
  82. package/esm/storages/inMemory/InMemoryStorageCS.js +2 -0
  83. package/esm/storages/inRedis/SplitsCacheInRedis.js +6 -2
  84. package/esm/storages/inRedis/index.js +5 -2
  85. package/esm/storages/pluggable/SplitsCachePluggable.js +6 -2
  86. package/esm/storages/pluggable/inMemoryWrapper.js +6 -7
  87. package/esm/storages/pluggable/index.js +5 -2
  88. package/esm/storages/pluggable/wrapperAdapter.js +0 -1
  89. package/esm/sync/offline/splitsParser/splitsParserFromFile.js +90 -88
  90. package/esm/sync/offline/splitsParser/splitsParserFromSettings.js +43 -41
  91. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +15 -5
  92. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +0 -8
  93. package/esm/sync/polling/updaters/mySegmentsUpdater.js +30 -10
  94. package/esm/sync/polling/updaters/segmentChangesUpdater.js +2 -4
  95. package/esm/sync/streaming/SSEClient/index.js +38 -20
  96. package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
  97. package/esm/sync/streaming/SSEHandler/NotificationParser.js +4 -1
  98. package/esm/sync/streaming/SSEHandler/index.js +9 -10
  99. package/esm/sync/streaming/SSEHandler/types.js +13 -1
  100. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
  101. package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
  102. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
  103. package/esm/sync/streaming/constants.js +2 -0
  104. package/esm/sync/streaming/mySegmentsV2utils.js +69 -0
  105. package/esm/sync/streaming/pushManager.js +150 -42
  106. package/esm/sync/submitters/metricsSyncTask.js +1 -1
  107. package/esm/sync/submitters/submitterSyncTask.js +2 -2
  108. package/esm/sync/syncManagerFromFile.js +11 -0
  109. package/esm/sync/syncManagerFromObject.js +10 -0
  110. package/esm/sync/syncManagerOffline.js +3 -3
  111. package/esm/sync/syncManagerOnline.js +18 -5
  112. package/esm/sync/syncTask.js +1 -1
  113. package/esm/trackers/impressionObserver/ImpressionObserver.js +0 -2
  114. package/esm/trackers/impressionObserver/buildKey.js +2 -9
  115. package/esm/trackers/impressionObserver/impressionObserverCS.js +2 -2
  116. package/esm/trackers/impressionObserver/impressionObserverSS.js +3 -3
  117. package/esm/utils/constants/index.js +3 -0
  118. package/esm/utils/decompress/index.js +424 -0
  119. package/esm/utils/murmur3/{commons.js → common.js} +1 -4
  120. package/esm/utils/murmur3/murmur3.js +1 -2
  121. package/esm/utils/murmur3/murmur3_128.js +7 -142
  122. package/esm/utils/murmur3/murmur3_128_x86.js +150 -0
  123. package/esm/utils/murmur3/murmur3_64.js +32 -0
  124. package/esm/utils/murmur3/utfx.js +96 -106
  125. package/esm/utils/promise/wrapper.js +14 -11
  126. package/esm/utils/settingsValidation/index.js +5 -2
  127. package/esm/utils/settingsValidation/localhost/index.js +16 -0
  128. package/esm/utils/settingsValidation/splitFilters.js +0 -1
  129. package/esm/utils/settingsValidation/storage/storageCS.js +16 -7
  130. package/esm/utils/settingsValidation/url.js +1 -1
  131. package/package.json +5 -5
  132. package/src/evaluator/matchers/matcherTypes.ts +2 -2
  133. package/src/evaluator/matchersTransform/index.ts +12 -12
  134. package/src/evaluator/value/sanitize.ts +7 -7
  135. package/src/listeners/browser.ts +5 -2
  136. package/src/listeners/node.ts +14 -2
  137. package/src/logger/constants.ts +2 -0
  138. package/src/logger/messages/error.ts +3 -2
  139. package/src/logger/messages/info.ts +2 -2
  140. package/src/logger/messages/warn.ts +3 -1
  141. package/src/readiness/readinessManager.ts +9 -7
  142. package/src/sdkFactory/index.ts +1 -1
  143. package/src/sdkFactory/types.ts +4 -5
  144. package/src/services/splitApi.ts +12 -3
  145. package/src/services/splitHttpClient.ts +6 -5
  146. package/src/services/types.ts +7 -3
  147. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  148. package/src/storages/inLocalStorage/index.ts +8 -4
  149. package/src/storages/inMemory/InMemoryStorage.ts +3 -0
  150. package/src/storages/inMemory/InMemoryStorageCS.ts +3 -0
  151. package/src/storages/inRedis/SplitsCacheInRedis.ts +3 -1
  152. package/src/storages/inRedis/index.ts +8 -4
  153. package/src/storages/pluggable/SegmentsCachePluggable.ts +1 -1
  154. package/src/storages/pluggable/SplitsCachePluggable.ts +3 -1
  155. package/src/storages/pluggable/inMemoryWrapper.ts +6 -7
  156. package/src/storages/pluggable/index.ts +8 -4
  157. package/src/storages/pluggable/wrapperAdapter.ts +0 -1
  158. package/src/storages/types.ts +18 -15
  159. package/src/sync/offline/splitsParser/splitsParserFromFile.ts +110 -105
  160. package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +45 -41
  161. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +15 -5
  162. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +0 -7
  163. package/src/sync/polling/types.ts +2 -1
  164. package/src/sync/polling/updaters/mySegmentsUpdater.ts +28 -10
  165. package/src/sync/polling/updaters/segmentChangesUpdater.ts +2 -3
  166. package/src/sync/streaming/AuthClient/types.ts +3 -0
  167. package/src/sync/streaming/SSEClient/index.ts +43 -23
  168. package/src/sync/streaming/SSEClient/types.ts +0 -1
  169. package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +8 -0
  170. package/src/sync/streaming/SSEHandler/NotificationParser.ts +4 -2
  171. package/src/sync/streaming/SSEHandler/index.ts +11 -20
  172. package/src/sync/streaming/SSEHandler/types.ts +37 -3
  173. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +7 -6
  174. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +2 -1
  175. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +4 -3
  176. package/src/sync/streaming/UpdateWorkers/types.ts +1 -1
  177. package/src/sync/streaming/constants.ts +2 -0
  178. package/src/sync/streaming/mySegmentsV2utils.ts +77 -0
  179. package/src/sync/streaming/pushManager.ts +145 -42
  180. package/src/sync/streaming/types.ts +14 -22
  181. package/src/sync/submitters/metricsSyncTask.ts +1 -1
  182. package/src/sync/submitters/submitterSyncTask.ts +2 -1
  183. package/src/sync/syncManagerFromFile.ts +13 -0
  184. package/src/sync/syncManagerFromObject.ts +12 -0
  185. package/src/sync/syncManagerOffline.ts +3 -3
  186. package/src/sync/syncManagerOnline.ts +19 -5
  187. package/src/sync/syncTask.ts +1 -1
  188. package/src/sync/types.ts +3 -1
  189. package/src/trackers/impressionObserver/ImpressionObserver.ts +4 -6
  190. package/src/trackers/impressionObserver/buildKey.ts +2 -16
  191. package/src/trackers/impressionObserver/impressionObserverCS.ts +2 -2
  192. package/src/trackers/impressionObserver/impressionObserverSS.ts +3 -3
  193. package/src/types.ts +16 -2
  194. package/src/utils/constants/index.ts +6 -1
  195. package/src/utils/decompress/index.ts +429 -0
  196. package/src/utils/lang/index.ts +1 -1
  197. package/src/utils/murmur3/{commons.ts → common.ts} +1 -5
  198. package/src/utils/murmur3/murmur3.ts +5 -5
  199. package/src/utils/murmur3/murmur3_128.ts +7 -180
  200. package/src/utils/murmur3/murmur3_128_x86.ts +188 -0
  201. package/src/utils/murmur3/murmur3_64.ts +36 -0
  202. package/src/utils/murmur3/utfx.ts +92 -110
  203. package/src/utils/promise/wrapper.ts +12 -9
  204. package/src/utils/settingsValidation/index.ts +8 -4
  205. package/src/utils/settingsValidation/localhost/index.ts +19 -0
  206. package/src/utils/settingsValidation/splitFilters.ts +0 -1
  207. package/src/utils/settingsValidation/storage/storageCS.ts +21 -8
  208. package/src/utils/settingsValidation/types.ts +2 -11
  209. package/src/utils/settingsValidation/url.ts +1 -1
  210. package/types/evaluator/matchers/matcherTypes.d.ts +2 -2
  211. package/types/listeners/browser.d.ts +3 -3
  212. package/types/listeners/node.d.ts +3 -2
  213. package/types/logger/constants.d.ts +2 -0
  214. package/types/sdkFactory/types.d.ts +4 -5
  215. package/types/services/types.d.ts +4 -0
  216. package/types/storages/inLocalStorage/index.d.ts +2 -2
  217. package/types/storages/inMemory/InMemoryStorage.d.ts +3 -0
  218. package/types/storages/inMemory/InMemoryStorageCS.d.ts +3 -0
  219. package/types/storages/inRedis/index.d.ts +2 -2
  220. package/types/storages/pluggable/index.d.ts +2 -2
  221. package/types/storages/types.d.ts +15 -15
  222. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +2 -7
  223. package/types/sync/offline/splitsParser/splitsParserFromSettings.d.ts +1 -5
  224. package/types/sync/polling/types.d.ts +2 -1
  225. package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
  226. package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
  227. package/types/sync/streaming/AuthClient/types.d.ts +2 -0
  228. package/types/sync/streaming/SSEClient/index.d.ts +9 -12
  229. package/types/sync/streaming/SSEClient/types.d.ts +0 -1
  230. package/types/sync/streaming/SSEHandler/NotificationParser.d.ts +3 -2
  231. package/types/sync/streaming/SSEHandler/types.d.ts +30 -2
  232. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +4 -3
  233. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  234. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  235. package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -1
  236. package/types/sync/streaming/constants.d.ts +3 -1
  237. package/types/sync/streaming/mySegmentsV2utils.d.ts +27 -0
  238. package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
  239. package/types/sync/streaming/types.d.ts +9 -5
  240. package/types/sync/submitters/submitterSyncTask.d.ts +1 -1
  241. package/types/sync/syncManagerFromFile.d.ts +2 -0
  242. package/types/sync/syncManagerFromObject.d.ts +2 -0
  243. package/types/sync/syncManagerOffline.d.ts +1 -1
  244. package/types/sync/syncTask.d.ts +1 -1
  245. package/types/sync/types.d.ts +2 -0
  246. package/types/trackers/impressionObserver/ImpressionObserver.d.ts +2 -2
  247. package/types/trackers/impressionObserver/buildKey.d.ts +1 -1
  248. package/types/trackers/impressionObserver/impressionObserverCS.d.ts +2 -2
  249. package/types/trackers/impressionObserver/impressionObserverSS.d.ts +2 -2
  250. package/types/types.d.ts +16 -2
  251. package/types/utils/constants/index.d.ts +5 -1
  252. package/types/utils/decompress/index.d.ts +16 -0
  253. package/types/utils/lang/index.d.ts +1 -1
  254. package/types/utils/murmur3/common.d.ts +12 -0
  255. package/types/utils/murmur3/murmur3.d.ts +2 -2
  256. package/types/utils/murmur3/murmur3_128.d.ts +5 -0
  257. package/types/utils/murmur3/murmur3_128_x86.d.ts +7 -0
  258. package/types/utils/murmur3/murmur3_64.d.ts +10 -0
  259. package/types/utils/murmur3/utfx.d.ts +24 -6
  260. package/types/utils/settingsValidation/index.d.ts +3 -2
  261. package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
  262. package/types/utils/settingsValidation/storage/storageCS.d.ts +7 -1
  263. package/types/utils/settingsValidation/types.d.ts +2 -10
  264. package/cjs/sync/streaming/pushManagerCS.js +0 -178
  265. package/cjs/sync/streaming/pushManagerSS.js +0 -128
  266. package/esm/sync/streaming/pushManagerCS.js +0 -174
  267. package/esm/sync/streaming/pushManagerSS.js +0 -124
  268. package/src/sync/streaming/pushManagerCS.ts +0 -237
  269. package/src/sync/streaming/pushManagerSS.ts +0 -177
@@ -3,7 +3,6 @@ import { ISSEClient } from './SSEClient/types';
3
3
  import { IStorageSync } from '../../storages/types';
4
4
  import { IReadinessManager } from '../../readiness/types';
5
5
  import { ISegmentsSyncTask, IPollingManager } from '../polling/types';
6
- import { IUpdateWorker } from './UpdateWorkers/types';
7
6
  import objectAssign from 'object-assign';
8
7
  import Backoff from '../../utils/Backoff';
9
8
  import SSEHandlerFactory from './SSEHandler';
@@ -16,9 +15,14 @@ import SSEClient from './SSEClient';
16
15
  import { IFetchAuth } from '../../services/types';
17
16
  import { ISettings } from '../../types';
18
17
  import { getMatching } from '../../utils/key';
19
- import { MY_SEGMENTS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP } from './constants';
18
+ import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
20
19
  import { IPlatform } from '../../sdkFactory/types';
21
- import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT } from '../../logger/constants';
20
+ import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 } from '../../logger/constants';
21
+ import { KeyList, UpdateStrategy } from './SSEHandler/types';
22
+ import { isInBitmap, parseBitmap, parseKeyList } from './mySegmentsV2utils';
23
+ import { ISet, _Set } from '../../utils/lang/sets';
24
+ import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
25
+ import { IAuthTokenPushEnabled } from './AuthClient/types';
22
26
 
23
27
  /**
24
28
  * PushManager factory:
@@ -34,11 +38,15 @@ export default function pushManagerFactory(
34
38
  settings: ISettings,
35
39
  ): IPushManagerCS | undefined {
36
40
 
41
+ // `userKey` is the matching key of main client in client-side SDK.
42
+ // It can be used to check if running on client-side or server-side SDK.
43
+ const userKey = settings.core.key ? getMatching(settings.core.key) : undefined; //
37
44
  const log = settings.log;
38
45
 
39
46
  let sseClient: ISSEClient;
40
47
  try {
41
- sseClient = new SSEClient(settings.urls.streaming, platform.getEventSource);
48
+ // `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
49
+ sseClient = new SSEClient(settings, userKey ? false : true, platform.getEventSource);
42
50
  } catch (e) {
43
51
  log.warn(STREAMING_FALLBACK, [e]);
44
52
  return;
@@ -50,54 +58,67 @@ export default function pushManagerFactory(
50
58
  const sseHandler = SSEHandlerFactory(log, pushEmitter);
51
59
  sseClient.setEventHandler(sseHandler);
52
60
 
61
+ // init workers
62
+ const segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
63
+ // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
64
+ const splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
65
+
53
66
  // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
54
67
  const userKeyHashes: Record<string, string> = {};
55
- const userKey = settings.core.key ? getMatching(settings.core.key) : undefined; // matching key of main client
68
+ // [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
69
+ // Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
70
+ const clients: Record<string, { hash64: Hash64, worker: MySegmentsUpdateWorker }> = {};
56
71
  if (userKey) {
57
72
  const hash = hashUserKey(userKey);
58
73
  userKeyHashes[hash] = userKey;
74
+ clients[userKey] = { hash64: hash64(userKey), worker: segmentsUpdateWorker as MySegmentsUpdateWorker };
59
75
  }
60
76
 
61
- // init workers
62
- const segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
63
- // [Only for server-side] we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
64
- const splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
65
- // [Only for client-side] map of user keys to their corresponding MySegmentsUpdateWorkers. It has a two-fold intention:
66
- // - stop workers all together when push is disconnected
67
- // - keep the current list of user keys to authenticate
68
- const workers: Record<string, IUpdateWorker> = {};
69
- if (userKey) workers[userKey] = segmentsUpdateWorker;
70
-
71
77
  // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
72
78
  let connectForNewClient = false;
73
79
 
74
- // flag that indicates if `disconnectPush` was called, either by the SyncManager (when the client is destroyed) or by a PUSH_NONRETRYABLE_ERROR error.
80
+ // flag that indicates if `stop/disconnectPush` was called, either by the SyncManager, when the client is destroyed, or due to a PUSH_NONRETRYABLE_ERROR error.
75
81
  // It is used to halt the `connectPush` process if it was in progress.
76
82
  let disconnected: boolean | undefined;
83
+ // flag that indicates a PUSH_NONRETRYABLE_ERROR, condition with which starting pushManager again is ignored.
84
+ let disabled: boolean | undefined; // `disabled` implies `disconnected === true`
77
85
 
78
86
  /** PushManager functions related to initialization */
79
87
 
80
88
  const connectPushRetryBackoff = new Backoff(connectPush, settings.scheduler.pushRetryBackoffBase);
81
89
 
82
- let timeoutId: ReturnType<typeof setTimeout>;
90
+ let timeoutIdTokenRefresh: ReturnType<typeof setTimeout>;
91
+ let timeoutIdSseOpen: ReturnType<typeof setTimeout>;
92
+
93
+ function scheduleTokenRefreshAndSse(authData: IAuthTokenPushEnabled) {
94
+ // clear scheduled tasks if exist
95
+ if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
96
+ if (timeoutIdSseOpen) clearTimeout(timeoutIdSseOpen);
83
97
 
84
- function scheduleTokenRefresh(issuedAt: number, expirationTime: number) {
85
- // clear scheduled token refresh if exists (needed when resuming PUSH)
86
- if (timeoutId) clearTimeout(timeoutId);
98
+ // Set token refresh 10 minutes before `expirationTime - issuedAt`
99
+ const decodedToken = authData.decodedToken;
100
+ const refreshTokenDelay = decodedToken.exp - decodedToken.iat - SECONDS_BEFORE_EXPIRATION;
101
+ // Default connDelay of 60 secs
102
+ const connDelay = typeof authData.connDelay === 'number' && authData.connDelay >= 0 ? authData.connDelay : 60;
87
103
 
88
- // Set token refresh 10 minutes before expirationTime
89
- const delayInSeconds = expirationTime - issuedAt - SECONDS_BEFORE_EXPIRATION;
104
+ log.info(STREAMING_REFRESH_TOKEN, [refreshTokenDelay, connDelay]);
90
105
 
91
- log.info(STREAMING_REFRESH_TOKEN, [delayInSeconds]);
106
+ timeoutIdTokenRefresh = setTimeout(connectPush, refreshTokenDelay * 1000);
92
107
 
93
- timeoutId = setTimeout(connectPush, delayInSeconds * 1000);
108
+ timeoutIdSseOpen = setTimeout(() => {
109
+ // halt if disconnected
110
+ if (disconnected) return;
111
+ sseClient.open(authData);
112
+ }, connDelay * 1000);
94
113
  }
95
114
 
96
115
  function connectPush() {
116
+ // Guard condition in case `stop/disconnectPush` has been called (e.g., calling SDK destroy, or app signal close/background)
117
+ if (disconnected) return;
118
+ log.info(STREAMING_CONNECTING, [disconnected === undefined ? '' : 'Re-']);
97
119
  disconnected = false;
98
- log.info(STREAMING_CONNECTING);
99
120
 
100
- const userKeys = userKey ? Object.keys(workers) : undefined;
121
+ const userKeys = userKey ? Object.keys(clients) : undefined;
101
122
  authenticate(userKeys).then(
102
123
  function (authData) {
103
124
  if (disconnected) return;
@@ -111,12 +132,10 @@ export default function pushManagerFactory(
111
132
  }
112
133
 
113
134
  // [Only for client-side] don't open SSE connection if a new shared client was added, since it means that a new authentication is taking place
114
- if (userKeys && userKeys.length < Object.keys(workers).length) return;
135
+ if (userKeys && userKeys.length < Object.keys(clients).length) return;
115
136
 
116
- // Connect to SSE and schedule refresh token
117
- const decodedToken = authData.decodedToken;
118
- sseClient.open(authData);
119
- scheduleTokenRefresh(decodedToken.iat, decodedToken.exp);
137
+ // Schedule SSE connection and refresh token
138
+ scheduleTokenRefreshAndSse(authData);
120
139
  }
121
140
  ).catch(
122
141
  function (error) {
@@ -138,11 +157,15 @@ export default function pushManagerFactory(
138
157
 
139
158
  // close SSE connection and cancel scheduled tasks
140
159
  function disconnectPush() {
160
+ // Halt disconnecting, just to avoid redundant logs if called multiple times
161
+ if (disconnected) return;
141
162
  disconnected = true;
142
- log.info(STREAMING_DISCONNECTING);
163
+
143
164
  sseClient.close();
165
+ log.info(STREAMING_DISCONNECTING);
144
166
 
145
- if (timeoutId) clearTimeout(timeoutId);
167
+ if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
168
+ if (timeoutIdSseOpen) clearTimeout(timeoutIdSseOpen);
146
169
  connectPushRetryBackoff.reset();
147
170
 
148
171
  stopWorkers();
@@ -151,18 +174,23 @@ export default function pushManagerFactory(
151
174
  // cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
152
175
  function stopWorkers() {
153
176
  splitsUpdateWorker.backoff.reset();
154
- if (userKey) forOwn(workers, worker => worker.backoff.reset());
177
+ if (userKey) forOwn(clients, ({ worker }) => worker.backoff.reset());
155
178
  else segmentsUpdateWorker.backoff.reset();
156
179
  }
157
180
 
158
181
  pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
159
182
 
160
- // restart backoff retry counter once push is connected
161
- pushEmitter.on(PUSH_SUBSYSTEM_UP, () => { connectPushRetryBackoff.reset(); });
183
+ // Only required when streaming connects after a PUSH_RETRYABLE_ERROR.
184
+ // Otherwise it is unnecessary (e.g, STREAMING_RESUMED).
185
+ pushEmitter.on(PUSH_SUBSYSTEM_UP, () => {
186
+ connectPushRetryBackoff.reset();
187
+ stopWorkers();
188
+ });
162
189
 
163
190
  /** Fallbacking without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
164
191
 
165
192
  pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
193
+ disabled = true;
166
194
  // Note: `stopWorkers` is been called twice, but it is not harmful
167
195
  disconnectPush();
168
196
  pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
@@ -182,21 +210,93 @@ export default function pushManagerFactory(
182
210
  pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
183
211
  });
184
212
 
213
+ /** STREAMING_RESET notification. Unlike a PUSH_RETRYABLE_ERROR, it doesn't emit PUSH_SUBSYSTEM_DOWN to fallback polling */
214
+
215
+ pushEmitter.on(ControlType.STREAMING_RESET, function handleStreamingReset() {
216
+ if (disconnected) return; // should never happen
217
+
218
+ // Minimum required clean-up.
219
+ // `disconnectPush` cannot be called because it sets `disconnected` and thus `connectPush` will not execute
220
+ if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
221
+
222
+ connectPush();
223
+ });
224
+
185
225
  /** Functions related to synchronization (Queues and Workers in the spec) */
186
226
 
187
227
  pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
188
228
  pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
229
+
189
230
  if (userKey) {
190
231
  pushEmitter.on(MY_SEGMENTS_UPDATE, function handleMySegmentsUpdate(parsedData, channel) {
191
232
  const userKeyHash = channel.split('_')[2];
192
233
  const userKey = userKeyHashes[userKeyHash];
193
- if (userKey && workers[userKey]) { // check context since it can be undefined if client has been destroyed
194
- const mySegmentsUpdateWorker = workers[userKey];
195
- mySegmentsUpdateWorker.put(
234
+ if (userKey && clients[userKey]) { // check existence since it can be undefined if client has been destroyed
235
+ clients[userKey].worker.put(
196
236
  parsedData.changeNumber,
197
237
  parsedData.includesPayload ? parsedData.segmentList ? parsedData.segmentList : [] : undefined);
198
238
  }
199
239
  });
240
+ pushEmitter.on(MY_SEGMENTS_UPDATE_V2, function handleMySegmentsUpdate(parsedData) {
241
+ switch (parsedData.u) {
242
+ case UpdateStrategy.BoundedFetchRequest: {
243
+ let bitmap: Uint8Array;
244
+ try {
245
+ bitmap = parseBitmap(parsedData.d, parsedData.c);
246
+ } catch (e) {
247
+ log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['BoundedFetchRequest', e]);
248
+ break;
249
+ }
250
+
251
+ forOwn(clients, ({ hash64, worker }) => {
252
+ if (isInBitmap(bitmap, hash64.hex)) {
253
+ worker.put(parsedData.changeNumber); // fetch mySegments
254
+ }
255
+ });
256
+ return;
257
+ }
258
+ case UpdateStrategy.KeyList: {
259
+ let keyList: KeyList, added: ISet<string>, removed: ISet<string>;
260
+ try {
261
+ keyList = parseKeyList(parsedData.d, parsedData.c);
262
+ added = new _Set(keyList.a);
263
+ removed = new _Set(keyList.r);
264
+ } catch (e) {
265
+ log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['KeyList', e]);
266
+ break;
267
+ }
268
+
269
+ forOwn(clients, ({ hash64, worker }) => {
270
+ const add = added.has(hash64.dec) ? true : removed.has(hash64.dec) ? false : undefined;
271
+ if (add !== undefined) {
272
+ worker.put(parsedData.changeNumber, {
273
+ name: parsedData.segmentName,
274
+ add
275
+ });
276
+ }
277
+ });
278
+ return;
279
+ }
280
+ case UpdateStrategy.SegmentRemoval:
281
+ if (!parsedData.segmentName) {
282
+ log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['SegmentRemoval', 'No segment name was provided']);
283
+ break;
284
+ }
285
+
286
+ forOwn(clients, ({ worker }) =>
287
+ worker.put(parsedData.changeNumber, {
288
+ name: parsedData.segmentName,
289
+ add: false
290
+ })
291
+ );
292
+ return;
293
+ }
294
+
295
+ // `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
296
+ forOwn(clients, ({ worker }) => {
297
+ worker.put(parsedData.changeNumber);
298
+ });
299
+ });
200
300
  } else {
201
301
  pushEmitter.on(SEGMENT_UPDATE, (segmentsUpdateWorker as SegmentsUpdateWorker).put);
202
302
  }
@@ -206,17 +306,19 @@ export default function pushManagerFactory(
206
306
  Object.create(pushEmitter),
207
307
  {
208
308
  // Expose functionality for starting and stoping push mode:
209
- stop: disconnectPush, // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which start polling.
309
+ stop: disconnectPush, // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
210
310
 
211
311
  start() {
312
+ // Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
313
+ if (disabled || disconnected === false) return;
314
+ disconnected = false;
212
315
  // Run in next event-loop cycle for optimization on client-side: if multiple clients are created in the same cycle than the factory, only one authentication is performed.
213
316
  setTimeout(connectPush);
214
317
  },
215
318
 
216
319
  // [Only for client-side]
217
320
  add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
218
- const mySegmentsUpdateWorker = new MySegmentsUpdateWorker(mySegmentsSyncTask);
219
- workers[userKey] = mySegmentsUpdateWorker;
321
+ clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
220
322
 
221
323
  const hash = hashUserKey(userKey);
222
324
 
@@ -239,6 +341,7 @@ export default function pushManagerFactory(
239
341
  remove(userKey: string) {
240
342
  const hash = hashUserKey(userKey);
241
343
  delete userKeyHashes[hash];
344
+ delete clients[userKey];
242
345
  }
243
346
  }
244
347
  );
@@ -1,4 +1,4 @@
1
- import { IMySegmentsUpdateData } from './SSEHandler/types';
1
+ import { IMySegmentsUpdateData, IMySegmentsUpdateV2Data, ISegmentUpdateData, ISplitUpdateData, ISplitKillData } from './SSEHandler/types';
2
2
  import { ITask } from '../types';
3
3
  import { IPollingManager, ISegmentsSyncTask } from '../polling/types';
4
4
  import { IReadinessManager } from '../../readiness/types';
@@ -6,6 +6,7 @@ import { IFetchAuth } from '../../services/types';
6
6
  import { IStorageSync } from '../../storages/types';
7
7
  import { IEventEmitter, ISettings } from '../../types';
8
8
  import { IPlatform } from '../../sdkFactory/types';
9
+ import { ControlType } from './constants';
9
10
 
10
11
  // Internal SDK events, subscribed by SyncManager and PushManager
11
12
  export type PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP'
@@ -15,6 +16,7 @@ export type PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR'
15
16
 
16
17
  // Update-type push notifications, handled by NotificationProcessor
17
18
  export type MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
19
+ export type MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
18
20
  export type SEGMENT_UPDATE = 'SEGMENT_UPDATE';
19
21
  export type SPLIT_KILL = 'SPLIT_KILL';
20
22
  export type SPLIT_UPDATE = 'SPLIT_UPDATE';
@@ -23,33 +25,23 @@ export type SPLIT_UPDATE = 'SPLIT_UPDATE';
23
25
  export type CONTROL = 'CONTROL';
24
26
  export type OCCUPANCY = 'OCCUPANCY';
25
27
 
26
- export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MY_SEGMENTS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL
28
+ export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MY_SEGMENTS_UPDATE | MY_SEGMENTS_UPDATE_V2 | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | ControlType.STREAMING_RESET
29
+
30
+ type IParsedData<T extends IPushEvent> =
31
+ T extends MY_SEGMENTS_UPDATE ? IMySegmentsUpdateData :
32
+ T extends MY_SEGMENTS_UPDATE_V2 ? IMySegmentsUpdateV2Data :
33
+ T extends SEGMENT_UPDATE ? ISegmentUpdateData :
34
+ T extends SPLIT_UPDATE ? ISplitUpdateData :
35
+ T extends SPLIT_KILL ? ISplitKillData : undefined;
27
36
 
28
37
  /**
29
38
  * EventEmitter used as Feedback Loop between the SyncManager and PushManager,
30
39
  * where the latter pushes messages and the former consumes it
31
40
  */
32
41
  export interface IPushEventEmitter extends IEventEmitter {
33
- once<T extends IPushEvent>(event: T, listener: (...args:
34
- T extends MY_SEGMENTS_UPDATE ? [parsedData: IMySegmentsUpdateData, channel: string] :
35
- T extends SEGMENT_UPDATE ? [changeNumber: number, segmentName: string] :
36
- T extends SPLIT_UPDATE ? [changeNumber: number] :
37
- T extends SPLIT_KILL ? [changeNumber: number, splitName: string, defaultTreatment: string] :
38
- any[]) => void): this;
39
-
40
- on<T extends IPushEvent>(event: T, listener: (...args:
41
- T extends MY_SEGMENTS_UPDATE ? [parsedData: IMySegmentsUpdateData, channel: string] :
42
- T extends SEGMENT_UPDATE ? [changeNumber: number, segmentName: string] :
43
- T extends SPLIT_UPDATE ? [changeNumber: number] :
44
- T extends SPLIT_KILL ? [changeNumber: number, splitName: string, defaultTreatment: string] :
45
- any[]) => void): this;
46
-
47
- emit<T extends IPushEvent>(event: T, ...args:
48
- T extends MY_SEGMENTS_UPDATE ? [parsedData: IMySegmentsUpdateData, channel: string] :
49
- T extends SEGMENT_UPDATE ? [changeNumber: number, segmentName: string] :
50
- T extends SPLIT_UPDATE ? [changeNumber: number] :
51
- T extends SPLIT_KILL ? [changeNumber: number, splitName: string, defaultTreatment: string] :
52
- any[]): boolean;
42
+ once<T extends IPushEvent>(event: T, listener: (parsedData: IParsedData<T>, channel: T extends MY_SEGMENTS_UPDATE ? string : undefined) => void): this;
43
+ on<T extends IPushEvent>(event: T, listener: (parsedData: IParsedData<T>, channel: T extends MY_SEGMENTS_UPDATE ? string : undefined) => void): this;
44
+ emit<T extends IPushEvent>(event: T, parsedData?: IParsedData<T>, channel?: T extends MY_SEGMENTS_UPDATE ? string : undefined): boolean;
53
45
  }
54
46
 
55
47
  /**
@@ -45,5 +45,5 @@ export function latenciesSyncTaskFactory(
45
45
  ): ISyncTask {
46
46
 
47
47
  // don't retry metrics.
48
- return submitterSyncTaskFactory(log, postMetricsLatencies, latenciesCache, metricsRefreshRate, 'latency metrics', latencyTracker, fromCache<number[]>('latencies'));
48
+ return submitterSyncTaskFactory(log, postMetricsLatencies, latenciesCache, metricsRefreshRate, 'latency metrics', latencyTracker, fromCache<number[]>('latencies'), 0, true);
49
49
  }
@@ -17,6 +17,7 @@ export function submitterSyncTaskFactory<TState extends { length?: number }>(
17
17
  latencyTracker?: ITimeTracker,
18
18
  fromCacheToPayload?: (cacheData: TState) => any,
19
19
  maxRetries: number = 0,
20
+ debugLogs?: boolean
20
21
  ): ISyncTask<[], void> {
21
22
 
22
23
  let retries = 0;
@@ -27,7 +28,7 @@ export function submitterSyncTaskFactory<TState extends { length?: number }>(
27
28
  const data = sourceCache.state();
28
29
 
29
30
  const dataCount: number | '' = typeof data.length === 'number' ? data.length : '';
30
- log.info(SUBMITTERS_PUSH, [dataCount, dataName]);
31
+ log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCount, dataName]);
31
32
  const latencyTrackerStop = latencyTracker && latencyTracker.start();
32
33
 
33
34
  const jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
@@ -0,0 +1,13 @@
1
+ import { splitsParserFromFileFactory } from './offline/splitsParser/splitsParserFromFile';
2
+ import { syncManagerOfflineFactory } from './syncManagerOffline';
3
+ import { SplitIO } from '../types';
4
+ import { LOCALHOST_MODE } from '../utils/constants';
5
+
6
+ // Factory of Localhost SyncManager based on yaml file.
7
+ // Requires Node 'fs' and 'path' APIs.
8
+ export function LocalhostFromFile(): SplitIO.LocalhostFactory {
9
+ const localhost = syncManagerOfflineFactory(splitsParserFromFileFactory);
10
+ // @ts-ignore
11
+ localhost.type = LOCALHOST_MODE;
12
+ return localhost;
13
+ }
@@ -0,0 +1,12 @@
1
+ import { splitsParserFromSettingsFactory } from './offline/splitsParser/splitsParserFromSettings';
2
+ import { syncManagerOfflineFactory } from './syncManagerOffline';
3
+ import { SplitIO } from '../types';
4
+ import { LOCALHOST_MODE } from '../utils/constants';
5
+
6
+ // Factory of Localhost SyncManager based on JS object.
7
+ export function LocalhostFromObject(): SplitIO.LocalhostFactory {
8
+ const localhost = syncManagerOfflineFactory(splitsParserFromSettingsFactory);
9
+ // @ts-ignore
10
+ localhost.type = LOCALHOST_MODE;
11
+ return localhost;
12
+ }
@@ -16,7 +16,7 @@ function flush() {
16
16
  * @param splitsParser e.g., `splitsParserFromFile`, `splitsParserFromSettings`.
17
17
  */
18
18
  export function syncManagerOfflineFactory(
19
- splitsParser: ISplitsParser
19
+ splitsParserFactory: () => ISplitsParser
20
20
  ): (params: ISyncManagerFactoryParams) => ISyncManagerCS {
21
21
 
22
22
  /**
@@ -29,7 +29,7 @@ export function syncManagerOfflineFactory(
29
29
  }: ISyncManagerFactoryParams): ISyncManagerCS {
30
30
 
31
31
  return objectAssign(
32
- fromObjectSyncTaskFactory(splitsParser, storage, readiness, settings),
32
+ fromObjectSyncTaskFactory(splitsParserFactory(), storage, readiness, settings),
33
33
  {
34
34
  // fake flush, that resolves immediately
35
35
  flush,
@@ -38,7 +38,7 @@ export function syncManagerOfflineFactory(
38
38
  shared(matchingKey: string, readinessManager: IReadinessManager): ISyncManager {
39
39
  return {
40
40
  start() {
41
- // In LOCALHOST mode, shared clients are ready in the next event cycle than created
41
+ // In LOCALHOST mode, shared clients are ready in the next event-loop cycle than created
42
42
  // SDK_READY cannot be emitted directly because this will not update the readiness status
43
43
  setTimeout(() => {
44
44
  readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // SDK_SPLITS_ARRIVED emitted by main SyncManager
@@ -75,17 +75,28 @@ export function syncManagerOnlineFactory(
75
75
  pollingManager.syncAll();
76
76
  }
77
77
 
78
- let running = false;
78
+ if (pushManager) {
79
+ pushManager.on(PUSH_SUBSYSTEM_UP, stopPollingAndSyncAll);
80
+ pushManager.on(PUSH_SUBSYSTEM_DOWN, startPolling);
81
+ }
82
+
83
+ let running = false; // flag that indicates whether the syncManager has been started (true) or stopped (false)
84
+ let startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization
79
85
 
80
86
  return {
87
+ pushManager,
81
88
 
89
+ /**
90
+ * Method used to start the syncManager for the first time, or resume it after being stopped.
91
+ */
82
92
  start() {
83
93
  // start syncing splits and segments
84
94
  if (pushManager) {
85
- pollingManager.syncAll();
86
- pushManager.on(PUSH_SUBSYSTEM_UP, stopPollingAndSyncAll);
87
- pushManager.on(PUSH_SUBSYSTEM_DOWN, startPolling);
88
- // Run in next event-loop cycle as in client-side SyncManager
95
+ // Doesn't call `syncAll` when the syncManager is resuming
96
+ if (startFirstTime) {
97
+ pollingManager.syncAll();
98
+ startFirstTime = false;
99
+ }
89
100
  pushManager.start();
90
101
  } else {
91
102
  pollingManager.start();
@@ -96,6 +107,9 @@ export function syncManagerOnlineFactory(
96
107
  running = true;
97
108
  },
98
109
 
110
+ /**
111
+ * Method used to stop/pause the syncManager.
112
+ */
99
113
  stop() {
100
114
  // stop syncing
101
115
  if (pushManager) pushManager.stop();
@@ -3,7 +3,7 @@ import { ILogger } from '../logger/types';
3
3
  import { ISyncTask } from './types';
4
4
 
5
5
  /**
6
- * Creates a syncTask that handles the periodic execution of a givan task ("start" and "stop" methods).
6
+ * Creates a syncTask that handles the periodic execution of a given task ("start" and "stop" methods).
7
7
  * The task can be executed once calling the "execute" method.
8
8
  * NOTE: Multiple calls to "execute" are not queued. Use "isExecuting" method to handle synchronization.
9
9
  *
package/src/sync/types.ts CHANGED
@@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types';
3
3
  import { ISplitApi } from '../services/types';
4
4
  import { IStorageSync } from '../storages/types';
5
5
  import { ISettings } from '../types';
6
+ import { IPushManager } from './streaming/types';
6
7
 
7
8
  export interface ITask<Input extends any[] = []> {
8
9
  /**
@@ -41,7 +42,8 @@ export interface ITimeTracker {
41
42
  /** SyncManager */
42
43
 
43
44
  export interface ISyncManager extends ITask {
44
- flush(): Promise<any>
45
+ flush(): Promise<any>,
46
+ pushManager?: IPushManager
45
47
  }
46
48
 
47
49
  export interface ISyncManagerCS extends ISyncManager {
@@ -2,18 +2,16 @@ import { ImpressionDTO } from '../../types';
2
2
  import LRUCache from '../../utils/LRUCache';
3
3
  import { IImpressionObserver } from './types';
4
4
 
5
- export default class ImpressionObserver implements IImpressionObserver {
6
- private cache: LRUCache<string | null, number>;
7
- private hasher: (impression: ImpressionDTO) => string | null;
5
+ export default class ImpressionObserver<K extends string | number> implements IImpressionObserver {
6
+ private cache: LRUCache<K, number>;
7
+ private hasher: (impression: ImpressionDTO) => K;
8
8
 
9
- constructor(size: number, hasher: (impression: ImpressionDTO) => string | null) {
9
+ constructor(size: number, hasher: (impression: ImpressionDTO) => K) {
10
10
  this.cache = new LRUCache(size);
11
11
  this.hasher = hasher;
12
12
  }
13
13
 
14
14
  testAndSet(impression: ImpressionDTO) {
15
- if (!impression) return;
16
-
17
15
  const hash = this.hasher(impression);
18
16
  const previous = this.cache.get(hash);
19
17
  this.cache.set(hash, impression.time);
@@ -1,19 +1,5 @@
1
1
  import { ImpressionDTO } from '../../types';
2
2
 
3
- const UNKNOWN = 'UNKNOWN';
4
-
5
- function _unknownIfNull(s: any) {
6
- return s ? s : UNKNOWN;
7
- }
8
-
9
- function _zeroIfNull(l: any) {
10
- return l ? l : 0;
11
- }
12
-
13
- export default function buildKey(impression: ImpressionDTO) {
14
- return `${_unknownIfNull(impression.keyName)}
15
- :${_unknownIfNull(impression.feature)}
16
- :${_unknownIfNull(impression.treatment)}
17
- :${_unknownIfNull(impression.label)}
18
- :${_zeroIfNull(impression.changeNumber)}`;
3
+ export function buildKey(impression: ImpressionDTO) {
4
+ return `${impression.keyName}:${impression.feature}:${impression.treatment}:${impression.label}:${impression.changeNumber}`;
19
5
  }
@@ -1,10 +1,10 @@
1
1
  import ImpressionObserver from './ImpressionObserver';
2
2
  import { hash } from '../../utils/murmur3/murmur3';
3
- import buildKey from './buildKey';
3
+ import { buildKey } from './buildKey';
4
4
  import { ImpressionDTO } from '../../types';
5
5
 
6
6
  export function hashImpression32(impression: ImpressionDTO) {
7
- return impression ? hash(buildKey(impression)).toString() : null;
7
+ return hash(buildKey(impression));
8
8
  }
9
9
 
10
10
  const LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
@@ -1,10 +1,10 @@
1
1
  import ImpressionObserver from './ImpressionObserver';
2
- import { hash128 } from '../../utils/murmur3/murmur3_128';
3
- import buildKey from './buildKey';
2
+ import { hash128 } from '../../utils/murmur3/murmur3_128_x86';
3
+ import { buildKey } from './buildKey';
4
4
  import { ImpressionDTO } from '../../types';
5
5
 
6
6
  export function hashImpression128(impression: ImpressionDTO) {
7
- return impression ? hash128(buildKey(impression)).toString() : null;
7
+ return hash128(buildKey(impression));
8
8
  }
9
9
 
10
10
  const LAST_SEEN_CACHE_SIZE = 500000; // cache up to 500k impression hashes