@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
@@ -57,15 +57,6 @@ export interface ICustomStorageWrapper {
57
57
  * The promise rejects if the operation fails.
58
58
  */
59
59
  getKeysByPrefix: (prefix: string) => Promise<string[]>
60
- /**
61
- * Returns all values which keys match the given prefix.
62
- *
63
- * @function getByPrefix
64
- * @param {string} prefix String prefix to match
65
- * @returns {Promise<string[]>} A promise that resolves with the list of values which keys match the given `prefix`.
66
- * The promise rejects if the operation fails.
67
- */
68
- getByPrefix: (prefix: string) => Promise<string[]>
69
60
  /**
70
61
  * Returns the values of all given `keys`.
71
62
  *
@@ -83,19 +74,19 @@ export interface ICustomStorageWrapper {
83
74
  *
84
75
  * @function incr
85
76
  * @param {string} key Key to increment
86
- * @returns {Promise<void>} A promise that resolves if the operation success. The promise rejects if the operation fails,
77
+ * @returns {Promise<number>} A promise that resolves with the value of key after the increment. The promise rejects if the operation fails,
87
78
  * for example, if there is a connection error or the key contains a string that can not be represented as integer.
88
79
  */
89
- incr: (key: string) => Promise<void | boolean>
80
+ incr: (key: string) => Promise<number>
90
81
  /**
91
82
  * Decrements in 1 the given `key` value or set it in -1 if the value doesn't exist.
92
83
  *
93
84
  * @function decr
94
85
  * @param {string} key Key to decrement
95
- * @returns {Promise<void>} A promise that resolves if the operation success. The promise rejects if the operation fails,
86
+ * @returns {Promise<number>} A promise that resolves with the value of key after the decrement. The promise rejects if the operation fails,
96
87
  * for example, if there is a connection error or the key contains a string that can not be represented as integer.
97
88
  */
98
- decr: (key: string) => Promise<void | boolean>
89
+ decr: (key: string) => Promise<number>
99
90
 
100
91
  /** Queue operations */
101
92
 
@@ -266,8 +257,8 @@ export interface ISegmentsCacheBase {
266
257
 
267
258
  // Same API for both variants: SegmentsCache and MySegmentsCache (client-side API)
268
259
  export interface ISegmentsCacheSync extends ISegmentsCacheBase {
269
- addToSegment(name: string, segmentKeys: string[]): boolean
270
- removeFromSegment(name: string, segmentKeys: string[]): boolean
260
+ addToSegment(name: string, segmentKeys?: string[]): boolean
261
+ removeFromSegment(name: string, segmentKeys?: string[]): boolean
271
262
  isInSegment(name: string, key?: string): boolean
272
263
  registerSegments(names: string[]): boolean
273
264
  getRegisteredSegments(): string[]
@@ -448,3 +439,15 @@ export interface IStorageFactoryParams {
448
439
  onReadyCb?: (error?: any) => void,
449
440
  metadata: IMetadata,
450
441
  }
442
+
443
+ export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'CUSTOM';
444
+
445
+ export type IStorageSyncFactory = {
446
+ type: StorageType,
447
+ (params: IStorageFactoryParams): IStorageSync
448
+ }
449
+
450
+ export type IStorageAsyncFactory = {
451
+ type: StorageType,
452
+ (params: IStorageFactoryParams): IStorageAsync
453
+ }
@@ -7,8 +7,9 @@ import yaml from 'js-yaml';
7
7
  import { isString, endsWith, find, forOwn, uniq, } from '../../../utils/lang';
8
8
  import parseCondition, { IMockSplitEntry } from './parseCondition';
9
9
  import { ISplitPartial } from '../../../dtos/types';
10
- import { SplitIO } from '../../../types';
10
+ import { ISettings, SplitIO } from '../../../types';
11
11
  import { ILogger } from '../../../logger/types';
12
+ import { ISplitsParser } from './types';
12
13
 
13
14
  const logPrefix = 'sync:offline:splits-fetcher: ';
14
15
 
@@ -16,8 +17,6 @@ type IYamlSplitEntry = Record<string, IMockSplitEntry>
16
17
 
17
18
  const DEFAULT_FILENAME = '.split';
18
19
 
19
- let previousMock = 'NO_MOCK_LOADED';
20
-
21
20
  function configFilesPath(configFilePath?: SplitIO.MockedFeaturesFilePath): SplitIO.MockedFeaturesFilePath {
22
21
  if (configFilePath === DEFAULT_FILENAME || !isString(configFilePath)) {
23
22
  let root = process.env.HOME;
@@ -39,96 +38,6 @@ function configFilesPath(configFilePath?: SplitIO.MockedFeaturesFilePath): Split
39
38
  return configFilePath as SplitIO.MockedFeaturesFilePath;
40
39
  }
41
40
 
42
- // Parse `.split` configuration file and return a map of "Split Objects"
43
- function readSplitConfigFile(log: ILogger, filePath: SplitIO.MockedFeaturesFilePath): false | Record<string, ISplitPartial> {
44
- const SPLIT_POSITION = 0;
45
- const TREATMENT_POSITION = 1;
46
- let data;
47
-
48
- try {
49
- data = fs.readFileSync(filePath, 'utf-8');
50
- } catch (e) {
51
- log.error(e.message);
52
-
53
- return {};
54
- }
55
-
56
- if (data === previousMock) return false;
57
- previousMock = data;
58
-
59
- const splitObjects = data.split(/\r?\n/).reduce((accum, line, index) => {
60
- let tuple: string | string[] = line.trim();
61
-
62
- if (tuple === '' || tuple.charAt(0) === '#') {
63
- log.debug(logPrefix + `Ignoring empty line or comment at #${index}`);
64
- } else {
65
- tuple = tuple.split(/\s+/);
66
-
67
- if (tuple.length !== 2) {
68
- log.debug(logPrefix + `Ignoring line since it does not have exactly two columns #${index}`);
69
- } else {
70
- const splitName = tuple[SPLIT_POSITION];
71
- const condition = parseCondition({ treatment: tuple[TREATMENT_POSITION] });
72
- accum[splitName] = { conditions: [condition], configurations: {}, trafficTypeName: 'localhost' };
73
- }
74
- }
75
-
76
- return accum;
77
- }, {} as Record<string, ISplitPartial>);
78
-
79
- return splitObjects;
80
- }
81
-
82
- // Parse `.yml` or `.yaml` configuration files and return a map of "Split Objects"
83
- function readYAMLConfigFile(log: ILogger, filePath: SplitIO.MockedFeaturesFilePath): false | Record<string, ISplitPartial> {
84
- let data = '';
85
- let yamldoc = null;
86
-
87
- try {
88
- data = fs.readFileSync(filePath, 'utf8');
89
-
90
- if (data === previousMock) return false;
91
- previousMock = data;
92
-
93
- yamldoc = yaml.safeLoad(data);
94
- } catch (e) {
95
- log.error(e);
96
-
97
- return {};
98
- }
99
-
100
- // Each entry will be mapped to a condition, but we'll also keep the configurations map.
101
- const mocksData = (yamldoc as IYamlSplitEntry[]).reduce((accum, splitEntry) => {
102
- const splitName = Object.keys(splitEntry)[0];
103
-
104
- if (!splitName || !isString(splitEntry[splitName].treatment))
105
- log.error(logPrefix + 'Ignoring entry on YAML since the format is incorrect.');
106
-
107
- const mockData = splitEntry[splitName];
108
-
109
- // "Template" for each split accumulated data
110
- if (!accum[splitName]) {
111
- accum[splitName] = {
112
- configurations: {}, conditions: [], treatments: [], trafficTypeName: 'localhost'
113
- };
114
- }
115
-
116
- // Assign the config if there is one on the mock
117
- if (mockData.config) accum[splitName].configurations[mockData.treatment] = mockData.config;
118
- // Parse the condition from the entry.
119
- const condition = parseCondition(mockData);
120
- accum[splitName].conditions[condition.conditionType === 'ROLLOUT' ? 'push' : 'unshift'](condition);
121
- // Also keep track of the treatments, will be useful for manager functionality.
122
- accum[splitName].treatments.push(mockData.treatment);
123
-
124
- return accum;
125
- }, {} as Record<string, Required<ISplitPartial> & { treatments: string[] }>);
126
-
127
- arrangeConditions(mocksData);
128
-
129
- return mocksData;
130
- }
131
-
132
41
  // This function is not pure nor meant to be. Here we apply modifications to cover
133
42
  // for behaviour that's ensured by the BE.
134
43
  function arrangeConditions(mocksData: Record<string, Required<ISplitPartial> & { treatments: string[] }>) {
@@ -160,18 +69,114 @@ function arrangeConditions(mocksData: Record<string, Required<ISplitPartial> & {
160
69
  });
161
70
  }
162
71
 
163
- // Load the content of a configuration file into an Object
164
- export default function splitsParserFromFile({ features, log }: { features?: SplitIO.MockedFeaturesFilePath, log: ILogger }): false | Record<string, ISplitPartial> {
165
- const filePath = configFilesPath(features);
166
- let mockData: false | Record<string, ISplitPartial>;
167
-
168
- // If we have a filePath, it means the extension is correct, choose the parser.
169
- if (endsWith(filePath, '.split')) {
170
- log.warn(logPrefix + '.split mocks will be deprecated soon in favor of YAML files, which provide more targeting power. Take a look in our documentation.');
171
- mockData = readSplitConfigFile(log, filePath);
172
- } else {
173
- mockData = readYAMLConfigFile(log, filePath);
72
+ export function splitsParserFromFileFactory(): ISplitsParser {
73
+
74
+ let previousMock = 'NO_MOCK_LOADED';
75
+
76
+ // Parse `.split` configuration file and return a map of "Split Objects"
77
+ function readSplitConfigFile(log: ILogger, filePath: SplitIO.MockedFeaturesFilePath): false | Record<string, ISplitPartial> {
78
+ const SPLIT_POSITION = 0;
79
+ const TREATMENT_POSITION = 1;
80
+ let data;
81
+
82
+ try {
83
+ data = fs.readFileSync(filePath, 'utf-8');
84
+ } catch (e) {
85
+ log.error(e.message);
86
+
87
+ return {};
88
+ }
89
+
90
+ if (data === previousMock) return false;
91
+ previousMock = data;
92
+
93
+ const splitObjects = data.split(/\r?\n/).reduce((accum, line, index) => {
94
+ let tuple: string | string[] = line.trim();
95
+
96
+ if (tuple === '' || tuple.charAt(0) === '#') {
97
+ log.debug(logPrefix + `Ignoring empty line or comment at #${index}`);
98
+ } else {
99
+ tuple = tuple.split(/\s+/);
100
+
101
+ if (tuple.length !== 2) {
102
+ log.debug(logPrefix + `Ignoring line since it does not have exactly two columns #${index}`);
103
+ } else {
104
+ const splitName = tuple[SPLIT_POSITION];
105
+ const condition = parseCondition({ treatment: tuple[TREATMENT_POSITION] });
106
+ accum[splitName] = { conditions: [condition], configurations: {}, trafficTypeName: 'localhost' };
107
+ }
108
+ }
109
+
110
+ return accum;
111
+ }, {} as Record<string, ISplitPartial>);
112
+
113
+ return splitObjects;
114
+ }
115
+
116
+ // Parse `.yml` or `.yaml` configuration files and return a map of "Split Objects"
117
+ function readYAMLConfigFile(log: ILogger, filePath: SplitIO.MockedFeaturesFilePath): false | Record<string, ISplitPartial> {
118
+ let data = '';
119
+ let yamldoc = null;
120
+
121
+ try {
122
+ data = fs.readFileSync(filePath, 'utf8');
123
+
124
+ if (data === previousMock) return false;
125
+ previousMock = data;
126
+
127
+ yamldoc = yaml.safeLoad(data);
128
+ } catch (e) {
129
+ log.error(e);
130
+
131
+ return {};
132
+ }
133
+
134
+ // Each entry will be mapped to a condition, but we'll also keep the configurations map.
135
+ const mocksData = (yamldoc as IYamlSplitEntry[]).reduce((accum, splitEntry) => {
136
+ const splitName = Object.keys(splitEntry)[0];
137
+
138
+ if (!splitName || !isString(splitEntry[splitName].treatment))
139
+ log.error(logPrefix + 'Ignoring entry on YAML since the format is incorrect.');
140
+
141
+ const mockData = splitEntry[splitName];
142
+
143
+ // "Template" for each split accumulated data
144
+ if (!accum[splitName]) {
145
+ accum[splitName] = {
146
+ configurations: {}, conditions: [], treatments: [], trafficTypeName: 'localhost'
147
+ };
148
+ }
149
+
150
+ // Assign the config if there is one on the mock
151
+ if (mockData.config) accum[splitName].configurations[mockData.treatment] = mockData.config;
152
+ // Parse the condition from the entry.
153
+ const condition = parseCondition(mockData);
154
+ accum[splitName].conditions[condition.conditionType === 'ROLLOUT' ? 'push' : 'unshift'](condition);
155
+ // Also keep track of the treatments, will be useful for manager functionality.
156
+ accum[splitName].treatments.push(mockData.treatment);
157
+
158
+ return accum;
159
+ }, {} as Record<string, Required<ISplitPartial> & { treatments: string[] }>);
160
+
161
+ arrangeConditions(mocksData);
162
+
163
+ return mocksData;
174
164
  }
175
165
 
176
- return mockData;
166
+ // Load the content of a configuration file into an Object
167
+ return function splitsParserFromFile({ features, log }: ISettings): false | Record<string, ISplitPartial> {
168
+ const filePath = configFilesPath(features as string);
169
+ let mockData: false | Record<string, ISplitPartial>;
170
+
171
+ // If we have a filePath, it means the extension is correct, choose the parser.
172
+ if (endsWith(filePath, '.split')) {
173
+ log.warn(logPrefix + '.split mocks will be deprecated soon in favor of YAML files, which provide more targeting power. Take a look in our documentation.');
174
+ mockData = readSplitConfigFile(log, filePath);
175
+ } else {
176
+ mockData = readYAMLConfigFile(log, filePath);
177
+ }
178
+
179
+ return mockData;
180
+ };
181
+
177
182
  }
@@ -3,8 +3,6 @@ import { ISettings, SplitIO } from '../../../types';
3
3
  import { isObject, forOwn } from '../../../utils/lang';
4
4
  import parseCondition from './parseCondition';
5
5
 
6
- let previousMock: SplitIO.MockedFeaturesMap = { 'emptyMock': '1' };
7
-
8
6
  function hasTreatmentChanged(prev: string | SplitIO.TreatmentWithConfig, curr: string | SplitIO.TreatmentWithConfig) {
9
7
  if (typeof prev !== typeof curr) return true;
10
8
 
@@ -15,54 +13,60 @@ function hasTreatmentChanged(prev: string | SplitIO.TreatmentWithConfig, curr: s
15
13
  }
16
14
  }
17
15
 
18
- function mockUpdated(currentData: SplitIO.MockedFeaturesMap) {
19
- const names = Object.keys(currentData);
16
+ export function splitsParserFromSettingsFactory() {
20
17
 
21
- // Different amount of items
22
- if (names.length !== Object.keys(previousMock).length) {
23
- previousMock = currentData;
24
- return true;
25
- }
18
+ let previousMock: SplitIO.MockedFeaturesMap = { 'emptyMock': '1' };
26
19
 
27
- return names.some(name => {
28
- const newSplit = !previousMock[name];
29
- const newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
30
- const changed = newSplit || newTreatment;
20
+ function mockUpdated(currentData: SplitIO.MockedFeaturesMap) {
21
+ const names = Object.keys(currentData);
31
22
 
32
- if (changed) previousMock = currentData;
23
+ // Different amount of items
24
+ if (names.length !== Object.keys(previousMock).length) {
25
+ previousMock = currentData;
26
+ return true;
27
+ }
33
28
 
34
- return changed;
35
- });
36
- }
29
+ return names.some(name => {
30
+ const newSplit = !previousMock[name];
31
+ const newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
32
+ const changed = newSplit || newTreatment;
37
33
 
38
- /**
39
- *
40
- * @param features validated object with mocked features mapping.
41
- */
42
- export default function splitsParserFromSettings(settings: ISettings): false | Record<string, ISplitPartial> {
43
- const features = settings.features as SplitIO.MockedFeaturesMap || {};
34
+ if (changed) previousMock = currentData;
44
35
 
45
- if (!mockUpdated(features)) return false;
36
+ return changed;
37
+ });
38
+ }
46
39
 
47
- const splitObjects: Record<string, ISplitPartial> = {};
40
+ /**
41
+ *
42
+ * @param settings validated object with mocked features mapping.
43
+ */
44
+ return function splitsParserFromSettings(settings: ISettings): false | Record<string, ISplitPartial> {
45
+ const features = settings.features as SplitIO.MockedFeaturesMap || {};
48
46
 
49
- forOwn(features, (data, splitName) => {
50
- let treatment = data;
51
- let config = null;
47
+ if (!mockUpdated(features)) return false;
52
48
 
53
- if (isObject(data)) {
54
- treatment = (data as SplitIO.TreatmentWithConfig).treatment;
55
- config = (data as SplitIO.TreatmentWithConfig).config || config;
56
- }
57
- const configurations: Record<string, string> = {};
58
- if (config !== null) configurations[treatment as string] = config;
49
+ const splitObjects: Record<string, ISplitPartial> = {};
50
+
51
+ forOwn(features, (data, splitName) => {
52
+ let treatment = data;
53
+ let config = null;
54
+
55
+ if (isObject(data)) {
56
+ treatment = (data as SplitIO.TreatmentWithConfig).treatment;
57
+ config = (data as SplitIO.TreatmentWithConfig).config || config;
58
+ }
59
+ const configurations: Record<string, string> = {};
60
+ if (config !== null) configurations[treatment as string] = config;
61
+
62
+ splitObjects[splitName] = {
63
+ trafficTypeName: 'localhost',
64
+ conditions: [parseCondition({ treatment: treatment as string })],
65
+ configurations
66
+ };
67
+ });
59
68
 
60
- splitObjects[splitName] = {
61
- trafficTypeName: 'localhost',
62
- conditions: [parseCondition({ treatment: treatment as string })],
63
- configurations
64
- };
65
- });
69
+ return splitObjects;
70
+ };
66
71
 
67
- return splitObjects;
68
72
  }
@@ -7,7 +7,7 @@ import syncTaskFactory from '../../syncTask';
7
7
  import { ISyncTask } from '../../types';
8
8
  import { ISettings } from '../../../types';
9
9
  import { CONTROL } from '../../../utils/constants';
10
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
10
+ import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
11
11
  import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/constants';
12
12
 
13
13
  /**
@@ -20,7 +20,8 @@ export function fromObjectUpdaterFactory(
20
20
  settings: ISettings,
21
21
  ): () => Promise<boolean> {
22
22
 
23
- const log = settings.log;
23
+ const log = settings.log, splitsCache = storage.splits;
24
+ let startingUp = true;
24
25
 
25
26
  return function objectUpdater() {
26
27
  const splits: [string, string][] = [];
@@ -53,11 +54,20 @@ export function fromObjectUpdaterFactory(
53
54
  });
54
55
 
55
56
  return Promise.all([
56
- storage.splits.clear(),
57
- storage.splits.addSplits(splits)
57
+ splitsCache.clear(), // required to sync removed splits from mock
58
+ splitsCache.addSplits(splits)
58
59
  ]).then(() => {
59
60
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
60
- readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
61
+
62
+ if (startingUp) {
63
+ startingUp = false;
64
+ Promise.resolve(splitsCache.checkCache()).then(cacheReady => {
65
+ // Emits SDK_READY_FROM_CACHE
66
+ if (cacheReady) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
67
+ // Emits SDK_READY
68
+ readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
69
+ });
70
+ }
61
71
  return true;
62
72
  });
63
73
  } else {
@@ -2,11 +2,12 @@ import { IReadinessManager } from '../../readiness/types';
2
2
  import { ISplitApi } from '../../services/types';
3
3
  import { IStorageSync } from '../../storages/types';
4
4
  import { ISettings } from '../../types';
5
+ import { SegmentsData } from '../streaming/SSEHandler/types';
5
6
  import { ITask, ISyncTask } from '../types';
6
7
 
7
8
  export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean], boolean> { }
8
9
 
9
- export interface ISegmentsSyncTask extends ISyncTask<[segmentNames?: string[], noCache?: boolean, fetchOnlyNew?: boolean], boolean> { }
10
+ export interface ISegmentsSyncTask extends ISyncTask<[segmentNames?: SegmentsData, noCache?: boolean, fetchOnlyNew?: boolean], boolean> { }
10
11
 
11
12
  export interface IPollingManager extends ITask {
12
13
  syncAll(): Promise<any>
@@ -5,6 +5,7 @@ import timeout from '../../../utils/promise/timeout';
5
5
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
6
6
  import { ILogger } from '../../../logger/types';
7
7
  import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
8
+ import { SegmentsData } from '../../streaming/SSEHandler/types';
8
9
 
9
10
  type IMySegmentsUpdater = (segmentList?: string[], noCache?: boolean) => Promise<boolean>
10
11
 
@@ -38,9 +39,23 @@ export function mySegmentsUpdaterFactory(
38
39
  }
39
40
 
40
41
  // @TODO if allowing custom storages, handle async execution
41
- function updateSegments(segments: string[]) {
42
- // Update the list of segment names available
43
- const shouldNotifyUpdate = mySegmentsCache.resetSegments(segments);
42
+ function updateSegments(segmentsData: SegmentsData) {
43
+
44
+ let shouldNotifyUpdate;
45
+ if (Array.isArray(segmentsData)) {
46
+ // Update the list of segment names available
47
+ shouldNotifyUpdate = mySegmentsCache.resetSegments(segmentsData);
48
+ } else {
49
+ // Add/Delete the segment
50
+ const { name, add } = segmentsData;
51
+ if (mySegmentsCache.isInSegment(name) !== add) {
52
+ shouldNotifyUpdate = true;
53
+ if (add) mySegmentsCache.addToSegment(name);
54
+ else mySegmentsCache.removeFromSegment(name);
55
+ } else {
56
+ shouldNotifyUpdate = false;
57
+ }
58
+ }
44
59
 
45
60
  // Notify update if required
46
61
  if (splitsCache.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
@@ -49,10 +64,10 @@ export function mySegmentsUpdaterFactory(
49
64
  }
50
65
  }
51
66
 
52
- function _mySegmentsUpdater(retry: number, segmentList?: string[], noCache?: boolean): Promise<boolean> {
53
- const updaterPromise: Promise<boolean> = segmentList ?
54
- // If segmentList is provided, there is no need to fetch mySegments
55
- new Promise((res) => { updateSegments(segmentList); res(true); }) :
67
+ function _mySegmentsUpdater(retry: number, segmentsData?: SegmentsData, noCache?: boolean): Promise<boolean> {
68
+ const updaterPromise: Promise<boolean> = segmentsData ?
69
+ // If segmentsData is provided, there is no need to fetch mySegments
70
+ new Promise((res) => { updateSegments(segmentsData); res(true); }) :
56
71
  // If not provided, fetch mySegments
57
72
  mySegmentsFetcher(noCache, _promiseDecorator).then(segments => {
58
73
  // Only when we have downloaded segments completely, we should not keep retrying anymore
@@ -79,11 +94,14 @@ export function mySegmentsUpdaterFactory(
79
94
  * MySegments updater returns a promise that resolves with a `false` boolean value if it fails to fetch mySegments or synchronize them with the storage.
80
95
  * Returned promise will not be rejected.
81
96
  *
82
- * @param {string[] | undefined} segmentList list of mySegments names to sync in the storage. If the list is `undefined`, it fetches them before syncing in the storage.
97
+ * @param {SegmentsData | undefined} segmentsData it can be:
98
+ * (1) the list of mySegments names to sync in the storage,
99
+ * (2) an object with a segment name and action (true: add, or false: delete) to update the storage,
100
+ * (3) or `undefined`, for which the updater will fetch mySegments in order to sync the storage.
83
101
  * @param {boolean | undefined} noCache true to revalidate data to fetch
84
102
  */
85
- return function mySegmentsUpdater(segmentList?: string[], noCache?: boolean) {
86
- return _mySegmentsUpdater(0, segmentList, noCache);
103
+ return function mySegmentsUpdater(segmentsData?: SegmentsData, noCache?: boolean) {
104
+ return _mySegmentsUpdater(0, segmentsData, noCache);
87
105
  };
88
106
 
89
107
  }
@@ -5,6 +5,7 @@ export interface IAuthTokenPushEnabled {
5
5
  token: string
6
6
  decodedToken: IDecodedJWTToken
7
7
  channels: { [channel: string]: string[] }
8
+ connDelay?: number
8
9
  }
9
10
 
10
11
  export interface IAuthTokenPushDisabled {
@@ -15,3 +16,5 @@ export interface IAuthTokenPushDisabled {
15
16
  export type IAuthToken = IAuthTokenPushDisabled | IAuthTokenPushEnabled
16
17
 
17
18
  export type IAuthenticate = (userKeys?: string[]) => Promise<IAuthToken>
19
+
20
+ export type IAuthenticateV2 = (isClientSide?: boolean) => Promise<IAuthToken>
@@ -35,7 +35,6 @@ export default class SSEClient implements ISSEClient {
35
35
  streamingUrl: string;
36
36
  connection?: InstanceType<typeof EventSource>;
37
37
  handler?: ISseEventHandler;
38
- authToken?: IAuthTokenPushEnabled;
39
38
  useHeaders?: boolean;
40
39
  headers: Record<string, string>;
41
40
 
@@ -47,14 +46,13 @@ export default class SSEClient implements ISSEClient {
47
46
  * @param getEventSource Function to get the EventSource constructor.
48
47
  * @throws 'EventSource API is not available. ' if EventSource is not available.
49
48
  */
50
- constructor(settings: ISettings, useHeaders?: boolean, getEventSource?: () => typeof EventSource | undefined) {
49
+ constructor(settings: ISettings, useHeaders?: boolean, getEventSource?: () => (typeof EventSource | undefined)) {
51
50
  // @ts-expect-error
52
51
  this.eventSource = getEventSource && getEventSource();
53
52
  // if eventSource is not available, throw an exception
54
53
  if (!this.eventSource) throw new Error('EventSource API is not available. ');
55
54
 
56
55
  this.streamingUrl = settings.urls.streaming + '/sse';
57
- this.reopen = this.reopen.bind(this);
58
56
  // @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
59
57
  this.useHeaders = useHeaders;
60
58
  this.headers = buildSSEHeaders(settings);
@@ -73,8 +71,6 @@ export default class SSEClient implements ISSEClient {
73
71
  open(authToken: IAuthTokenPushEnabled) {
74
72
  this.close(); // it closes connection if previously opened
75
73
 
76
- this.authToken = authToken;
77
-
78
74
  const channelsQueryParam = Object.keys(authToken.channels).map(
79
75
  function (channel) {
80
76
  const params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
@@ -102,14 +98,4 @@ export default class SSEClient implements ISSEClient {
102
98
  close() {
103
99
  if (this.connection) this.connection.close();
104
100
  }
105
-
106
- /**
107
- * Re-open the connection with the last given authToken.
108
- *
109
- * @throws {TypeError} if `open` has not been previously called with an authToken
110
- * @TODO this method is not used currently and could be removed.
111
- */
112
- reopen() {
113
- this.open(this.authToken as IAuthTokenPushEnabled);
114
- }
115
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