@splitsoftware/splitio-commons 1.3.1 → 1.3.2-rc.2

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 (219) hide show
  1. package/cjs/consent/sdkUserConsent.js +1 -1
  2. package/cjs/listeners/browser.js +5 -4
  3. package/cjs/logger/constants.js +2 -1
  4. package/cjs/logger/messages/error.js +2 -1
  5. package/cjs/logger/messages/info.js +3 -3
  6. package/cjs/logger/messages/warn.js +2 -2
  7. package/cjs/sdkClient/client.js +17 -3
  8. package/cjs/sdkClient/sdkClient.js +4 -1
  9. package/cjs/sdkFactory/index.js +16 -19
  10. package/cjs/services/splitApi.js +15 -14
  11. package/cjs/services/splitHttpClient.js +4 -1
  12. package/cjs/storages/AbstractSegmentsCacheSync.js +0 -5
  13. package/cjs/storages/KeyBuilderSS.js +12 -19
  14. package/cjs/storages/findLatencyIndex.js +11 -6
  15. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +13 -1
  16. package/cjs/storages/inLocalStorage/index.js +4 -1
  17. package/cjs/storages/inMemory/InMemoryStorage.js +2 -0
  18. package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -0
  19. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +6 -0
  20. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +6 -0
  21. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +165 -0
  22. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +29 -0
  23. package/cjs/storages/inRedis/index.js +2 -4
  24. package/cjs/storages/pluggable/TelemetryCachePluggable.js +27 -0
  25. package/cjs/storages/pluggable/index.js +2 -1
  26. package/cjs/sync/polling/pollingManagerCS.js +1 -1
  27. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +2 -2
  28. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +0 -3
  29. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -8
  30. package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -6
  31. package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +20 -13
  32. package/cjs/sync/streaming/SSEHandler/index.js +21 -15
  33. package/cjs/sync/streaming/pushManager.js +7 -4
  34. package/cjs/sync/submitters/eventsSubmitter.js +28 -0
  35. package/cjs/sync/submitters/{impressionCountsSyncTask.js → impressionCountsSubmitter.js} +10 -7
  36. package/cjs/sync/submitters/{impressionsSyncTask.js → impressionsSubmitter.js} +8 -8
  37. package/cjs/sync/submitters/submitter.js +66 -0
  38. package/cjs/sync/submitters/submitterManager.js +12 -10
  39. package/cjs/sync/submitters/telemetrySubmitter.js +128 -0
  40. package/cjs/sync/syncManagerOnline.js +6 -2
  41. package/cjs/trackers/eventTracker.js +5 -1
  42. package/cjs/trackers/impressionsTracker.js +9 -1
  43. package/cjs/trackers/telemetryTracker.js +65 -0
  44. package/cjs/utils/constants/index.js +40 -1
  45. package/cjs/utils/inputValidation/apiKey.js +12 -11
  46. package/cjs/utils/settingsValidation/index.js +35 -11
  47. package/cjs/utils/settingsValidation/url.js +4 -0
  48. package/cjs/utils/timeTracker/index.js +1 -0
  49. package/cjs/utils/timeTracker/timer.js +2 -2
  50. package/esm/consent/sdkUserConsent.js +1 -1
  51. package/esm/listeners/browser.js +3 -2
  52. package/esm/logger/constants.js +1 -0
  53. package/esm/logger/messages/error.js +2 -1
  54. package/esm/logger/messages/info.js +3 -3
  55. package/esm/logger/messages/warn.js +2 -2
  56. package/esm/sdkClient/client.js +18 -4
  57. package/esm/sdkClient/sdkClient.js +4 -1
  58. package/esm/sdkFactory/index.js +16 -19
  59. package/esm/services/splitApi.js +15 -14
  60. package/esm/services/splitHttpClient.js +4 -1
  61. package/esm/storages/AbstractSegmentsCacheSync.js +0 -5
  62. package/esm/storages/KeyBuilderSS.js +12 -19
  63. package/esm/storages/findLatencyIndex.js +11 -6
  64. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +13 -1
  65. package/esm/storages/inLocalStorage/index.js +5 -2
  66. package/esm/storages/inMemory/InMemoryStorage.js +3 -1
  67. package/esm/storages/inMemory/InMemoryStorageCS.js +4 -1
  68. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +6 -0
  69. package/esm/storages/inMemory/SegmentsCacheInMemory.js +6 -0
  70. package/esm/storages/inMemory/TelemetryCacheInMemory.js +161 -0
  71. package/esm/storages/inRedis/TelemetryCacheInRedis.js +26 -0
  72. package/esm/storages/inRedis/index.js +2 -4
  73. package/esm/storages/pluggable/TelemetryCachePluggable.js +24 -0
  74. package/esm/storages/pluggable/index.js +2 -1
  75. package/esm/sync/polling/pollingManagerCS.js +1 -1
  76. package/esm/sync/polling/syncTasks/splitsSyncTask.js +2 -2
  77. package/esm/sync/polling/updaters/mySegmentsUpdater.js +0 -3
  78. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -8
  79. package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -6
  80. package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +8 -1
  81. package/esm/sync/streaming/SSEHandler/index.js +21 -15
  82. package/esm/sync/streaming/pushManager.js +7 -4
  83. package/esm/sync/submitters/eventsSubmitter.js +24 -0
  84. package/esm/sync/submitters/{impressionCountsSyncTask.js → impressionCountsSubmitter.js} +8 -5
  85. package/esm/sync/submitters/{impressionsSyncTask.js → impressionsSubmitter.js} +6 -6
  86. package/esm/sync/submitters/submitter.js +61 -0
  87. package/esm/sync/submitters/submitterManager.js +12 -10
  88. package/esm/sync/submitters/telemetrySubmitter.js +122 -0
  89. package/esm/sync/syncManagerOnline.js +6 -2
  90. package/esm/trackers/eventTracker.js +6 -2
  91. package/esm/trackers/impressionsTracker.js +10 -2
  92. package/esm/trackers/telemetryTracker.js +61 -0
  93. package/esm/utils/constants/index.js +38 -0
  94. package/esm/utils/inputValidation/apiKey.js +2 -1
  95. package/esm/utils/settingsValidation/index.js +34 -10
  96. package/esm/utils/settingsValidation/url.js +4 -0
  97. package/esm/utils/timeTracker/index.js +1 -0
  98. package/esm/utils/timeTracker/timer.js +2 -2
  99. package/package.json +1 -1
  100. package/src/consent/sdkUserConsent.ts +1 -1
  101. package/src/listeners/browser.ts +3 -2
  102. package/src/logger/constants.ts +1 -0
  103. package/src/logger/messages/error.ts +2 -1
  104. package/src/logger/messages/info.ts +3 -3
  105. package/src/logger/messages/warn.ts +2 -2
  106. package/src/sdkClient/client.ts +23 -4
  107. package/src/sdkClient/sdkClient.ts +4 -1
  108. package/src/sdkFactory/index.ts +22 -24
  109. package/src/sdkFactory/types.ts +32 -15
  110. package/src/services/splitApi.ts +17 -14
  111. package/src/services/splitHttpClient.ts +6 -3
  112. package/src/services/types.ts +7 -5
  113. package/src/storages/AbstractSegmentsCacheSync.ts +8 -3
  114. package/src/storages/KeyBuilderSS.ts +13 -50
  115. package/src/storages/findLatencyIndex.ts +12 -3
  116. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +13 -1
  117. package/src/storages/inLocalStorage/index.ts +5 -2
  118. package/src/storages/inMemory/InMemoryStorage.ts +3 -1
  119. package/src/storages/inMemory/InMemoryStorageCS.ts +4 -1
  120. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +8 -0
  121. package/src/storages/inMemory/SegmentsCacheInMemory.ts +6 -0
  122. package/src/storages/inMemory/TelemetryCacheInMemory.ts +210 -0
  123. package/src/storages/inRedis/TelemetryCacheInRedis.ts +29 -0
  124. package/src/storages/inRedis/index.ts +2 -4
  125. package/src/storages/pluggable/TelemetryCachePluggable.ts +26 -0
  126. package/src/storages/pluggable/index.ts +2 -1
  127. package/src/storages/types.ts +84 -32
  128. package/src/sync/offline/syncManagerOffline.ts +4 -3
  129. package/src/sync/polling/pollingManagerCS.ts +3 -3
  130. package/src/sync/polling/pollingManagerSS.ts +2 -2
  131. package/src/sync/polling/syncTasks/splitsSyncTask.ts +2 -0
  132. package/src/sync/polling/updaters/mySegmentsUpdater.ts +0 -4
  133. package/src/sync/polling/updaters/segmentChangesUpdater.ts +2 -10
  134. package/src/sync/polling/updaters/splitChangesUpdater.ts +3 -6
  135. package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +11 -1
  136. package/src/sync/streaming/SSEHandler/index.ts +21 -14
  137. package/src/sync/streaming/pushManager.ts +11 -7
  138. package/src/sync/submitters/eventsSubmitter.ts +35 -0
  139. package/src/sync/submitters/{impressionCountsSyncTask.ts → impressionCountsSubmitter.ts} +15 -15
  140. package/src/sync/submitters/{impressionsSyncTask.ts → impressionsSubmitter.ts} +12 -16
  141. package/src/sync/submitters/{submitterSyncTask.ts → submitter.ts} +34 -16
  142. package/src/sync/submitters/submitterManager.ts +14 -11
  143. package/src/sync/submitters/telemetrySubmitter.ts +143 -0
  144. package/src/sync/submitters/types.ts +123 -0
  145. package/src/sync/syncManagerOnline.ts +13 -7
  146. package/src/sync/types.ts +0 -15
  147. package/src/trackers/eventTracker.ts +7 -3
  148. package/src/trackers/impressionsTracker.ts +11 -3
  149. package/src/trackers/telemetryTracker.ts +63 -0
  150. package/src/trackers/types.ts +24 -0
  151. package/src/types.ts +35 -6
  152. package/src/utils/constants/index.ts +45 -0
  153. package/src/utils/inputValidation/apiKey.ts +2 -1
  154. package/src/utils/settingsValidation/index.ts +35 -11
  155. package/src/utils/settingsValidation/url.ts +4 -0
  156. package/src/utils/timeTracker/index.ts +1 -1
  157. package/src/utils/timeTracker/timer.ts +3 -3
  158. package/types/logger/constants.d.ts +1 -0
  159. package/types/sdkFactory/types.d.ts +29 -14
  160. package/types/services/splitApi.d.ts +2 -1
  161. package/types/services/types.d.ts +8 -5
  162. package/types/storages/AbstractSegmentsCacheSync.d.ts +7 -3
  163. package/types/storages/KeyBuilderSS.d.ts +3 -3
  164. package/types/storages/findLatencyIndex.d.ts +7 -1
  165. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -0
  166. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +2 -0
  167. package/types/storages/inMemory/SegmentsCacheInMemory.d.ts +1 -0
  168. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -2
  169. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +3 -3
  170. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +2 -2
  171. package/types/storages/types.d.ts +71 -22
  172. package/types/sync/offline/syncManagerOffline.d.ts +3 -2
  173. package/types/sync/polling/pollingManagerCS.d.ts +2 -2
  174. package/types/sync/polling/pollingManagerSS.d.ts +2 -2
  175. package/types/sync/polling/syncTasks/splitsSyncTask.d.ts +1 -1
  176. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  177. package/types/sync/streaming/SSEHandler/NotificationKeeper.d.ts +2 -1
  178. package/types/sync/streaming/SSEHandler/index.d.ts +2 -1
  179. package/types/sync/streaming/pushManager.d.ts +2 -2
  180. package/types/sync/submitters/eventsSubmitter.d.ts +5 -0
  181. package/types/sync/submitters/impressionCountsSubmitter.d.ts +10 -0
  182. package/types/sync/submitters/impressionsSubmitter.d.ts +11 -0
  183. package/types/sync/submitters/submitter.d.ts +12 -0
  184. package/types/sync/submitters/submitterManager.d.ts +2 -2
  185. package/types/sync/submitters/telemetrySubmitter.d.ts +24 -0
  186. package/types/sync/submitters/telemetrySyncTask.d.ts +0 -27
  187. package/types/sync/submitters/types.d.ts +107 -0
  188. package/types/sync/syncManagerOnline.d.ts +3 -2
  189. package/types/sync/types.d.ts +0 -13
  190. package/types/trackers/eventTracker.d.ts +2 -2
  191. package/types/trackers/impressionsTracker.d.ts +2 -2
  192. package/types/trackers/telemetryTracker.d.ts +2 -3
  193. package/types/trackers/types.d.ts +22 -0
  194. package/types/types.d.ts +33 -4
  195. package/types/utils/constants/index.d.ts +37 -0
  196. package/types/utils/inputValidation/apiKey.d.ts +1 -0
  197. package/types/utils/settingsValidation/index.d.ts +40 -0
  198. package/types/utils/timeTracker/index.d.ts +1 -1
  199. package/types/utils/timeTracker/timer.d.ts +1 -1
  200. package/cjs/storages/inMemory/CountsCacheInMemory.js +0 -38
  201. package/cjs/storages/inMemory/LatenciesCacheInMemory.js +0 -43
  202. package/cjs/storages/inRedis/CountsCacheInRedis.js +0 -16
  203. package/cjs/storages/inRedis/LatenciesCacheInRedis.js +0 -18
  204. package/cjs/sync/submitters/eventsSyncTask.js +0 -44
  205. package/cjs/sync/submitters/metricsSyncTask.js +0 -31
  206. package/cjs/sync/submitters/submitterSyncTask.js +0 -44
  207. package/esm/storages/inMemory/CountsCacheInMemory.js +0 -35
  208. package/esm/storages/inMemory/LatenciesCacheInMemory.js +0 -40
  209. package/esm/storages/inRedis/CountsCacheInRedis.js +0 -13
  210. package/esm/storages/inRedis/LatenciesCacheInRedis.js +0 -15
  211. package/esm/sync/submitters/eventsSyncTask.js +0 -40
  212. package/esm/sync/submitters/metricsSyncTask.js +0 -26
  213. package/esm/sync/submitters/submitterSyncTask.js +0 -40
  214. package/src/storages/inMemory/CountsCacheInMemory.ts +0 -37
  215. package/src/storages/inMemory/LatenciesCacheInMemory.ts +0 -45
  216. package/src/storages/inRedis/CountsCacheInRedis.ts +0 -20
  217. package/src/storages/inRedis/LatenciesCacheInRedis.ts +0 -23
  218. package/src/sync/submitters/eventsSyncTask.ts +0 -57
  219. package/src/sync/submitters/metricsSyncTask.ts +0 -49
