@splitsoftware/splitio-commons 1.2.1-rc.1 → 1.2.1-rc.10

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 (278) hide show
  1. package/cjs/evaluator/Engine.js +6 -6
  2. package/cjs/evaluator/combiners/and.js +1 -1
  3. package/cjs/evaluator/combiners/ifelseif.js +2 -2
  4. package/cjs/evaluator/condition/engineUtils.js +2 -2
  5. package/cjs/evaluator/condition/index.js +4 -4
  6. package/cjs/evaluator/index.js +5 -5
  7. package/cjs/evaluator/matchers/cont_all.js +1 -1
  8. package/cjs/evaluator/matchers/cont_any.js +1 -1
  9. package/cjs/evaluator/matchers/cont_str.js +1 -1
  10. package/cjs/evaluator/matchers/dependency.js +1 -1
  11. package/cjs/evaluator/matchers/eq_set.js +1 -1
  12. package/cjs/evaluator/matchers/ew.js +1 -1
  13. package/cjs/evaluator/matchers/part_of.js +1 -1
  14. package/cjs/evaluator/matchers/segment.js +1 -1
  15. package/cjs/evaluator/matchers/sw.js +1 -1
  16. package/cjs/evaluator/matchers/whitelist.js +1 -1
  17. package/cjs/evaluator/matchersTransform/index.js +12 -12
  18. package/cjs/evaluator/parser/index.js +6 -6
  19. package/cjs/evaluator/treatments/index.js +1 -1
  20. package/cjs/evaluator/value/index.js +1 -1
  21. package/cjs/evaluator/value/sanitize.js +4 -4
  22. package/cjs/integrations/browser.js +3 -3
  23. package/cjs/integrations/ga/GaToSplit.js +14 -14
  24. package/cjs/integrations/ga/GoogleAnalyticsToSplit.js +5 -3
  25. package/cjs/integrations/ga/SplitToGa.js +1 -1
  26. package/cjs/integrations/ga/SplitToGoogleAnalytics.js +4 -2
  27. package/cjs/listeners/browser.js +15 -11
  28. package/cjs/listeners/node.js +1 -1
  29. package/cjs/logger/constants.js +8 -3
  30. package/cjs/logger/index.js +2 -2
  31. package/cjs/logger/messages/debug.js +4 -4
  32. package/cjs/logger/messages/error.js +5 -4
  33. package/cjs/logger/messages/info.js +7 -5
  34. package/cjs/logger/messages/warn.js +1 -1
  35. package/cjs/logger/sdkLogger.js +1 -1
  36. package/cjs/readiness/readinessManager.js +2 -2
  37. package/cjs/readiness/sdkReadinessManager.js +4 -4
  38. package/cjs/sdkClient/client.js +12 -11
  39. package/cjs/sdkClient/clientAttributesDecoration.js +4 -4
  40. package/cjs/sdkClient/clientCS.js +3 -3
  41. package/cjs/sdkClient/clientInputValidation.js +20 -22
  42. package/cjs/sdkClient/sdkClient.js +3 -6
  43. package/cjs/sdkClient/sdkClientMethod.js +1 -1
  44. package/cjs/sdkClient/sdkClientMethodCS.js +6 -10
  45. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +7 -15
  46. package/cjs/sdkFactory/index.js +14 -12
  47. package/cjs/sdkFactory/userConsentProps.js +37 -0
  48. package/cjs/sdkManager/index.js +11 -11
  49. package/cjs/services/splitApi.js +3 -2
  50. package/cjs/services/splitHttpClient.js +2 -2
  51. package/cjs/storages/KeyBuilder.js +2 -6
  52. package/cjs/storages/KeyBuilderCS.js +13 -3
  53. package/cjs/storages/KeyBuilderSS.js +1 -1
  54. package/cjs/storages/findLatencyIndex.js +1 -1
  55. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +24 -4
  56. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +12 -12
  57. package/cjs/storages/inLocalStorage/index.js +4 -4
  58. package/cjs/storages/inMemory/AttributesCacheInMemory.js +1 -1
  59. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
  60. package/cjs/storages/inMemory/ImpressionsCacheInMemory.js +15 -1
  61. package/cjs/storages/inMemory/InMemoryStorage.js +1 -1
  62. package/cjs/storages/inMemory/InMemoryStorageCS.js +1 -1
  63. package/cjs/storages/inMemory/LatenciesCacheInMemory.js +1 -1
  64. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +1 -1
  65. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +2 -2
  66. package/cjs/storages/inMemory/SplitsCacheInMemory.js +5 -5
  67. package/cjs/storages/inRedis/LatenciesCacheInRedis.js +1 -1
  68. package/cjs/storages/inRedis/RedisAdapter.js +24 -11
  69. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  70. package/cjs/storages/inRedis/SplitsCacheInRedis.js +3 -3
  71. package/cjs/storages/inRedis/index.js +1 -1
  72. package/cjs/storages/pluggable/SegmentsCachePluggable.js +1 -1
  73. package/cjs/storages/pluggable/SplitsCachePluggable.js +3 -3
  74. package/cjs/storages/pluggable/inMemoryWrapper.js +4 -4
  75. package/cjs/storages/pluggable/index.js +6 -6
  76. package/cjs/sync/offline/LocalhostFromFile.js +1 -1
  77. package/cjs/sync/offline/LocalhostFromObject.js +1 -1
  78. package/cjs/sync/offline/splitsParser/parseCondition.js +1 -1
  79. package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +13 -13
  80. package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +3 -3
  81. package/cjs/sync/offline/syncManagerOffline.js +1 -1
  82. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -2
  83. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  84. package/cjs/sync/polling/pollingManagerCS.js +6 -6
  85. package/cjs/sync/polling/pollingManagerSS.js +3 -3
  86. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +1 -1
  87. package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  88. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  89. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +1 -1
  90. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +2 -2
  91. package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -2
  92. package/cjs/sync/streaming/AuthClient/index.js +3 -3
  93. package/cjs/sync/streaming/SSEClient/index.js +2 -1
  94. package/cjs/sync/streaming/SSEHandler/NotificationParser.js +1 -1
  95. package/cjs/sync/streaming/SSEHandler/index.js +3 -3
  96. package/cjs/sync/streaming/mySegmentsV2utils.js +1 -1
  97. package/cjs/sync/streaming/pushManager.js +24 -17
  98. package/cjs/sync/submitters/eventsSyncTask.js +17 -5
  99. package/cjs/sync/submitters/impressionCountsSyncTask.js +1 -1
  100. package/cjs/sync/submitters/impressionsSyncTask.js +15 -3
  101. package/cjs/sync/submitters/metricsSyncTask.js +3 -3
  102. package/cjs/sync/submitters/submitterManager.js +4 -4
  103. package/cjs/sync/submitters/submitterSyncTask.js +1 -1
  104. package/cjs/sync/syncManagerOnline.js +12 -8
  105. package/cjs/trackers/eventTracker.js +11 -4
  106. package/cjs/trackers/impressionObserver/impressionObserverCS.js +1 -1
  107. package/cjs/trackers/impressionObserver/impressionObserverSS.js +1 -1
  108. package/cjs/trackers/impressionObserver/utils.js +8 -1
  109. package/cjs/trackers/impressionsTracker.js +9 -8
  110. package/cjs/utils/MinEvents.js +2 -1
  111. package/cjs/utils/consent.js +10 -0
  112. package/cjs/utils/constants/index.js +5 -1
  113. package/cjs/utils/inputValidation/apiKey.js +1 -1
  114. package/cjs/utils/inputValidation/attribute.js +4 -4
  115. package/cjs/utils/inputValidation/attributes.js +2 -2
  116. package/cjs/utils/inputValidation/event.js +1 -1
  117. package/cjs/utils/inputValidation/eventProperties.js +5 -5
  118. package/cjs/utils/inputValidation/eventValue.js +1 -1
  119. package/cjs/utils/inputValidation/key.js +6 -5
  120. package/cjs/utils/inputValidation/preloadedData.js +8 -8
  121. package/cjs/utils/inputValidation/split.js +1 -1
  122. package/cjs/utils/inputValidation/splits.js +2 -2
  123. package/cjs/utils/inputValidation/trafficType.js +1 -1
  124. package/cjs/utils/inputValidation/trafficTypeExistance.js +1 -1
  125. package/cjs/utils/jwt/index.js +1 -1
  126. package/cjs/utils/key/index.js +3 -3
  127. package/cjs/utils/lang/index.js +13 -4
  128. package/cjs/utils/lang/maps.js +16 -2
  129. package/cjs/utils/murmur3/common.js +1 -1
  130. package/cjs/utils/murmur3/murmur3.js +10 -10
  131. package/cjs/utils/murmur3/murmur3_128.js +1 -1
  132. package/cjs/utils/murmur3/murmur3_128_x86.js +37 -37
  133. package/cjs/utils/murmur3/murmur3_64.js +1 -1
  134. package/cjs/utils/settingsValidation/consent.js +16 -0
  135. package/cjs/utils/settingsValidation/impressionsMode.js +6 -6
  136. package/cjs/utils/settingsValidation/index.js +32 -14
  137. package/cjs/utils/settingsValidation/integrations/configurable.js +1 -1
  138. package/cjs/utils/settingsValidation/integrations/pluggable.js +1 -1
  139. package/cjs/utils/settingsValidation/localhost/builtin.js +2 -2
  140. package/cjs/utils/settingsValidation/logger/builtinLogger.js +3 -3
  141. package/cjs/utils/settingsValidation/logger/commons.js +1 -1
  142. package/cjs/utils/settingsValidation/logger/pluggableLogger.js +1 -1
  143. package/cjs/utils/settingsValidation/runtime.js +11 -0
  144. package/cjs/utils/settingsValidation/splitFilters.js +1 -1
  145. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  146. package/cjs/utils/timeTracker/index.js +3 -3
  147. package/esm/integrations/ga/GoogleAnalyticsToSplit.js +4 -2
  148. package/esm/integrations/ga/SplitToGoogleAnalytics.js +4 -2
  149. package/esm/listeners/browser.js +14 -10
  150. package/esm/logger/constants.js +5 -2
  151. package/esm/logger/messages/debug.js +3 -3
  152. package/esm/logger/messages/error.js +4 -3
  153. package/esm/logger/messages/info.js +6 -4
  154. package/esm/sdkClient/client.js +3 -2
  155. package/esm/sdkClient/clientCS.js +1 -1
  156. package/esm/sdkClient/clientInputValidation.js +6 -8
  157. package/esm/sdkClient/sdkClient.js +1 -4
  158. package/esm/sdkClient/sdkClientMethodCS.js +1 -5
  159. package/esm/sdkClient/sdkClientMethodCSWithTT.js +1 -9
  160. package/esm/sdkFactory/index.js +8 -6
  161. package/esm/sdkFactory/userConsentProps.js +33 -0
  162. package/esm/services/splitApi.js +2 -1
  163. package/esm/storages/KeyBuilder.js +2 -6
  164. package/esm/storages/KeyBuilderCS.js +11 -1
  165. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -3
  166. package/esm/storages/inLocalStorage/index.js +1 -1
  167. package/esm/storages/inMemory/ImpressionsCacheInMemory.js +15 -1
  168. package/esm/storages/inMemory/InMemoryStorage.js +1 -1
  169. package/esm/storages/inMemory/InMemoryStorageCS.js +1 -1
  170. package/esm/storages/inRedis/RedisAdapter.js +15 -2
  171. package/esm/storages/pluggable/index.js +2 -2
  172. package/esm/sync/offline/splitsParser/splitsParserFromFile.js +1 -1
  173. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +2 -2
  174. package/esm/sync/streaming/SSEClient/index.js +2 -1
  175. package/esm/sync/streaming/pushManager.js +9 -2
  176. package/esm/sync/submitters/eventsSyncTask.js +18 -6
  177. package/esm/sync/submitters/impressionsSyncTask.js +13 -1
  178. package/esm/sync/syncManagerOnline.js +11 -7
  179. package/esm/trackers/eventTracker.js +8 -1
  180. package/esm/trackers/impressionObserver/utils.js +7 -1
  181. package/esm/trackers/impressionsTracker.js +6 -5
  182. package/esm/utils/consent.js +6 -0
  183. package/esm/utils/constants/index.js +4 -0
  184. package/esm/utils/inputValidation/attributes.js +1 -1
  185. package/esm/utils/inputValidation/key.js +2 -1
  186. package/esm/utils/lang/index.js +12 -4
  187. package/esm/utils/lang/maps.js +14 -1
  188. package/esm/utils/settingsValidation/consent.js +12 -0
  189. package/esm/utils/settingsValidation/impressionsMode.js +7 -7
  190. package/esm/utils/settingsValidation/index.js +28 -10
  191. package/esm/utils/settingsValidation/runtime.js +7 -0
  192. package/package.json +8 -8
  193. package/src/evaluator/parser/index.ts +1 -1
  194. package/src/evaluator/types.ts +2 -2
  195. package/src/evaluator/value/index.ts +2 -2
  196. package/src/evaluator/value/sanitize.ts +2 -2
  197. package/src/integrations/ga/GoogleAnalyticsToSplit.ts +7 -4
  198. package/src/integrations/ga/SplitToGoogleAnalytics.ts +7 -4
  199. package/src/integrations/pluggable.ts +2 -2
  200. package/src/integrations/types.ts +5 -0
  201. package/src/listeners/browser.ts +13 -9
  202. package/src/logger/constants.ts +5 -2
  203. package/src/logger/messages/debug.ts +3 -3
  204. package/src/logger/messages/error.ts +4 -3
  205. package/src/logger/messages/info.ts +6 -4
  206. package/src/logger/types.ts +4 -0
  207. package/src/sdkClient/client.ts +3 -2
  208. package/src/sdkClient/clientCS.ts +1 -1
  209. package/src/sdkClient/clientInputValidation.ts +8 -7
  210. package/src/sdkClient/sdkClient.ts +2 -5
  211. package/src/sdkClient/sdkClientMethodCS.ts +1 -6
  212. package/src/sdkClient/sdkClientMethodCSWithTT.ts +2 -11
  213. package/src/sdkFactory/index.ts +9 -7
  214. package/src/sdkFactory/types.ts +2 -1
  215. package/src/sdkFactory/userConsentProps.ts +40 -0
  216. package/src/storages/KeyBuilder.ts +2 -6
  217. package/src/storages/KeyBuilderCS.ts +13 -1
  218. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +23 -3
  219. package/src/storages/inLocalStorage/index.ts +1 -1
  220. package/src/storages/inMemory/ImpressionsCacheInMemory.ts +22 -1
  221. package/src/storages/inMemory/InMemoryStorage.ts +1 -1
  222. package/src/storages/inMemory/InMemoryStorageCS.ts +1 -1
  223. package/src/storages/inRedis/RedisAdapter.ts +8 -2
  224. package/src/storages/pluggable/index.ts +2 -2
  225. package/src/storages/types.ts +6 -2
  226. package/src/sync/offline/splitsParser/splitsParserFromFile.ts +1 -1
  227. package/src/sync/streaming/SSEClient/index.ts +2 -1
  228. package/src/sync/streaming/pushManager.ts +11 -2
  229. package/src/sync/submitters/eventsSyncTask.ts +19 -6
  230. package/src/sync/submitters/impressionsSyncTask.ts +16 -1
  231. package/src/sync/syncManagerOnline.ts +13 -7
  232. package/src/sync/types.ts +4 -1
  233. package/src/trackers/eventTracker.ts +11 -3
  234. package/src/trackers/impressionObserver/utils.ts +8 -1
  235. package/src/trackers/impressionsTracker.ts +7 -8
  236. package/src/types.ts +22 -1
  237. package/src/utils/consent.ts +8 -0
  238. package/src/utils/constants/index.ts +5 -0
  239. package/src/utils/inputValidation/attributes.ts +1 -2
  240. package/src/utils/lang/index.ts +15 -4
  241. package/src/utils/lang/maps.ts +15 -1
  242. package/src/utils/settingsValidation/consent.ts +16 -0
  243. package/src/utils/settingsValidation/impressionsMode.ts +8 -8
  244. package/src/utils/settingsValidation/index.ts +29 -10
  245. package/src/utils/settingsValidation/runtime.ts +9 -0
  246. package/src/utils/settingsValidation/types.ts +12 -6
  247. package/types/evaluator/types.d.ts +2 -2
  248. package/types/evaluator/value/index.d.ts +1 -1
  249. package/types/evaluator/value/sanitize.d.ts +1 -1
  250. package/types/integrations/ga/GoogleAnalyticsToSplit.d.ts +2 -2
  251. package/types/integrations/ga/SplitToGoogleAnalytics.d.ts +2 -3
  252. package/types/integrations/types.d.ts +4 -0
  253. package/types/logger/constants.d.ts +5 -2
  254. package/types/logger/types.d.ts +4 -0
  255. package/types/sdkClient/clientAttributesDecoration.d.ts +1 -1
  256. package/types/sdkClient/clientInputValidation.d.ts +2 -3
  257. package/types/sdkFactory/types.d.ts +1 -1
  258. package/types/sdkFactory/userConsentProps.d.ts +6 -0
  259. package/types/storages/KeyBuilderCS.d.ts +2 -0
  260. package/types/storages/inMemory/ImpressionsCacheInMemory.d.ts +9 -0
  261. package/types/storages/inRedis/RedisAdapter.d.ts +1 -1
  262. package/types/storages/types.d.ts +3 -1
  263. package/types/sync/types.d.ts +3 -0
  264. package/types/trackers/eventTracker.d.ts +2 -2
  265. package/types/trackers/impressionObserver/utils.d.ts +4 -0
  266. package/types/trackers/impressionsTracker.d.ts +2 -3
  267. package/types/types.d.ts +22 -1
  268. package/types/utils/consent.d.ts +2 -0
  269. package/types/utils/constants/index.d.ts +3 -0
  270. package/types/utils/lang/index.d.ts +6 -1
  271. package/types/utils/lang/maps.d.ts +7 -0
  272. package/types/utils/settingsValidation/consent.d.ts +6 -0
  273. package/types/utils/settingsValidation/impressionsMode.d.ts +1 -1
  274. package/types/utils/settingsValidation/runtime/browser.d.ts +2 -0
  275. package/types/utils/settingsValidation/runtime/node.d.ts +2 -0
  276. package/types/utils/settingsValidation/runtime.d.ts +2 -0
  277. package/types/utils/settingsValidation/types.d.ts +12 -6
  278. package/types/utils/settingsValidation/userConsent.d.ts +5 -0
