@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
@@ -1,3 +1,4 @@
1
+ import { ISettings } from '../../../types';
1
2
  import { IAuthTokenPushEnabled } from '../AuthClient/types';
2
3
  import { ISSEClient, ISseEventHandler } from './types';
3
4
 
@@ -5,6 +6,26 @@ const VERSION = '1.1';
5
6
 
6
7
  const CONTROL_CHANNEL_REGEX = /^control_/;
7
8
 
9
+ /**
10
+ * Build metadata headers for SSE connection.
11
+ *
12
+ * @param {ISettings} settings Validated settings.
13
+ * @returns {Record<string, string>} Headers object
14
+ */
15
+ function buildSSEHeaders(settings: ISettings) {
16
+ const headers: Record<string, string> = {
17
+ SplitSDKClientKey: settings.core.authorizationKey.slice(-4),
18
+ SplitSDKVersion: settings.version,
19
+ };
20
+
21
+ // ip and hostname are false if IPAddressesEnabled is false
22
+ const { ip, hostname } = settings.runtime;
23
+ if (ip) headers['SplitSDKMachineIP'] = ip;
24
+ if (hostname) headers['SplitSDKMachineName'] = hostname;
25
+
26
+ return headers;
27
+ }
28
+
8
29
  /**
9
30
  * Handles streaming connections with EventSource API
10
31
  */