@@ -4,7 +4,7 @@ import { getMatching, getBucketing } from '../utils/key';
4
4
  import { validateSplitExistance } from '../utils/inputValidation/splitExistance';
5
5
  import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance';
6
6
  import { SDK_NOT_READY } from '../utils/labels';
7
- import { CONTROL } from '../utils/constants';
7
+ import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK } from '../utils/constants';
8
8
  import { IEvaluationResult } from '../evaluator/types';
9
9
  import { SplitIO, ImpressionDTO } from '../types';
10
10
  import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
@@ -13,16 +13,19 @@ import { ISdkFactoryContext } from '../sdkFactory/types';
13
13
  /**
14
14
  * Creator of base client with getTreatments and track methods.
15
15
  */
16
- // @TODO missing time tracking to collect telemetry
17
16
  export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
18
- const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker } = params;
17
+ const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker, telemetryTracker } = params;
19
18
  const { log, mode } = settings;
20
19
 
21
20
  function getTreatment(key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, withConfig = false) {
21
+ const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
22
+
22
23
  const wrapUp = (evaluationResult: IEvaluationResult) => {
23
24
  const queue: ImpressionDTO[] = [];
24
25
  const treatment = processEvaluation(evaluationResult, splitName, key, attributes, withConfig, `getTreatment${withConfig ? 'withConfig' : ''}`, queue);
25
26
  impressionsTracker.track(queue, attributes);
27
+
28
+ stopTelemetryTracker(queue[0] && queue[0].label);
26
29
  return treatment;
27
30
  };
28
31
 
@@ -36,6 +39,8 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
36
39
  }
