@splitsoftware/splitio-commons 1.6.2-rc.0 → 1.6.2-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) 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/listeners/browser.js +2 -1
  6. package/cjs/logger/constants.js +2 -1
  7. package/cjs/sdkClient/client.js +19 -7
  8. package/cjs/sdkClient/sdkClient.js +3 -1
  9. package/cjs/sdkFactory/index.js +24 -7
  10. package/cjs/sdkManager/index.js +3 -11
  11. package/cjs/services/splitApi.js +24 -4
  12. package/cjs/storages/AbstractSplitsCacheAsync.js +8 -10
  13. package/cjs/storages/AbstractSplitsCacheSync.js +8 -10
  14. package/cjs/storages/KeyBuilderSS.js +54 -9
  15. package/cjs/storages/dataLoader.js +1 -1
  16. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  17. package/cjs/storages/inLocalStorage/index.js +5 -1
  18. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  19. package/cjs/storages/inMemory/InMemoryStorage.js +6 -2
  20. package/cjs/storages/inMemory/InMemoryStorageCS.js +6 -2
  21. package/cjs/storages/inMemory/SplitsCacheInMemory.js +7 -10
  22. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +10 -5
  23. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +73 -0
  24. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +78 -0
  25. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  26. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +50 -0
  27. package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
  28. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  29. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +59 -0
  30. package/cjs/storages/inRedis/constants.js +4 -1
  31. package/cjs/storages/inRedis/index.js +17 -2
  32. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +43 -0
  33. package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
  34. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  35. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +50 -0
  36. package/cjs/storages/pluggable/index.js +42 -17
  37. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  38. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
  39. package/cjs/sync/submitters/submitterManager.js +3 -0
  40. package/cjs/sync/submitters/telemetrySubmitter.js +8 -4
  41. package/cjs/sync/submitters/uniqueKeysSubmitter.js +27 -0
  42. package/cjs/trackers/impressionsTracker.js +22 -41
  43. package/cjs/trackers/strategy/strategyDebug.js +25 -0
  44. package/cjs/trackers/strategy/strategyNone.js +29 -0
  45. package/cjs/trackers/strategy/strategyOptimized.js +35 -0
  46. package/cjs/trackers/telemetryTracker.js +6 -0
  47. package/cjs/trackers/uniqueKeysTracker.js +38 -0
  48. package/cjs/utils/constants/index.js +4 -2
  49. package/cjs/utils/lang/maps.js +15 -7
  50. package/cjs/utils/redis/RedisMock.js +31 -0
  51. package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
  52. package/esm/consent/sdkUserConsent.js +2 -2
  53. package/esm/evaluator/index.js +15 -16
  54. package/esm/integrations/ga/GaToSplit.js +8 -5
  55. package/esm/listeners/browser.js +3 -2
  56. package/esm/logger/constants.js +1 -0
  57. package/esm/sdkClient/client.js +19 -7
  58. package/esm/sdkClient/sdkClient.js +3 -1
  59. package/esm/sdkFactory/index.js +24 -7
  60. package/esm/sdkManager/index.js +3 -11
  61. package/esm/services/splitApi.js +24 -4
  62. package/esm/storages/AbstractSplitsCacheAsync.js +8 -10
  63. package/esm/storages/AbstractSplitsCacheSync.js +8 -10
  64. package/esm/storages/KeyBuilderSS.js +50 -8
  65. package/esm/storages/dataLoader.js +1 -1
  66. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  67. package/esm/storages/inLocalStorage/index.js +6 -2
  68. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  69. package/esm/storages/inMemory/InMemoryStorage.js +8 -4
  70. package/esm/storages/inMemory/InMemoryStorageCS.js +7 -3
  71. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -10
  72. package/esm/storages/inMemory/TelemetryCacheInMemory.js +9 -5
  73. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +70 -0
  74. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +75 -0
  75. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  76. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +47 -0
  77. package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
  78. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  79. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +56 -0
  80. package/esm/storages/inRedis/constants.js +3 -0
  81. package/esm/storages/inRedis/index.js +18 -3
  82. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +40 -0
  83. package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
  84. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  85. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +47 -0
  86. package/esm/storages/pluggable/index.js +43 -18
  87. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  88. package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
  89. package/esm/sync/submitters/submitterManager.js +3 -0
  90. package/esm/sync/submitters/telemetrySubmitter.js +9 -5
  91. package/esm/sync/submitters/uniqueKeysSubmitter.js +23 -0
  92. package/esm/trackers/impressionsTracker.js +22 -41
  93. package/esm/trackers/strategy/strategyDebug.js +21 -0
  94. package/esm/trackers/strategy/strategyNone.js +25 -0
  95. package/esm/trackers/strategy/strategyOptimized.js +31 -0
  96. package/esm/trackers/telemetryTracker.js +6 -0
  97. package/esm/trackers/uniqueKeysTracker.js +34 -0
  98. package/esm/utils/constants/index.js +2 -0
  99. package/esm/utils/lang/maps.js +15 -7
  100. package/esm/utils/redis/RedisMock.js +28 -0
  101. package/esm/utils/settingsValidation/impressionsMode.js +3 -3
  102. package/package.json +1 -2
  103. package/src/consent/sdkUserConsent.ts +2 -2
  104. package/src/evaluator/index.ts +14 -15
  105. package/src/integrations/ga/GaToSplit.ts +9 -5
  106. package/src/integrations/types.ts +2 -1
  107. package/src/listeners/browser.ts +3 -2
  108. package/src/logger/constants.ts +1 -0
  109. package/src/sdkClient/client.ts +21 -8
  110. package/src/sdkClient/sdkClient.ts +3 -1
  111. package/src/sdkFactory/index.ts +28 -8
  112. package/src/sdkFactory/types.ts +7 -4
  113. package/src/sdkManager/index.ts +3 -12
  114. package/src/services/splitApi.ts +26 -4
  115. package/src/services/types.ts +8 -2
  116. package/src/storages/AbstractSplitsCacheAsync.ts +13 -15
  117. package/src/storages/AbstractSplitsCacheSync.ts +15 -17
  118. package/src/storages/KeyBuilderSS.ts +61 -9
  119. package/src/storages/dataLoader.ts +1 -1
  120. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -11
  121. package/src/storages/inLocalStorage/index.ts +5 -2
  122. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
  123. package/src/storages/inMemory/InMemoryStorage.ts +7 -4
  124. package/src/storages/inMemory/InMemoryStorageCS.ts +6 -3
  125. package/src/storages/inMemory/SplitsCacheInMemory.ts +10 -14
  126. package/src/storages/inMemory/TelemetryCacheInMemory.ts +10 -6
  127. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +82 -0
  128. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +88 -0
  129. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  130. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +53 -0
  131. package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
  132. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
  133. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +65 -0
  134. package/src/storages/inRedis/constants.ts +3 -0
  135. package/src/storages/inRedis/index.ts +15 -5
  136. package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +47 -0
  137. package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
  138. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
  139. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +56 -0
  140. package/src/storages/pluggable/index.ts +44 -19
  141. package/src/storages/types.ts +50 -29
  142. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
  143. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
  144. package/src/sync/submitters/submitterManager.ts +2 -0
  145. package/src/sync/submitters/telemetrySubmitter.ts +15 -8
  146. package/src/sync/submitters/types.ts +38 -7
  147. package/src/sync/submitters/uniqueKeysSubmitter.ts +36 -0
  148. package/src/trackers/impressionsTracker.ts +27 -48
  149. package/src/trackers/strategy/strategyDebug.ts +28 -0
  150. package/src/trackers/strategy/strategyNone.ts +34 -0
  151. package/src/trackers/strategy/strategyOptimized.ts +42 -0
  152. package/src/trackers/telemetryTracker.ts +7 -2
  153. package/src/trackers/types.ts +32 -0
  154. package/src/trackers/uniqueKeysTracker.ts +48 -0
  155. package/src/types.ts +1 -1
  156. package/src/utils/constants/index.ts +2 -0
  157. package/src/utils/lang/maps.ts +20 -8
  158. package/src/utils/redis/RedisMock.ts +33 -0
  159. package/src/utils/settingsValidation/impressionsMode.ts +3 -3
  160. package/src/utils/settingsValidation/index.ts +1 -0
  161. package/types/integrations/types.d.ts +2 -1
  162. package/types/logger/browser/{debugLogger.d.ts → DebugLogger.d.ts} +0 -0
  163. package/types/logger/browser/{errorLogger.d.ts → ErrorLogger.d.ts} +0 -0
  164. package/types/logger/browser/{infoLogger.d.ts → InfoLogger.d.ts} +0 -0
  165. package/types/logger/browser/{warnLogger.d.ts → WarnLogger.d.ts} +0 -0
  166. package/types/logger/constants.d.ts +1 -0
  167. package/types/sdkFactory/types.d.ts +4 -2
  168. package/types/services/types.d.ts +6 -2
  169. package/types/storages/AbstractSplitsCacheAsync.d.ts +7 -6
  170. package/types/storages/AbstractSplitsCacheSync.d.ts +6 -6
  171. package/types/storages/KeyBuilderSS.d.ts +9 -2
  172. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +3 -4
  173. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
  174. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
  175. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
  176. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
  177. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +37 -0
  178. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  179. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +14 -0
  180. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -5
  181. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  182. package/types/storages/inRedis/constants.d.ts +3 -0
  183. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +15 -0
  184. package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +14 -0
  185. package/types/storages/pluggable/SplitsCachePluggable.d.ts +6 -5
  186. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  187. package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +14 -0
  188. package/types/storages/types.d.ts +38 -25
  189. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  190. package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
  191. package/types/sync/submitters/types.d.ts +30 -7
  192. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -8
  193. package/types/trackers/impressionsTracker.d.ts +4 -6
  194. package/types/trackers/types.d.ts +27 -0
  195. package/types/trackers/uniqueKeysTracker.d.ts +3 -3
  196. package/types/types.d.ts +1 -1
  197. package/types/utils/constants/index.d.ts +2 -0
  198. package/types/utils/lang/maps.d.ts +6 -2
  199. package/types/utils/redis/RedisMock.d.ts +4 -0
  200. package/src/splitio.d.ts +0 -1602
  201. package/types/integrations/ga/GaToSplitPlugin.d.ts +0 -3
  202. package/types/integrations/ga/SplitToGaPlugin.d.ts +0 -4
  203. package/types/integrations/ga/autoRequire.d.ts +0 -4
  204. package/types/logger/codes.d.ts +0 -2
  205. package/types/logger/codesConstants.d.ts +0 -117
  206. package/types/logger/codesConstantsBrowser.d.ts +0 -2
  207. package/types/logger/codesConstantsNode.d.ts +0 -14
  208. package/types/logger/codesDebug.d.ts +0 -1
  209. package/types/logger/codesDebugBrowser.d.ts +0 -1
  210. package/types/logger/codesDebugNode.d.ts +0 -1
  211. package/types/logger/codesError.d.ts +0 -1
  212. package/types/logger/codesErrorNode.d.ts +0 -1
  213. package/types/logger/codesInfo.d.ts +0 -1
  214. package/types/logger/codesWarn.d.ts +0 -1
  215. package/types/logger/codesWarnNode.d.ts +0 -1
  216. package/types/logger/debugLogger.d.ts +0 -2
  217. package/types/logger/errorLogger.d.ts +0 -2
  218. package/types/logger/infoLogger.d.ts +0 -2
  219. package/types/logger/messages/debugBrowser.d.ts +0 -1
  220. package/types/logger/messages/debugNode.d.ts +0 -1
  221. package/types/logger/messages/errorNode.d.ts +0 -1
  222. package/types/logger/messages/warnNode.d.ts +0 -1
  223. package/types/logger/noopLogger.d.ts +0 -2
  224. package/types/logger/warnLogger.d.ts +0 -2
  225. package/types/sdkClient/types.d.ts +0 -18
  226. package/types/sdkFactory/userConsentProps.d.ts +0 -6
  227. package/types/sdkManager/sdkManagerMethod.d.ts +0 -6
  228. package/types/storages/getRegisteredSegments.d.ts +0 -10
  229. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  230. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  231. package/types/storages/inMemory/index.d.ts +0 -10
  232. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  233. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  234. package/types/storages/parseSegments.d.ts +0 -6
  235. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  236. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  237. package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +0 -35
  238. package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +0 -35
  239. package/types/sync/streaming/AuthClient/indexV1.d.ts +0 -12
  240. package/types/sync/streaming/AuthClient/indexV2.d.ts +0 -8
  241. package/types/sync/streaming/pushManagerCS.d.ts +0 -1
  242. package/types/sync/streaming/pushManagerNoUsers.d.ts +0 -13
  243. package/types/sync/streaming/pushManagerSS.d.ts +0 -1
  244. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  245. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  246. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  247. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  248. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  249. package/types/sync/submitters/telemetrySyncTask.d.ts +0 -0
  250. package/types/sync/syncManagerFromFile.d.ts +0 -2
  251. package/types/sync/syncManagerFromObject.d.ts +0 -2
  252. package/types/sync/syncManagerOffline.d.ts +0 -9
  253. package/types/sync/syncTaskComposite.d.ts +0 -5
  254. package/types/trackers/telemetryRecorder.d.ts +0 -0
  255. package/types/utils/EventEmitter.d.ts +0 -4
  256. package/types/utils/consent.d.ts +0 -2
  257. package/types/utils/lang/errors.d.ts +0 -10
  258. package/types/utils/murmur3/commons.d.ts +0 -12
  259. package/types/utils/settingsValidation/buildMetadata.d.ts +0 -3
  260. package/types/utils/settingsValidation/localhost/index.d.ts +0 -9
  261. package/types/utils/settingsValidation/logger.d.ts +0 -11
  262. package/types/utils/settingsValidation/runtime/browser.d.ts +0 -2
  263. package/types/utils/settingsValidation/runtime/node.d.ts +0 -2
  264. package/types/utils/settingsValidation/userConsent.d.ts +0 -5
  265. package/types/utils/timeTracker/index.d.ts +0 -1
