@splitsoftware/splitio-commons 1.6.2-rc.1 → 1.6.2-rc.11

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 (197) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/consent/sdkUserConsent.js +2 -2
  3. package/cjs/evaluator/index.js +15 -16
  4. package/cjs/integrations/ga/GaToSplit.js +8 -5
  5. package/cjs/sdkClient/client.js +19 -7
  6. package/cjs/sdkClient/sdkClient.js +3 -1
  7. package/cjs/sdkFactory/index.js +15 -6
  8. package/cjs/sdkManager/index.js +3 -11
  9. package/cjs/services/splitApi.js +6 -6
  10. package/cjs/storages/AbstractSplitsCacheAsync.js +8 -10
  11. package/cjs/storages/AbstractSplitsCacheSync.js +8 -10
  12. package/cjs/storages/KeyBuilderSS.js +54 -9
  13. package/cjs/storages/dataLoader.js +1 -1
  14. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  15. package/cjs/storages/inLocalStorage/index.js +5 -1
  16. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  17. package/cjs/storages/inMemory/InMemoryStorage.js +6 -2
  18. package/cjs/storages/inMemory/InMemoryStorageCS.js +6 -2
  19. package/cjs/storages/inMemory/SplitsCacheInMemory.js +7 -10
  20. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +10 -5
  21. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +72 -0
  22. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +76 -0
  23. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  24. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +85 -0
  25. package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
  26. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  27. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +71 -0
  28. package/cjs/storages/inRedis/constants.js +4 -1
  29. package/cjs/storages/inRedis/index.js +17 -2
  30. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +81 -0
  31. package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
  32. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  33. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +61 -0
  34. package/cjs/storages/pluggable/index.js +46 -17
  35. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  36. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
  37. package/cjs/sync/submitters/telemetrySubmitter.js +8 -4
  38. package/cjs/sync/submitters/uniqueKeysSubmitter.js +16 -59
  39. package/cjs/trackers/impressionsTracker.js +17 -15
  40. package/cjs/trackers/strategy/strategyNone.js +1 -1
  41. package/cjs/trackers/strategy/strategyOptimized.js +2 -1
  42. package/cjs/trackers/telemetryTracker.js +6 -0
  43. package/cjs/trackers/uniqueKeysTracker.js +11 -42
  44. package/cjs/utils/constants/index.js +3 -2
  45. package/cjs/utils/lang/maps.js +15 -7
  46. package/cjs/utils/redis/RedisMock.js +31 -0
  47. package/cjs/utils/settingsValidation/index.js +0 -3
  48. package/esm/consent/sdkUserConsent.js +2 -2
  49. package/esm/evaluator/index.js +15 -16
  50. package/esm/integrations/ga/GaToSplit.js +8 -5
  51. package/esm/sdkClient/client.js +19 -7
  52. package/esm/sdkClient/sdkClient.js +3 -1
  53. package/esm/sdkFactory/index.js +16 -7
  54. package/esm/sdkManager/index.js +3 -11
  55. package/esm/services/splitApi.js +6 -6
  56. package/esm/storages/AbstractSplitsCacheAsync.js +8 -10
  57. package/esm/storages/AbstractSplitsCacheSync.js +8 -10
  58. package/esm/storages/KeyBuilderSS.js +50 -8
  59. package/esm/storages/dataLoader.js +1 -1
  60. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  61. package/esm/storages/inLocalStorage/index.js +6 -2
  62. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  63. package/esm/storages/inMemory/InMemoryStorage.js +8 -4
  64. package/esm/storages/inMemory/InMemoryStorageCS.js +7 -3
  65. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -10
  66. package/esm/storages/inMemory/TelemetryCacheInMemory.js +9 -5
  67. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +68 -0
  68. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +73 -0
  69. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  70. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +82 -0
  71. package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
  72. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  73. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +68 -0
  74. package/esm/storages/inRedis/constants.js +3 -0
  75. package/esm/storages/inRedis/index.js +18 -3
  76. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +78 -0
  77. package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
  78. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  79. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +58 -0
  80. package/esm/storages/pluggable/index.js +47 -18
  81. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  82. package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
  83. package/esm/sync/submitters/telemetrySubmitter.js +9 -5
  84. package/esm/sync/submitters/uniqueKeysSubmitter.js +15 -56
  85. package/esm/trackers/impressionsTracker.js +17 -15
  86. package/esm/trackers/strategy/strategyNone.js +1 -1
  87. package/esm/trackers/strategy/strategyOptimized.js +2 -1
  88. package/esm/trackers/telemetryTracker.js +6 -0
  89. package/esm/trackers/uniqueKeysTracker.js +11 -42
  90. package/esm/utils/constants/index.js +1 -0
  91. package/esm/utils/lang/maps.js +15 -7
  92. package/esm/utils/redis/RedisMock.js +28 -0
  93. package/esm/utils/settingsValidation/index.js +0 -3
  94. package/package.json +1 -2
  95. package/src/consent/sdkUserConsent.ts +2 -2
  96. package/src/evaluator/index.ts +14 -15
  97. package/src/integrations/ga/GaToSplit.ts +9 -5
  98. package/src/integrations/types.ts +2 -1
  99. package/src/logger/.DS_Store +0 -0
  100. package/src/sdkClient/client.ts +21 -8
  101. package/src/sdkClient/sdkClient.ts +3 -1
  102. package/src/sdkFactory/index.ts +17 -7
  103. package/src/sdkManager/index.ts +3 -12
  104. package/src/services/splitApi.ts +6 -6
  105. package/src/services/types.ts +2 -2
  106. package/src/storages/AbstractSplitsCacheAsync.ts +13 -15
  107. package/src/storages/AbstractSplitsCacheSync.ts +15 -17
  108. package/src/storages/KeyBuilderSS.ts +61 -9
  109. package/src/storages/dataLoader.ts +1 -1
  110. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -11
  111. package/src/storages/inLocalStorage/index.ts +5 -2
  112. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
  113. package/src/storages/inMemory/InMemoryStorage.ts +7 -4
  114. package/src/storages/inMemory/InMemoryStorageCS.ts +6 -3
  115. package/src/storages/inMemory/SplitsCacheInMemory.ts +10 -14
  116. package/src/storages/inMemory/TelemetryCacheInMemory.ts +10 -6
  117. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +80 -0
  118. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +86 -0
  119. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  120. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +95 -0
  121. package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
  122. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
  123. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +77 -0
  124. package/src/storages/inRedis/constants.ts +3 -0
  125. package/src/storages/inRedis/index.ts +15 -5
  126. package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +92 -0
  127. package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
  128. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
  129. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +67 -0
  130. package/src/storages/pluggable/index.ts +51 -19
  131. package/src/storages/types.ts +38 -30
  132. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
  133. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
  134. package/src/sync/submitters/telemetrySubmitter.ts +15 -8
  135. package/src/sync/submitters/types.ts +26 -12
  136. package/src/sync/submitters/uniqueKeysSubmitter.ts +18 -61
  137. package/src/trackers/impressionsTracker.ts +16 -15
  138. package/src/trackers/strategy/strategyNone.ts +1 -1
  139. package/src/trackers/strategy/strategyOptimized.ts +1 -1
  140. package/src/trackers/telemetryTracker.ts +7 -2
  141. package/src/trackers/types.ts +9 -7
  142. package/src/trackers/uniqueKeysTracker.ts +15 -47
  143. package/src/types.ts +0 -1
  144. package/src/utils/constants/index.ts +1 -0
  145. package/src/utils/lang/maps.ts +20 -8
  146. package/src/utils/redis/RedisMock.ts +33 -0
  147. package/src/utils/settingsValidation/index.ts +1 -4
  148. package/types/integrations/types.d.ts +2 -1
  149. package/types/services/types.d.ts +2 -2
  150. package/types/storages/AbstractSplitsCacheAsync.d.ts +7 -6
  151. package/types/storages/AbstractSplitsCacheSync.d.ts +6 -6
  152. package/types/storages/KeyBuilderSS.d.ts +9 -2
  153. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +3 -4
  154. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
  155. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
  156. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
  157. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
  158. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +35 -0
  159. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  160. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +16 -0
  161. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -5
  162. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  163. package/types/storages/inRedis/constants.d.ts +3 -0
  164. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +21 -0
  165. package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +16 -0
  166. package/types/storages/pluggable/SplitsCachePluggable.d.ts +6 -5
  167. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  168. package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +20 -0
  169. package/types/storages/types.d.ts +35 -35
  170. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  171. package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
  172. package/types/sync/submitters/types.d.ts +19 -12
  173. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -14
  174. package/types/trackers/types.d.ts +9 -11
  175. package/types/trackers/uniqueKeysTracker.d.ts +3 -3
  176. package/types/types.d.ts +0 -1
  177. package/types/utils/constants/index.d.ts +1 -0
  178. package/types/utils/lang/maps.d.ts +6 -2
  179. package/types/utils/redis/RedisMock.d.ts +4 -0
  180. package/types/utils/settingsValidation/index.d.ts +0 -1
  181. package/types/sdkClient/types.d.ts +0 -18
  182. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  183. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  184. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  185. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  186. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  187. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  188. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  189. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  190. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  191. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  192. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  193. package/types/sync/syncTaskComposite.d.ts +0 -5
  194. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  195. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  196. package/types/trackers/filter/types.d.ts +0 -5
  197. package/types/utils/timeTracker/index.d.ts +0 -70