37
40
 
38
41
  function getTreatments(key: SplitIO.SplitKey, splitNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false) {
42
+ const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
43
+
39
44
  const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
40
45
  const queue: ImpressionDTO[] = [];
41
46
  const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
@@ -43,6 +48,8 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
43
48
  treatments[splitName] = processEvaluation(evaluationResults[splitName], splitName, key, attributes, withConfig, `getTreatments${withConfig ? 'withConfig' : ''}`, queue);
44
49
  });
45
50
  impressionsTracker.track(queue, attributes);
51
+
52
+ stopTelemetryTracker(queue[0] && queue[0].label);
46
53
  return treatments;
47
54
  };
48
55
 
@@ -101,6 +108,8 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
101
108
  }
102
109
 
103
110
  function track(key: SplitIO.SplitKey, trafficTypeName: string, eventTypeId: string, value?: number, properties?: SplitIO.Properties, size = 1024) {
111
+ const stopTelemetryTracker = telemetryTracker.trackEval(TRACK);
112
+
104
113
  const matchingKey = getMatching(key);
105
114
  const timestamp = Date.now();
106
115
  const eventData: SplitIO.EventData = {
@@ -115,7 +124,17 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
115
124
  // This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event.
116
125
  validateTrafficTypeExistance(log, readinessManager, storage.splits, mode, trafficTypeName, 'track');
117
126
 
118
- return eventTracker.track(eventData, size);
127
+ const result = eventTracker.track(eventData, size);
128
+
129
+ if (thenable(result)) {
130
+ return result.then((result) => {
131
+ stopTelemetryTracker();
132
+ return result;
133
+ });
134
+ } else {
135
+ stopTelemetryTracker();
136
+ return result;
137
+ }
119
138
  }
120
139
 
121
140
  return {
@@ -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 } = params;
12
+ const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker } = params;
13
13
 