@@ -14,23 +35,27 @@ export default class SSEClient implements ISSEClient {
14
35
  streamingUrl: string;
15
36
  connection?: InstanceType<typeof EventSource>;
16
37
  handler?: ISseEventHandler;
17
- authToken?: IAuthTokenPushEnabled;
38
+ useHeaders?: boolean;
39
+ headers: Record<string, string>;
18
40
 
19
41
  /**
20
42
  * SSEClient constructor.
21
43
  *
22
- * @param streamingUrl URL
23
- * @param eventSource optional EventSource constructor to use instead of the global one
24
- * @throws 'EventSource API is not available. ' if EventSource is not passed and global one is not available
44
+ * @param settings Validated settings.
45
+ * @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
46
+ * @param getEventSource Function to get the EventSource constructor.
47
+ * @throws 'EventSource API is not available. ' if EventSource is not available.
25
48
  */
26
- constructor(streamingUrl: string, getEventSource?: () => typeof EventSource | undefined) {
49
+ constructor(settings: ISettings, useHeaders?: boolean, getEventSource?: () => (typeof EventSource | undefined)) {
27
50
  // @ts-expect-error
28
51
  this.eventSource = getEventSource && getEventSource();
29
52
  // if eventSource is not available, throw an exception
30
53
  if (!this.eventSource) throw new Error('EventSource API is not available. ');
31
54
 
32
- this.streamingUrl = streamingUrl + '/sse';
33
- this.reopen = this.reopen.bind(this);
55
+ this.streamingUrl = settings.urls.streaming + '/sse';
56
+ // @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
57
+ this.useHeaders = useHeaders;
58
+ this.headers = buildSSEHeaders(settings);
34
59
  }
35
60
 
36
61
  setEventHandler(handler: ISseEventHandler) {
@@ -41,13 +66,11 @@ export default class SSEClient implements ISSEClient {
41
66
  * Open the connection with a given authToken
42
67
  *
43
68
  * @param {IAuthTokenPushEnabled} authToken
44
- * @throws {TypeError} if `authToken` is undefined
69
+ * @throws {TypeError} Will throw an error if `authToken` is undefined
45
70
  */
46
71
  open(authToken: IAuthTokenPushEnabled) {
47
72
  this.close(); // it closes connection if previously opened
48
73
 
49
- this.authToken = authToken;
50
-
51
74
  const channelsQueryParam = Object.keys(authToken.channels).map(
52
75
  function (channel) {
53
76
  const params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
@@ -56,12 +79,18 @@ export default class SSEClient implements ISSEClient {
56
79
  ).join(',');
57
80
  const url = `${this.streamingUrl}?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${VERSION}&heartbeats=true`; // same results using `&heartbeats=false`
58
81
 
59
- this.connection = new this.eventSource(url);
82
+ this.connection = new this.eventSource(
83
+ // For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
84
+ // because native EventSource implementations for browser doesn't support headers.
85
+ this.useHeaders ? url : url + `&SplitSDKVersion=${this.headers.SplitSDKVersion}&SplitSDKClientKey=${this.headers.SplitSDKClientKey}`,
86
+ // @ts-ignore. For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
87
+ this.useHeaders ? { headers: this.headers } : undefined
88
+ );
60
89
 
61
90
  if (this.handler) { // no need to check if SSEClient is used only by PushManager
62
- this.connection.onopen = this.handler.handleOpen;
63
- this.connection.onmessage = this.handler.handleMessage;
64
- this.connection.onerror = this.handler.handleError;
91
+ this.connection.addEventListener('open', this.handler.handleOpen);
92
+ this.connection.addEventListener('message', this.handler.handleMessage);
93
+ this.connection.addEventListener('error', this.handler.handleError);
65
94
  }
66
95
  }
67
96
 
@@ -69,13 +98,4 @@ export default class SSEClient implements ISSEClient {
69
98
  close() {
70
99
  if (this.connection) this.connection.close();
71
100
  }
72
-
73
- /**
74
- * Re-open the connection with the last given authToken.
75
- *
76
- * @throws {TypeError} if `open` has not been previously called with an authToken
77
- */
78
- reopen() {
79
- this.open(this.authToken as IAuthTokenPushEnabled);
80
- }
81
101
  }
@@ -8,7 +8,6 @@ export interface ISseEventHandler {
8
8
 
9
9
  export interface ISSEClient {
10
10
  open(authToken: IAuthTokenPushEnabled): void,
11
- reopen(): void,
12
11
  close(): void,
13
12
  setEventHandler(handler: ISseEventHandler): void
14
13
  }
@@ -62,6 +62,14 @@ export default function notificationKeeperFactory(pushEmitter: IPushEventEmitter
62
62
  },
63
63
 
64
64
  handleControlEvent(controlType: ControlType, channel: string, timestamp: number) {
65
+ /* STREAMING_RESET control event is handled by PushManager directly since it doesn't require
66
+ * tracking timestamp and state like OCCUPANCY or CONTROL. It also ignores previous
67
+ * OCCUPANCY and CONTROL notifications, and whether PUSH_SUBSYSTEM_DOWN has been emitted or not */
68
+ if (controlType === ControlType.STREAMING_RESET) {
69
+ pushEmitter.emit(controlType);
70
+ return;
71
+ }
72
+
65
73
  for (let i = 0; i < channels.length; i++) {
66
74
  const c = channels[i];
67
75
  if (c.regex.test(channel)) {
@@ -22,10 +22,12 @@ export function errorParser(error: Event): INotificationError {
22
22
  * Also assigns the type OCCUPANCY, if it corresponds, so that all supported messages (e.g., SPLIT_UPDATE, CONTROL) have a type.
23
23
  *
24
24
  * @param message
25
- * @returns parsed notification message
25
+ * @returns parsed notification message or undefined if the given event data is falsy (e.g, '' or undefined).
26
+ * For example, the EventSource implementation of React-Native for iOS emits a message event with empty data for Ably keepalive comments.
26
27
  * @throws {SyntaxError} if `message.data` or `JSON.parse(message.data).data` are invalid JSON strings
27
28
  */
28
- export function messageParser(message: MessageEvent): INotificationMessage {
29
+ export function messageParser(message: MessageEvent): INotificationMessage | undefined {
30
+ if (!message.data) return;
29
31
  const messageData = JSON.parse(message.data);
30
32
  messageData.parsedData = JSON.parse(messageData.data);
31
33
 
@@ -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
+ }