@@ -1,8 +1,13 @@
1
1
  import { ILogger } from '../../logger/types';
2
- import { Method } from '../../sync/submitters/types';
3
- import { KeyBuilderSS } from '../KeyBuilderSS';
2
+ import { Method, MultiConfigs, MultiMethodExceptions, MultiMethodLatencies } from '../../sync/submitters/types';
3
+ import { KeyBuilderSS, parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
4
4
  import { IPluggableStorageWrapper, ITelemetryCacheAsync } from '../types';
5
5
  import { findLatencyIndex } from '../findLatencyIndex';
6
+ import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
7
+ import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
8
+ import { isString, isNaNNumber } from '../../utils/lang';
9
+ import { _Map } from '../../utils/lang/maps';
10
+ import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
6
11
 
7
12
  export class TelemetryCachePluggable implements ITelemetryCacheAsync {
8
13
 
@@ -23,4 +28,144 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync {
23
28
  .catch(() => { /* Handle rejections for telemetry */ });
24
29
  }
25
30
 
31
+ recordConfig() {
32
+ const value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_PLUGGABLE));
33
+ return this.wrapper.set(this.keys.buildInitKey(), value).catch(() => { /* Handle rejections for telemetry */ });
34
+ }
35
+
36
+ /**
37
+ * Pop telemetry latencies.
38
+ * The returned promise rejects if wrapper operations fail.
39
+ */
40
+ popLatencies(): Promise<MultiMethodLatencies> {
41
+ return this.wrapper.getKeysByPrefix(this.keys.latencyPrefix).then(latencyKeys => {
42
+ return latencyKeys.length ?
43
+ this.wrapper.getMany(latencyKeys).then(latencies => {
44
+
45
+ const result: MultiMethodLatencies = new _Map();
46
+
47
+ for (let i = 0; i < latencyKeys.length; i++) {
48
+ const field = latencyKeys[i].split('::')[1];
49
+
50
+ const parsedField = parseLatencyField(field);
51
+ if (isString(parsedField)) {
52
+ this.log.error(`Ignoring invalid latency field: ${field}: ${parsedField}`);
53
+ continue;
54
+ }
55
+
56
+ // @ts-ignore
57
+ const count = parseInt(latencies[i]);
58
+ if (isNaNNumber(count)) {
59
+ this.log.error(`Ignoring latency with invalid count: ${latencies[i]}`);
60
+ continue;
61
+ }
62
+
63
+ const [metadata, method, bucket] = parsedField;
64
+
65
+ if (bucket >= MAX_LATENCY_BUCKET_COUNT) {
66
+ this.log.error(`Ignoring latency with invalid bucket: ${bucket}`);
67
+ continue;
68
+ }
69
+
70
+ if (!result.has(metadata)) result.set(metadata, {
71
+ t: newBuckets(),
72
+ ts: newBuckets(),
73
+ tc: newBuckets(),
74
+ tcs: newBuckets(),
75
+ tr: newBuckets(),
76
+ });
77
+
78
+ result.get(metadata)![method][bucket] = count;
79
+ }
80
+
81
+ return Promise.all(latencyKeys.map((latencyKey) => this.wrapper.del(latencyKey))).then(() => result);
82
+ }) :
83
+ // If latencyKeys is empty, return an empty map.
84
+ new _Map();
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Pop telemetry exceptions.
90
+ * The returned promise rejects if wrapper operations fail.
91
+ */
92
+ popExceptions(): Promise<MultiMethodExceptions> {
93
+ return this.wrapper.getKeysByPrefix(this.keys.exceptionPrefix).then(exceptionKeys => {
94
+ return exceptionKeys.length ?
95
+ this.wrapper.getMany(exceptionKeys).then(exceptions => {
96
+
97
+ const result: MultiMethodExceptions = new _Map();
98
+
99
+ for (let i = 0; i < exceptionKeys.length; i++) {
100
+ const field = exceptionKeys[i].split('::')[1];
101
+
102
+ const parsedField = parseExceptionField(field);
103
+ if (isString(parsedField)) {
104
+ this.log.error(`Ignoring invalid exception field: ${field}: ${parsedField}`);
105
+ continue;
106
+ }
107
+
108
+ // @ts-ignore
109
+ const count = parseInt(exceptions[i]);
110
+ if (isNaNNumber(count)) {
111
+ this.log.error(`Ignoring exception with invalid count: ${exceptions[i]}`);
112
+ continue;
113
+ }
114
+
115
+ const [metadata, method] = parsedField;
116
+
117
+ if (!result.has(metadata)) result.set(metadata, {
118
+ t: 0,
119
+ ts: 0,
120
+ tc: 0,
121
+ tcs: 0,
122
+ tr: 0,
123
+ });
124
+
125
+ result.get(metadata)![method] = count;
126
+ }
127
+
128
+ return Promise.all(exceptionKeys.map((exceptionKey) => this.wrapper.del(exceptionKey))).then(() => result);
129
+ }) :
130
+ // If exceptionKeys is empty, return an empty map.
131
+ new _Map();
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Pop telemetry configs.
137
+ * The returned promise rejects if wrapper operations fail.
138
+ */
139
+ popConfigs(): Promise<MultiConfigs> {
140
+ return this.wrapper.getKeysByPrefix(this.keys.initPrefix).then(configKeys => {
141
+ return configKeys.length ?
142
+ this.wrapper.getMany(configKeys).then(configs => {
143
+
144
+ const result: MultiConfigs = new _Map();
145
+
146
+ for (let i = 0; i < configKeys.length; i++) {
147
+ const field = configKeys[i].split('::')[1];
148
+
149
+ const parsedField = parseMetadata(field);
150
+ if (isString(parsedField)) {
151
+ this.log.error(`Ignoring invalid config field: ${field}: ${parsedField}`);
152
+ continue;
153
+ }
154
+
155
+ const [metadata] = parsedField;
156
+
157
+ try { // @ts-ignore
158
+ const config = JSON.parse(configs[i]);
159
+ result.set(metadata, config);
160
+ } catch (e) {
161
+ this.log.error(`Ignoring invalid config: ${configs[i]}`);
162
+ }
163
+ }
164
+
165
+ return Promise.all(configKeys.map((configKey) => this.wrapper.del(configKey))).then(() => result);
166
+ }) :
167
+ // If configKeys is empty, return an empty map.
168
+ new _Map();
169
+ });
170
+ }
26
171
  }
@@ -0,0 +1,56 @@
1
+ import { IPluggableStorageWrapper, IUniqueKeysCacheBase } from '../types';
2
+ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
3
+ import { setToArray } from '../../utils/lang/sets';
4
+ import { DEFAULT_CACHE_SIZE, REFRESH_RATE } from '../inRedis/constants';
5
+ import { LOG_PREFIX } from './constants';
6
+ import { ILogger } from '../../logger/types';
7
+
8
+ export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
9
+
10
+ private readonly log: ILogger;
11
+ private readonly key: string;
12
+ private readonly wrapper: IPluggableStorageWrapper;
13
+ private readonly refreshRate: number;
14
+ private intervalId: any;
15
+
16
+ constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, uniqueKeysQueueSize = DEFAULT_CACHE_SIZE, refreshRate = REFRESH_RATE) {
17
+ super(uniqueKeysQueueSize);
18
+ this.log = log;
19
+ this.key = key;
20
+ this.wrapper = wrapper;
21
+ this.refreshRate = refreshRate;
22
+ this.onFullQueue = () => { this.storeUniqueKeys(); };
23
+ }
24
+
25
+ storeUniqueKeys() {
26
+ const featureNames = Object.keys(this.uniqueKeysTracker);
27
+ if (!featureNames.length) return Promise.resolve(false);
28
+
29
+ const pipeline = featureNames.reduce<Promise<any>>((pipeline, featureName) => {
30
+ const featureKeys = setToArray(this.uniqueKeysTracker[featureName]);
31
+ const uniqueKeysPayload = {
32
+ f: featureName,
33
+ ks: featureKeys
34
+ };
35
+
36
+ return pipeline.then(() => this.wrapper.pushItems(this.key, [JSON.stringify(uniqueKeysPayload)]));
37
+ }, Promise.resolve());
38
+
39
+ this.clear();
40
+ return pipeline.catch(err => {
41
+ this.log.error(`${LOG_PREFIX}Error in uniqueKeys pipeline: ${err}.`);
42
+ return false;
43
+ });
44
+ }
45
+
46
+
47
+ start() {
48
+ this.intervalId = setInterval(this.storeUniqueKeys.bind(this), this.refreshRate);
49
+ }
50
+
51
+ stop() {
52
+ clearInterval(this.intervalId);
53
+ return this.storeUniqueKeys();
54
+ }
55
+
56
+ }
@@ -1,4 +1,4 @@
1
- import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types';
1
+ import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams, ITelemetryCacheAsync } from '../types';
2
2
 
3
3
  import { KeyBuilderSS } from '../KeyBuilderSS';
4
4
  import { SplitsCachePluggable } from './SplitsCachePluggable';
@@ -8,10 +8,16 @@ import { EventsCachePluggable } from './EventsCachePluggable';
8
8
  import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
9
9
  import { isObject } from '../../utils/lang';
10
10
  import { validatePrefix } from '../KeyBuilder';
11
- import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
11
+ import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
12
12
  import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
13
13
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
14
14
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
15
+ import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
16
+ import { TelemetryCachePluggable } from './TelemetryCachePluggable';
17
+ import { ImpressionCountsCachePluggable } from './ImpressionCountsCachePluggable';
18
+ import { UniqueKeysCachePluggable } from './UniqueKeysCachePluggable';
19
+ import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
20
+ import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
15
21
 
16
22
  const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
17
23
  const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
@@ -35,16 +41,6 @@ function validatePluggableStorageOptions(options: any) {
35
41
  if (missingMethods.length) throw new Error(`${NO_VALID_WRAPPER_INTERFACE} The following methods are missing or invalid: ${missingMethods}`);
36
42
  }
37
43
 
38
- // subscription to wrapper connect event in order to emit SDK_READY event
39
- function wrapperConnect(wrapper: IPluggableStorageWrapper, onReadyCb: (error?: any) => void) {
40
- wrapper.connect().then(() => {
41
- onReadyCb();
42
- // At the moment, we don't synchronize config with pluggable storage
43
- }).catch((e) => {
44
- onReadyCb(e || new Error('Error connecting wrapper'));
45
- });
46
- }
47
-
48
44
  // Async return type in `client.track` method on consumer partial mode
49
45
  // No need to promisify impressions cache
50
46
  function promisifyEventsTrack(events: any) {
@@ -64,31 +60,60 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
64
60
 
65
61
  const prefix = validatePrefix(options.prefix);
66
62
 
67
- function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
63
+ function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
64
+ const { log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, impressionsMode, matchingKey } = params;
68
65
  const keys = new KeyBuilderSS(prefix, metadata);
69
66
  const wrapper = wrapperAdapter(log, options.wrapper);
70
67
  const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
68
+ const telemetry = shouldRecordTelemetry(params) ?
69
+ isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) :
70
+ undefined;
71
+ const impressionCountsCache = impressionsMode !== DEBUG ?
72
+ isPartialConsumer ? new ImpressionCountsCacheInMemory() : new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
73
+ undefined;
74
+ const uniqueKeysCache = impressionsMode === NONE ?
75
+ isPartialConsumer ?
76
+ matchingKey === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
77
+ new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
78
+ undefined;
71
79
 
72
80
  // Connects to wrapper and emits SDK_READY event on main client
73
- wrapperConnect(wrapper, onReadyCb);
81
+ const connectPromise = wrapper.connect().then(() => {
82
+ onReadyCb();
83
+
84
+ // Start periodic flush of async storages
85
+ if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
86
+ if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
87
+
88
+ // If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
89
+ if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig && mode) (telemetry as ITelemetryCacheAsync).recordConfig();
90
+ }).catch((e) => {
91
+ e = e || new Error('Error connecting wrapper');
92
+ onReadyCb(e);
93
+ return e;
94
+ });
74
95
 
75
96
  return {
76
97
  splits: new SplitsCachePluggable(log, keys, wrapper),
77
98
  segments: new SegmentsCachePluggable(log, keys, wrapper),
78
99
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
79
- impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
100
+ impressionCounts: impressionCountsCache,
80
101
  events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
81
- // @TODO Not using TelemetryCachePluggable yet because it's not supported by the Split Synchronizer, and needs to drop or queue operations while the wrapper is not ready
82
- // telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
102
+ telemetry,
103
+ uniqueKeys: uniqueKeysCache,
83
104
 
84
105
  // Disconnect the underlying storage
85
106
  destroy() {
86
- return wrapper.disconnect();
107
+ return Promise.all([
108
+ impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
109
+ uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
110
+ ]).then(() => wrapper.disconnect());
87
111
  },
88
112
 
89
113
  // emits SDK_READY event on shared clients and returns a reference to the storage
90
114
  shared(_, onReadyCb) {
91
- wrapperConnect(wrapper, onReadyCb);
115
+ connectPromise.then(onReadyCb);
116
+
92
117
  return {
93
118
  ...this,
94
119
  // no-op destroy, to disconnect the wrapper only when the main client is destroyed
@@ -1,6 +1,6 @@
1
- import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types';
1
+ import { MaybeThenable, IMetadata, ISplitFiltersValidation, ISplit } from '../dtos/types';
2
2
  import { ILogger } from '../logger/types';
3
- import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent } from '../sync/submitters/types';
3
+ import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
4
4
  import { SplitIO, ImpressionDTO, SDKMode } from '../types';
5
5
 
6
6
  /**
@@ -70,23 +70,25 @@ export interface IPluggableStorageWrapper {
70
70
  /** Integer operations */
71
71
 
72
72
  /**
73
- * Increments in 1 the given `key` value or set it to 1 if the value doesn't exist.
73
+ * Increments the number stored at `key` by `increment` (or 1 if `increment` is not provided), or set it to `increment` (or 1) if the value doesn't exist.
74
74
  *
75
75
  * @function incr
76
76
  * @param {string} key Key to increment
77
+ * @param {number} increment Value to increment by
77
78
  * @returns {Promise<number>} A promise that resolves with the value of key after the increment. The promise rejects if the operation fails,
78
79
  * for example, if there is a connection error or the key contains a string that can not be represented as integer.
79
80
  */
80
- incr: (key: string) => Promise<number>
81
+ incr: (key: string, increment?: number) => Promise<number>
81
82
  /**
82
- * Decrements in 1 the given `key` value or set it to -1 if the value doesn't exist.
83
+ * Decrements the number stored at `key` by `decrement` (or 1 if `decrement` is not provided), or set it to minus `decrement` (or minus 1) if the value doesn't exist.
83
84
  *
84
85
  * @function decr
85
86
  * @param {string} key Key to decrement
87
+ * @param {number} decrement Value to decrement by
86
88
  * @returns {Promise<number>} A promise that resolves with the value of key after the decrement. The promise rejects if the operation fails,
87
89
  * for example, if there is a connection error or the key contains a string that can not be represented as integer.
88
90
  */
89
- decr: (key: string) => Promise<number>
91
+ decr: (key: string, decrement?: number) => Promise<number>
90
92
 
91
93
  /** Queue operations */
92
94
 
@@ -191,14 +193,14 @@ export interface IPluggableStorageWrapper {
191
193
  /** Splits cache */
192
194
 
193
195
  export interface ISplitsCacheBase {
194
- addSplits(entries: [string, string][]): MaybeThenable<boolean[] | void>,
196
+ addSplits(entries: [string, ISplit][]): MaybeThenable<boolean[] | void>,
195
197
  removeSplits(names: string[]): MaybeThenable<boolean[] | void>,
196
- getSplit(name: string): MaybeThenable<string | null>,
197
- getSplits(names: string[]): MaybeThenable<Record<string, string | null>>, // `fetchMany` in spec
198
+ getSplit(name: string): MaybeThenable<ISplit | null>,
199
+ getSplits(names: string[]): MaybeThenable<Record<string, ISplit | null>>, // `fetchMany` in spec
198
200
  setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>,
199
201
  // should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
200
202
  getChangeNumber(): MaybeThenable<number>,
201
- getAll(): MaybeThenable<string[]>,
203
+ getAll(): MaybeThenable<ISplit[]>,
202
204
  getSplitNames(): MaybeThenable<string[]>,
203
205
  // should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
204
206
  trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
@@ -211,13 +213,13 @@ export interface ISplitsCacheBase {
211
213
  }
212
214
 
213
215
  export interface ISplitsCacheSync extends ISplitsCacheBase {
214
- addSplits(entries: [string, string][]): boolean[],
216
+ addSplits(entries: [string, ISplit][]): boolean[],
215
217
  removeSplits(names: string[]): boolean[],
216
- getSplit(name: string): string | null,
217
- getSplits(names: string[]): Record<string, string | null>,
218
+ getSplit(name: string): ISplit | null,
219
+ getSplits(names: string[]): Record<string, ISplit | null>,
218
220
  setChangeNumber(changeNumber: number): boolean,
219
221
  getChangeNumber(): number,
220
- getAll(): string[],
222
+ getAll(): ISplit[],
221
223
  getSplitNames(): string[],
222
224
  trafficTypeExists(trafficType: string): boolean,
223
225
  usesSegments(): boolean,
@@ -227,13 +229,13 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
227
229
  }
228
230
 
229
231
  export interface ISplitsCacheAsync extends ISplitsCacheBase {
230
- addSplits(entries: [string, string][]): Promise<boolean[] | void>,
232
+ addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>,
231
233
  removeSplits(names: string[]): Promise<boolean[] | void>,
232
- getSplit(name: string): Promise<string | null>,
233
- getSplits(names: string[]): Promise<Record<string, string | null>>,
234
+ getSplit(name: string): Promise<ISplit | null>,
235
+ getSplits(names: string[]): Promise<Record<string, ISplit | null>>,
234
236
  setChangeNumber(changeNumber: number): Promise<boolean | void>,
235
237
  getChangeNumber(): Promise<number>,
236
- getAll(): Promise<string[]>,
238
+ getAll(): Promise<ISplit[]>,
237
239
  getSplitNames(): Promise<string[]>,
238
240
  trafficTypeExists(trafficType: string): Promise<boolean>,
239
241
  usesSegments(): Promise<boolean>,
@@ -347,7 +349,7 @@ export interface IEventsCacheAsync extends IEventsCacheBase, IRecorderCacheProdu
347
349
  * Only in memory. Named `ImpressionsCounter` in spec.
348
350
  */
349
351
  export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<Record<string, number>> {
350
- // Used by impressions tracker
352
+ // Used by impressions tracker
351
353
  track(featureName: string, timeFrame: number, amount: number): void
352
354
 
353
355
  // Used by impressions count submitter in standalone and producer mode
@@ -355,6 +357,17 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
355
357
  pop(toMerge?: Record<string, number> ): Record<string, number> // pop cache data
356
358
  }
357
359
 
360
+ export interface IUniqueKeysCacheBase {
361
+ // Used by unique Keys tracker
362
+ track(key: string, value: string): void
363
+
364
+ // Used by unique keys submitter in standalone and producer mode
365
+ isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
366
+ pop(): UniqueKeysPayloadSs | UniqueKeysPayloadCs // pop cache data
367
+ /* Registers callback for full queue */
368
+ setOnFullQueueCb(cb: () => void): void,
369
+ clear(): void
370
+ }
358
371
 
359
372
  /**
360
373
  * Telemetry storage interface for standalone and partial consumer modes.
@@ -423,18 +436,19 @@ export interface ITelemetryCacheSync extends ITelemetryStorageConsumerSync, ITel
423
436
  */
424
437
 
425
438
  export interface ITelemetryEvaluationConsumerAsync {
426
- popExceptions(): Promise<MethodExceptions>;
427
- popLatencies(): Promise<MethodLatencies>;
439
+ popLatencies(): Promise<MultiMethodLatencies>;
440
+ popExceptions(): Promise<MultiMethodExceptions>;
441
+ popConfigs(): Promise<MultiConfigs>;
428
442
  }
429
443
 
430
444
  export interface ITelemetryEvaluationProducerAsync {
431
445
  recordLatency(method: Method, latencyMs: number): Promise<any>;
432
446
  recordException(method: Method): Promise<any>;
447
+ recordConfig(): Promise<any>;
433
448
  }
434
449
 
435
450
  // ATM it only implements the producer API, used by the SDK in consumer mode.
436
- // @TODO implement consumer API for JS Synchronizer.
437
- export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync { }
451
+ export interface ITelemetryCacheAsync extends ITelemetryEvaluationProducerAsync, ITelemetryEvaluationConsumerAsync { }
438
452
 
439
453
  /**
440
454
  * Storages
@@ -444,15 +458,18 @@ export interface IStorageBase<
444
458
  TSplitsCache extends ISplitsCacheBase,
445
459
  TSegmentsCache extends ISegmentsCacheBase,
446
460
  TImpressionsCache extends IImpressionsCacheBase,
461
+ TImpressionsCountCache extends IImpressionCountsCacheSync,
447
462
  TEventsCache extends IEventsCacheBase,
448
- TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync
463
+ TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
464
+ TUniqueKeysCache extends IUniqueKeysCacheBase
449
465
  > {
450
466
  splits: TSplitsCache,
451
467
  segments: TSegmentsCache,
452
468
  impressions: TImpressionsCache,
453
- impressionCounts?: IImpressionCountsCacheSync,
469
+ impressionCounts?: TImpressionsCountCache,
454
470
  events: TEventsCache,
455
- telemetry?: TTelemetryCache
471
+ telemetry?: TTelemetryCache,
472
+ uniqueKeys?: TUniqueKeysCache,
456
473
  destroy(): void | Promise<void>,
457
474
  shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
458
475
  }
@@ -461,16 +478,20 @@ export interface IStorageSync extends IStorageBase<
461
478
  ISplitsCacheSync,
462
479
  ISegmentsCacheSync,
463
480
  IImpressionsCacheSync,
481
+ IImpressionCountsCacheSync,
464
482
  IEventsCacheSync,
465
- ITelemetryCacheSync
483
+ ITelemetryCacheSync,
484
+ IUniqueKeysCacheBase
466
485
  > { }
467
486
 
468
487
  export interface IStorageAsync extends IStorageBase<
469
488
  ISplitsCacheAsync,
470
489
  ISegmentsCacheAsync,
471
490
  IImpressionsCacheAsync | IImpressionsCacheSync,
491
+ IImpressionCountsCacheSync,
472
492
  IEventsCacheAsync | IEventsCacheSync,
473
- ITelemetryCacheAsync
493
+ ITelemetryCacheAsync | ITelemetryCacheSync,
494
+ IUniqueKeysCacheBase
474
495
  > { }
475
496
 
476
497
  /** StorageFactory */
@@ -483,7 +504,7 @@ export interface IStorageFactoryParams {
483
504
  eventsQueueSize?: number,
484
505
  optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
485
506
  mode: SDKMode,
486
-
507
+ impressionsMode?: string,
487
508
  // ATM, only used by InLocalStorage
488
509
  matchingKey?: string, /* undefined on server-side SDKs */
489
510
  splitFiltersValidation?: ISplitFiltersValidation,
@@ -2,7 +2,7 @@ import { forOwn } from '../../../utils/lang';
2
2
  import { IReadinessManager } from '../../../readiness/types';
3
3
  import { ISplitsCacheSync } from '../../../storages/types';
4
4
  import { ISplitsParser } from '../splitsParser/types';
5
- import { ISplitPartial } from '../../../dtos/types';
5
+ import { ISplit, ISplitPartial } from '../../../dtos/types';
6
6
  import { syncTaskFactory } from '../../syncTask';
7
7
  import { ISyncTask } from '../../types';
8
8
  import { ISettings } from '../../../types';
@@ -24,7 +24,7 @@ export function fromObjectUpdaterFactory(
24
24
  let startingUp = true;
25
25
 
26
26
  return function objectUpdater() {
27
- const splits: [string, string][] = [];
27
+ const splits: [string, ISplit][] = [];
28
28
  let loadError = null;
29
29
  let splitsMock: false | Record<string, ISplitPartial> = {};
30
30
  try {
@@ -38,9 +38,8 @@ export function fromObjectUpdaterFactory(
38
38
  log.debug(SYNC_OFFLINE_DATA, [JSON.stringify(splitsMock)]);
39
39
 
40
40
  forOwn(splitsMock, function (val, name) {
41
- splits.push([
42
- name,
43
- JSON.stringify({
41
+ splits.push([ // @ts-ignore Split changeNumber and seed is undefined in localhost mode
42
+ name, {
44
43
  name,
45
44
  status: 'ACTIVE',
46
45
  killed: false,
@@ -49,7 +48,7 @@ export function fromObjectUpdaterFactory(
49
48
  conditions: val.conditions || [],
50
49
  configurations: val.configurations,
51
50
  trafficTypeName: val.trafficTypeName
52
- })
51
+ }
53
52
  ]);
54
53
  });
55
54
 
@@ -40,7 +40,7 @@ export function parseSegments({ conditions }: ISplit): ISet<string> {
40
40
  }
41
41
 
42
42
  interface ISplitMutations {
43
- added: [string, string][],
43
+ added: [string, ISplit][],
44
44
  removed: string[],
45
45
  segments: string[]
46
46
  }
@@ -54,7 +54,7 @@ export function computeSplitsMutation(entries: ISplit[]): ISplitMutations {
54
54
  const segments = new _Set<string>();
55
55
  const computed = entries.reduce((accum, split) => {
56
56
  if (split.status === 'ACTIVE') {
57
- accum.added.push([split.name, JSON.stringify(split)]);
57
+ accum.added.push([split.name, split]);
58
58
 
59
59
  parseSegments(split).forEach((segmentName: string) => {
60
60
  segments.add(segmentName);
@@ -4,6 +4,7 @@ import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
4
4
  import { telemetrySubmitterFactory } from './telemetrySubmitter';
5
5
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
6
6
  import { ISubmitterManager } from './types';
7
+ import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
7
8
 
8
9
  export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager {
9
10
 
@@ -15,6 +16,7 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
15
16
  const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
16
17
  if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
17
18
  const telemetrySubmitter = telemetrySubmitterFactory(params);
19
+ if (params.uniqueKeysTracker) submitters.push(uniqueKeysSubmitterFactory(params));
18
20
 
19
21
  return {
20
22
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
@@ -1,7 +1,7 @@
1
1
  import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types';
2
2
  import { submitterFactory, firstPushWindowDecorator } from './submitter';
3
3
  import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload, TelemetryConfigStats } 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, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
4
+ import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, NONE, DEBUG_ENUM, OPTIMIZED_ENUM, NONE_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
5
5
  import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
6
6
  import { ConsentStatus, ISettings, SDKMode } from '../../types';
7
7
  import { base } from '../../utils/settingsValidation';
@@ -9,11 +9,12 @@ import { usedKeysMap } from '../../utils/inputValidation/apiKey';
9
9
  import { timer } from '../../utils/timeTracker/timer';
10
10
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
11
11
  import { objectAssign } from '../../utils/lang/objectAssign';
12
+ import { isStorageSync } from '../../trackers/impressionObserver/utils';
12
13
 
13
14
  /**
14
15
  * Converts data from telemetry cache into /metrics/usage request payload.
15
16
  */
16
- export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits: ISplitsCacheSync, segments: ISegmentsCacheSync) {
17
+ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits?: ISplitsCacheSync, segments?: ISegmentsCacheSync) {
17
18
  return {
18
19
  isEmpty() { return false; }, // There is always data in telemetry cache
19
20
  clear() { }, // No-op
@@ -31,9 +32,9 @@ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, split
31
32
  iQ: telemetry.getImpressionStats(QUEUED),
32
33
  iDe: telemetry.getImpressionStats(DEDUPED),
33
34
  iDr: telemetry.getImpressionStats(DROPPED),
34
- spC: splits.getSplitNames().length,
35
- seC: segments.getRegisteredSegments().length,
36
- skC: segments.getKeysCount(),
35
+ spC: splits && splits.getSplitNames().length,
36
+ seC: segments && segments.getRegisteredSegments().length,
37
+ skC: segments && segments.getKeysCount(),
37
38
  sL: telemetry.getSessionLength(),
38
39
  eQ: telemetry.getEventStats(QUEUED),
39
40
  eD: telemetry.getEventStats(DROPPED),
@@ -52,8 +53,9 @@ const OPERATION_MODE_MAP = {
52
53
 
53
54
  const IMPRESSIONS_MODE_MAP = {
54
55
  [OPTIMIZED]: OPTIMIZED_ENUM,
55
- [DEBUG]: DEBUG_ENUM
56
- } as Record<ISettings['sync']['impressionsMode'], (0 | 1)>;
56
+ [DEBUG]: DEBUG_ENUM,
57
+ [NONE]: NONE_ENUM
58
+ } as Record<ISettings['sync']['impressionsMode'], (0 | 1 | 2)>;
57
59
 
58
60
  const USER_CONSENT_MAP = {
59
61
  [CONSENT_UNKNOWN]: 1,
@@ -136,7 +138,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
136
138
  const startTime = timer(now);
137
139
 
138
140
  const submitter = firstPushWindowDecorator(
139
- submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true),
141
+ submitterFactory(
142
+ log, splitApi.postMetricsUsage,
143
+ // @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
144
+ isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry),
145
+ telemetryRefreshRate, 'telemetry stats', undefined, 0, true
146
+ ),
140
147
  telemetryRefreshRate
141
148
  );
142
149