14
14
  return objectAssign(
15
15
  // Proto-linkage of the readiness Event Emitter
@@ -25,6 +25,9 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
25
25
  // Sdk destroy
26
26
  {
27
27
  destroy() {
28
+ // record stat before flushing data
29
+ if (!isSharedClient) telemetryTracker.sessionLength();
30
+
28
31
  // Stop background jobs
29
32
  syncManager && syncManager.stop();
30
33
  const flush = syncManager ? syncManager.flush() : Promise.resolve();
@@ -1,10 +1,10 @@
1
- import { ISdkFactoryParams } from './types';
1
+ import { ISdkFactoryContext, ISdkFactoryContextSync, ISdkFactoryParams } from './types';
2
2
  import { sdkReadinessManagerFactory } from '../readiness/sdkReadinessManager';
3
3
  import { impressionsTrackerFactory } from '../trackers/impressionsTracker';
4
4
  import { eventTrackerFactory } from '../trackers/eventTracker';
5
- import { IStorageFactoryParams, IStorageSync } from '../storages/types';
5
+ import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
6
+ import { IStorageFactoryParams } from '../storages/types';
6
7
  import { SplitIO } from '../types';
7
- import { ISplitApi } from '../services/types';
8
8
  import { getMatching } from '../utils/key';
9
9
  import { shouldBeOptimized } from '../trackers/impressionObserver/utils';
10
10
  import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
@@ -24,13 +24,14 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
24
24
  integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory } = params;
25
25
  const log = settings.log;
26
26
 
27
- // @TODO handle non-recoverable errors: not start sync, mark the SDK as destroyed, etc.
27
+ // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid API Key, etc.
28
+ // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
29
+
28
30
  // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
29
31
  validateAndTrackApiKey(log, settings.core.authorizationKey);
30
32
 
31
- // @TODO handle non-recoverable error, such as, `fetch` api not available, invalid API Key, etc.
32
33
  const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout);
33
- const readinessManager = sdkReadinessManager.readinessManager;
34
+ const readiness = sdkReadinessManager.readinessManager;
34
35
 
35
36
  // @TODO consider passing the settings object, so that each storage access only what it needs
36
37
  const storageFactoryParams: IStorageFactoryParams = {
@@ -49,8 +50,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
49
50
  // or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events.
50
51
  onReadyCb: (error) => {
51
52
  if (error) return; // Don't emit SDK_READY if storage failed to connect. Error message is logged by wrapperAdapter
52
- readinessManager.splits.emit(SDK_SPLITS_ARRIVED);
53
- readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
53
+ readiness.splits.emit(SDK_SPLITS_ARRIVED);
54
+ readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
54
55
  },
55
56
  metadata: metadataBuilder(settings),
56
57
  log
@@ -59,29 +60,26 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
59
60
  const storage = storageFactory(storageFactoryParams);
60
61
  // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
61
62
 
62
- // splitApi is used by SyncManager and Browser signal listener
63
- const splitApi = splitApiFactory && splitApiFactory(settings, platform);
64
-
65
- const syncManager = syncManagerFactory && syncManagerFactory({
66
- settings,
67
- splitApi: splitApi as ISplitApi,
68
- storage: storage as IStorageSync,
69
- readiness: sdkReadinessManager.readinessManager,
70
- platform
71
- });
72
-
73
63
  const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage });
74
64
 
75
65
  // trackers
76
66
  const observer = impressionsObserverFactory && impressionsObserverFactory();
