@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
@@ -1,7 +1,7 @@
1
1
  import { ISegmentChangesFetcher } from '../fetchers/types';
2
2
  import { ISegmentsCacheBase } from '../../../storages/types';
3
3
  import { IReadinessManager } from '../../../readiness/types';
4
- import { ISegmentChangesResponse, MaybeThenable } from '../../../dtos/types';
4
+ import { MaybeThenable } from '../../../dtos/types';
5
5
  import { findIndex } from '../../../utils/lang';
6
6
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
7
7
  import { ILogger } from '../../../logger/types';
@@ -30,14 +30,6 @@ export function segmentChangesUpdaterFactory(
30
30
 
31
31
  let readyOnAlreadyExistentState = true;
32
32
 
33
- /** telemetry decorator for `segmentChangesFetcher` promise */
34
- function _promiseDecorator(promise: Promise<ISegmentChangesResponse[]>) {
35
- return promise;
36
- // @TODO handle telemetry?
37
- // const collectMetrics = startingUp || isNode; // If we are on the browser, only collect this metric for first fetch. On node do it always.
38
- // splitsPromise = tracker.start(tracker.TaskNames.SPLITS_FETCH, collectMetrics ? metricCollectors : false, splitsPromise);
39
- }
40
-
41
33
  /**
42
34
  * Segments updater returns a promise that resolves with a `false` boolean value if it fails at least to fetch a segment or synchronize it with the storage.
43
35
  * Thus, a false result doesn't imply that SDK_SEGMENTS_ARRIVED was not emitted.
@@ -67,7 +59,7 @@ export function segmentChangesUpdaterFactory(
67
59
  // if fetchOnlyNew flag, avoid processing already fetched segments
68
60
  if (fetchOnlyNew && since !== -1) return -1;
69
61
 
70
- return segmentChangesFetcher(since, segmentName, noCache, _promiseDecorator).then(function (changes) {
62
+ return segmentChangesFetcher(since, segmentName, noCache).then(function (changes) {
71
63
  let changeNumber = -1;
72
64
  const results: MaybeThenable<boolean | void>[] = [];
73
65
  changes.forEach(x => {
@@ -93,18 +93,15 @@ export function splitChangesUpdaterFactory(
93
93
  splitsEventEmitter?: ISplitsEventEmitter,
94
94
  requestTimeoutBeforeReady: number = 0,
95
95
  retriesOnFailureBeforeReady: number = 0,
96
+ isClientSide?: boolean
96
97
  ): ISplitChangesUpdater {
97
98
 
98
99
  let startingUp = true;
99
100
 
100
- /** timeout and telemetry decorator for `splitChangesFetcher` promise */
101
+ /** timeout decorator for `splitChangesFetcher` promise */
101
102
  function _promiseDecorator<T>(promise: Promise<T>) {
102
103
  if (startingUp && requestTimeoutBeforeReady) promise = timeout(requestTimeoutBeforeReady, promise);
103
104
  return promise;
104
-
105
- // @TODO telemetry
106
- // const collectMetrics = startingUp || isNode; // If we are on the browser, only collect this metric for first fetch. On node do it always.
107
- // splitsPromise = tracker.start(tracker.TaskNames.SPLITS_FETCH, collectMetrics ? metricCollectors : false, splitsPromise);
108
105
  }
109
106
 
110
107
  /**
@@ -144,7 +141,7 @@ export function splitChangesUpdaterFactory(
144
141
 
145
142
  if (splitsEventEmitter) {
146
143
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
147
- return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && checkAllSegmentsExist(segments)))
144
+ return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && (isClientSide || checkAllSegmentsExist(segments))))
148
145
  .catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
149
146
  .then(emitSplitsArrivedEvent => {
150
147
  // emit SDK events
@@ -1,7 +1,11 @@
1
+ import { ITelemetryTracker } from '../../../trackers/types';
2
+ import { CONNECTION_ESTABLISHED, DISABLED, ENABLED, OCCUPANCY_PRI, OCCUPANCY_SEC, PAUSED, STREAMING_STATUS } from '../../../utils/constants';
3
+ import { StreamingEventType } from '../../submitters/types';
1
4
  import { ControlType, PUSH_SUBSYSTEM_UP, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN } from '../constants';
2
5
  import { IPushEventEmitter } from '../types';
3
6
 
4
7
  const CONTROL_CHANNEL_REGEXS = [/control_pri$/, /control_sec$/];
8
+ const STREAMING_EVENT_TYPES: StreamingEventType[] = [OCCUPANCY_PRI, OCCUPANCY_SEC];
5
9
 
6
10
  /**
7
11
  * Factory of notification keeper, which process OCCUPANCY and CONTROL notifications and emits the corresponding push events.
@@ -9,7 +13,7 @@ const CONTROL_CHANNEL_REGEXS = [/control_pri$/, /control_sec$/];
9
13
  * @param pushEmitter emitter for events related to streaming support
10
14
  */
11
15
  // @TODO update logic to handle OCCUPANCY for any region and rename according to new spec (e.g.: PUSH_SUBSYSTEM_UP --> PUSH_SUBSYSTEM_UP)
12
- export function notificationKeeperFactory(pushEmitter: IPushEventEmitter) {
16
+ export function notificationKeeperFactory(pushEmitter: IPushEventEmitter, telemetryTracker: ITelemetryTracker) {
13
17
 
14
18
  let channels = CONTROL_CHANNEL_REGEXS.map(regex => ({
15
19
  regex,
@@ -30,6 +34,7 @@ export function notificationKeeperFactory(pushEmitter: IPushEventEmitter) {
30
34
 
31
35
  return {
32
36
  handleOpen() {
37
+ telemetryTracker.streamingEvent(CONNECTION_ESTABLISHED);
33
38
  pushEmitter.emit(PUSH_SUBSYSTEM_UP);
34
39
  },
35
40
 
@@ -41,6 +46,8 @@ export function notificationKeeperFactory(pushEmitter: IPushEventEmitter) {
41
46
  for (let i = 0; i < channels.length; i++) {
42
47
  const c = channels[i];
43
48
  if (c.regex.test(channel)) {
49
+ telemetryTracker.streamingEvent(STREAMING_EVENT_TYPES[i], publishers);
50
+
44
51
  if (timestamp > c.oTime) {
45
52
  c.oTime = timestamp;
46
53
  c.hasPublishers = publishers !== 0;
@@ -76,11 +83,14 @@ export function notificationKeeperFactory(pushEmitter: IPushEventEmitter) {
76
83
  if (timestamp > c.cTime) {
77
84
  c.cTime = timestamp;
78
85
  if (controlType === ControlType.STREAMING_DISABLED) {
86
+ telemetryTracker.streamingEvent(STREAMING_STATUS, DISABLED);
79
87
  pushEmitter.emit(PUSH_NONRETRYABLE_ERROR);
80
88
  } else if (hasPublishers) {
81
89
  if (controlType === ControlType.STREAMING_PAUSED && hasResumed) {
90
+ telemetryTracker.streamingEvent(STREAMING_STATUS, PAUSED);
82
91
  pushEmitter.emit(PUSH_SUBSYSTEM_DOWN);
83
92
  } else if (controlType === ControlType.STREAMING_RESUMED && !hasResumed) {
93
+ telemetryTracker.streamingEvent(STREAMING_STATUS, ENABLED);
84
94
  pushEmitter.emit(PUSH_SUBSYSTEM_UP);
85
95
  }
86
96
  // nothing to do when hasPublishers === false:
@@ -6,18 +6,8 @@ import { ISseEventHandler } from '../SSEClient/types';
6
6
  import { INotificationError, INotificationMessage } from './types';
7
7
  import { ILogger } from '../../../logger/types';
8
8
  import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
9
-
10
- function isRetryableError(error: INotificationError) {
11
- if (error.parsedData && error.parsedData.code) {
12
- const code = error.parsedData.code;
13
- // 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
14
- if (40140 <= code && code <= 40149) return true;
15
- // Others 4XX errors (e.g., bad request from the SDK)
16
- if (40000 <= code && code <= 49999) return false;
17
- }
18
- // network errors or 5XX HTTP errors
19
- return true;
20
- }
9
+ import { ABLY_ERROR, NON_REQUESTED, SSE_CONNECTION_ERROR } from '../../../utils/constants';
10
+ import { ITelemetryTracker } from '../../../trackers/types';
21
11
 
22
12
  /**
23
13
  * Factory for SSEHandler, which processes SSEClient messages and emits the corresponding push events.
@@ -25,9 +15,26 @@ function isRetryableError(error: INotificationError) {
25
15
  * @param log factory logger
26
16
  * @param pushEmitter emitter for events related to streaming support
27
17
  */
28
- export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter): ISseEventHandler {
18
+ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter, telemetryTracker: ITelemetryTracker): ISseEventHandler {
29
19
 
30
- const notificationKeeper = notificationKeeperFactory(pushEmitter);
20
+ const notificationKeeper = notificationKeeperFactory(pushEmitter, telemetryTracker);
21
+
22
+ function isRetryableError(error: INotificationError): boolean {
23
+ if (error.parsedData && error.parsedData.code) {
24
+ // Ably error
25
+ const code = error.parsedData.code;
26
+ telemetryTracker.streamingEvent(ABLY_ERROR, code);
27
+
28
+ // 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
29
+ if (40140 <= code && code <= 40149) return true;
30
+ // Others 4XX errors (e.g., bad request from the SDK)
31
+ if (40000 <= code && code <= 49999) return false;
32
+ } else {
33
+ // network errors or 5XX HTTP errors
34
+ telemetryTracker.streamingEvent(SSE_CONNECTION_ERROR, NON_REQUESTED);
35
+ }
36
+ return true;
37
+ }
31
38
 
32
39
  return {
33
40
  handleOpen() {
@@ -18,7 +18,8 @@ import { isInBitmap, parseBitmap, parseKeyList } from './mySegmentsV2utils';
18
18
  import { ISet, _Set } from '../../utils/lang/sets';
19
19
  import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
20
20
  import { IAuthTokenPushEnabled } from './AuthClient/types';
21
- import { ISyncManagerFactoryParams } from '../types';
21
+ import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
22
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
22
23
 
23
24
  /**
24
25
  * PushManager factory:
@@ -26,11 +27,11 @@ import { ISyncManagerFactoryParams } from '../types';
26
27
  * - for client-side, with support for multiple clients, if key is provided in settings
27
28
  */
28
29
  export function pushManagerFactory(
29
- params: ISyncManagerFactoryParams,
30
+ params: ISdkFactoryContextSync,
30
31
  pollingManager: IPollingManager,
31
32
  ): IPushManager | undefined {
32
33
 
33
- const { settings, storage, splitApi, readiness, platform } = params;
34
+ const { settings, storage, splitApi, readiness, platform, telemetryTracker } = params;
34
35
 
35
36
  // `userKey` is the matching key of main client in client-side SDK.
36
37
  // It can be used to check if running on client-side or server-side SDK.
@@ -49,7 +50,7 @@ export function pushManagerFactory(
49
50
 
50
51
  // init feedback loop
51
52
  const pushEmitter = new platform.EventEmitter() as IPushEventEmitter;
52
- const sseHandler = SSEHandlerFactory(log, pushEmitter);
53
+ const sseHandler = SSEHandlerFactory(log, pushEmitter, telemetryTracker);
53
54
  sseClient.setEventHandler(sseHandler);
54
55
 
55
56
  // init workers
@@ -101,6 +102,8 @@ export function pushManagerFactory(
101
102
  if (disconnected) return;
102
103
  sseClient.open(authData);
103
104
  }, connDelay * 1000);
105
+
106
+ telemetryTracker.streamingEvent(TOKEN_REFRESH, decodedToken.exp);
104
107
  }
105
108
 
106
109
  function connectPush() {
@@ -137,6 +140,7 @@ export function pushManagerFactory(
137
140
 
138
141
  // Handle 4XX HTTP errors: 401 (invalid API Key) or 400 (using incorrect API Key, i.e., client-side API Key on server-side)
139
142
  if (error.statusCode >= 400 && error.statusCode < 500) {
143
+ telemetryTracker.streamingEvent(AUTH_REJECTION);
140
144
  pushEmitter.emit(PUSH_NONRETRYABLE_ERROR);
141
145
  return;
142
146
  }
@@ -179,7 +183,7 @@ export function pushManagerFactory(
179
183
  stopWorkers();
180
184
  });
181
185
 
182
- /** Fallbacking without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
186
+ /** Fallback to polling without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
183
187
 
184
188
  pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
185
189
  disabled = true;
@@ -188,7 +192,7 @@ export function pushManagerFactory(
188
192
  pushEmitter.emit(PUSH_SUBSYSTEM_DOWN); // no harm if polling already
189
193
  });
190
194
 
191
- /** Fallbacking with retry due to recoverable SSE and Authentication errors */
195
+ /** Fallback to polling with retry due to recoverable SSE and Authentication errors */
192
196
 
193
197
  pushEmitter.on(PUSH_RETRYABLE_ERROR, function handleRetryableError() { // HTTP or network error in SSE connection
194
198
  // SSE connection is closed to avoid repeated errors due to retries
@@ -316,7 +320,7 @@ export function pushManagerFactory(
316
320
  },
317
321
 
318
322
  // true/false if start or stop was called last respectively
319
- isRunning(){
323
+ isRunning() {
320
324
  return disconnected === false;
321
325
  },
322
326
 
@@ -0,0 +1,35 @@
1
+ import { submitterFactory, firstPushWindowDecorator } from './submitter';
2
+ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
3
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
4
+
5
+ const DATA_NAME = 'events';
6
+
7
+ /**
8
+ * Submitter that periodically posts tracked events
9
+ */
10
+ export function eventsSubmitterFactory(params: ISdkFactoryContextSync) {
11
+
12
+ const {
13
+ settings: { log, scheduler: { eventsPushRate }, startup: { eventsFirstPushWindow } },
14
+ splitApi: { postEventsBulk },
15
+ storage: { events },
16
+ } = params;
17
+
18
+ // don't retry events.
19
+ let submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate, DATA_NAME);
20
+
21
+ // Set a timer for the first push window of events.
22
+ if (eventsFirstPushWindow > 0) submitter = firstPushWindowDecorator(submitter, eventsFirstPushWindow);
23
+
24
+ // register events submitter to be executed when events cache is full
25
+ events.setOnFullQueueCb(() => {
26
+ if (submitter.isRunning()) {
27
+ log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
28
+ submitter.execute();
29
+ }
30
+ // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
31
+ // Data will be sent when submitter is resumed.
32
+ });
33
+
34
+ return submitter;
35
+ }
@@ -1,9 +1,6 @@
1
- import { ISyncTask, ITimeTracker } from '../types';
2
- import { IPostTestImpressionsCount } from '../../services/types';
3
- import { IImpressionCountsCacheSync } from '../../storages/types';
4
- import { submitterSyncTaskFactory } from './submitterSyncTask';
1
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
2
+ import { submitterFactory } from './submitter';
5
3
  import { ImpressionCountsPayload } from './types';
6
- import { ILogger } from '../../logger/types';
7
4
 
8
5
  /**
9
6
  * Converts `impressionCounts` data from cache into request payload.
@@ -32,15 +29,18 @@ export function fromImpressionCountsCollector(impressionsCount: Record<string, n
32
29
  const IMPRESSIONS_COUNT_RATE = 1800000; // 30 minutes
33
30
 
34
31
  /**
35
- * Sync task that periodically posts impression counts
32
+ * Submitter that periodically posts impression counts
36
33
  */
37
- export function impressionCountsSyncTaskFactory(
38
- log: ILogger,
39
- postTestImpressionsCount: IPostTestImpressionsCount,
40
- impressionCountsCache: IImpressionCountsCacheSync,
41
- latencyTracker?: ITimeTracker
42
- ): ISyncTask {
43
-
44
- // retry impressions counts only once.
45
- return submitterSyncTaskFactory(log, postTestImpressionsCount, impressionCountsCache, IMPRESSIONS_COUNT_RATE, 'impression counts', latencyTracker, fromImpressionCountsCollector, 1);
34
+ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync) {
35
+
36
+ const {
37
+ settings: { log },
38
+ splitApi: { postTestImpressionsCount },
39
+ storage: { impressionCounts }
40
+ } = params;
41
+
42
+ if (impressionCounts) {
43
+ // retry impressions counts only once.
44
+ return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
45
+ }
46
46
  }
@@ -1,12 +1,9 @@
1
1
  import { groupBy, forOwn } from '../../utils/lang';
2
- import { ISyncTask, ITimeTracker } from '../types';
3
- import { IPostTestImpressionsBulk } from '../../services/types';
4
- import { IImpressionsCacheSync } from '../../storages/types';
5
2
  import { ImpressionDTO } from '../../types';
6
- import { submitterSyncTaskFactory } from './submitterSyncTask';
3
+ import { submitterFactory } from './submitter';
7
4
  import { ImpressionsPayload } from './types';
8
- import { ILogger } from '../../logger/types';
9
5
  import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
6
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
10
7
 
11
8
  const DATA_NAME = 'impressions';
12
9
 
@@ -41,22 +38,21 @@ export function fromImpressionsCollector(sendLabels: boolean, data: ImpressionDT
41
38
  }
42
39
 
43
40
  /**
44
- * Sync task that periodically posts impressions data
41
+ * Submitter that periodically posts impressions data
45
42
  */
46
- export function impressionsSyncTaskFactory(
47
- log: ILogger,
48
- postTestImpressionsBulk: IPostTestImpressionsBulk,
49
- impressionsCache: IImpressionsCacheSync,
50
- impressionsRefreshRate: number,
51
- sendLabels = false,
52
- latencyTracker?: ITimeTracker,
53
- ): ISyncTask {
43
+ export function impressionsSubmitterFactory(params: ISdkFactoryContextSync) {
44
+
45
+ const {
46
+ settings: { log, scheduler: { impressionsRefreshRate }, core: { labelsEnabled } },
47
+ splitApi: { postTestImpressionsBulk },
48
+ storage: { impressions }
49
+ } = params;
54
50
 
55
51
  // retry impressions only once.
56
- const syncTask = submitterSyncTaskFactory(log, postTestImpressionsBulk, impressionsCache, impressionsRefreshRate, DATA_NAME, latencyTracker, fromImpressionsCollector.bind(undefined, sendLabels), 1);
52
+ const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, DATA_NAME, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
57
53
 
58
54
  // register impressions submitter to be executed when impressions cache is full
59
- impressionsCache.setOnFullQueueCb(() => {
55
+ impressions.setOnFullQueueCb(() => {
60
56
  if (syncTask.isRunning()) {
61
57
  log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
62
58
  syncTask.execute();
@@ -1,23 +1,22 @@
1
1
  import { syncTaskFactory } from '../syncTask';
2
- import { ISyncTask, ITimeTracker } from '../types';
2
+ import { ISyncTask } from '../types';
3
3
  import { IRecorderCacheProducerSync } from '../../storages/types';
4
4
  import { ILogger } from '../../logger/types';
5
5
  import { SUBMITTERS_PUSH, SUBMITTERS_PUSH_FAILS, SUBMITTERS_PUSH_RETRY } from '../../logger/constants';
6
6
  import { IResponse } from '../../services/types';
7
7
 
8
8
  /**
9
- * Base function to create submitter sync tasks, such as ImpressionsSyncTask and EventsSyncTask
9
+ * Base function to create submitters, such as ImpressionsSubmitter and EventsSubmitter
10
10
  */
11
- export function submitterSyncTaskFactory<TState extends { length?: number }>(
11
+ export function submitterFactory<TState>(
12
12
  log: ILogger,
13
13
  postClient: (body: string) => Promise<IResponse>,
14
14
  sourceCache: IRecorderCacheProducerSync<TState>,
15
15
  postRate: number,
16
16
  dataName: string,
17
- latencyTracker?: ITimeTracker,
18
17
  fromCacheToPayload?: (cacheData: TState) => any,
19
18
  maxRetries: number = 0,
20
- debugLogs?: boolean
19
+ debugLogs?: boolean // true for telemetry submitters
21
20
  ): ISyncTask<[], void> {
22
21
 
23
22
  let retries = 0;
@@ -26,33 +25,52 @@ export function submitterSyncTaskFactory<TState extends { length?: number }>(
26
25
  if (sourceCache.isEmpty()) return Promise.resolve();
27
26
 
28
27
  const data = sourceCache.state();
29
-
30
- const dataCount: number | '' = typeof data.length === 'number' ? data.length : '';
31
- log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCount, dataName]);
32
- const latencyTrackerStop = latencyTracker && latencyTracker.start();
28
+ // @ts-ignore
29
+ const dataCountMessage = typeof data.length === 'number' ? `${data.length} ${dataName}` : dataName;
30
+ log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCountMessage]);
33
31
 
34
32
  const jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
35
33
  if (!maxRetries) sourceCache.clear();
36
34
 
37
- const postPromise = postClient(jsonPayload).then(() => {
35
+ return postClient(jsonPayload).then(() => {
38
36
  retries = 0;
39
37
  sourceCache.clear(); // we clear the queue if request successes.
40
38
  }).catch(err => {
41
39
  if (!maxRetries) {
42
- log.warn(SUBMITTERS_PUSH_FAILS, [dataCount, dataName, err]);
40
+ log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
43
41
  } else if (retries === maxRetries) {
44
42
  retries = 0;
45
43
  sourceCache.clear(); // we clear the queue if request fails after retries.
46
- log.warn(SUBMITTERS_PUSH_FAILS, [dataCount, dataName, err]);
44
+ log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
47
45
  } else {
48
46
  retries++;
49
- log.warn(SUBMITTERS_PUSH_RETRY, [dataCount, dataName, err]);
47
+ log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_RETRY, [dataCountMessage, err]);
50
48
  }
51
49
  });
52
-
53
- // if latencyTracker provided, attach stop callback to postEventsPromise
54
- return latencyTrackerStop ? postPromise.then(latencyTrackerStop).catch(latencyTrackerStop) : postPromise;
55
50
  }
56
51
 
57
52
  return syncTaskFactory(log, postData, postRate, dataName + ' submitter');
58
53
  }
54
+
55
+ /**
56
+ * Decorates a provided submitter with a first execution window
57
+ */
58
+ export function firstPushWindowDecorator(submitter: ISyncTask, firstPushWindow: number) {
59
+ let running = false;
60
+ let stopEventPublisherTimeout: ReturnType<typeof setTimeout>;
61
+ const originalStart = submitter.start;
62
+ submitter.start = () => {
63
+ running = true;
64
+ stopEventPublisherTimeout = setTimeout(originalStart, firstPushWindow);
65
+ };
66
+ const originalStop = submitter.stop;
67
+ submitter.stop = () => {
68
+ running = false;
69
+ clearTimeout(stopEventPublisherTimeout);
70
+ originalStop();
71
+ };
72
+ submitter.isRunning = () => {
73
+ return running;
74
+ };
75
+ return submitter;
76
+ }
@@ -1,18 +1,21 @@
1
1
  import { syncTaskComposite } from '../syncTaskComposite';
2
- import { eventsSyncTaskFactory } from './eventsSyncTask';
3
- import { impressionsSyncTaskFactory } from './impressionsSyncTask';
4
- import { impressionCountsSyncTaskFactory } from './impressionCountsSyncTask';
5
- import { ISyncManagerFactoryParams } from '../types';
2
+ import { eventsSubmitterFactory } from './eventsSubmitter';
3
+ import { impressionsSubmitterFactory } from './impressionsSubmitter';
4
+ import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
5
+ import { telemetrySubmitterFactory } from './telemetrySubmitter';
6
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
6
7
 
7
- export function submitterManagerFactory(params: ISyncManagerFactoryParams) {
8
+ export function submitterManagerFactory(params: ISdkFactoryContextSync) {
8
9
 
9
- const { settings, storage, splitApi } = params;
10
- const log = settings.log;
11
10
  const submitters = [
12
- impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled),
13
- eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow)
14
- // @TODO add telemetry submitter
11
+ impressionsSubmitterFactory(params),
12
+ eventsSubmitterFactory(params)
15
13
  ];
16
- if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts));
14
+
15
+ const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
16
+ if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
17
+ const telemetrySubmitter = telemetrySubmitterFactory(params);
18
+ if (telemetrySubmitter) submitters.push(telemetrySubmitter);
19
+
17
20
  return syncTaskComposite(submitters);
18
21
  }
@@ -0,0 +1,143 @@
1
+ import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types';
2
+ import { submitterFactory, firstPushWindowDecorator } from './submitter';
3
+ import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload } from './types';
4
+ import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM } from '../../utils/constants';
5
+ import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
6
+ import { ISettings } from '../../types';
7
+ import { base } from '../../utils/settingsValidation';
8
+ import { usedKeysMap } from '../../utils/inputValidation/apiKey';
9
+ import { timer } from '../../utils/timeTracker/timer';
10
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
11
+
12
+ /**
13
+ * Converts data from telemetry cache into /metrics/usage request payload.
14
+ */
15
+ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits: ISplitsCacheSync, segments: ISegmentsCacheSync) {
16
+ return {
17
+ isEmpty() { return false; }, // There is always data in telemetry cache
18
+ clear() { }, // No-op
19
+
20
+ // @TODO consider moving inside telemetry cache for code size reduction
21
+ state(): TelemetryUsageStatsPayload {
22
+ return {
23
+ lS: telemetry.getLastSynchronization(),
24
+ mL: telemetry.popLatencies(),
25
+ mE: telemetry.popExceptions(),
26
+ hE: telemetry.popHttpErrors(),
27
+ hL: telemetry.popHttpLatencies(),
28
+ tR: telemetry.popTokenRefreshes(),
29
+ aR: telemetry.popAuthRejections(),
30
+ iQ: telemetry.getImpressionStats(QUEUED),
31
+ iDe: telemetry.getImpressionStats(DEDUPED),
32
+ iDr: telemetry.getImpressionStats(DROPPED),
33
+ spC: splits.getSplitNames().length,
34
+ seC: segments.getRegisteredSegments().length,
35
+ skC: segments.getKeysCount(),
36
+ sL: telemetry.getSessionLength(),
37
+ eQ: telemetry.getEventStats(QUEUED),
38
+ eD: telemetry.getEventStats(DROPPED),
39
+ sE: telemetry.popStreamingEvents(),
40
+ t: telemetry.popTags(),
41
+ };
42
+ }
43
+ };
44
+ }
45
+
46
+ const OPERATION_MODE_MAP = {
47
+ [STANDALONE_MODE]: STANDALONE_ENUM,
48
+ [CONSUMER_MODE]: CONSUMER_ENUM,
49
+ [CONSUMER_PARTIAL_MODE]: CONSUMER_PARTIAL_ENUM
50
+ } as Record<ISettings['mode'], (0 | 1 | 2)>;
51
+
52
+ const IMPRESSIONS_MODE_MAP = {
53
+ [OPTIMIZED]: OPTIMIZED_ENUM,
54
+ [DEBUG]: DEBUG_ENUM
55
+ } as Record<ISettings['sync']['impressionsMode'], (0 | 1)>;
56
+
57
+ function getActiveFactories() {
58
+ return Object.keys(usedKeysMap).length;
59
+ }
60
+
61
+ function getRedundantActiveFactories() {
62
+ return Object.keys(usedKeysMap).reduce((acum, apiKey) => {
63
+ return acum + usedKeysMap[apiKey] - 1;
64
+ }, 0);
65
+ }
66
+
67
+ /**
68
+ * Converts data from telemetry cache and settings into /metrics/config request payload.
69
+ */
70
+ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, settings: ISettings) {
71
+ return {
72
+ isEmpty() { return false; },
73
+ clear() { },
74
+
75
+ state(): TelemetryConfigStatsPayload {
76
+ const { urls, scheduler } = settings;
77
+
78
+ return {
79
+ oM: OPERATION_MODE_MAP[settings.mode], // @ts-ignore lower case of storage type
80
+ st: settings.storage.type.toLowerCase(),
81
+ sE: settings.streamingEnabled,
82
+ rR: {
83
+ sp: scheduler.featuresRefreshRate,
84
+ se: scheduler.segmentsRefreshRate,
85
+ im: scheduler.impressionsRefreshRate,
86
+ ev: scheduler.eventsPushRate,
87
+ te: scheduler.telemetryRefreshRate,
88
+ }, // refreshRates
89
+ uO: {
90
+ s: urls.sdk !== base.urls.sdk,
91
+ e: urls.events !== base.urls.events,
92
+ a: urls.auth !== base.urls.auth,
93
+ st: urls.streaming !== base.urls.streaming,
94
+ t: urls.telemetry !== base.urls.telemetry,
95
+ }, // urlOverrides
96
+ iQ: scheduler.impressionsQueueSize,
97
+ eQ: scheduler.eventsQueueSize,
98
+ iM: IMPRESSIONS_MODE_MAP[settings.sync.impressionsMode],
99
+ iL: settings.impressionListener ? true : false,
100
+ hP: false, // @TODO proxy not supported
101
+ aF: getActiveFactories(),
102
+ rF: getRedundantActiveFactories(),
103
+ tR: telemetry.getTimeUntilReady() as number,
104
+ tC: telemetry.getTimeUntilReadyFromCache(),
105
+ nR: telemetry.getNonReadyUsage(),
106
+ t: telemetry.popTags(),
107
+ i: settings.integrations && settings.integrations.map(int => int.type),
108
+ };
109
+ }
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Submitter that periodically posts telemetry data
115
+ */
116
+ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
117
+ const { storage: { splits, segments, telemetry } } = params;
118
+ if (!telemetry) return; // No submitter created if telemetry cache is not defined
119
+
120
+ const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, splitApi, platform: { now }, readiness } = params;
121
+ const startTime = timer(now || Date.now);
122
+
123
+ const submitter = firstPushWindowDecorator(
124
+ submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true),
125
+ telemetryRefreshRate
126
+ );
127
+
128
+ readiness.gate.once(SDK_READY_FROM_CACHE, () => {
129
+ telemetry.recordTimeUntilReadyFromCache(startTime());
130
+ });
131
+
132
+ readiness.gate.once(SDK_READY, () => {
133
+ telemetry.recordTimeUntilReady(startTime());
134
+
135
+ // Post config data when the SDK is ready and if the telemetry submitter was started
136
+ if (submitter.isRunning()) {
137
+ const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, 'telemetry config', undefined, 0, true);
138
+ postMetricsConfigTask.execute();
139
+ }
140
+ });
141
+
142
+ return submitter;
143
+ }