@splitsoftware/splitio-commons 0.1.1-canary.9 → 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 (254) 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 +1 -2
  5. package/cjs/listeners/node.js +0 -3
  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 -4
  12. package/cjs/services/splitApi.js +1 -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/updaters/mySegmentsUpdater.js +30 -10
  28. package/cjs/sync/streaming/SSEClient/index.js +0 -11
  29. package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
  30. package/cjs/sync/streaming/SSEHandler/NotificationParser.js +4 -1
  31. package/cjs/sync/streaming/SSEHandler/index.js +8 -9
  32. package/cjs/sync/streaming/SSEHandler/types.js +14 -0
  33. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
  34. package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
  35. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
  36. package/cjs/sync/streaming/constants.js +3 -1
  37. package/cjs/sync/streaming/mySegmentsV2utils.js +75 -0
  38. package/cjs/sync/streaming/pushManager.js +141 -40
  39. package/cjs/sync/submitters/metricsSyncTask.js +1 -1
  40. package/cjs/sync/submitters/submitterSyncTask.js +2 -2
  41. package/cjs/sync/syncManagerFromFile.js +15 -0
  42. package/cjs/sync/syncManagerFromObject.js +14 -0
  43. package/cjs/sync/syncManagerOffline.js +3 -3
  44. package/cjs/sync/syncManagerOnline.js +5 -3
  45. package/cjs/trackers/impressionObserver/ImpressionObserver.js +0 -2
  46. package/cjs/trackers/impressionObserver/buildKey.js +3 -9
  47. package/cjs/trackers/impressionObserver/impressionObserverCS.js +2 -2
  48. package/cjs/trackers/impressionObserver/impressionObserverSS.js +3 -3
  49. package/cjs/utils/constants/index.js +4 -1
  50. package/cjs/utils/decompress/index.js +427 -0
  51. package/cjs/utils/murmur3/{commons.js → common.js} +2 -6
  52. package/cjs/utils/murmur3/murmur3.js +11 -12
  53. package/cjs/utils/murmur3/murmur3_128.js +7 -142
  54. package/cjs/utils/murmur3/murmur3_128_x86.js +154 -0
  55. package/cjs/utils/murmur3/murmur3_64.js +36 -0
  56. package/cjs/utils/murmur3/utfx.js +100 -106
  57. package/cjs/utils/promise/wrapper.js +14 -11
  58. package/cjs/utils/settingsValidation/index.js +5 -2
  59. package/cjs/utils/settingsValidation/localhost/index.js +20 -0
  60. package/cjs/utils/settingsValidation/splitFilters.js +0 -1
  61. package/cjs/utils/settingsValidation/storage/storageCS.js +18 -8
  62. package/cjs/utils/settingsValidation/url.js +1 -1
  63. package/esm/evaluator/matchers/matcherTypes.js +2 -2
  64. package/esm/evaluator/matchersTransform/index.js +12 -12
  65. package/esm/evaluator/value/sanitize.js +7 -7
  66. package/esm/listeners/browser.js +1 -2
  67. package/esm/listeners/node.js +0 -3
  68. package/esm/logger/constants.js +2 -0
  69. package/esm/logger/messages/error.js +3 -2
  70. package/esm/logger/messages/info.js +2 -2
  71. package/esm/logger/messages/warn.js +2 -1
  72. package/esm/readiness/readinessManager.js +10 -7
  73. package/esm/sdkFactory/index.js +1 -4
  74. package/esm/services/splitApi.js +1 -1
  75. package/esm/services/splitHttpClient.js +5 -4
  76. package/esm/storages/AbstractSplitsCacheSync.js +1 -1
  77. package/esm/storages/inLocalStorage/index.js +5 -2
  78. package/esm/storages/inMemory/InMemoryStorage.js +2 -0
  79. package/esm/storages/inMemory/InMemoryStorageCS.js +2 -0
  80. package/esm/storages/inRedis/SplitsCacheInRedis.js +6 -2
  81. package/esm/storages/inRedis/index.js +5 -2
  82. package/esm/storages/pluggable/SplitsCachePluggable.js +6 -2
  83. package/esm/storages/pluggable/inMemoryWrapper.js +6 -7
  84. package/esm/storages/pluggable/index.js +5 -2
  85. package/esm/storages/pluggable/wrapperAdapter.js +0 -1
  86. package/esm/sync/offline/splitsParser/splitsParserFromFile.js +90 -88
  87. package/esm/sync/offline/splitsParser/splitsParserFromSettings.js +43 -41
  88. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +15 -5
  89. package/esm/sync/polling/updaters/mySegmentsUpdater.js +30 -10
  90. package/esm/sync/streaming/SSEClient/index.js +0 -11
  91. package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
  92. package/esm/sync/streaming/SSEHandler/NotificationParser.js +4 -1
  93. package/esm/sync/streaming/SSEHandler/index.js +9 -10
  94. package/esm/sync/streaming/SSEHandler/types.js +13 -1
  95. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
  96. package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
  97. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
  98. package/esm/sync/streaming/constants.js +2 -0
  99. package/esm/sync/streaming/mySegmentsV2utils.js +69 -0
  100. package/esm/sync/streaming/pushManager.js +143 -42
  101. package/esm/sync/submitters/metricsSyncTask.js +1 -1
  102. package/esm/sync/submitters/submitterSyncTask.js +2 -2
  103. package/esm/sync/syncManagerFromFile.js +11 -0
  104. package/esm/sync/syncManagerFromObject.js +10 -0
  105. package/esm/sync/syncManagerOffline.js +3 -3
  106. package/esm/sync/syncManagerOnline.js +5 -3
  107. package/esm/trackers/impressionObserver/ImpressionObserver.js +0 -2
  108. package/esm/trackers/impressionObserver/buildKey.js +2 -9
  109. package/esm/trackers/impressionObserver/impressionObserverCS.js +2 -2
  110. package/esm/trackers/impressionObserver/impressionObserverSS.js +3 -3
  111. package/esm/utils/constants/index.js +3 -0
  112. package/esm/utils/decompress/index.js +424 -0
  113. package/esm/utils/murmur3/{commons.js → common.js} +1 -4
  114. package/esm/utils/murmur3/murmur3.js +1 -2
  115. package/esm/utils/murmur3/murmur3_128.js +7 -142
  116. package/esm/utils/murmur3/murmur3_128_x86.js +150 -0
  117. package/esm/utils/murmur3/murmur3_64.js +32 -0
  118. package/esm/utils/murmur3/utfx.js +96 -106
  119. package/esm/utils/promise/wrapper.js +14 -11
  120. package/esm/utils/settingsValidation/index.js +5 -2
  121. package/esm/utils/settingsValidation/localhost/index.js +16 -0
  122. package/esm/utils/settingsValidation/splitFilters.js +0 -1
  123. package/esm/utils/settingsValidation/storage/storageCS.js +16 -7
  124. package/esm/utils/settingsValidation/url.js +1 -1
  125. package/package.json +5 -5
  126. package/src/evaluator/matchers/matcherTypes.ts +2 -2
  127. package/src/evaluator/matchersTransform/index.ts +12 -12
  128. package/src/evaluator/value/sanitize.ts +7 -7
  129. package/src/listeners/browser.ts +1 -1
  130. package/src/listeners/node.ts +1 -2
  131. package/src/logger/constants.ts +2 -0
  132. package/src/logger/messages/error.ts +3 -2
  133. package/src/logger/messages/info.ts +2 -2
  134. package/src/logger/messages/warn.ts +3 -1
  135. package/src/readiness/readinessManager.ts +9 -7
  136. package/src/sdkFactory/index.ts +1 -3
  137. package/src/sdkFactory/types.ts +3 -3
  138. package/src/services/splitApi.ts +2 -3
  139. package/src/services/splitHttpClient.ts +6 -5
  140. package/src/services/types.ts +5 -5
  141. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  142. package/src/storages/inLocalStorage/index.ts +8 -4
  143. package/src/storages/inMemory/InMemoryStorage.ts +3 -0
  144. package/src/storages/inMemory/InMemoryStorageCS.ts +3 -0
  145. package/src/storages/inRedis/SplitsCacheInRedis.ts +3 -1
  146. package/src/storages/inRedis/index.ts +8 -4
  147. package/src/storages/pluggable/SplitsCachePluggable.ts +3 -1
  148. package/src/storages/pluggable/inMemoryWrapper.ts +6 -7
  149. package/src/storages/pluggable/index.ts +8 -4
  150. package/src/storages/pluggable/wrapperAdapter.ts +0 -1
  151. package/src/storages/types.ts +18 -15
  152. package/src/sync/offline/splitsParser/splitsParserFromFile.ts +110 -105
  153. package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +45 -41
  154. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +15 -5
  155. package/src/sync/polling/types.ts +2 -1
  156. package/src/sync/polling/updaters/mySegmentsUpdater.ts +28 -10
  157. package/src/sync/streaming/AuthClient/types.ts +3 -0
  158. package/src/sync/streaming/SSEClient/index.ts +1 -15
  159. package/src/sync/streaming/SSEClient/types.ts +0 -1
  160. package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +8 -0
  161. package/src/sync/streaming/SSEHandler/NotificationParser.ts +4 -2
  162. package/src/sync/streaming/SSEHandler/index.ts +11 -20
  163. package/src/sync/streaming/SSEHandler/types.ts +37 -3
  164. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +7 -6
  165. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +2 -1
  166. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +4 -3
  167. package/src/sync/streaming/UpdateWorkers/types.ts +1 -1
  168. package/src/sync/streaming/constants.ts +2 -0
  169. package/src/sync/streaming/mySegmentsV2utils.ts +77 -0
  170. package/src/sync/streaming/pushManager.ts +139 -42
  171. package/src/sync/streaming/types.ts +14 -22
  172. package/src/sync/submitters/metricsSyncTask.ts +1 -1
  173. package/src/sync/submitters/submitterSyncTask.ts +2 -1
  174. package/src/sync/syncManagerFromFile.ts +13 -0
  175. package/src/sync/syncManagerFromObject.ts +12 -0
  176. package/src/sync/syncManagerOffline.ts +3 -3
  177. package/src/sync/syncManagerOnline.ts +6 -3
  178. package/src/trackers/impressionObserver/ImpressionObserver.ts +4 -6
  179. package/src/trackers/impressionObserver/buildKey.ts +2 -16
  180. package/src/trackers/impressionObserver/impressionObserverCS.ts +2 -2
  181. package/src/trackers/impressionObserver/impressionObserverSS.ts +3 -3
  182. package/src/types.ts +16 -2
  183. package/src/utils/constants/index.ts +6 -1
  184. package/src/utils/decompress/index.ts +429 -0
  185. package/src/utils/murmur3/{commons.ts → common.ts} +1 -5
  186. package/src/utils/murmur3/murmur3.ts +5 -5
  187. package/src/utils/murmur3/murmur3_128.ts +7 -180
  188. package/src/utils/murmur3/murmur3_128_x86.ts +188 -0
  189. package/src/utils/murmur3/murmur3_64.ts +36 -0
  190. package/src/utils/murmur3/utfx.ts +92 -110
  191. package/src/utils/promise/wrapper.ts +12 -9
  192. package/src/utils/settingsValidation/index.ts +8 -4
  193. package/src/utils/settingsValidation/localhost/index.ts +19 -0
  194. package/src/utils/settingsValidation/splitFilters.ts +0 -1
  195. package/src/utils/settingsValidation/storage/storageCS.ts +21 -8
  196. package/src/utils/settingsValidation/types.ts +2 -11
  197. package/src/utils/settingsValidation/url.ts +1 -1
  198. package/types/evaluator/matchers/matcherTypes.d.ts +2 -2
  199. package/types/listeners/browser.d.ts +1 -0
  200. package/types/listeners/node.d.ts +0 -1
  201. package/types/logger/constants.d.ts +2 -0
  202. package/types/sdkFactory/types.d.ts +3 -3
  203. package/types/services/types.d.ts +1 -0
  204. package/types/storages/inLocalStorage/index.d.ts +2 -2
  205. package/types/storages/inMemory/InMemoryStorage.d.ts +3 -0
  206. package/types/storages/inMemory/InMemoryStorageCS.d.ts +3 -0
  207. package/types/storages/inRedis/index.d.ts +2 -2
  208. package/types/storages/pluggable/index.d.ts +2 -2
  209. package/types/storages/types.d.ts +15 -15
  210. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +2 -7
  211. package/types/sync/offline/splitsParser/splitsParserFromSettings.d.ts +1 -5
  212. package/types/sync/polling/types.d.ts +2 -1
  213. package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
  214. package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
  215. package/types/sync/streaming/AuthClient/types.d.ts +2 -0
  216. package/types/sync/streaming/SSEClient/index.d.ts +1 -9
  217. package/types/sync/streaming/SSEClient/types.d.ts +0 -1
  218. package/types/sync/streaming/SSEHandler/NotificationParser.d.ts +3 -2
  219. package/types/sync/streaming/SSEHandler/types.d.ts +30 -2
  220. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +4 -3
  221. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  222. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  223. package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -1
  224. package/types/sync/streaming/constants.d.ts +3 -1
  225. package/types/sync/streaming/mySegmentsV2utils.d.ts +27 -0
  226. package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
  227. package/types/sync/streaming/types.d.ts +9 -5
  228. package/types/sync/submitters/submitterSyncTask.d.ts +1 -1
  229. package/types/sync/syncManagerFromFile.d.ts +2 -0
  230. package/types/sync/syncManagerFromObject.d.ts +2 -0
  231. package/types/sync/syncManagerOffline.d.ts +1 -1
  232. package/types/trackers/impressionObserver/ImpressionObserver.d.ts +2 -2
  233. package/types/trackers/impressionObserver/buildKey.d.ts +1 -1
  234. package/types/trackers/impressionObserver/impressionObserverCS.d.ts +2 -2
  235. package/types/trackers/impressionObserver/impressionObserverSS.d.ts +2 -2
  236. package/types/types.d.ts +16 -2
  237. package/types/utils/constants/index.d.ts +5 -1
  238. package/types/utils/decompress/index.d.ts +16 -0
  239. package/types/utils/murmur3/common.d.ts +12 -0
  240. package/types/utils/murmur3/murmur3.d.ts +2 -2
  241. package/types/utils/murmur3/murmur3_128.d.ts +5 -0
  242. package/types/utils/murmur3/murmur3_128_x86.d.ts +7 -0
  243. package/types/utils/murmur3/murmur3_64.d.ts +10 -0
  244. package/types/utils/murmur3/utfx.d.ts +24 -6
  245. package/types/utils/settingsValidation/index.d.ts +3 -2
  246. package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
  247. package/types/utils/settingsValidation/storage/storageCS.d.ts +7 -1
  248. package/types/utils/settingsValidation/types.d.ts +2 -10
  249. package/cjs/sync/streaming/pushManagerCS.js +0 -179
  250. package/cjs/sync/streaming/pushManagerSS.js +0 -128
  251. package/esm/sync/streaming/pushManagerCS.js +0 -175
  252. package/esm/sync/streaming/pushManagerSS.js +0 -124
  253. package/src/sync/streaming/pushManagerCS.ts +0 -238
  254. package/src/sync/streaming/pushManagerSS.ts +0 -177