77
- const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, integrationsManager, observer, storage.impressionCounts);
78
- const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager);
67
+ const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, integrationsManager, observer, storage.impressionCounts, storage.telemetry);
68
+ const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
69
+ const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
70
+
71
+ // splitApi is used by SyncManager and Browser signal listener
72
+ const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
73
+
74
+ const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, readiness, settings, storage, platform };
75
+
76
+ const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
77
+ ctx.syncManager = syncManager;
79
78
 
80
- // signal listener
81
79
  const signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
80
+ ctx.signalListener = signalListener;
82
81
 
83
- // Sdk client and manager
84
- const ctx = { eventTracker, impressionsTracker, sdkReadinessManager, settings, storage, syncManager, signalListener };
82
+ // SDK client and manager
85
83
  const clientMethod = sdkClientMethodFactory(ctx);
86
84
  const managerInstance = sdkManagerFactory(log, storage.splits, sdkReadinessManager);
87
85
 
@@ -1,33 +1,50 @@
1
1
  import { IIntegrationManager, IIntegrationFactoryParams } from '../integrations/types';
2
2
  import { ISignalListener } from '../listeners/types';
3
3
  import { ILogger } from '../logger/types';
4
- import { ISdkReadinessManager } from '../readiness/types';
4
+ import { IReadinessManager, ISdkReadinessManager } from '../readiness/types';
5
5
  import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
6
6
  import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types';
7
- import { ISyncManager, ISyncManagerFactoryParams } from '../sync/types';
7
+ import { ISyncManager } from '../sync/types';
8
8
  import { IImpressionObserver } from '../trackers/impressionObserver/types';
9
- import { IImpressionsTracker, IEventTracker } from '../trackers/types';
9
+ import { IImpressionsTracker, IEventTracker, ITelemetryTracker } from '../trackers/types';
10
10
  import { SplitIO, ISettings, IEventEmitter } from '../types';
11
11
 
12
+ /**
13
+ * Environment related dependencies.
14
+ * These getters are called a fixed number of times per factory instantiation.
15
+ */
16
+ export interface IPlatform {
17
+ getOptions?: () => object
18
+ getFetch?: () => (IFetch | undefined)
19
+ getEventSource?: () => (IEventSourceConstructor | undefined)
20
+ EventEmitter: new () => IEventEmitter,
21
+ now?: () => number
22
+ }
23
+
12
24
  export interface ISdkFactoryContext {
13
- storage: IStorageSync | IStorageAsync,
25
+ platform: IPlatform,
14
26
  sdkReadinessManager: ISdkReadinessManager,
27
+ readiness: IReadinessManager,
15
28
  settings: ISettings
16
29
  impressionsTracker: IImpressionsTracker,
17
30
  eventTracker: IEventTracker,
31
+ telemetryTracker: ITelemetryTracker,
32
+ storage: IStorageSync | IStorageAsync,
18
33
  signalListener?: ISignalListener
34
+ splitApi?: ISplitApi
19
35
  syncManager?: ISyncManager,
20
36
  }
21
37
 
22
- /**
23
- * Environment related dependencies.
24
- * These getters are called a fixed number of times per factory instantiation.
25
- */
26
- export interface IPlatform {
27
- getOptions?: () => object
28
- getFetch?: () => (IFetch | undefined)
29
- getEventSource?: () => (IEventSourceConstructor | undefined)
30
- EventEmitter: new () => IEventEmitter
38
+ export interface ISdkFactoryContextSync extends ISdkFactoryContext {
39
+ storage: IStorageSync,
40
+ splitApi: ISplitApi
41
+ syncManager: ISyncManager,
42
+ }
43
+
44
+ export interface ISdkFactoryContextAsync extends ISdkFactoryContext {
45
+ storage: IStorageAsync,
46
+ splitApi: undefined,
47
+ syncManager: undefined
31
48
  }
32
49
 
33
50
  /**
@@ -47,12 +64,12 @@ export interface ISdkFactoryParams {
47
64
 
48
65
  // Factory of Split Api (HTTP Client Service).
49
66
  // It is not required when providing an asynchronous storage or offline SyncManager
50
- splitApiFactory?: (settings: ISettings, platform: IPlatform) => ISplitApi,
67
+ splitApiFactory?: (settings: ISettings, platform: IPlatform, telemetryTracker: ITelemetryTracker) => ISplitApi,
51
68
 
52
69
  // SyncManager factory.
53
70
  // Not required when providing an asynchronous storage (consumer mode), but required in standalone mode to avoid SDK timeout.
54
71
  // It can create an offline or online sync manager, with or without streaming support.
55
- syncManagerFactory?: (params: ISyncManagerFactoryParams) => ISyncManager,
72
+ syncManagerFactory?: (params: ISdkFactoryContextSync) => ISyncManager,
56
73
 
57
74
  // Sdk manager factory
58
75
  sdkManagerFactory: (
@@ -3,6 +3,8 @@ import { ISettings } from '../types';
3
3
  import { splitHttpClientFactory } from './splitHttpClient';
4
4
  import { ISplitApi } from './types';
5
5
  import { objectAssign } from '../utils/lang/objectAssign';
6
+ import { ITelemetryTracker } from '../trackers/types';
7
+ import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
6
8
 
7
9
  const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
8
10
 
@@ -18,7 +20,8 @@ function userKeyToQueryParam(userKey: string) {
18
20
  */
19
21
  export function splitApiFactory(
20
22
  settings: Pick<ISettings, 'urls' | 'sync' | 'log' | 'version' | 'runtime' | 'core'>,
21
- platform: Pick<IPlatform, 'getFetch' | 'getOptions'>
23
+ platform: Pick<IPlatform, 'getFetch' | 'getOptions'>,
24
+ telemetryTracker: ITelemetryTracker
22
25
  ): ISplitApi {
23
26
 
24
27
  const urls = settings.urls;
@@ -44,17 +47,17 @@ export function splitApiFactory(
44
47
  if (queryParams) // accounting the possibility that `userKeys` and thus `queryParams` are empty
45
48
  url += '?' + queryParams;
46
49
  }
47
- return splitHttpClient(url);
50
+ return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
48
51
  },
49
52
 
50
53
  fetchSplitChanges(since: number, noCache?: boolean) {
51
54
  const url = `${urls.sdk}/splitChanges?since=${since}${filterQueryString || ''}`;
52
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
55
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS));
53
56
  },