@@ -64,10 +64,11 @@ export interface ISdkFactoryParams {
64
64
  serviceApi: ISplitApi | undefined) => ISignalListener, // Used by BrowserSignalListener
65
65
 
66
66
  // @TODO review impressionListener and integrations interfaces. What about handling impressionListener as an integration ?
67
- impressionListener?: SplitIO.IImpressionListener,
68
67
  integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined,
69
68
 
70
69
  // Impression observer factory. If provided, will be used for impressions dedupe
71
70
  impressionsObserverFactory?: () => IImpressionObserver
72
71
 
72
+ // Optional function to assign additional properties to the factory instance
73
+ extraProps?: (settings: ISettings, syncManager?: ISyncManager) => object
73
74
  }
@@ -0,0 +1,40 @@
1
+ import { ERROR_NOT_BOOLEAN, USER_CONSENT_UPDATED, USER_CONSENT_NOT_UPDATED } from '../logger/constants';
2
+ import { ISyncManager } from '../sync/types';
3
+ import { ISettings } from '../types';
4
+ import { CONSENT_GRANTED, CONSENT_DECLINED } from '../utils/constants';
5
+ import { isBoolean } from '../utils/lang';
6
+
7
+ // Extend client-side factory instances with user consent getter/setter
8
+ export function userConsentProps(settings: ISettings, syncManager?: ISyncManager) {
9
+
10
+ const log = settings.log;
11
+
12
+ return {
13
+ setUserConsent(consent: unknown) {
14
+ // validate input param
15
+ if (!isBoolean(consent)) {
16
+ log.warn(ERROR_NOT_BOOLEAN, ['setUserConsent']);
17
+ return false;
18
+ }
19
+
20
+ const newConsentStatus = consent ? CONSENT_GRANTED : CONSENT_DECLINED;
21
+
22
+ if (settings.userConsent !== newConsentStatus) { // @ts-ignore, modify readonly prop
23
+ settings.userConsent = newConsentStatus;
24
+
25
+ if (consent) syncManager?.submitter?.start(); // resumes submitters if transitioning to GRANTED
26
+ else syncManager?.submitter?.stop(); // pauses submitters if transitioning to DECLINED
27
+
28
+ log.info(USER_CONSENT_UPDATED, [settings.userConsent, newConsentStatus]);
29
+ } else {
30
+ log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
31
+ }
32
+
33
+ return true;
34
+ },
35
+
36
+ getUserConsent() {
37
+ return settings.userConsent;
38
+ }
39
+ };
40
+ }
@@ -1,15 +1,11 @@
1
- import { endsWith, startsWith } from '../utils/lang';
1
+ import { startsWith } from '../utils/lang';
2
2
 