@@ -1,9 +1,9 @@
1
1
  import { errorParser, messageParser } from './NotificationParser';
2
2
  import notificationKeeperFactory from './NotificationKeeper';
3
- import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
3
+ import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
4
4
  import { IPushEventEmitter } from '../types';
5
5
  import { ISseEventHandler } from '../SSEClient/types';
6
- import { INotificationError } from './types';
6
+ import { INotificationError, INotificationMessage } from './types';
7
7
  import { ILogger } from '../../../logger/types';
8
8
  import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
9
9
 
@@ -43,7 +43,7 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
43
43
  log.warn(STREAMING_PARSING_ERROR_FAILS, [err]);
44
44
  }
45
45
 
46
- let errorMessage = errorWithParsedData.parsedData && errorWithParsedData.parsedData.message;
46
+ let errorMessage = (errorWithParsedData.parsedData && errorWithParsedData.parsedData.message) || errorWithParsedData.message;
47
47
  log.error(ERROR_STREAMING_SSE, [errorMessage]);
48
48
 
49
49
  if (isRetryableError(errorWithParsedData)) {
@@ -55,9 +55,10 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
55
55
 
56
56
  /* NotificationProcessor */
57
57
  handleMessage(message) {
58
- let messageWithParsedData;
58
+ let messageWithParsedData: INotificationMessage | undefined;
59
59
  try {
60
60
  messageWithParsedData = messageParser(message);
61
+ if (!messageWithParsedData) return; // Messages with empty data are ignored
61
62
  } catch (err) {
62
63
  log.warn(STREAMING_PARSING_MESSAGE_FAILS, [err]);
63
64
  return;
@@ -67,30 +68,19 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
67
68
  log.debug(STREAMING_NEW_MESSAGE, [data]);
68
69
 
69
70
  // we only handle update events if streaming is up.
70
- if (!notificationKeeper.isStreamingUp() && parsedData.type !== OCCUPANCY && parsedData.type !== CONTROL)
71
+ if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1)
71
72
  return;
72
73
 
73
74
  switch (parsedData.type) {
74
75
  /* update events */
75
76
  case SPLIT_UPDATE:
76
- pushEmitter.emit(SPLIT_UPDATE,
77
- parsedData.changeNumber);
78
- break;
79
77
  case SEGMENT_UPDATE:
80
- pushEmitter.emit(SEGMENT_UPDATE,
81
- parsedData.changeNumber,
82
- parsedData.segmentName);
78
+ case MY_SEGMENTS_UPDATE_V2:
79
+ case SPLIT_KILL:
80
+ pushEmitter.emit(parsedData.type, parsedData);
83
81
  break;
84
82
  case MY_SEGMENTS_UPDATE:
85
- pushEmitter.emit(MY_SEGMENTS_UPDATE,
86
- parsedData,
87
- channel);
88
- break;
89
- case SPLIT_KILL:
90
- pushEmitter.emit(SPLIT_KILL,
91
- parsedData.changeNumber,
92
- parsedData.splitName,
93
- parsedData.defaultTreatment);
83
+ pushEmitter.emit(parsedData.type, parsedData, channel);
94
84
  break;
95
85
 
96
86
  /* occupancy & control events, handled by NotificationManagerKeeper */
@@ -100,6 +90,7 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
100
90
  case CONTROL:
101
91
  notificationKeeper.handleControlEvent(parsedData.controlType, channel, timestamp);
102
92
  break;
93
+
103
94
  default:
104
95
  break;
105
96
  }
@@ -1,5 +1,5 @@
1
1
  import { ControlType } from '../constants';
2
- import { MY_SEGMENTS_UPDATE, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY } from '../types';
2
+ import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY } from '../types';
3
3
 
4
4
  export interface IMySegmentsUpdateData {
5
5
  type: MY_SEGMENTS_UPDATE,
@@ -8,6 +8,33 @@ export interface IMySegmentsUpdateData {
8
8
  segmentList?: string[]
9
9
  }
10
10
 
11
+ export enum Compression {
12
+ None = 0,
13
+ Gzip = 1,
14
+ Zlib = 2
15
+ }
16
+
17
+ export enum UpdateStrategy {
18
+ UnboundedFetchRequest = 0,
19
+ BoundedFetchRequest = 1,
20
+ KeyList = 2,
21
+ SegmentRemoval = 3
22
+ }
23
+
24
+ export interface KeyList {
25
+ a?: string[], // decimal hash64 of user keys
26
+ r?: string[], // decimal hash64 of user keys
27
+ }
28
+
29
+ export interface IMySegmentsUpdateV2Data {
30
+ type: MY_SEGMENTS_UPDATE_V2,
31
+ changeNumber: number,
32
+ segmentName: string,
33
+ c: Compression,
34
+ d: string,
35
+ u: UpdateStrategy,
36
+ }
37
+
11
38
  export interface ISegmentUpdateData {
12
39
  type: SEGMENT_UPDATE,
13
40
  changeNumber: number,
@@ -38,6 +65,13 @@ export interface IOccupancyData {
38
65
  }
39
66
  }
40
67
 
41
- export type INotificationData = IMySegmentsUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
68
+ export type INotificationData = IMySegmentsUpdateData | IMySegmentsUpdateV2Data | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
42
69
  export type INotificationMessage = { parsedData: INotificationData, channel: string, timestamp: number, data: string }
43
- export type INotificationError = Event & { parsedData?: any }
70
+ export type INotificationError = Event & { parsedData?: any, message?: string }
71
+
72
+ export type SegmentsData = string[] | {
73
+ /* segment name */
74
+ name: string,
75
+ /* action: `true` for add, and `false` for delete */
76
+ add: boolean
77
+ }
@@ -1,6 +1,7 @@
1
1
  import { ISegmentsSyncTask } from '../../polling/types';
2
2
  import Backoff from '../../../utils/Backoff';
3
3
  import { IUpdateWorker } from './types';
4
+ import { SegmentsData } from '../SSEHandler/types';
4
5
 
5
6
  /**
6
7
  * MySegmentsUpdateWorker class
@@ -10,7 +11,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
10
11
  private readonly mySegmentsSyncTask: ISegmentsSyncTask;
11
12
  private maxChangeNumber: number;
12
13
  private handleNewEvent: boolean;
13
- private segmentList?: string[];
14
+ private segmentsData?: SegmentsData;
14
15
  private currentChangeNumber: number;
15
16
  readonly backoff: Backoff;
16
17
 
@@ -21,7 +22,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
21
22
  this.mySegmentsSyncTask = mySegmentsSyncTask;
22
23
  this.maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
23
24
  this.handleNewEvent = false;
24
- this.segmentList = undefined; // keeps the segmentList (if included in payload) from the queued event with maximum changeNumber
25
+ this.segmentsData = undefined; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
25
26
  this.currentChangeNumber = -1; // @TODO: remove once `/mySegments` endpoint provides the changeNumber
26
27
  this.put = this.put.bind(this);
27
28
  this.__handleMySegmentsUpdateCall = this.__handleMySegmentsUpdateCall.bind(this);
@@ -36,7 +37,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
36
37
  const currentMaxChangeNumber = this.maxChangeNumber;
37
38
 
38
39
  // fetch mySegments revalidating data if cached
39
- this.mySegmentsSyncTask.execute(this.segmentList, true).then((result) => {
40
+ this.mySegmentsSyncTask.execute(this.segmentsData, true).then((result) => {
40
41
  if (result !== false) // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
41
42
  this.currentChangeNumber = Math.max(this.currentChangeNumber, currentMaxChangeNumber); // use `currentMaxChangeNumber`, in case that `this.maxChangeNumber` was updated during fetch.
42
43
  if (this.handleNewEvent) {
@@ -52,15 +53,15 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
52
53
  * Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
53
54
  *
54
55
  * @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
55
- * @param {string[] | undefined} segmentList might be undefined
56
+ * @param {SegmentsData | undefined} segmentsData might be undefined
56
57
  */
57
- put(changeNumber: number, segmentList?: string[]) {
58
+ put(changeNumber: number, segmentsData?: SegmentsData) {
58
59
  if (changeNumber <= this.currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
59
60
 
60
61
  this.maxChangeNumber = changeNumber;
61
62
  this.handleNewEvent = true;
62
63
  this.backoff.reset();
63
- this.segmentList = segmentList;
64
+ this.segmentsData = segmentsData;
64
65
 
65
66
  if (this.mySegmentsSyncTask.isExecuting()) return;
66
67
 
@@ -1,6 +1,7 @@
1
1
  import { ISegmentsCacheSync } from '../../../storages/types';
2
2
  import Backoff from '../../../utils/Backoff';
3
3
  import { ISegmentsSyncTask } from '../../polling/types';
4
+ import { ISegmentUpdateData } from '../SSEHandler/types';
4
5
  import { IUpdateWorker } from './types';
5
6
 
6
7
  /**
@@ -66,7 +67,7 @@ export default class SegmentsUpdateWorker implements IUpdateWorker {
66
67
  * @param {number} changeNumber change number of the SEGMENT_UPDATE notification
67
68
  * @param {string} segmentName segment name of the SEGMENT_UPDATE notification
68
69
  */
69
- put(changeNumber: number, segmentName: string) {
70
+ put({ changeNumber, segmentName }: ISegmentUpdateData) {
70
71
  const currentChangeNumber = this.segmentsCache.getChangeNumber(segmentName);
71
72
 
72
73
  if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumbers[segmentName]) return;
@@ -3,6 +3,7 @@ import { ISplitsEventEmitter } from '../../../readiness/types';
3
3
  import { ISplitsCacheSync } from '../../../storages/types';
4
4
  import Backoff from '../../../utils/Backoff';
5
5
  import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
6
+ import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
6
7
  import { IUpdateWorker } from './types';
7
8
 
8
9
  /**
@@ -60,7 +61,7 @@ export default class SplitsUpdateWorker implements IUpdateWorker {
60
61
  *
61
62
  * @param {number} changeNumber change number of the SPLIT_UPDATE notification
62
63
  */
63
- put(changeNumber: number) {
64
+ put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
64
65
  const currentChangeNumber = this.splitsCache.getChangeNumber();
65
66
 
66
67
  if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
@@ -81,14 +82,14 @@ export default class SplitsUpdateWorker implements IUpdateWorker {
81
82
  * @param {string} splitName name of split to kill
82
83
  * @param {string} defaultTreatment default treatment value
83
84
  */
84
- killSplit(changeNumber: number, splitName: string, defaultTreatment: string) {
85
+ killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
85
86
  // @TODO handle retry due to errors in storage, once we allow the definition of custom async storages
86
87
  if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
87
88
  // trigger an SDK_UPDATE if Split was killed locally
88
89
  this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
89
90
  }
90
91
  // queues the SplitChanges fetch (only if changeNumber is newer)
91
- this.put(changeNumber);
92
+ this.put({ changeNumber });
92
93
  }
93
94
 
94
95
  }
@@ -2,5 +2,5 @@ import Backoff from '../../../utils/Backoff';
2
2
 
3
3
  export interface IUpdateWorker {
4
4
  readonly backoff: Backoff,
5
- put(changeNumber: number, ...args: any[]): void
5
+ put(...args: any[]): void
6
6
  }
@@ -26,6 +26,7 @@ export const PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN';
26
26
 
27
27
  // Update-type push notifications, handled by NotificationProcessor
28
28
  export const MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
29
+ export const MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
29
30
  export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
30
31
  export const SPLIT_KILL = 'SPLIT_KILL';
31
32
  export const SPLIT_UPDATE = 'SPLIT_UPDATE';
@@ -38,4 +39,5 @@ export enum ControlType {
38
39
  STREAMING_DISABLED = 'STREAMING_DISABLED',
39
40
  STREAMING_PAUSED = 'STREAMING_PAUSED',
40
41
  STREAMING_RESUMED = 'STREAMING_RESUMED',
42
+ STREAMING_RESET = 'STREAMING_RESET',
41
43
  }
@@ -0,0 +1,77 @@
1
+ import { algorithms } from '../../utils/decompress';
2
+ import { decodeFromBase64 } from '../../utils/base64';
3
+ import { Compression, KeyList } from './SSEHandler/types';
4
+
5
+ const GZIP = 1;
6
+ const ZLIB = 2;
7
+
8
+ function Uint8ArrayToString(myUint8Arr: Uint8Array) { // @ts-ignore
9
+ return String.fromCharCode.apply(null, myUint8Arr);
10
+ }
11
+
12
+ function StringToUint8Array(myString: string) {
13
+ const charCodes = myString.split('').map((e) => e.charCodeAt(0));
14
+ return new Uint8Array(charCodes);
15
+ }
16
+
17
+ /**
18
+ * Decode and decompress 'data' with 'compression' algorithm
19
+ *
20
+ * @param {string} data
21
+ * @param {number} compression 1 GZIP, 2 ZLIB
22
+ * @returns {Uint8Array}
23
+ * @throws if data string cannot be decoded, decompressed or the provided compression value is invalid (not 1 or 2)
24
+ */
25
+ function decompress(data: string, compression: Compression) {
26
+ let compressData = decodeFromBase64(data);
27
+ const binData = StringToUint8Array(compressData);
28
+
29
+ if (typeof algorithms === 'string') throw new Error(algorithms);
30
+ if (compression === GZIP) return algorithms.gunzipSync(binData);
31
+ if (compression === ZLIB) return algorithms.unzlibSync(binData);
32
+ throw new Error(`Invalid compression algorithm #${compression}`);
33
+ }
34
+
35
+ /**
36
+ * Decode, decompress and parse the provided 'data' into a KeyList object
37
+ *
38
+ * @param {string} data
39
+ * @param {number} compression
40
+ * @returns {{a?: string[], r?: string[] }}
41
+ * @throws if data string cannot be decoded, decompressed or parsed
42
+ */
43
+ export function parseKeyList(data: string, compression: Compression): KeyList {
44
+ const binKeyList = decompress(data, compression);
45
+ const strKeyList = Uint8ArrayToString(binKeyList);
46
+
47
+ // replace numbers to strings, to avoid losing precision
48
+ return JSON.parse(strKeyList.replace(/\d+/g, '"$&"'));
49
+ }
50
+
51
+ /**
52
+ * Decode, decompress and parse the provided 'data' into a Bitmap object
53
+ *
54
+ * @param {string} data
55
+ * @param {number} compression
56
+ * @returns {Uint8Array}
57
+ * @throws if data string cannot be decoded or decompressed
58
+ */
59
+ export function parseBitmap(data: string, compression: Compression) {
60
+ return decompress(data, compression);
61
+ }
62
+
63
+ /**
64
+ * Check if the 'bitmap' bit at 'hash64hex' position is 1
65
+ *
66
+ * @param {Uint8Array} bitmap
67
+ * @param {string} hash64hex 16-chars string, representing a number in hexa
68
+ * @returns {boolean}
69
+ */
70
+ export function isInBitmap(bitmap: Uint8Array, hash64hex: string) {
71
+ // using the lowest 32 bits as index, to avoid losing precision when converting to number
72
+ const index = parseInt(hash64hex.slice(8), 16) % (bitmap.length * 8);
73
+
74
+ const internal = Math.floor(index / 8);
75
+ const offset = index % 8;
76
+ return (bitmap[internal] & 1 << offset) > 0;
77
+ }
@@ -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:
@@ -54,53 +58,67 @@ export default function pushManagerFactory(
54
58
  const sseHandler = SSEHandlerFactory(log, pushEmitter);
55
59
  sseClient.setEventHandler(sseHandler);
56
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
+
57
66
  // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
58
67
  const userKeyHashes: Record<string, string> = {};
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 }> = {};
59
71
  if (userKey) {
60
72
  const hash = hashUserKey(userKey);
61
73
  userKeyHashes[hash] = userKey;
74
+ clients[userKey] = { hash64: hash64(userKey), worker: segmentsUpdateWorker as MySegmentsUpdateWorker };
62
75
  }
63
76
 
64
- // init workers
65
- const segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
66
- // [Only for server-side] we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
67
- const splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
68
- // [Only for client-side] map of user keys to their corresponding MySegmentsUpdateWorkers. It has a two-fold intention:
69
- // - stop workers all together when push is disconnected
70
- // - keep the current list of user keys to authenticate
71
- const workers: Record<string, IUpdateWorker> = {};
72
- if (userKey) workers[userKey] = segmentsUpdateWorker;
73
-
74
77
  // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
75
78
  let connectForNewClient = false;
76
79
 
77
- // 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.
78
81
  // It is used to halt the `connectPush` process if it was in progress.
79
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`
80
85
 
81
86
  /** PushManager functions related to initialization */
82
87
 
83
88
  const connectPushRetryBackoff = new Backoff(connectPush, settings.scheduler.pushRetryBackoffBase);
84
89
 
85
- 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);
86
97
 
87
- function scheduleTokenRefresh(issuedAt: number, expirationTime: number) {
88
- // clear scheduled token refresh if exists (needed when resuming PUSH)
89
- 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;
90
103
 
91
- // Set token refresh 10 minutes before expirationTime
92
- const delayInSeconds = expirationTime - issuedAt - SECONDS_BEFORE_EXPIRATION;
104
+ log.info(STREAMING_REFRESH_TOKEN, [refreshTokenDelay, connDelay]);
93
105
 
94
- log.info(STREAMING_REFRESH_TOKEN, [delayInSeconds]);
106
+ timeoutIdTokenRefresh = setTimeout(connectPush, refreshTokenDelay * 1000);
95
107
 
96
- timeoutId = setTimeout(connectPush, delayInSeconds * 1000);
108
+ timeoutIdSseOpen = setTimeout(() => {
109
+ // halt if disconnected
110
+ if (disconnected) return;
111
+ sseClient.open(authData);
112
+ }, connDelay * 1000);
97
113
  }
98
114
 
99
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-']);
100
119
  disconnected = false;
101
- log.info(STREAMING_CONNECTING);
102
120
 
103
- const userKeys = userKey ? Object.keys(workers) : undefined;
121
+ const userKeys = userKey ? Object.keys(clients) : undefined;
104
122
  authenticate(userKeys).then(
105
123
  function (authData) {
106
124
  if (disconnected) return;
@@ -114,12 +132,10 @@ export default function pushManagerFactory(
114
132
  }
115
133
 
116
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
117
- if (userKeys && userKeys.length < Object.keys(workers).length) return;
135
+ if (userKeys && userKeys.length < Object.keys(clients).length) return;
118
136
 
119
- // Connect to SSE and schedule refresh token
120
- const decodedToken = authData.decodedToken;
121
- sseClient.open(authData);
122
- scheduleTokenRefresh(decodedToken.iat, decodedToken.exp);
137
+ // Schedule SSE connection and refresh token
138
+ scheduleTokenRefreshAndSse(authData);
123
139
  }
124
140
  ).catch(
125
141
  function (error) {
@@ -141,11 +157,15 @@ export default function pushManagerFactory(
141
157
 
142
158
  // close SSE connection and cancel scheduled tasks
143
159
  function disconnectPush() {
144
- sseClient.close();
160
+ // Halt disconnecting, just to avoid redundant logs if called multiple times
161
+ if (disconnected) return;
145
162
  disconnected = true;
163
+
164
+ sseClient.close();
146
165
  log.info(STREAMING_DISCONNECTING);
147
166
 
148
- if (timeoutId) clearTimeout(timeoutId);
167
+ if (timeoutIdTokenRefresh) clearTimeout(timeoutIdTokenRefresh);
168
+ if (timeoutIdSseOpen) clearTimeout(timeoutIdSseOpen);
149
169
  connectPushRetryBackoff.reset();
150
170
 
151
171
  stopWorkers();
@@ -154,18 +174,23 @@ export default function pushManagerFactory(
154
174
  // cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
155
175
  function stopWorkers() {
156
176
  splitsUpdateWorker.backoff.reset();
157
- if (userKey) forOwn(workers, worker => worker.backoff.reset());
177
+ if (userKey) forOwn(clients, ({ worker }) => worker.backoff.reset());
158
178
  else segmentsUpdateWorker.backoff.reset();
159
179
  }
160
180
 
161
181
  pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
162
182
 
163
- // restart backoff retry counter once push is connected
164
- 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
+ });
165
189
 
166
190
  /** Fallbacking without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
167
191
 
168
192
  pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
193
+ disabled = true;
169
194
  // Note: `stopWorkers` is been called twice, but it is not harmful
170
195
  disconnectPush();
171
196
  pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
@@ -185,21 +210,93 @@ export default function pushManagerFactory(
185
210
  pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
186
211
  });
187
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
+
188
225
  /** Functions related to synchronization (Queues and Workers in the spec) */
189
226
 
190
227
  pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
191
228
  pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
229
+
192
230
  if (userKey) {
193
231
  pushEmitter.on(MY_SEGMENTS_UPDATE, function handleMySegmentsUpdate(parsedData, channel) {
194
232
  const userKeyHash = channel.split('_')[2];
195
233
  const userKey = userKeyHashes[userKeyHash];
196
- if (userKey && workers[userKey]) { // check context since it can be undefined if client has been destroyed
197
- const mySegmentsUpdateWorker = workers[userKey];
198
- mySegmentsUpdateWorker.put(
234
+ if (userKey && clients[userKey]) { // check existence since it can be undefined if client has been destroyed
235
+ clients[userKey].worker.put(
199
236
  parsedData.changeNumber,
200
237
  parsedData.includesPayload ? parsedData.segmentList ? parsedData.segmentList : [] : undefined);
201
238
  }
202
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
+ });
203
300
  } else {
204
301
  pushEmitter.on(SEGMENT_UPDATE, (segmentsUpdateWorker as SegmentsUpdateWorker).put);
205
302
  }
@@ -209,11 +306,11 @@ export default function pushManagerFactory(
209
306
  Object.create(pushEmitter),
210
307
  {
211
308
  // Expose functionality for starting and stoping push mode:
212
- 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.
213
310
 
214
311
  start() {
215
- // Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times.
216
- if (disconnected === false) return;
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;
217
314
  disconnected = false;
218
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.
219
316
  setTimeout(connectPush);
@@ -221,8 +318,7 @@ export default function pushManagerFactory(
221
318
 
222
319
  // [Only for client-side]
223
320
  add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
224
- const mySegmentsUpdateWorker = new MySegmentsUpdateWorker(mySegmentsSyncTask);
225
- workers[userKey] = mySegmentsUpdateWorker;
321
+ clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
226
322
 
227
323
  const hash = hashUserKey(userKey);
228
324
 
@@ -245,6 +341,7 @@ export default function pushManagerFactory(
245
341
  remove(userKey: string) {
246
342
  const hash = hashUserKey(userKey);
247
343
  delete userKeyHashes[hash];
344
+ delete clients[userKey];
248
345
  }
249
346
  }
250
347
  );