54
57
 
55
58
  fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean) {
56
59
  const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}`;
57
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
60
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
58
61
  },
59
62
 
60
63
  fetchMySegments(userMatchingKey: string, noCache?: boolean) {
@@ -65,7 +68,7 @@ export function splitApiFactory(
65
68
  * - match user keys with special characters. E.g.: 'foo%bar', 'foo/bar'
66
69
  */
67
70
  const url = `${urls.sdk}/mySegments/${encodeURIComponent(userMatchingKey)}`;
68
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
71
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(MY_SEGMENT));
69
72
  },
70
73
 
71
74
  /**
@@ -76,7 +79,7 @@ export function splitApiFactory(
76
79
  */
77
80
  postEventsBulk(body: string, headers?: Record<string, string>) {
78
81
  const url = `${urls.events}/events/bulk`;
79
- return splitHttpClient(url, { method: 'POST', body, headers });
82
+ return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(EVENTS));
80
83
  },
81
84
 
82
85
  /**
@@ -90,7 +93,7 @@ export function splitApiFactory(
90
93
  return splitHttpClient(url, {
91
94
  // Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
92
95
  method: 'POST', body, headers: objectAssign({ SplitSDKImpressionsMode }, headers)
93
- });
96
+ }, telemetryTracker.trackHttp(IMPRESSIONS));
94
97
  },
95
98
 
96
99
  /**
@@ -101,17 +104,17 @@ export function splitApiFactory(
101
104
  */
102
105
  postTestImpressionsCount(body: string, headers?: Record<string, string>) {
103
106
  const url = `${urls.events}/testImpressions/count`;
104
- return splitHttpClient(url, { method: 'POST', body, headers });
107
+ return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
105
108
  },
106
109
 
107
- postMetricsCounters(body: string) {
108
- const url = `${urls.events}/metrics/counters`;
109
- return splitHttpClient(url, { method: 'POST', body }, true);
110
+ postMetricsConfig(body: string) {
111
+ const url = `${urls.telemetry}/v1/metrics/config`;
112
+ return splitHttpClient(url, { method: 'POST', body }, telemetryTracker.trackHttp(TELEMETRY), true);
110
113
  },
111
114
 
112
- postMetricsTimes(body: string) {
113
- const url = `${urls.events}/metrics/times`;
114
- return splitHttpClient(url, { method: 'POST', body }, true);
115
+ postMetricsUsage(body: string) {
116
+ const url = `${urls.telemetry}/v1/metrics/usage`;
117
+ return splitHttpClient(url, { method: 'POST', body }, telemetryTracker.trackHttp(TELEMETRY), true);
115
118
  }
116
119
  };
117
120
  }
@@ -1,4 +1,4 @@
1
- import { IFetch, IRequestOptions, IResponse, ISplitHttpClient } from './types';
1
+ import { IFetch, IRequestOptions, IResponse, ISplitHttpClient, NetworkError } from './types';
2
2
  import { objectAssign } from '../utils/lang/objectAssign';
3
3
  import { ERROR_HTTP, ERROR_CLIENT_CANNOT_GET_READY } from '../logger/constants';
4
4
  import { ISettings } from '../types';
@@ -31,7 +31,7 @@ export function splitHttpClientFactory(settings: Pick<ISettings, 'log' | 'versio
31
31
  if (ip) headers['SplitSDKMachineIP'] = ip;
32
32
  if (hostname) headers['SplitSDKMachineName'] = hostname;
33
33
 
34
- return function httpClient(url: string, reqOpts: IRequestOptions = {}, logErrorsAsInfo: boolean = false): Promise<IResponse> {
34
+ return function httpClient(url: string, reqOpts: IRequestOptions = {}, latencyTracker: (error?: NetworkError) => void = () => { }, logErrorsAsInfo: boolean = false): Promise<IResponse> {
35
35
 
36
36
  const request = objectAssign({
37
37
  headers: reqOpts.headers ? objectAssign({}, headers, reqOpts.headers) : headers,
@@ -46,6 +46,7 @@ export function splitHttpClientFactory(settings: Pick<ISettings, 'log' | 'versio
46
46
  if (!response.ok) {
47
47
  return response.text().then(message => Promise.reject({ response, message }));
48
48
  }
49
+ latencyTracker();
49
50
  return response;
50
51
  })
51
52
  .catch(error => {
@@ -68,9 +69,11 @@ export function splitHttpClientFactory(settings: Pick<ISettings, 'log' | 'versio
68
69
  log[logErrorsAsInfo ? 'info' : 'error'](ERROR_HTTP, [resp ? resp.status : 'NO_STATUS', url, msg]);
69
70
  }
70
71
 
71
- const networkError: Error & { statusCode?: number } = new Error(msg);
72
+ const networkError: NetworkError = new Error(msg);
72
73
  // passes `undefined` as statusCode if not an HTTP error (resp === undefined)
73
74
  networkError.statusCode = resp && resp.status;
75
+
76
+ latencyTracker(networkError);
74
77
  throw networkError;
75
78
  }) : Promise.reject(new Error(messageNoFetch));
76
79
  };
@@ -23,13 +23,15 @@ export type IResponse = {
23
23
  // }
24
24
  }
25
25
 
26
+ export type NetworkError = Error & { statusCode?: number }
27
+
26
28
  // Reduced version of Fetch API
27
29
  export type IFetch = (url: string, options?: IRequestOptions) => Promise<IResponse>
28
30
 
29
31
  // IFetch specialization
30
32
  export type IHealthCheckAPI = () => Promise<boolean>
31
33
 
32
- export type ISplitHttpClient = (url: string, options?: IRequestOptions, logErrorsAsInfo?: boolean) => Promise<IResponse>
34
+ export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean) => Promise<IResponse>
33
35
 
34
36
  export type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>
35
37
 
@@ -45,9 +47,9 @@ export type IPostTestImpressionsBulk = (body: string, headers?: Record<string, s
45
47
 
46
48
  export type IPostTestImpressionsCount = (body: string, headers?: Record<string, string>) => Promise<IResponse>
47
49
 
48
- export type IPostMetricsCounters = (body: string) => Promise<IResponse>
50
+ export type IPostMetricsConfig = (body: string) => Promise<IResponse>
49
51
 
50
- export type IPostMetricsTimes = (body: string) => Promise<IResponse>
52
+ export type IPostMetricsUsage = (body: string) => Promise<IResponse>
51
53
 
52
54
  export interface ISplitApi {
53
55
  getSdkAPIHealthCheck: IHealthCheckAPI
@@ -59,8 +61,8 @@ export interface ISplitApi {
59
61
  postEventsBulk: IPostEventsBulk
60
62
  postTestImpressionsBulk: IPostTestImpressionsBulk
61
63
  postTestImpressionsCount: IPostTestImpressionsCount
62
- postMetricsCounters: IPostMetricsCounters
63
- postMetricsTimes: IPostMetricsTimes
64
+ postMetricsConfig: IPostMetricsConfig
65
+ postMetricsUsage: IPostMetricsUsage
64
66
  }
65
67
 
66
68
  // Minimal version of EventSource API used by the SDK
@@ -37,10 +37,15 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
37
37
  registerSegments(names: string[]): boolean { return false; }
38
38
 
39
39
  /**
40
- * For server-side synchronizer: get the list of segments in the cache.
41
- * For client-side synchronizer: the method is not used.
40
+ * For server-side synchronizer: get the list of segments to fetch changes.
41
+ * Also used for the `seC` (segment count) telemetry stat.
42
+ */
43
+ abstract getRegisteredSegments(): string[]
44
+
45
+ /**
46
+ * Only used for the `skC`(segment keys count) telemetry stat: 1 for client-side, and total count of keys in server-side.
42
47
  */
43
- getRegisteredSegments(): string[] { return []; }
48
+ abstract getKeysCount(): number
44
49
 
45
50
  /**
46
51
  * For server-side synchronizer: set the change number of `name` segment.
@@ -1,9 +1,14 @@
1
1
  import { KeyBuilder } from './KeyBuilder';
2
2
  import { IMetadata } from '../dtos/types';
3
+ import { Method } from '../sync/submitters/types';
3
4
 
4
- // NOT USED
5
- // const everythingAfterCount = /count\.([^/]+)$/;
6
- // const latencyMetricNameAndBucket = /latency\.([^/]+)\.bucket\.([0-9]+)$/;
5
+ const methodNames: Record<Method, string> = {
6
+ t: 'treatment',
7
+ ts: 'treatments',
8
+ tc: 'treatmentWithConfig',
9
+ tcs: 'treatmentsWithConfig',
10
+ tr: 'track'
11
+ };
7
12
 
8
13
  export class KeyBuilderSS extends KeyBuilder {
9
14
 
@@ -19,7 +24,7 @@ export class KeyBuilderSS extends KeyBuilder {
19
24
  }
20
25
 
21
26
  private buildVersionablePrefix() {
22
- return `${this.prefix}/${this.metadata.s}/${this.metadata.i}`;
27
+ return `${this.metadata.s}/${this.metadata.n}/${this.metadata.i}`;
23
28
  }
24
29
 
25
30
  buildImpressionsKey() {
@@ -30,58 +35,16 @@ export class KeyBuilderSS extends KeyBuilder {
30
35
  return `${this.prefix}.events`;
31
36
  }
32
37
 
33
- private buildLatencyKeyPrefix() {
34
- return `${this.buildVersionablePrefix()}/latency`;
38
+ buildLatencyKey(method: Method, bucket: number) {
39
+ return `${this.prefix}.telemetry.latencies::${this.buildVersionablePrefix()}/${methodNames[method]}/${bucket}`;
35
40
  }
36
41
 
37
- buildLatencyKey(metricName: string, bucketNumber: number | string) {
38
- return `${this.buildLatencyKeyPrefix()}.${metricName}.bucket.${bucketNumber}`;
42
+ buildExceptionKey(method: Method) {
43
+ return `${this.prefix}.telemetry.exceptions::${this.buildVersionablePrefix()}/${methodNames[method]}`;
39
44
  }
40
45
 
41
- buildCountKey(metricName: string) {
42
- return `${this.buildVersionablePrefix()}/count.${metricName}`;
43
- }
44
-
45
- // NOT USED
46
- // buildGaugeKey(metricName: string) {
47
- // return `${this.buildVersionablePrefix()}/gauge.${metricName}`;
48
- // }
49
-
50
- // NOT USED
51
- // searchPatternForCountKeys() {
52
- // return `${this.buildVersionablePrefix()}/count.*`;
53
- // }
54
-
55
46
  searchPatternForSplitKeys() {
56
47
  return `${this.buildSplitKeyPrefix()}*`;
57
48
  }
58
49
 
59
- // NOT USED
60
- // searchPatternForLatency() {
61
- // return `${this.buildLatencyKeyPrefix()}.*`;
62
- // }
63
-
64
- // NOT USED
65
- // extractCounterName(counterKey: string) {
66
- // const m = counterKey.match(everythingAfterCount);
67
- // if (m && m.length) {
68
- // return m[1]; // everything after count
69
- // } else {
70
- // throw new Error('Invalid counter key provided');
71
- // }
72
- // }
73
-
74
- // NOT USED
75
- // extractLatencyMetricNameAndBucket(latencyKey: string) {
76
- // const parts = latencyKey.match(latencyMetricNameAndBucket);
77
-
78
- // if (parts && parts.length > 2) {
79
- // return {
80
- // metricName: parts[1],
81
- // bucketNumber: parts[2]
82
- // };
83
- // } else {
84
- // throw new Error('Invalid counter key provided');
85
- // }
86
- // }
87
50
  }
@@ -1,7 +1,16 @@
1
1
  import { isNaNNumber } from '../utils/lang';
2
2
 
3
- // @TODO add unit tests
4
- export function findLatencyIndex(latency: number, min = 0, max = 23, base = 1.5): number {
5
- const index = Math.min(max, Math.max(min, Math.floor(Math.log(latency) / Math.log(base))));
3
+ const MIN = 0;
4
+ const MAX = 22;
5
+ const BASE = 1.5;
6
+
7
+ /**
8
+ * Calculates buckets from latency in milliseconds
9
+ *
10
+ * @param latencyInMs
11
+ * @returns a bucket index from 0 to 22 inclusive
12
+ */
13
+ export function findLatencyIndex(latencyInMs: number): number {
14
+ const index = Math.min(MAX, Math.max(MIN, Math.ceil(Math.log(latencyInMs) / Math.log(BASE))));
6
15
  return isNaNNumber(index) ? 0 : index; // index is NaN if latency is not a positive number
7
16
  }
@@ -72,7 +72,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
72
72
  if (segmentName) {
73
73
  accum.push(segmentName);
74
74
  } else {
75
- // @BREAKING: This is only to clean up "old" keys. Remove this whole else code block.
75
+ // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block and reuse `getRegisteredSegments` method.
76
76
  segmentName = this.keys.extractOldSegmentKey(key);
77
77
 
78
78
  if (segmentName) { // this was an old segment key, let's clean up.
@@ -121,4 +121,16 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
121
121
  return isDiff;
122
122
  }
123
123
 
124
+ getRegisteredSegments(): string[] {
125
+ return Object.keys(localStorage).reduce<string[]>((accum, key) => {
126
+ const segmentName = this.keys.extractSegmentName(key);
127
+ if (segmentName) accum.push(segmentName);
128
+ return accum;
129
+ }, []);
130
+ }
131
+
132
+ getKeysCount() {
133
+ return 1;
134
+ }
135
+
124
136
  }
@@ -12,7 +12,8 @@ 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 { STORAGE_LOCALSTORAGE } from '../../utils/constants';
15
+ import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
16
+ import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
16
17
 
17
18
  export interface InLocalStorageOptions {
18
19
  prefix?: string
@@ -29,7 +30,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
29
30
 
30
31
  // Fallback to InMemoryStorage if LocalStorage API is not available
31
32
  if (!isLocalStorageAvailable()) {
32
- params.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage');
33
+ params.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
33
34
  return InMemoryStorageCSFactory(params);
34
35
  }
35
36
 
@@ -43,6 +44,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
43
44
  impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
44
45
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
45
46
  events: new EventsCacheInMemory(params.eventsQueueSize),
47
+ telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
46
48
 
47
49
  destroy() {
48
50
  this.splits = new SplitsCacheInMemory();
@@ -62,6 +64,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
62
64
  impressions: this.impressions,
63
65
  impressionCounts: this.impressionCounts,
64
66
  events: this.events,
67
+ telemetry: this.telemetry,
65
68
 
66
69
  destroy() {
67
70
  this.splits = new SplitsCacheInMemory();
@@ -4,7 +4,8 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { IStorageFactoryParams, IStorageSync } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
- import { STORAGE_MEMORY } from '../../utils/constants';
7
+ import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
+ import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
8
9
 
9
10
  /**
10
11
  * InMemory storage factory for standalone server-side SplitFactory
@@ -19,6 +20,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
19
20
  impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
20
21
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
21
22
  events: new EventsCacheInMemory(params.eventsQueueSize),
23
+ telemetry: params.mode !== LOCALHOST_MODE ? new TelemetryCacheInMemory() : undefined, // Always track telemetry in standalone mode on server-side
22
24
 
23
25
  // When using MEMORY we should clean all the caches to leave them empty
24
26
  destroy() {