@@ -12,6 +12,7 @@ import { SplitIO } from '../../types';
12
12
  import { Identity, GoogleAnalyticsToSplitOptions } from './types';
13
13
  import { ILogger } from '../../logger/types';
14
14
  import { IIntegrationFactoryParams } from '../types';
15
+ import { ITelemetryTracker } from '../../trackers/types';
15
16
 
16
17
  const logPrefix = 'ga-to-split: ';
17
18
  const logNameMapper = 'ga-to-split:mapper';
@@ -25,7 +26,7 @@ const logNameMapper = 'ga-to-split:mapper';
25
26
  * @param log Logger instance.
26
27
  * @param autoRequire If true, log error when auto-require script is not detected
27
28
  */
28
- function providePlugin(window: any, pluginName: string, pluginConstructor: Function, log: ILogger, autoRequire?: boolean) {
29
+ function providePlugin(window: any, pluginName: string, pluginConstructor: Function, log: ILogger, autoRequire: boolean, telemetryTracker?: ITelemetryTracker) {
29
30
  // get reference to global command queue. Init it if not defined yet.
30
31
  const gaAlias = window.GoogleAnalyticsObject || 'ga';
31
32
  window[gaAlias] = window[gaAlias] || function () {
@@ -35,10 +36,13 @@ function providePlugin(window: any, pluginName: string, pluginConstructor: Funct
35
36
  // provides the plugin for use with analytics.js.
36
37
  window[gaAlias]('provide', pluginName, pluginConstructor);
37
38
 
38
- if (autoRequire && (!window[gaAlias].q || window[gaAlias].q.push === [].push)) {
39
- // Expecting spy on ga.q push method but not found
39
+ const hasAutoRequire = window[gaAlias].q && window[gaAlias].q.push !== [].push;
40
+ if (autoRequire && !hasAutoRequire) { // Expecting spy on ga.q push method but not found
40
41
  log.error(logPrefix + 'integration is configured to autorequire the splitTracker plugin, but the necessary script does not seem to have run. Please check the docs.');
41
42
  }
43
+ if (telemetryTracker && hasAutoRequire) {
44
+ telemetryTracker.addTag('integration:ga-autorequire');
45
+ }
42
46
  }
43
47
 
44
48
  // Default mapping: object used for building the default mapper from hits to Split events
@@ -191,7 +195,7 @@ export function fixEventTypeId(log: ILogger, eventTypeId: any) {
191
195
  */
192
196
  export function GaToSplit(sdkOptions: GoogleAnalyticsToSplitOptions, params: IIntegrationFactoryParams) {
193
197
 
194
- const { storage, settings: { core: coreSettings, log } } = params;
198
+ const { storage, settings: { core: coreSettings, log }, telemetryTracker } = params;
195
199
 
196
200
  const defaultOptions = {
197
201
  prefix: defaultPrefix,
@@ -291,5 +295,5 @@ export function GaToSplit(sdkOptions: GoogleAnalyticsToSplitOptions, params: IIn
291
295
  }
292
296
 
293
297
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
294
- providePlugin(window, 'splitTracker', SplitTracker, log, sdkOptions.autoRequire === true);
298
+ providePlugin(window, 'splitTracker', SplitTracker, log, sdkOptions.autoRequire === true, telemetryTracker);
295
299
  }
@@ -1,5 +1,5 @@
1
1
  import { IEventsCacheBase } from '../storages/types';
2
- import { IEventsHandler, IImpressionsHandler } from '../trackers/types';
2
+ import { IEventsHandler, IImpressionsHandler, ITelemetryTracker } from '../trackers/types';
3
3
  import { ISettings, SplitIO } from '../types';
4
4
 
5
5
  export interface IIntegration {
@@ -11,6 +11,7 @@ export type IIntegrationManager = IEventsHandler & IImpressionsHandler;
11
11
  export interface IIntegrationFactoryParams {
12
12
  storage: { events: IEventsCacheBase }
13
13
  settings: ISettings
14
+ telemetryTracker: ITelemetryTracker
14
15
  }
15
16
 
16
17
  export type IntegrationFactory = {
Binary file
@@ -9,6 +9,17 @@ import { IEvaluationResult } from '../evaluator/types';
9
9
  import { SplitIO, ImpressionDTO } from '../types';
10
10
  import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
11
11
  import { ISdkFactoryContext } from '../sdkFactory/types';
12
+ import { isStorageSync } from '../trackers/impressionObserver/utils';
13
+
14
+ const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
15
+
16
+ function treatmentsNotReady(splitNames: string[]) {
17
+ const evaluations: Record<string, IEvaluationResult> = {};
18
+ splitNames.forEach(splitName => {
19
+ evaluations[splitName] = treatmentNotReady;
20
+ });
21
+ return evaluations;
22
+ }
12
23
 
13
24
  /**
14
25
  * Creator of base client with getTreatments and track methods.
@@ -29,7 +40,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
29
40
  return treatment;
30
41
  };
31
42
 
32
- const evaluation = evaluateFeature(log, key, splitName, attributes, storage);
43
+ const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
44
+ evaluateFeature(log, key, splitName, attributes, storage) :
45
+ isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
46
+ treatmentNotReady :
47
+ Promise.resolve(treatmentNotReady); // Promisify if async
33
48
 
34
49
  return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
35
50
  }
@@ -53,7 +68,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
53
68
  return treatments;
54
69
  };
55
70
 
56
- const evaluations = evaluateFeatures(log, key, splitNames, attributes, storage);
71
+ const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
72
+ evaluateFeatures(log, key, splitNames, attributes, storage) :
73
+ isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
74
+ treatmentsNotReady(splitNames) :
75
+ Promise.resolve(treatmentsNotReady(splitNames)); // Promisify if async
57
76
 
58
77
  return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
59
78
  }
@@ -72,15 +91,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
72
91
  invokingMethodName: string,
73
92
  queue: ImpressionDTO[]
74
93
  ): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
75
- const isSdkReady = readinessManager.isReady() || readinessManager.isReadyFromCache();
76
94
  const matchingKey = getMatching(key);
77
95
  const bucketingKey = getBucketing(key);
78
96
 
79
- // If the SDK was not ready, treatment may be incorrect due to having Splits but not segments data.
80
- if (!isSdkReady) {
81
- evaluation = { treatment: CONTROL, label: SDK_NOT_READY };
82
- }
83
-
84
97
  const { treatment, label, changeNumber, config = null } = evaluation;
85
98
  log.info(IMPRESSION, [splitName, matchingKey, treatment, label]);
86
99
 
@@ -9,7 +9,7 @@ import { ISdkFactoryContext } from '../sdkFactory/types';
9
9
  * Creates an Sdk client, i.e., a base client with status and destroy interface
10
10
  */
11
11
  export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: boolean): SplitIO.IClient | SplitIO.IAsyncClient {
12
- const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker } = params;
12
+ const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker, uniqueKeysTracker } = params;
13
13
 
14
14
  return objectAssign(
15
15
  // Proto-linkage of the readiness Event Emitter
@@ -39,6 +39,8 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
39
39
 
40
40
  // Release the API Key if it is the main client
41
41
  if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
42
+
43
+ if (uniqueKeysTracker) uniqueKeysTracker.stop();
42
44
 
43
45
  // Cleanup storage
44
46
  return storage.destroy();
@@ -17,7 +17,7 @@ import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
17
17
  import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
18
18
  import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
19
19
  import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
20
- import { NONE } from '../utils/constants';
20
+ import { NONE, OPTIMIZED } from '../utils/constants';
21
21
 
22
22
  /**
23
23
  * Modular SDK factory
@@ -29,7 +29,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
29
29
  integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
30
30
  filterAdapterFactory } = params;
31
31
  const log = settings.log;
32
- const impressionsMode = settings.sync.impressionsMode;
33
32
 
34
33
  // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid API Key, etc.
35
34
  // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
@@ -52,6 +51,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
52
51
 
53
52
  // ATM, only used by PluggableStorage
54
53
  mode: settings.mode,
54
+ impressionsMode: settings.sync.impressionsMode,
55
55
 
56
56
  // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined,
57
57
  // or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events.
@@ -67,16 +67,26 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
67
67
  const storage = storageFactory(storageFactoryParams);
68
68
  // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
69
69
 
70
- const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage });
70
+ const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
71
+ const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
71
72
 
72
73
  const observer = impressionsObserverFactory();
73
- const uniqueKeysTracker = impressionsMode === NONE ? uniqueKeysTrackerFactory(log, filterAdapterFactory && filterAdapterFactory()) : undefined;
74
- const strategy = (storageFactoryParams.optimize) ? strategyOptimizedFactory(observer, storage.impressionCounts!) :
75
- (impressionsMode === NONE) ? strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!) : strategyDebugFactory(observer);
74
+ const uniqueKeysTracker = storageFactoryParams.impressionsMode === NONE ? uniqueKeysTrackerFactory(log, storage.uniqueKeys!, filterAdapterFactory && filterAdapterFactory()) : undefined;
75
+
76
+ let strategy;
77
+ switch (storageFactoryParams.impressionsMode) {
78
+ case OPTIMIZED:
79
+ strategy = strategyOptimizedFactory(observer, storage.impressionCounts!);
80
+ break;
81
+ case NONE:
82
+ strategy = strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!);
83
+ break;
84
+ default:
85
+ strategy = strategyDebugFactory(observer);
86
+ }
76
87
 
77
88
  const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
78
89
  const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
79
- const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
80
90
 
81
91
  // splitApi is used by SyncManager and Browser signal listener
82
92
  const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
@@ -18,16 +18,7 @@ function collectTreatments(splitObject: ISplit) {
18
18
  return allTreatmentsCondition ? allTreatmentsCondition.partitions.map(v => v.treatment) : [];
19
19
  }
20
20
 
21
- function objectToView(json: string | null): SplitIO.SplitView | null {
22
- let splitObject: ISplit | null;
23
-
24
- try {
25
- // @ts-expect-error
26
- splitObject = JSON.parse(json);
27
- } catch (e) {
28
- return null;
29
- }
30
-
21
+ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
31
22
  if (!splitObject) return null;
32
23
 
33
24
  return {
@@ -40,10 +31,10 @@ function objectToView(json: string | null): SplitIO.SplitView | null {
40
31
  };
41
32
  }
42
33
 
43
- function objectsToViews(jsons: string[]) {
34
+ function objectsToViews(splitObjects: ISplit[]) {
44
35
  let views: SplitIO.SplitView[] = [];
45
36
 
46
- jsons.forEach(split => {
37
+ splitObjects.forEach(split => {
47
38
  const view = objectToView(split);
48
39
  if (view) views.push(view);
49
40
  });
@@ -114,7 +114,7 @@ export function splitApiFactory(
114
114
  * @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
115
115
  */
116
116
  postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
117
- const url = `${urls.telemetry}/api/v1/keys/cs`;
117
+ const url = `${urls.telemetry}/v1/keys/cs`;
118
118
  return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
119
119
  },
120
120
 
@@ -125,18 +125,18 @@ export function splitApiFactory(
125
125
  * @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
126
126
  */
127
127
  postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
128
- const url = `${urls.telemetry}/api/v1/keys/ss`;
128
+ const url = `${urls.telemetry}/v1/keys/ss`;
129
129
  return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
130
130
  },
131
131
 
132
- postMetricsConfig(body: string) {
132
+ postMetricsConfig(body: string, headers?: Record<string, string>) {
133
133
  const url = `${urls.telemetry}/v1/metrics/config`;
134
- return splitHttpClient(url, { method: 'POST', body }, telemetryTracker.trackHttp(TELEMETRY), true);
134
+ return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
135
135
  },
136
136
 
137
- postMetricsUsage(body: string) {
137
+ postMetricsUsage(body: string, headers?: Record<string, string>) {
138
138
  const url = `${urls.telemetry}/v1/metrics/usage`;
139
- return splitHttpClient(url, { method: 'POST', body }, telemetryTracker.trackHttp(TELEMETRY), true);
139
+ return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
140
140
  }
141
141
  };
142
142
  }
@@ -51,9 +51,9 @@ export type IPostTestImpressionsBulk = (body: string, headers?: Record<string, s
51
51
 
52
52
  export type IPostTestImpressionsCount = (body: string, headers?: Record<string, string>) => Promise<IResponse>
53
53
 
54
- export type IPostMetricsConfig = (body: string) => Promise<IResponse>
54
+ export type IPostMetricsConfig = (body: string, headers?: Record<string, string>) => Promise<IResponse>
55
55
 
56
- export type IPostMetricsUsage = (body: string) => Promise<IResponse>
56
+ export type IPostMetricsUsage = (body: string, headers?: Record<string, string>) => Promise<IResponse>
57
57
 
58
58
  export interface ISplitApi {
59
59
  getSdkAPIHealthCheck: IHealthCheckAPI
@@ -1,5 +1,6 @@
1
1
  import { ISplitsCacheAsync } from './types';
2
2
  import { ISplit } from '../dtos/types';
3
+ import { objectAssign } from '../utils/lang/objectAssign';
3
4
 
4
5
  /**
5
6
  * This class provides a skeletal implementation of the ISplitsCacheAsync interface
@@ -7,14 +8,14 @@ import { ISplit } from '../dtos/types';
7
8
  */
8
9
  export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
9
10
 
10
- abstract addSplit(name: string, split: string): Promise<boolean>
11
- abstract addSplits(entries: [string, string][]): Promise<boolean[] | void>
11
+ abstract addSplit(name: string, split: ISplit): Promise<boolean>
12
+ abstract addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>
12
13
  abstract removeSplits(names: string[]): Promise<boolean[] | void>
13
- abstract getSplit(name: string): Promise<string | null>
14
- abstract getSplits(names: string[]): Promise<Record<string, string | null>>
14
+ abstract getSplit(name: string): Promise<ISplit | null>
15
+ abstract getSplits(names: string[]): Promise<Record<string, ISplit | null>>
15
16
  abstract setChangeNumber(changeNumber: number): Promise<boolean | void>
16
17
  abstract getChangeNumber(): Promise<number>
17
- abstract getAll(): Promise<string[]>
18
+ abstract getAll(): Promise<ISplit[]>
18
19
  abstract getSplitNames(): Promise<string[]>
19
20
  abstract trafficTypeExists(trafficType: string): Promise<boolean>
20
21
  abstract clear(): Promise<boolean | void>
@@ -40,23 +41,20 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
40
41
  * @param {string} name
41
42
  * @param {string} defaultTreatment
42
43
  * @param {number} changeNumber
43
- * @returns {Promise} a promise that is resolved once the split kill operation is performed. The fulfillment value is a boolean: `true` if the kill success updating the split or `false` if no split is updated,
44
+ * @returns {Promise} a promise that is resolved once the split kill operation is performed. The fulfillment value is a boolean: `true` if the operation successed updating the split or `false` if no split is updated,
44
45
  * for instance, if the `changeNumber` is old, or if the split is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
45
46
  * The promise will never be rejected.
46
47
  */
47
48
  killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean> {
48
49
  return this.getSplit(name).then(split => {
49
50
 
50
- if (split) {
51
- const parsedSplit: ISplit = JSON.parse(split);
52
- if (!parsedSplit.changeNumber || parsedSplit.changeNumber < changeNumber) {
53
- parsedSplit.killed = true;
54
- parsedSplit.defaultTreatment = defaultTreatment;
55
- parsedSplit.changeNumber = changeNumber;
56
- const newSplit = JSON.stringify(parsedSplit);
51
+ if (split && (!split.changeNumber || split.changeNumber < changeNumber)) {
52
+ const newSplit = objectAssign({}, split);
53
+ newSplit.killed = true;
54
+ newSplit.defaultTreatment = defaultTreatment;
55
+ newSplit.changeNumber = changeNumber;
57
56
 
58
- return this.addSplit(name, newSplit);
59
- }
57
+ return this.addSplit(name, newSplit);
60
58
  }
61
59
  return false;
62
60
  }).catch(() => false);
@@ -1,5 +1,6 @@
1
1
  import { ISplitsCacheSync } from './types';
2
2
  import { ISplit } from '../dtos/types';
3
+ import { objectAssign } from '../utils/lang/objectAssign';
3
4
 
4
5
  /**
5
6
  * This class provides a skeletal implementation of the ISplitsCacheSync interface
@@ -7,9 +8,9 @@ import { ISplit } from '../dtos/types';
7
8
  */
8
9
  export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
9
10
 
10
- abstract addSplit(name: string, split: string): boolean;
11
+ abstract addSplit(name: string, split: ISplit): boolean
11
12
 
12
- addSplits(entries: [string, string][]): boolean[] {
13
+ addSplits(entries: [string, ISplit][]): boolean[] {
13
14
  return entries.map(keyValuePair => this.addSplit(keyValuePair[0], keyValuePair[1]));
14
15
  }
15
16
 
@@ -19,10 +20,10 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
19
20
  return names.map(name => this.removeSplit(name));
20
21
  }
21
22
 
22
- abstract getSplit(name: string): string | null
23
+ abstract getSplit(name: string): ISplit | null
23
24
 
24
- getSplits(names: string[]): Record<string, string | null> {
25
- const splits: Record<string, string | null> = {};
25
+ getSplits(names: string[]): Record<string, ISplit | null> {
26
+ const splits: Record<string, ISplit | null> = {};
26
27
  names.forEach(name => {
27
28
  splits[name] = this.getSplit(name);
28
29
  });
@@ -33,8 +34,8 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
33
34
 
34
35
  abstract getChangeNumber(): number
35
36
 
36
- getAll(): string[] {
37
- return this.getSplitNames().map(key => this.getSplit(key) as string);
37
+ getAll(): ISplit[] {
38
+ return this.getSplitNames().map(key => this.getSplit(key) as ISplit);
38
39
  }
39
40
 
40
41
  abstract getSplitNames(): string[]
@@ -60,22 +61,19 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
60
61
  * @param {string} name
61
62
  * @param {string} defaultTreatment
62
63
  * @param {number} changeNumber
63
- * @returns {Promise} a promise that is resolved once the split kill is performed. The fulfillment value is a boolean: `true` if the kill success updating the split or `false` if no split is updated,
64
+ * @returns {boolean} `true` if the operation successed updating the split, or `false` if no split is updated,
64
65
  * for instance, if the `changeNumber` is old, or if the split is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
65
66
  */
66
67
  killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean {
67
68
  const split = this.getSplit(name);
68
69
 
69
- if (split) {
70
- const parsedSplit: ISplit = JSON.parse(split);
71
- if (!parsedSplit.changeNumber || parsedSplit.changeNumber < changeNumber) {
72
- parsedSplit.killed = true;
73
- parsedSplit.defaultTreatment = defaultTreatment;
74
- parsedSplit.changeNumber = changeNumber;
75
- const newSplit = JSON.stringify(parsedSplit);
70
+ if (split && (!split.changeNumber || split.changeNumber < changeNumber)) {
71
+ const newSplit = objectAssign({}, split);
72
+ newSplit.killed = true;
73
+ newSplit.defaultTreatment = defaultTreatment;
74
+ newSplit.changeNumber = changeNumber;
76
75
 
77
- return this.addSplit(name, newSplit);
78
- }
76
+ return this.addSplit(name, newSplit);
79
77
  }
80
78
  return false;
81
79
  }
@@ -1,8 +1,9 @@
1
1
  import { KeyBuilder } from './KeyBuilder';
2
2
  import { IMetadata } from '../dtos/types';
3
3
  import { Method } from '../sync/submitters/types';
4
+ import { MAX_LATENCY_BUCKET_COUNT } from './inMemory/TelemetryCacheInMemory';
4
5
 
5
- const methodNames: Record<Method, string> = {
6
+ const METHOD_NAMES: Record<Method, string> = {
6
7
  t: 'treatment',
7
8
  ts: 'treatments',
8
9
  tc: 'treatmentWithConfig',
@@ -12,11 +13,17 @@ const methodNames: Record<Method, string> = {
12
13
 
13
14
  export class KeyBuilderSS extends KeyBuilder {
14
15
 
15
- protected readonly metadata: IMetadata;
16
+ latencyPrefix: string;
17
+ exceptionPrefix: string;
18
+ initPrefix: string;
19
+ private versionablePrefix: string;
16
20
 
17
21
  constructor(prefix: string, metadata: IMetadata) {
18
22
  super(prefix);
19
- this.metadata = metadata;
23
+ this.latencyPrefix = `${this.prefix}.telemetry.latencies`;
24
+ this.exceptionPrefix = `${this.prefix}.telemetry.exceptions`;
25
+ this.initPrefix = `${this.prefix}.telemetry.init`;
26
+ this.versionablePrefix = `${metadata.s}/${metadata.n}/${metadata.i}`;
20
27
  }
21
28
 
22
29
  buildRegisteredSegmentsKey() {
@@ -27,6 +34,14 @@ export class KeyBuilderSS extends KeyBuilder {
27
34
  return `${this.prefix}.impressions`;
28
35
  }
29
36
 
37
+ buildImpressionsCountKey() {
38
+ return `${this.prefix}.impressions.count`;
39
+ }
40
+
41
+ buildUniqueKeysKey() {
42
+ return `${this.prefix}.uniquekeys`;
43
+ }
44
+
30
45
  buildEventsKey() {
31
46
  return `${this.prefix}.events`;
32
47
  }
@@ -38,19 +53,56 @@ export class KeyBuilderSS extends KeyBuilder {
38
53
  /* Telemetry keys */
39
54
 
40
55
  buildLatencyKey(method: Method, bucket: number) {
41
- return `${this.prefix}.telemetry.latencies::${this.buildVersionablePrefix()}/${methodNames[method]}/${bucket}`;
56
+ return `${this.latencyPrefix}::${this.versionablePrefix}/${METHOD_NAMES[method]}/${bucket}`;
42
57
  }
43
58
 
44
59
  buildExceptionKey(method: Method) {
45
- return `${this.prefix}.telemetry.exceptions::${this.buildVersionablePrefix()}/${methodNames[method]}`;
60
+ return `${this.exceptionPrefix}::${this.versionablePrefix}/${METHOD_NAMES[method]}`;
46
61
  }
47
62
 
48
63
  buildInitKey() {
49
- return `${this.prefix}.telemetry.init::${this.buildVersionablePrefix()}`;
64
+ return `${this.initPrefix}::${this.versionablePrefix}`;
50
65
  }
51
66
 
52
- private buildVersionablePrefix() {
53
- return `${this.metadata.s}/${this.metadata.n}/${this.metadata.i}`;
54
- }
67
+ }
68
+
69
+ // Used by consumer methods of TelemetryCacheInRedis and TelemetryCachePluggable
70
+
71
+ const REVERSE_METHOD_NAMES = Object.keys(METHOD_NAMES).reduce((acc, key) => {
72
+ acc[METHOD_NAMES[key as Method]] = key as Method;
73
+ return acc;
74
+ }, {} as Record<string, Method>);
75
+
76
+
77
+ export function parseMetadata(field: string): [metadata: string] | string {
78
+ const parts = field.split('/');
79
+ if (parts.length !== 3) return `invalid subsection count. Expected 3, got: ${parts.length}`;
80
+
81
+ const [s /* metadata.s */, n /* metadata.n */, i /* metadata.i */] = parts;
82
+ return [JSON.stringify({ s, n, i })];
83
+ }
84
+
85
+ export function parseExceptionField(field: string): [metadata: string, method: Method] | string {
86
+ const parts = field.split('/');
87
+ if (parts.length !== 4) return `invalid subsection count. Expected 4, got: ${parts.length}`;
88
+
89
+ const [s /* metadata.s */, n /* metadata.n */, i /* metadata.i */, m] = parts;
90
+ const method = REVERSE_METHOD_NAMES[m];
91
+ if (!method) return `unknown method '${m}'`;
92
+
93
+ return [JSON.stringify({ s, n, i }), method];
94
+ }
95
+
96
+ export function parseLatencyField(field: string): [metadata: string, method: Method, bucket: number] | string {
97
+ const parts = field.split('/');
98
+ if (parts.length !== 5) return `invalid subsection count. Expected 5, got: ${parts.length}`;
99
+
100
+ const [s /* metadata.s */, n /* metadata.n */, i /* metadata.i */, m, b] = parts;
101
+ const method = REVERSE_METHOD_NAMES[m];
102
+ if (!method) return `unknown method '${m}'`;
103
+
104
+ const bucket = parseInt(b);
105
+ if (isNaN(bucket) || bucket >= MAX_LATENCY_BUCKET_COUNT) return `invalid bucket. Expected a number between 0 and ${MAX_LATENCY_BUCKET_COUNT - 1}, got: ${b}`;
55
106
 
107
+ return [JSON.stringify({ s, n, i }), method, bucket];
56
108
  }
@@ -39,7 +39,7 @@ export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoa
39
39
  storage.splits.setChangeNumber(since);
40
40
 
41
41
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
42
- storage.splits.addSplits(Object.keys(splitsData).map(splitName => [splitName, splitsData[splitName]]));
42
+ storage.splits.addSplits(Object.keys(splitsData).map(splitName => JSON.parse(splitsData[splitName])));
43
43
 
44
44
  // add mySegments data
45
45
  let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
@@ -38,7 +38,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
38
38
  else localStorage.removeItem(key);
39
39
  }
40
40
 
41
- private _decrementCounts(split: ISplit) {
41
+ private _decrementCounts(split: ISplit | null) {
42
42
  try {
43
43
  if (split) {
44
44
  if (split.trafficTypeName) {
@@ -99,18 +99,16 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
99
99
  this.hasSync = false;
100
100
  }
101
101
 
102
- addSplit(name: string, split: string) {
102
+ addSplit(name: string, split: ISplit) {
103
103
  try {
104
104
  const splitKey = this.keys.buildSplitKey(name);
105
105
  const splitFromLocalStorage = localStorage.getItem(splitKey);
106
106
  const previousSplit = splitFromLocalStorage ? JSON.parse(splitFromLocalStorage) : null;
107
107
  this._decrementCounts(previousSplit);
108
108
 
109
- localStorage.setItem(splitKey, split);
109
+ localStorage.setItem(splitKey, JSON.stringify(split));
110
110
 
111
- const parsedSplit = split ? JSON.parse(split) : null;
112
-
113
- this._incrementCounts(parsedSplit);
111
+ this._incrementCounts(split);
114
112
 
115
113
  return true;
116
114
  } catch (e) {
@@ -124,8 +122,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
124
122
  const split = this.getSplit(name);
125
123
  localStorage.removeItem(this.keys.buildSplitKey(name));
126
124
 
127
- const parsedSplit = JSON.parse(split as string);
128
- this._decrementCounts(parsedSplit);
125
+ this._decrementCounts(split);
129
126
 
130
127
  return true;
131
128
  } catch (e) {
@@ -135,7 +132,8 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
135
132
  }
136
133
 
137
134
  getSplit(name: string) {
138
- return localStorage.getItem(this.keys.buildSplitKey(name));
135
+ const item = localStorage.getItem(this.keys.buildSplitKey(name));
136
+ return item && JSON.parse(item);
139
137
  }
140
138
 
141
139
  setChangeNumber(changeNumber: number): boolean {
@@ -184,7 +182,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
184
182
  return n;
185
183
  }
186
184
 
187
- getSplitNames() {
185
+ getSplitNames(): string[] {
188
186
  const len = localStorage.length;
189
187
  const accum = [];
190
188
 
@@ -231,7 +229,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
231
229
 
232
230
  /**
233
231
  * Clean Splits cache if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
234
- * Clean operation (clear) also updates `lastUpdated` timestamp with current time.
235
232
  *
236
233
  * @param {number | undefined} expirationTimestamp if the value is not a number, data will not be cleaned
237
234
  */
@@ -12,8 +12,9 @@ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
12
12
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
13
13
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
14
14
  import { LOG_PREFIX } from './constants';
15
- import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
15
+ import { NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
16
16
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
17
+ import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
17
18
 
18
19
  export interface InLocalStorageOptions {
19
20
  prefix?: string
@@ -44,7 +45,8 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
44
45
  impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
45
46
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
46
47
  events: new EventsCacheInMemory(params.eventsQueueSize),
47
- telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
48
+ telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory() : undefined,
49
+ uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
48
50
 
49
51
  destroy() {
50
52
  this.splits = new SplitsCacheInMemory();
@@ -52,6 +54,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
52
54
  this.impressions.clear();
53
55
  this.impressionCounts && this.impressionCounts.clear();
54
56
  this.events.clear();
57
+ this.uniqueKeys?.clear();
55
58
  },
56
59
 
57
60
  // When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).