3
3
  const everythingAtTheEnd = /[^.]+$/;
4
4
 
5
5
  const DEFAULT_PREFIX = 'SPLITIO';
6
6
 
7
7
  export function validatePrefix(prefix: unknown) {
8
- return prefix && typeof prefix === 'string' ?
9
- endsWith(prefix, '.' + DEFAULT_PREFIX) ?
10
- prefix : // suffix already appended
11
- prefix + '.' + DEFAULT_PREFIX : // append suffix
12
- DEFAULT_PREFIX; // use default prefix if none is provided
8
+ return prefix ? prefix + '.SPLITIO' : 'SPLITIO';
13
9
  }
14
10
 
15
11
  export class KeyBuilder {
@@ -16,10 +16,22 @@ export class KeyBuilderCS extends KeyBuilder {
16
16
  * @override
17
17
  */
18
18
  buildSegmentNameKey(segmentName: string) {
19
- return `${this.matchingKey}.${this.prefix}.segment.${segmentName}`;
19
+ return `${this.prefix}.${this.matchingKey}.segment.${segmentName}`;
20
20
  }
21
21
 
22
22
  extractSegmentName(builtSegmentKeyName: string) {
23
+ const prefix = `${this.prefix}.${this.matchingKey}.segment.`;
24
+
25
+ if (startsWith(builtSegmentKeyName, prefix))
26
+ return builtSegmentKeyName.substr(prefix.length);
27
+ }
28
+
29
+ // @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
30
+ buildOldSegmentNameKey(segmentName: string) {
31
+ return `${this.matchingKey}.${this.prefix}.segment.${segmentName}`;
32
+ }
33
+ // @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
34
+ extractOldSegmentKey(builtSegmentKeyName: string) {
23
35
  const prefix = `${this.matchingKey}.${this.prefix}.segment.`;
24
36
 
25
37
  if (startsWith(builtSegmentKeyName, prefix))
@@ -67,9 +67,29 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
67
67
 
68
68
  // Scan current values from localStorage
69
69
  const storedSegmentNames = Object.keys(localStorage).reduce((accum, key) => {
70
- const name = this.keys.extractSegmentName(key);
71
-
72
- if (name) accum.push(name);
70
+ let segmentName = this.keys.extractSegmentName(key);
71
+
72
+ if (segmentName) {
73
+ accum.push(segmentName);
74
+ } else {
75
+ // @BREAKING: This is only to clean up "old" keys. Remove this whole else code block.
76
+ segmentName = this.keys.extractOldSegmentKey(key);
77
+
78
+ if (segmentName) { // this was an old segment key, let's clean up.
79
+ const newSegmentKey = this.keys.buildSegmentNameKey(segmentName);
80
+ try {
81
+ // If the new format key is not there, create it.
82
+ if (!localStorage.getItem(newSegmentKey) && names.indexOf(segmentName) > -1) {
83
+ localStorage.setItem(newSegmentKey, DEFINED);
84
+ // we are migrating a segment, let's track it.
85
+ accum.push(segmentName);
86
+ }
87
+ localStorage.removeItem(key); // we migrated the current key, let's delete it.
88
+ } catch (e) {
89
+ this.log.error(e);
90
+ }
91
+ }
92
+ }
73
93
 
74
94
  return accum;
75
95
  }, [] as string[]);
@@ -40,7 +40,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
40
40
  return {
41
41
  splits: new SplitsCacheInLocal(log, keys, expirationTimestamp, params.splitFiltersValidation),
42
42
  segments: new MySegmentsCacheInLocal(log, keys),
43
- impressions: new ImpressionsCacheInMemory(),
43
+ impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
44
44
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
45
45
  events: new EventsCacheInMemory(params.eventsQueueSize),
46
46
 
@@ -3,13 +3,34 @@ import { ImpressionDTO } from '../../types';
3
3
 
4
4
  export class ImpressionsCacheInMemory implements IImpressionsCacheSync {
5
5
 
6
- private queue: ImpressionDTO[] = [];
6
+ private onFullQueue?: () => void;
7
+ private readonly maxQueue: number;
8
+ private queue: ImpressionDTO[];
9
+
10
+ /**
11
+ *
12
+ * @param impressionsQueueSize number of queued impressions to call onFullQueueCb.
13
+ * Default value is 0, that means no maximum value, in case we want to avoid this being triggered.
14
+ */
15
+ constructor(impressionsQueueSize: number = 0) {
16
+ this.maxQueue = impressionsQueueSize;
17
+ this.queue = [];
18
+ }
19
+
20
+ setOnFullQueueCb(cb: () => void) {
21
+ this.onFullQueue = cb;
22
+ }
7
23
 
8
24
  /**
9
25
  * Store impressions in sequential order
10
26
  */
11
27
  track(data: ImpressionDTO[]) {
12
28
  this.queue.push(...data);
29
+
30
+ // Check if the cache queue is full and we need to flush it.
31
+ if (this.maxQueue > 0 && this.queue.length >= this.maxQueue && this.onFullQueue) {
32
+ this.onFullQueue();
33
+ }
13
34
  }
14
35
 
15
36
  /**
@@ -16,7 +16,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
16
16
  return {
17
17
  splits: new SplitsCacheInMemory(),
18
18
  segments: new SegmentsCacheInMemory(),
19
- impressions: new ImpressionsCacheInMemory(),
19
+ impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
20
20
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
21
21
  events: new EventsCacheInMemory(params.eventsQueueSize),
22
22
 
@@ -16,7 +16,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
16
16
  return {
17
17
  splits: new SplitsCacheInMemory(),
18
18
  segments: new MySegmentsCacheInMemory(),
19
- impressions: new ImpressionsCacheInMemory(),
19
+ impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
20
20
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
21
21
  events: new EventsCacheInMemory(params.eventsQueueSize),
22
22
 
@@ -164,6 +164,12 @@ export class RedisAdapter extends ioredis {
164
164
  } else { // If it IS the string URL, that'll be the first param for ioredis.
165
165
  result.unshift(options.url);
166
166
  }
167
+ if (options.connectionTimeout) {
168
+ merge(opts, { connectTimeout: options.connectionTimeout });
169
+ }
170
+ if (options.tls) {
171
+ merge(opts, { tls: options.tls });
172
+ }
167
173
 
168
174
  return result;
169
175
  }
@@ -171,9 +177,9 @@ export class RedisAdapter extends ioredis {
171
177
  /**
172
178
  * Parses the options into what we care about.
173
179
  */
174
- static _defineOptions({ connectionTimeout, operationTimeout, url, host, port, db, pass }: Record<string, any>) {
180
+ static _defineOptions({ connectionTimeout, operationTimeout, url, host, port, db, pass, tls }: Record<string, any>) {
175
181
  const parsedOptions = {
176
- connectionTimeout, operationTimeout, url, host, port, db, pass
182
+ connectionTimeout, operationTimeout, url, host, port, db, pass, tls
177
183
  };
178
184
 
179
185
  return merge({}, DEFAULT_OPTIONS, parsedOptions);
@@ -63,7 +63,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
63
63
 
64
64
  const prefix = validatePrefix(options.prefix);
65
65
 
66
- function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
66
+ function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
67
67
  const keys = new KeyBuilderSS(prefix, metadata);
68
68
  const wrapper = wrapperAdapter(log, options.wrapper);
69
69
  const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
@@ -74,7 +74,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
74
74
  return {
75
75
  splits: new SplitsCachePluggable(log, keys, wrapper),
76
76
  segments: new SegmentsCachePluggable(log, keys, wrapper),
77
- impressions: isPartialConsumer ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
77
+ impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
78
78
  impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
79
79
  events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
80
80
  // @TODO add telemetry cache when required
@@ -298,7 +298,7 @@ export interface IRecorderCacheProducerSync<T> {
298
298
  // @TODO names are inconsistent with spec
299
299
  /* Checks if cache is empty. Returns true if the cache was just created or cleared */
300
300
  isEmpty(): boolean
301
- /* clears cache data */
301
+ /* Clears cache data */
302
302
  clear(): void
303
303
  /* Gets cache data */
304
304
  state(): T
@@ -307,10 +307,13 @@ export interface IRecorderCacheProducerSync<T> {
307
307
 
308
308
  export interface IImpressionsCacheSync extends IImpressionsCacheBase, IRecorderCacheProducerSync<ImpressionDTO[]> {
309
309
  track(data: ImpressionDTO[]): void
310
+ /* Registers callback for full queue */
311
+ setOnFullQueueCb(cb: () => void): void
310
312
  }
311
313
 
312
314
  export interface IEventsCacheSync extends IEventsCacheBase, IRecorderCacheProducerSync<SplitIO.EventData[]> {
313
315
  track(data: SplitIO.EventData, size?: number): boolean
316
+ /* Registers callback for full queue */
314
317
  setOnFullQueueCb(cb: () => void): void
315
318
  }
316
319
 
@@ -423,6 +426,7 @@ export type DataLoader = (storage: IStorageSync, matchingKey: string) => void
423
426
 
424
427
  export interface IStorageFactoryParams {
425
428
  log: ILogger,
429
+ impressionsQueueSize?: number,
426
430
  eventsQueueSize?: number,
427
431
  optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
428
432
 
@@ -443,7 +447,7 @@ export interface IStorageFactoryParams {
443
447
  export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';
444
448
 
445
449
  export type IStorageSyncFactory = {
446
- type: StorageType,
450
+ readonly type: StorageType,
447
451
  (params: IStorageFactoryParams): IStorageSync
448
452
  }
449
453
 
@@ -82,7 +82,7 @@ export function splitsParserFromFileFactory(): ISplitsParser {
82
82
  try {
83
83
  data = fs.readFileSync(filePath, 'utf-8');
84
84
  } catch (e) {
85
- log.error(e.message);
85
+ log.error(e && (e as Error).message);
86
86
 
87
87
  return {};
88
88
  }
@@ -1,5 +1,6 @@
1
1
  import { IEventSourceConstructor } from '../../../services/types';
2
2
  import { ISettings } from '../../../types';
3
+ import { isString } from '../../../utils/lang';
3
4
  import { IAuthTokenPushEnabled } from '../AuthClient/types';
4
5
  import { ISSEClient, ISseEventHandler } from './types';
5
6
 
@@ -15,7 +16,7 @@ const CONTROL_CHANNEL_REGEX = /^control_/;
15
16
  */
16
17
  function buildSSEHeaders(settings: ISettings) {
17
18
  const headers: Record<string, string> = {
18
- SplitSDKClientKey: settings.core.authorizationKey.slice(-4),
19
+ SplitSDKClientKey: isString(settings.core.authorizationKey) ? settings.core.authorizationKey.slice(-4) : '',
19
20
  SplitSDKVersion: settings.version,
20
21
  };
21
22
 
@@ -71,6 +71,7 @@ export function pushManagerFactory(
71
71
  // It is used to halt the `connectPush` process if it was in progress.
72
72
  let disconnected: boolean | undefined;
73
73
  // flag that indicates a PUSH_NONRETRYABLE_ERROR, condition with which starting pushManager again is ignored.
74
+ // true if STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE or Auth errors.
74
75
  let disabled: boolean | undefined; // `disabled` implies `disconnected === true`
75
76
 
76
77
  /** PushManager functions related to initialization */
@@ -296,12 +297,15 @@ export function pushManagerFactory(
296
297
  // Expose Event Emitter functionality and Event constants
297
298
  Object.create(pushEmitter),
298
299
  {
299
- // Stop/pause push mode
300
+ // Stop/pause push mode.
301
+ // It doesn't emit events. Neither PUSH_SUBSYSTEM_DOWN to start polling.
300
302
  stop() {
301
303
  disconnectPush(); // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
302
304
  if (userKey) this.remove(userKey); // Necessary to properly resume streaming in client-side (e.g., RN SDK transition to foreground).
303
305
  },
304
- // Start/resume push mode
306
+
307
+ // Start/resume push mode.
308
+ // It eventually emits PUSH_SUBSYSTEM_DOWN, that starts polling, or PUSH_SUBSYSTEM_UP, that executes a syncAll
305
309
  start() {
306
310
  // Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
307
311
  if (disabled || disconnected === false) return;
@@ -311,6 +315,11 @@ export function pushManagerFactory(
311
315
  else setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
312
316
  },
313
317
 
318
+ // true/false if start or stop was called last respectively
319
+ isRunning(){
320
+ return disconnected === false;
321
+ },
322
+
314
323
  // [Only for client-side]
315
324
  add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
316
325
  const hash = hashUserKey(userKey);
@@ -3,7 +3,9 @@ import { IPostEventsBulk } from '../../services/types';
3
3
  import { ISyncTask, ITimeTracker } from '../types';
4
4
  import { submitterSyncTaskFactory } from './submitterSyncTask';
5
5
  import { ILogger } from '../../logger/types';
6
- import { SUBMITTERS_PUSH_FULL_EVENTS_QUEUE } from '../../logger/constants';
6
+ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
7
+
8
+ const DATA_NAME = 'events';
7
9
 
8
10
  /**
9
11
  * Sync task that periodically posts tracked events
@@ -18,26 +20,37 @@ export function eventsSyncTaskFactory(
18
20
  ): ISyncTask {
19
21
 
20
22
  // don't retry events.
21
- const syncTask = submitterSyncTaskFactory(log, postEventsBulk, eventsCache, eventsPushRate, 'queued events', latencyTracker);
23
+ const syncTask = submitterSyncTaskFactory(log, postEventsBulk, eventsCache, eventsPushRate, DATA_NAME, latencyTracker);
22
24
 
23
- // Set a timer for the first push of events,
25
+ // Set a timer for the first push window of events.
26
+ // Not implemented in the base submitter or sync task, since this feature is only used by the events submitter.
24
27
  if (eventsFirstPushWindow > 0) {
28
+ let running = false;
25
29
  let stopEventPublisherTimeout: ReturnType<typeof setTimeout>;
26
30
  const originalStart = syncTask.start;
27
31
  syncTask.start = () => {
32
+ running = true;
28
33
  stopEventPublisherTimeout = setTimeout(originalStart, eventsFirstPushWindow);
29
34
  };
30
35
  const originalStop = syncTask.stop;
31
36
  syncTask.stop = () => {
37
+ running = false;
32
38
  clearTimeout(stopEventPublisherTimeout);
33
39
  originalStop();
34
40
  };
41
+ syncTask.isRunning = () => {
42
+ return running;
43
+ };
35
44
  }
36
45
 
37
- // register eventsSubmitter to be executed when events cache is full
46
+ // register events submitter to be executed when events cache is full
38
47
  eventsCache.setOnFullQueueCb(() => {
39
- log.info(SUBMITTERS_PUSH_FULL_EVENTS_QUEUE);
40
- syncTask.execute();
48
+ if (syncTask.isRunning()) {
49
+ log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
50
+ syncTask.execute();
51
+ }
52
+ // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
53
+ // Data will be sent when submitter is resumed.
41
54
  });
42
55
 
43
56
  return syncTask;
@@ -6,6 +6,9 @@ import { ImpressionDTO } from '../../types';
6
6
  import { submitterSyncTaskFactory } from './submitterSyncTask';
7
7
  import { ImpressionsPayload } from './types';
8
8
  import { ILogger } from '../../logger/types';
9
+ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
10
+
11
+ const DATA_NAME = 'impressions';
9
12
 
10
13
  /**
11
14
  * Converts `impressions` data from cache into request payload.
@@ -50,5 +53,17 @@ export function impressionsSyncTaskFactory(
50
53
  ): ISyncTask {
51
54
 
52
55
  // retry impressions only once.
53
- return submitterSyncTaskFactory(log, postTestImpressionsBulk, impressionsCache, impressionsRefreshRate, 'impressions', latencyTracker, fromImpressionsCollector.bind(undefined, sendLabels), 1);
56
+ const syncTask = submitterSyncTaskFactory(log, postTestImpressionsBulk, impressionsCache, impressionsRefreshRate, DATA_NAME, latencyTracker, fromImpressionsCollector.bind(undefined, sendLabels), 1);
57
+
58
+ // register impressions submitter to be executed when impressions cache is full
59
+ impressionsCache.setOnFullQueueCb(() => {
60
+ if (syncTask.isRunning()) {
61
+ log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
62
+ syncTask.execute();
63
+ }
64
+ // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
65
+ // Data will be sent when submitter is resumed.
66
+ });
67
+
68
+ return syncTask;
54
69
  }
@@ -6,6 +6,7 @@ import { IPushManager } from './streaming/types';
6
6
  import { IPollingManager, IPollingManagerCS } from './polling/types';
7
7
  import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
8
8
  import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
9
+ import { isConsentGranted } from '../utils/consent';
9
10
 
10
11
  /**
11
12
  * Online SyncManager factory.
@@ -25,7 +26,7 @@ export function syncManagerOnlineFactory(
25
26
  */
26
27
  return function (params: ISyncManagerFactoryParams): ISyncManagerCS {
27
28
 
28
- const { log, streamingEnabled } = params.settings;
29
+ const { settings, settings: { log, streamingEnabled } } = params;
29
30
 
30
31
  /** Polling Manager */
31
32
  const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
@@ -39,7 +40,6 @@ export function syncManagerOnlineFactory(
39
40
  // It is not inyected as push and polling managers, because at the moment it is required
40
41
  const submitter = submitterManagerFactory(params);
41
42
 
42
-
43
43
  /** Sync Manager logic */
44
44
 
45
45
  function startPolling() {
@@ -69,12 +69,18 @@ export function syncManagerOnlineFactory(
69
69
  let startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization
70
70
 
71
71
  return {
72
+ // Exposed for fine-grained control of synchronization.
73
+ // E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline).
74
+ pollingManager,
72
75
  pushManager,
76
+ submitter,
73
77
 
74
78
  /**
75
79
  * Method used to start the syncManager for the first time, or resume it after being stopped.
76
80
  */
77
81
  start() {
82
+ running = true;
83
+
78
84
  // start syncing splits and segments
79
85
  if (pollingManager) {
80
86
  if (pushManager) {
@@ -90,21 +96,21 @@ export function syncManagerOnlineFactory(
90
96
  }
91
97
 
92
98
  // start periodic data recording (events, impressions, telemetry).
93
- if (submitter) submitter.start();
94
- running = true;
99
+ if (isConsentGranted(settings)) submitter.start();
95
100
  },
96
101
 
97
102
  /**
98
103
  * Method used to stop/pause the syncManager.
99
104
  */
100
105
  stop() {
106
+ running = false;
107
+
101
108
  // stop syncing
102
109
  if (pushManager) pushManager.stop();
103
110
  if (pollingManager && pollingManager.isRunning()) pollingManager.stop();
104
111
 
105
112
  // stop periodic data recording (events, impressions, telemetry).
106
- if (submitter) submitter.stop();
107
- running = false;
113
+ submitter.stop();
108
114
  },
109
115
 
110
116
  isRunning() {
@@ -112,7 +118,7 @@ export function syncManagerOnlineFactory(
112
118
  },
113
119
 
114
120
  flush() {
115
- if (submitter) return submitter.execute();
121
+ if (isConsentGranted(settings)) return submitter.execute();
116
122
  else return Promise.resolve();
117
123
  },
118
124
 
package/src/sync/types.ts CHANGED
@@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types';
3
3
  import { ISplitApi } from '../services/types';
4
4
  import { IStorageSync } from '../storages/types';
5
5
  import { ISettings } from '../types';
6
+ import { IPollingManager } from './polling/types';
6
7
  import { IPushManager } from './streaming/types';
7
8
 
8
9
  export interface ITask<Input extends any[] = []> {
@@ -43,7 +44,9 @@ export interface ITimeTracker {
43
44
 
44
45
  export interface ISyncManager extends ITask {
45
46
  flush(): Promise<any>,
46
- pushManager?: IPushManager
47
+ pushManager?: IPushManager,
48
+ pollingManager?: IPollingManager,
49
+ submitter?: ISyncTask
47
50
  }
48
51
 
49
52
  export interface ISyncManagerCS extends ISyncManager {
@@ -2,9 +2,10 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { IEventsCacheBase } from '../storages/types';
4
4
  import { IEventsHandler, IEventTracker } from './types';
5
- import { SplitIO } from '../types';
6
- import { ILogger } from '../logger/types';
5
+ import { ISettings, SplitIO } from '../types';
7
6
  import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constants';
7
+ import { CONSENT_DECLINED } from '../utils/constants';
8
+ import { isStorageSync } from './impressionObserver/utils';
8
9
 
9
10
  /**
10
11
  * Event tracker stores events in cache and pass them to the integrations manager if provided.
@@ -13,11 +14,14 @@ import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constant
13
14
  * @param integrationsManager optional event handler used for integrations
14
15
  */
15
16
  export function eventTrackerFactory(
16
- log: ILogger,
17
+ settings: ISettings,
17
18
  eventsCache: IEventsCacheBase,
18
19
  integrationsManager?: IEventsHandler
19
20
  ): IEventTracker {
20
21
 
22
+ const log = settings.log;
23
+ const isSync = isStorageSync(settings);
24
+
21
25
  function queueEventsCallback(eventData: SplitIO.EventData, tracked: boolean) {
22
26
  const { eventTypeId, trafficTypeName, key, value, timestamp, properties } = eventData;
23
27
  // Logging every prop would be too much.
@@ -44,6 +48,10 @@ export function eventTrackerFactory(
44
48
 
45
49
  return {
46
50
  track(eventData: SplitIO.EventData, size?: number) {
51
+ if (settings.userConsent === CONSENT_DECLINED) {
52
+ return isSync ? false : Promise.resolve(false);
53
+ }
54
+
47
55
  const tracked = eventsCache.track(eventData, size);
48
56
 
49
57
  if (thenable(tracked)) {
@@ -1,4 +1,4 @@
1
- import { CONSUMER_PARTIAL_MODE, OPTIMIZED, PRODUCER_MODE, STANDALONE_MODE } from '../../utils/constants';
1
+ import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE, OPTIMIZED, PRODUCER_MODE, STANDALONE_MODE } from '../../utils/constants';
2
2
  import { ISettings } from '../../types';
3
3
 
4
4
  /**
@@ -15,3 +15,10 @@ export function shouldBeOptimized(settings: ISettings) {
15
15
  if (!shouldAddPt(settings)) return false;
16
16
  return settings.sync.impressionsMode === OPTIMIZED ? true : false;
17
17
  }
18
+
19
+ /**
20
+ * Storage is async if mode is consumer or partial consumer
21
+ */
22
+ export function isStorageSync(settings: ISettings) {
23
+ return [CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) === -1 ? true : false;
24
+ }
@@ -5,8 +5,8 @@ import { IImpressionCountsCacheSync, IImpressionsCacheBase } from '../storages/t
5
5
  import { IImpressionsHandler, IImpressionsTracker } from './types';
6
6
  import { SplitIO, ImpressionDTO, ISettings } from '../types';
7
7
  import { IImpressionObserver } from './impressionObserver/types';
8
- import { ILogger } from '../logger/types';
9
8
  import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIONS_LISTENER } from '../logger/constants';
9
+ import { CONSENT_DECLINED } from '../utils/constants';
10
10
 
11
11
  /**
12
12
  * Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
@@ -19,22 +19,21 @@ import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIO
19
19
  * @param countsCache optional cache to save impressions count. If provided, impressions will be deduped (OPTIMIZED mode)
20
20
  */
21
21
  export function impressionsTrackerFactory(
22
- log: ILogger,
22
+ settings: ISettings,
23
23
  impressionsCache: IImpressionsCacheBase,
24
-
25
- // @TODO consider passing only an optional integrationsManager to handle impressions
26
- { runtime: { ip, hostname }, version }: Pick<ISettings, 'version' | 'runtime'>,
27
- impressionListener?: SplitIO.IImpressionListener,
28
24
  integrationsManager?: IImpressionsHandler,
29
-
30
25
  // if observer is provided, it implies `shouldAddPreviousTime` flag (i.e., if impressions previous time should be added or not)
31
26
  observer?: IImpressionObserver,
32
27
  // if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
33
28
  countsCache?: IImpressionCountsCacheSync
34
29
  ): IImpressionsTracker {
35
30
 
31
+ const { log, impressionListener, runtime: { ip, hostname }, version } = settings;
32
+
36
33
  return {
37
34
  track(impressions: ImpressionDTO[], attributes?: SplitIO.Attributes) {
35
+ if (settings.userConsent === CONSENT_DECLINED) return;
36
+
38
37
  const impressionsCount = impressions.length;
39
38
 
40
39
  const impressionsToStore: ImpressionDTO[] = []; // Track only the impressions that are going to be stored
@@ -85,7 +84,7 @@ export function impressionsTrackerFactory(
85
84
  // integrationsManager.handleImpression does not throw errors
86
85
  if (integrationsManager) integrationsManager.handleImpression(impressionData);
87
86
 
88
- try { // An exception on the listeners should not break the SDK.
87
+ try { // @ts-ignore. An exception on the listeners should not break the SDK.
89
88
  if (impressionListener) impressionListener.logImpression(impressionData);
90
89
  } catch (err) {
91
90
  log.error(ERROR_IMPRESSIONS_LISTENER, [err]);