@splitsoftware/splitio-commons 1.6.2-rc.8 → 1.6.2-rc.9

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 (164) hide show
  1. package/CHANGES.txt +2 -1
  2. package/cjs/consent/sdkUserConsent.js +2 -2
  3. package/cjs/evaluator/index.js +5 -5
  4. package/cjs/listeners/browser.js +1 -2
  5. package/cjs/logger/constants.js +1 -2
  6. package/cjs/sdkClient/client.js +19 -7
  7. package/cjs/sdkClient/sdkClient.js +1 -3
  8. package/cjs/sdkFactory/index.js +5 -23
  9. package/cjs/services/splitApi.js +4 -24
  10. package/cjs/storages/KeyBuilderSS.js +48 -15
  11. package/cjs/storages/inLocalStorage/index.js +1 -5
  12. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -12
  13. package/cjs/storages/inMemory/InMemoryStorage.js +2 -6
  14. package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -6
  15. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +9 -6
  16. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  17. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  18. package/cjs/storages/inRedis/constants.js +1 -4
  19. package/cjs/storages/inRedis/index.js +2 -17
  20. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  21. package/cjs/storages/pluggable/index.js +19 -15
  22. package/cjs/sync/submitters/submitterManager.js +0 -3
  23. package/cjs/sync/submitters/telemetrySubmitter.js +7 -5
  24. package/cjs/trackers/impressionsTracker.js +41 -22
  25. package/cjs/utils/constants/index.js +2 -4
  26. package/cjs/utils/lang/maps.js +15 -7
  27. package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
  28. package/cjs/utils/settingsValidation/index.js +0 -3
  29. package/esm/consent/sdkUserConsent.js +2 -2
  30. package/esm/evaluator/index.js +5 -5
  31. package/esm/listeners/browser.js +2 -3
  32. package/esm/logger/constants.js +0 -1
  33. package/esm/sdkClient/client.js +19 -7
  34. package/esm/sdkClient/sdkClient.js +1 -3
  35. package/esm/sdkFactory/index.js +5 -23
  36. package/esm/services/splitApi.js +4 -24
  37. package/esm/storages/KeyBuilderSS.js +44 -14
  38. package/esm/storages/inLocalStorage/index.js +2 -6
  39. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -12
  40. package/esm/storages/inMemory/InMemoryStorage.js +4 -8
  41. package/esm/storages/inMemory/InMemoryStorageCS.js +3 -7
  42. package/esm/storages/inMemory/TelemetryCacheInMemory.js +8 -6
  43. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  44. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  45. package/esm/storages/inRedis/constants.js +0 -3
  46. package/esm/storages/inRedis/index.js +3 -18
  47. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  48. package/esm/storages/pluggable/index.js +19 -15
  49. package/esm/sync/submitters/submitterManager.js +0 -3
  50. package/esm/sync/submitters/telemetrySubmitter.js +8 -6
  51. package/esm/trackers/impressionsTracker.js +41 -22
  52. package/esm/utils/constants/index.js +0 -2
  53. package/esm/utils/lang/maps.js +15 -7
  54. package/esm/utils/settingsValidation/impressionsMode.js +3 -3
  55. package/esm/utils/settingsValidation/index.js +0 -3
  56. package/package.json +2 -1
  57. package/src/consent/sdkUserConsent.ts +2 -2
  58. package/src/evaluator/index.ts +6 -6
  59. package/src/listeners/browser.ts +2 -3
  60. package/src/logger/.DS_Store +0 -0
  61. package/src/logger/constants.ts +0 -1
  62. package/src/sdkClient/client.ts +21 -8
  63. package/src/sdkClient/sdkClient.ts +1 -3
  64. package/src/sdkFactory/index.ts +5 -26
  65. package/src/sdkFactory/types.ts +4 -7
  66. package/src/services/splitApi.ts +4 -26
  67. package/src/services/types.ts +2 -8
  68. package/src/storages/KeyBuilderSS.ts +53 -17
  69. package/src/storages/inLocalStorage/index.ts +2 -5
  70. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +1 -16
  71. package/src/storages/inMemory/InMemoryStorage.ts +4 -7
  72. package/src/storages/inMemory/InMemoryStorageCS.ts +3 -7
  73. package/src/storages/inMemory/TelemetryCacheInMemory.ts +9 -7
  74. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  75. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
  76. package/src/storages/inRedis/constants.ts +0 -3
  77. package/src/storages/inRedis/index.ts +5 -15
  78. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
  79. package/src/storages/pluggable/index.ts +20 -16
  80. package/src/storages/types.ts +13 -31
  81. package/src/sync/submitters/submitterManager.ts +0 -2
  82. package/src/sync/submitters/telemetrySubmitter.ts +14 -9
  83. package/src/sync/submitters/types.ts +40 -26
  84. package/src/trackers/impressionsTracker.ts +48 -27
  85. package/src/trackers/types.ts +0 -28
  86. package/src/types.ts +1 -3
  87. package/src/utils/constants/index.ts +0 -2
  88. package/src/utils/lang/maps.ts +20 -8
  89. package/src/utils/settingsValidation/impressionsMode.ts +3 -3
  90. package/src/utils/settingsValidation/index.ts +0 -4
  91. package/types/logger/constants.d.ts +0 -1
  92. package/types/sdkFactory/types.d.ts +2 -4
  93. package/types/services/types.d.ts +2 -6
  94. package/types/storages/KeyBuilderSS.d.ts +7 -4
  95. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +1 -5
  96. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
  97. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  98. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  99. package/types/storages/inRedis/constants.d.ts +0 -3
  100. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  101. package/types/storages/types.d.ts +10 -18
  102. package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
  103. package/types/sync/submitters/types.d.ts +13 -24
  104. package/types/trackers/impressionsTracker.d.ts +6 -4
  105. package/types/trackers/types.d.ts +0 -23
  106. package/types/types.d.ts +1 -3
  107. package/types/utils/constants/index.d.ts +0 -2
  108. package/types/utils/lang/maps.d.ts +6 -2
  109. package/types/utils/settingsValidation/index.d.ts +0 -1
  110. package/cjs/storages/inMemory/uniqueKeysCacheInMemory.js +0 -73
  111. package/cjs/storages/inMemory/uniqueKeysCacheInMemoryCS.js +0 -78
  112. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +0 -50
  113. package/cjs/storages/inRedis/uniqueKeysCacheInRedis.js +0 -59
  114. package/cjs/sync/submitters/uniqueKeysSubmitter.js +0 -26
  115. package/cjs/trackers/strategy/strategyDebug.js +0 -25
  116. package/cjs/trackers/strategy/strategyNone.js +0 -29
  117. package/cjs/trackers/strategy/strategyOptimized.js +0 -35
  118. package/cjs/trackers/uniqueKeysTracker.js +0 -38
  119. package/esm/storages/inMemory/uniqueKeysCacheInMemory.js +0 -70
  120. package/esm/storages/inMemory/uniqueKeysCacheInMemoryCS.js +0 -75
  121. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +0 -47
  122. package/esm/storages/inRedis/uniqueKeysCacheInRedis.js +0 -56
  123. package/esm/sync/submitters/uniqueKeysSubmitter.js +0 -22
  124. package/esm/trackers/strategy/strategyDebug.js +0 -21
  125. package/esm/trackers/strategy/strategyNone.js +0 -25
  126. package/esm/trackers/strategy/strategyOptimized.js +0 -31
  127. package/esm/trackers/uniqueKeysTracker.js +0 -34
  128. package/src/storages/inMemory/uniqueKeysCacheInMemory.ts +0 -82
  129. package/src/storages/inMemory/uniqueKeysCacheInMemoryCS.ts +0 -88
  130. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +0 -52
  131. package/src/storages/inRedis/uniqueKeysCacheInRedis.ts +0 -64
  132. package/src/sync/submitters/uniqueKeysSubmitter.ts +0 -35
  133. package/src/trackers/strategy/strategyDebug.ts +0 -28
  134. package/src/trackers/strategy/strategyNone.ts +0 -34
  135. package/src/trackers/strategy/strategyOptimized.ts +0 -42
  136. package/src/trackers/uniqueKeysTracker.ts +0 -48
  137. package/types/sdkClient/types.d.ts +0 -18
  138. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  139. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  140. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +0 -35
  141. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +0 -37
  142. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  143. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +0 -14
  144. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  145. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +0 -15
  146. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  147. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  148. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  149. package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +0 -5
  150. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  151. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  152. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  153. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  154. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -5
  155. package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +0 -5
  156. package/types/sync/syncTaskComposite.d.ts +0 -5
  157. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  158. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  159. package/types/trackers/filter/types.d.ts +0 -5
  160. package/types/trackers/strategy/strategyDebug.d.ts +0 -9
  161. package/types/trackers/strategy/strategyNone.d.ts +0 -10
  162. package/types/trackers/strategy/strategyOptimized.d.ts +0 -11
  163. package/types/trackers/uniqueKeysTracker.d.ts +0 -13
  164. package/types/utils/timeTracker/index.d.ts +0 -70
@@ -1,4 +1,10 @@
1
+ import { parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
1
2
  import { findLatencyIndex } from '../findLatencyIndex';
3
+ import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
4
+ import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
5
+ import { isString, isNaNNumber } from '../../utils/lang';
6
+ import { _Map } from '../../utils/lang/maps';
7
+ import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
2
8
  var TelemetryCachePluggable = /** @class */ (function () {
3
9
  /**
4
10
  * Create a Telemetry cache that uses a storage wrapper.
@@ -19,6 +25,126 @@ var TelemetryCachePluggable = /** @class */ (function () {
19
25
  return this.wrapper.incr(this.keys.buildExceptionKey(method))
20
26
  .catch(function () { });
21
27
  };
28
+ TelemetryCachePluggable.prototype.recordConfig = function () {
29
+ var value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_PLUGGABLE));
30
+ return this.wrapper.set(this.keys.buildInitKey(), value).catch(function () { });
31
+ };
32
+ /**
33
+ * Pop telemetry latencies.
34
+ * The returned promise rejects if wrapper operations fail.
35
+ */
36
+ TelemetryCachePluggable.prototype.popLatencies = function () {
37
+ var _this = this;
38
+ return this.wrapper.getKeysByPrefix(this.keys.latencyPrefix).then(function (latencyKeys) {
39
+ return latencyKeys.length ?
40
+ _this.wrapper.getMany(latencyKeys).then(function (latencies) {
41
+ var result = new _Map();
42
+ for (var i = 0; i < latencyKeys.length; i++) {
43
+ var field = latencyKeys[i].split('::')[1];
44
+ var parsedField = parseLatencyField(field);
45
+ if (isString(parsedField)) {
46
+ _this.log.error("Ignoring invalid latency field: " + field + ": " + parsedField);
47
+ continue;
48
+ }
49
+ // @ts-ignore
50
+ var count = parseInt(latencies[i]);
51
+ if (isNaNNumber(count)) {
52
+ _this.log.error("Ignoring latency with invalid count: " + latencies[i]);
53
+ continue;
54
+ }
55
+ var metadata = parsedField[0], method = parsedField[1], bucket = parsedField[2];
56
+ if (bucket >= MAX_LATENCY_BUCKET_COUNT) {
57
+ _this.log.error("Ignoring latency with invalid bucket: " + bucket);
58
+ continue;
59
+ }
60
+ if (!result.has(metadata))
61
+ result.set(metadata, {
62
+ t: newBuckets(),
63
+ ts: newBuckets(),
64
+ tc: newBuckets(),
65
+ tcs: newBuckets(),
66
+ tr: newBuckets(),
67
+ });
68
+ result.get(metadata)[method][bucket] = count;
69
+ }
70
+ return Promise.all(latencyKeys.map(function (latencyKey) { return _this.wrapper.del(latencyKey); })).then(function () { return result; });
71
+ }) :
72
+ // If latencyKeys is empty, return an empty map.
73
+ new _Map();
74
+ });
75
+ };
76
+ /**
77
+ * Pop telemetry exceptions.
78
+ * The returned promise rejects if wrapper operations fail.
79
+ */
80
+ TelemetryCachePluggable.prototype.popExceptions = function () {
81
+ var _this = this;
82
+ return this.wrapper.getKeysByPrefix(this.keys.exceptionPrefix).then(function (exceptionKeys) {
83
+ return exceptionKeys.length ?
84
+ _this.wrapper.getMany(exceptionKeys).then(function (exceptions) {
85
+ var result = new _Map();
86
+ for (var i = 0; i < exceptionKeys.length; i++) {
87
+ var field = exceptionKeys[i].split('::')[1];
88
+ var parsedField = parseExceptionField(field);
89
+ if (isString(parsedField)) {
90
+ _this.log.error("Ignoring invalid exception field: " + field + ": " + parsedField);
91
+ continue;
92
+ }
93
+ // @ts-ignore
94
+ var count = parseInt(exceptions[i]);
95
+ if (isNaNNumber(count)) {
96
+ _this.log.error("Ignoring exception with invalid count: " + exceptions[i]);
97
+ continue;
98
+ }
99
+ var metadata = parsedField[0], method = parsedField[1];
100
+ if (!result.has(metadata))
101
+ result.set(metadata, {
102
+ t: 0,
103
+ ts: 0,
104
+ tc: 0,
105
+ tcs: 0,
106
+ tr: 0,
107
+ });
108
+ result.get(metadata)[method] = count;
109
+ }
110
+ return Promise.all(exceptionKeys.map(function (exceptionKey) { return _this.wrapper.del(exceptionKey); })).then(function () { return result; });
111
+ }) :
112
+ // If exceptionKeys is empty, return an empty map.
113
+ new _Map();
114
+ });
115
+ };
116
+ /**
117
+ * Pop telemetry configs.
118
+ * The returned promise rejects if wrapper operations fail.
119
+ */
120
+ TelemetryCachePluggable.prototype.popConfigs = function () {
121
+ var _this = this;
122
+ return this.wrapper.getKeysByPrefix(this.keys.initPrefix).then(function (configKeys) {
123
+ return configKeys.length ?
124
+ _this.wrapper.getMany(configKeys).then(function (configs) {
125
+ var result = new _Map();
126
+ for (var i = 0; i < configKeys.length; i++) {
127
+ var field = configKeys[i].split('::')[1];
128
+ var parsedField = parseMetadata(field);
129
+ if (isString(parsedField)) {
130
+ _this.log.error("Ignoring invalid config field: " + field + ": " + parsedField);
131
+ continue;
132
+ }
133
+ var metadata = parsedField[0];
134
+ try { // @ts-ignore
135
+ var config = JSON.parse(configs[i]);
136
+ result.set(metadata, config);
137
+ }
138
+ catch (e) {
139
+ _this.log.error("Ignoring invalid config: " + configs[i]);
140
+ }
141
+ }
142
+ return Promise.all(configKeys.map(function (configKey) { return _this.wrapper.del(configKey); })).then(function () { return result; });
143
+ }) :
144
+ // If configKeys is empty, return an empty map.
145
+ new _Map();
146
+ });
147
+ };
22
148
  return TelemetryCachePluggable;
23
149
  }());
24
150
  export { TelemetryCachePluggable };
@@ -11,6 +11,8 @@ import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants'
11
11
  import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
12
12
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
13
13
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
14
+ import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
15
+ import { TelemetryCachePluggable } from './TelemetryCachePluggable';
14
16
  var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
15
17
  var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
16
18
  /**
@@ -27,15 +29,6 @@ function validatePluggableStorageOptions(options) {
27
29
  if (missingMethods.length)
28
30
  throw new Error(NO_VALID_WRAPPER_INTERFACE + " The following methods are missing or invalid: " + missingMethods);
29
31
  }
30
- // subscription to wrapper connect event in order to emit SDK_READY event
31
- function wrapperConnect(wrapper, onReadyCb) {
32
- wrapper.connect().then(function () {
33
- onReadyCb();
34
- // At the moment, we don't synchronize config with pluggable storage
35
- }).catch(function (e) {
36
- onReadyCb(e || new Error('Error connecting wrapper'));
37
- });
38
- }
39
32
  // Async return type in `client.track` method on consumer partial mode
40
33
  // No need to promisify impressions cache
41
34
  function promisifyEventsTrack(events) {
@@ -51,28 +44,39 @@ function promisifyEventsTrack(events) {
51
44
  export function PluggableStorage(options) {
52
45
  validatePluggableStorageOptions(options);
53
46
  var prefix = validatePrefix(options.prefix);
54
- function PluggableStorageFactory(_a) {
55
- var log = _a.log, metadata = _a.metadata, onReadyCb = _a.onReadyCb, mode = _a.mode, eventsQueueSize = _a.eventsQueueSize, impressionsQueueSize = _a.impressionsQueueSize, optimize = _a.optimize;
47
+ function PluggableStorageFactory(params) {
48
+ var log = params.log, metadata = params.metadata, onReadyCb = params.onReadyCb, mode = params.mode, eventsQueueSize = params.eventsQueueSize, impressionsQueueSize = params.impressionsQueueSize, optimize = params.optimize;
56
49
  var keys = new KeyBuilderSS(prefix, metadata);
57
50
  var wrapper = wrapperAdapter(log, options.wrapper);
58
51
  var isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
52
+ var telemetry = shouldRecordTelemetry(params) ?
53
+ isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) :
54
+ undefined;
59
55
  // Connects to wrapper and emits SDK_READY event on main client
60
- wrapperConnect(wrapper, onReadyCb);
56
+ var connectPromise = wrapper.connect().then(function () {
57
+ onReadyCb();
58
+ // If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
59
+ if (telemetry && telemetry.recordConfig && mode)
60
+ telemetry.recordConfig();
61
+ }).catch(function (e) {
62
+ e = e || new Error('Error connecting wrapper');
63
+ onReadyCb(e);
64
+ return e;
65
+ });
61
66
  return {
62
67
  splits: new SplitsCachePluggable(log, keys, wrapper),
63
68
  segments: new SegmentsCachePluggable(log, keys, wrapper),
64
69
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
65
70
  impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
66
71
  events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
67
- // @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
68
- // telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
72
+ telemetry: telemetry,
69
73
  // Disconnect the underlying storage
70
74
  destroy: function () {
71
75
  return wrapper.disconnect();
72
76
  },
73
77
  // emits SDK_READY event on shared clients and returns a reference to the storage
74
78
  shared: function (_, onReadyCb) {
75
- wrapperConnect(wrapper, onReadyCb);
79
+ connectPromise.then(onReadyCb);
76
80
  return __assign(__assign({}, this), {
77
81
  // no-op destroy, to disconnect the wrapper only when the main client is destroyed
78
82
  destroy: function () { } });
@@ -2,7 +2,6 @@ import { eventsSubmitterFactory } from './eventsSubmitter';
2
2
  import { impressionsSubmitterFactory } from './impressionsSubmitter';
3
3
  import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
4
4
  import { telemetrySubmitterFactory } from './telemetrySubmitter';
5
- import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
6
5
  export function submitterManagerFactory(params) {
7
6
  var submitters = [
8
7
  impressionsSubmitterFactory(params),
@@ -12,8 +11,6 @@ export function submitterManagerFactory(params) {
12
11
  if (impressionCountsSubmitter)
13
12
  submitters.push(impressionCountsSubmitter);
14
13
  var telemetrySubmitter = telemetrySubmitterFactory(params);
15
- if (params.uniqueKeysTracker)
16
- submitters.push(uniqueKeysSubmitterFactory(params));
17
14
  return {
18
15
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
19
16
  start: function (onlyTelemetry) {
@@ -1,11 +1,12 @@
1
1
  var _a, _b, _c;
2
2
  import { submitterFactory, firstPushWindowDecorator } from './submitter';
3
- 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';
3
+ 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
4
  import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
5
5
  import { base } from '../../utils/settingsValidation';
6
6
  import { usedKeysMap } from '../../utils/inputValidation/apiKey';
7
7
  import { timer } from '../../utils/timeTracker/timer';
8
8
  import { objectAssign } from '../../utils/lang/objectAssign';
9
+ import { isStorageSync } from '../../trackers/impressionObserver/utils';
9
10
  /**
10
11
  * Converts data from telemetry cache into /metrics/usage request payload.
11
12
  */
@@ -26,9 +27,9 @@ export function telemetryCacheStatsAdapter(telemetry, splits, segments) {
26
27
  iQ: telemetry.getImpressionStats(QUEUED),
27
28
  iDe: telemetry.getImpressionStats(DEDUPED),
28
29
  iDr: telemetry.getImpressionStats(DROPPED),
29
- spC: splits.getSplitNames().length,
30
- seC: segments.getRegisteredSegments().length,
31
- skC: segments.getKeysCount(),
30
+ spC: splits && splits.getSplitNames().length,
31
+ seC: segments && segments.getRegisteredSegments().length,
32
+ skC: segments && segments.getKeysCount(),
32
33
  sL: telemetry.getSessionLength(),
33
34
  eQ: telemetry.getEventStats(QUEUED),
34
35
  eD: telemetry.getEventStats(DROPPED),
@@ -46,7 +47,6 @@ var OPERATION_MODE_MAP = (_a = {},
46
47
  var IMPRESSIONS_MODE_MAP = (_b = {},
47
48
  _b[OPTIMIZED] = OPTIMIZED_ENUM,
48
49
  _b[DEBUG] = DEBUG_ENUM,
49
- _b[NONE] = NONE_ENUM,
50
50
  _b);
51
51
  var USER_CONSENT_MAP = (_c = {},
52
52
  _c[CONSENT_UNKNOWN] = 1,
@@ -120,7 +120,9 @@ export function telemetrySubmitterFactory(params) {
120
120
  return; // No submitter created if telemetry cache is not defined
121
121
  var settings = params.settings, _b = params.settings, log = _b.log, telemetryRefreshRate = _b.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
122
122
  var startTime = timer(now);
123
- var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
123
+ var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage,
124
+ // @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
125
+ isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
124
126
  readiness.gate.once(SDK_READY_FROM_CACHE, function () {
125
127
  telemetry.recordTimeUntilReadyFromCache(startTime());
126
128
  });
@@ -1,5 +1,6 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
+ import { truncateTimeFrame } from '../utils/time';
3
4
  import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIONS_LISTENER } from '../logger/constants';
4
5
  import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
5
6
  /**
@@ -9,34 +10,52 @@ import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
9
10
  * @param metadata runtime metadata (ip, hostname and version)
10
11
  * @param impressionListener optional impression listener
11
12
  * @param integrationsManager optional integrations manager
12
- * @param strategy strategy for impressions tracking.
13
+ * @param observer optional impression observer. If provided, previous time (pt property) is included in impression instances
14
+ * @param countsCache optional cache to save impressions count. If provided, impressions will be deduped (OPTIMIZED mode)
13
15
  */
14
- export function impressionsTrackerFactory(settings, impressionsCache, strategy, integrationsManager, telemetryCache) {
16
+ export function impressionsTrackerFactory(settings, impressionsCache, integrationsManager,
17
+ // if observer is provided, it implies `shouldAddPreviousTime` flag (i.e., if impressions previous time should be added or not)
18
+ observer,
19
+ // if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
20
+ countsCache, telemetryCache) {
15
21
  var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
16
22
  return {
17
23
  track: function (impressions, attributes) {
18
24
  if (settings.userConsent === CONSENT_DECLINED)
19
25
  return;
20
26
  var impressionsCount = impressions.length;
21
- var _a = strategy.process(impressions), impressionsToStore = _a.impressionsToStore, impressionsToListener = _a.impressionsToListener, deduped = _a.deduped;
22
- var impressionsToListenerCount = impressionsToListener.length;
23
- if (impressionsToStore.length > 0) {
24
- var res = impressionsCache.track(impressionsToStore);
25
- // If we're on an async storage, handle error and log it.
26
- if (thenable(res)) {
27
- res.then(function () {
28
- log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
29
- }).catch(function (err) {
30
- log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
31
- });
27
+ var impressionsToStore = []; // Track only the impressions that are going to be stored
28
+ // Wraps impressions to store and adds previousTime if it corresponds
29
+ impressions.forEach(function (impression) {
30
+ if (observer) {
31
+ // Adds previous time if it is enabled
32
+ impression.pt = observer.testAndSet(impression);
32
33
  }
33
- else {
34
- // Record when impressionsCache is sync only (standalone mode)
35
- // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
36
- if (telemetryCache) {
37
- telemetryCache.recordImpressionStats(QUEUED, impressionsToStore.length);
38
- telemetryCache.recordImpressionStats(DEDUPED, deduped);
39
- }
34
+ var now = Date.now();
35
+ if (countsCache) {
36
+ // Increments impression counter per featureName
37
+ countsCache.track(impression.feature, now, 1);
38
+ }
39
+ // Checks if the impression should be added in queue to be sent
40
+ if (!countsCache || !impression.pt || impression.pt < truncateTimeFrame(now)) {
41
+ impressionsToStore.push(impression);
42
+ }
43
+ });
44
+ var res = impressionsCache.track(impressionsToStore);
45
+ // If we're on an async storage, handle error and log it.
46
+ if (thenable(res)) {
47
+ res.then(function () {
48
+ log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
49
+ }).catch(function (err) {
50
+ log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
51
+ });
52
+ }
53
+ else {
54
+ // Record when impressionsCache is sync only (standalone mode)
55
+ // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
56
+ if (telemetryCache) {
57
+ telemetryCache.recordImpressionStats(QUEUED, impressionsToStore.length);
58
+ telemetryCache.recordImpressionStats(DEDUPED, impressions.length - impressionsToStore.length);
40
59
  }
41
60
  }
42
61
  // @TODO next block might be handled by the integration manager. In that case, the metadata object doesn't need to be passed in the constructor
@@ -44,7 +63,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, strategy,
44
63
  var _loop_1 = function (i) {
45
64
  var impressionData = {
46
65
  // copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
47
- impression: objectAssign({}, impressionsToListener[i]),
66
+ impression: objectAssign({}, impressions[i]),
48
67
  attributes: attributes,
49
68
  ip: ip,
50
69
  hostname: hostname,
@@ -64,7 +83,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, strategy,
64
83
  }
65
84
  }, 0);
66
85
  };
67
- for (var i = 0; i < impressionsToListenerCount; i++) {
86
+ for (var i = 0; i < impressionsCount; i++) {
68
87
  _loop_1(i);
69
88
  }
70
89
  }
@@ -13,7 +13,6 @@ export var SPLIT_EVENT = 'EVENT';
13
13
  // Impression collection modes
14
14
  export var DEBUG = 'DEBUG';
15
15
  export var OPTIMIZED = 'OPTIMIZED';
16
- export var NONE = 'NONE';
17
16
  // SDK Modes
18
17
  export var LOCALHOST_MODE = 'localhost';
19
18
  export var STANDALONE_MODE = 'standalone';
@@ -38,7 +37,6 @@ export var CONSUMER_ENUM = 1;
38
37
  export var CONSUMER_PARTIAL_ENUM = 2;
39
38
  export var OPTIMIZED_ENUM = 0;
40
39
  export var DEBUG_ENUM = 1;
41
- export var NONE_ENUM = 2;
42
40
  export var SPLITS = 'sp';
43
41
  export var IMPRESSIONS = 'im';
44
42
  export var IMPRESSIONS_COUNT = 'ic';
@@ -37,13 +37,6 @@ var MapPoly = /** @class */ (function () {
37
37
  this.__mapKeysData__.length = 0;
38
38
  this.__mapValuesData__.length = 0;
39
39
  };
40
- MapPoly.prototype.set = function (key, value) {
41
- var index = this.__mapKeysData__.indexOf(key);
42
- if (index === -1)
43
- index = this.__mapKeysData__.push(key) - 1;
44
- this.__mapValuesData__[index] = value;
45
- return this;
46
- };
47
40
  MapPoly.prototype.delete = function (key) {
48
41
  var index = this.__mapKeysData__.indexOf(key);
49
42
  if (index === -1)
@@ -52,12 +45,27 @@ var MapPoly = /** @class */ (function () {
52
45
  this.__mapValuesData__.splice(index, 1);
53
46
  return true;
54
47
  };
48
+ MapPoly.prototype.forEach = function (callbackfn, thisArg) {
49
+ for (var i = 0; i < this.__mapKeysData__.length; i++) {
50
+ callbackfn.call(thisArg, this.__mapValuesData__[i], this.__mapKeysData__[i], this);
51
+ }
52
+ };
55
53
  MapPoly.prototype.get = function (key) {
56
54
  var index = this.__mapKeysData__.indexOf(key);
57
55
  if (index === -1)
58
56
  return;
59
57
  return this.__mapValuesData__[index];
60
58
  };
59
+ MapPoly.prototype.has = function (key) {
60
+ return this.__mapKeysData__.indexOf(key) !== -1;
61
+ };
62
+ MapPoly.prototype.set = function (key, value) {
63
+ var index = this.__mapKeysData__.indexOf(key);
64
+ if (index === -1)
65
+ index = this.__mapKeysData__.push(key) - 1;
66
+ this.__mapValuesData__[index] = value;
67
+ return this;
68
+ };
61
69
  Object.defineProperty(MapPoly.prototype, "size", {
62
70
  get: function () {
63
71
  return this.__mapKeysData__.length;
@@ -1,10 +1,10 @@
1
1
  import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants';
2
- import { DEBUG, OPTIMIZED, NONE } from '../constants';
2
+ import { DEBUG, OPTIMIZED } from '../constants';
3
3
  import { stringToUpperCase } from '../lang';
4
4
  export function validImpressionsMode(log, impressionsMode) {
5
5
  impressionsMode = stringToUpperCase(impressionsMode);
6
- if ([DEBUG, OPTIMIZED, NONE].indexOf(impressionsMode) > -1)
6
+ if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) > -1)
7
7
  return impressionsMode;
8
- log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED, NONE], OPTIMIZED]);
8
+ log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED], OPTIMIZED]);
9
9
  return OPTIMIZED;
10
10
  }
@@ -31,8 +31,6 @@ export var base = {
31
31
  telemetryRefreshRate: 3600,
32
32
  // publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
33
33
  impressionsRefreshRate: 300,
34
- // publish unique Keys each 900 sec (15 min)
35
- uniqueKeysRefreshRate: 900,
36
34
  // fetch offline changes each 15 sec
37
35
  offlineRefreshRate: 15,
38
36
  // publish events every 60 seconds after the first flush
@@ -111,7 +109,6 @@ export function settingsValidation(config, validationParams) {
111
109
  scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
112
110
  scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
113
111
  scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
114
- scheduler.uniqueKeysRefreshRate = fromSecondsToMillis(scheduler.uniqueKeysRefreshRate);
115
112
  scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
116
113
  // Default impressionsRefreshRate for DEBUG mode is 60 secs
117
114
  if (get(config, 'scheduler.impressionsRefreshRate') === undefined && withDefaults.sync.impressionsMode === DEBUG)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.6.2-rc.8",
3
+ "version": "1.6.2-rc.9",
4
4
  "description": "Split Javascript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -24,6 +24,7 @@
24
24
  "build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs",
25
25
  "test": "jest",
26
26
  "test:coverage": "jest --coverage",
27
+ "all": "npm run check && npm run build && npm run test",
27
28
  "publish:rc": "npm run check && npm run test && npm run build && npm publish --tag rc",
28
29
  "publish:stable": "npm run check && npm run test && npm run build && npm publish"
29
30
  },
@@ -40,8 +40,8 @@ export function createUserConsentAPI(params: ISdkFactoryContext) {
40
40
 
41
41
  // @ts-ignore, clear method is present in storage for standalone and partial consumer mode
42
42
  if (events.clear) events.clear(); // @ts-ignore
43
- if (impressions.clear) impressions.clear();// @ts-ignore
44
- if (impressionCounts && impressionCounts.clear) impressionCounts.clear();
43
+ if (impressions.clear) impressions.clear();
44
+ if (impressionCounts) impressionCounts.clear();
45
45
  }
46
46
  } else {
47
47
  log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
@@ -29,19 +29,19 @@ export function evaluateFeature(
29
29
  attributes: SplitIO.Attributes | undefined,
30
30
  storage: IStorageSync | IStorageAsync,
31
31
  ): MaybeThenable<IEvaluationResult> {
32
- let stringifiedSplit;
32
+ let parsedSplit;
33
33
 
34
34
  try {
35
- stringifiedSplit = storage.splits.getSplit(splitName);
35
+ parsedSplit = storage.splits.getSplit(splitName);
36
36
  } catch (e) {
37
37
  // Exception on sync `getSplit` storage. Not possible ATM with InMemory and InLocal storages.
38
38
  return treatmentException;
39
39
  }
40
40
 
41
- if (thenable(stringifiedSplit)) {
42
- return stringifiedSplit.then((result) => getEvaluation(
41
+ if (thenable(parsedSplit)) {
42
+ return parsedSplit.then((split) => getEvaluation(
43
43
  log,
44
- result,
44
+ split,
45
45
  key,
46
46
  attributes,
47
47
  storage,
@@ -54,7 +54,7 @@ export function evaluateFeature(
54
54
 
55
55
  return getEvaluation(
56
56
  log,
57
- stringifiedSplit,
57
+ parsedSplit,
58
58
  key,
59
59
  attributes,
60
60
  storage,
@@ -7,7 +7,7 @@ import { fromImpressionCountsCollector } from '../sync/submitters/impressionCoun
7
7
  import { IResponse, ISplitApi } from '../services/types';
8
8
  import { ImpressionDTO, ISettings } from '../types';
9
9
  import { ImpressionsPayload } from '../sync/submitters/types';
10
- import { OPTIMIZED, DEBUG, NONE } from '../utils/constants';
10
+ import { OPTIMIZED, DEBUG } from '../utils/constants';
11
11
  import { objectAssign } from '../utils/lang/objectAssign';
12
12
  import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
13
13
  import { ISyncManager } from '../sync/types';
@@ -88,10 +88,9 @@ export class BrowserSignalListener implements ISignalListener {
88
88
  // Flush impressions & events data if there is user consent
89
89
  if (isConsentGranted(this.settings)) {
90
90
  const eventsUrl = this.settings.urls.events;
91
- const sim = this.settings.sync.impressionsMode;
92
91
  const extraMetadata = {
93
92
  // sim stands for Sync/Split Impressions Mode
94
- sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
93
+ sim: this.settings.sync.impressionsMode === OPTIMIZED ? OPTIMIZED : DEBUG
95
94
  };
96
95
 
97
96
  this._flushData(eventsUrl + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
Binary file
@@ -143,5 +143,4 @@ export const LOG_PREFIX_SYNC_POLLING = LOG_PREFIX_SYNC + ':polling-manager: ';
143
143
  export const LOG_PREFIX_SYNC_SUBMITTERS = LOG_PREFIX_SYNC + ':submitter: ';
144
144
  export const LOG_PREFIX_IMPRESSIONS_TRACKER = 'impressions-tracker: ';
145
145
  export const LOG_PREFIX_EVENTS_TRACKER = 'events-tracker: ';
146
- export const LOG_PREFIX_UNIQUE_KEYS_TRACKER = 'unique-keys-tracker: ';
147
146
  export const LOG_PREFIX_CLEANUP = 'cleanup: ';
@@ -9,6 +9,17 @@ import { IEvaluationResult } from '../evaluator/types';
9
9
  import { SplitIO, ImpressionDTO } from '../types';
10
10
  import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
11
11
  import { ISdkFactoryContext } from '../sdkFactory/types';
12
+ import { isStorageSync } from '../trackers/impressionObserver/utils';
13
+
14
+ const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
15
+
16
+ function treatmentsNotReady(splitNames: string[]) {
17
+ const evaluations: Record<string, IEvaluationResult> = {};
18
+ splitNames.forEach(splitName => {
19
+ evaluations[splitName] = treatmentNotReady;
20
+ });
21
+ return evaluations;
22
+ }
12
23
 
13
24
  /**
14
25
  * Creator of base client with getTreatments and track methods.
@@ -29,7 +40,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
29
40
  return treatment;
30
41
  };
31
42
 
32
- const evaluation = evaluateFeature(log, key, splitName, attributes, storage);
43
+ const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
44
+ evaluateFeature(log, key, splitName, attributes, storage) :
45
+ isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
46
+ treatmentNotReady :
47
+ Promise.resolve(treatmentNotReady); // Promisify if async
33
48
 
34
49
  return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
35
50
  }
@@ -53,7 +68,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
53
68
  return treatments;
54
69
  };
55
70
 
56
- const evaluations = evaluateFeatures(log, key, splitNames, attributes, storage);
71
+ const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
72
+ evaluateFeatures(log, key, splitNames, attributes, storage) :
73
+ isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
74
+ treatmentsNotReady(splitNames) :
75
+ Promise.resolve(treatmentsNotReady(splitNames)); // Promisify if async
57
76
 
58
77
  return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
59
78
  }
@@ -72,15 +91,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
72
91
  invokingMethodName: string,
73
92
  queue: ImpressionDTO[]
74
93
  ): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
75
- const isSdkReady = readinessManager.isReady() || readinessManager.isReadyFromCache();
76
94
  const matchingKey = getMatching(key);
77
95
  const bucketingKey = getBucketing(key);
78
96
 
79
- // If the SDK was not ready, treatment may be incorrect due to having Splits but not segments data.
80
- if (!isSdkReady) {
81
- evaluation = { treatment: CONTROL, label: SDK_NOT_READY };
82
- }
83
-
84
97
  const { treatment, label, changeNumber, config = null } = evaluation;
85
98
  log.info(IMPRESSION, [splitName, matchingKey, treatment, label]);
86
99
 
@@ -9,7 +9,7 @@ import { ISdkFactoryContext } from '../sdkFactory/types';
9
9
  * Creates an Sdk client, i.e., a base client with status and destroy interface
10
10
  */
11
11
  export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: boolean): SplitIO.IClient | SplitIO.IAsyncClient {
12
- const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker, uniqueKeysTracker } = 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
@@ -39,8 +39,6 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
39
39
 
40
40
  // Release the API Key if it is the main client
41
41
  if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
42
-
43
- if (uniqueKeysTracker) uniqueKeysTracker.stop();
44
42
 
45
43
  // Cleanup storage
46
44
  return storage.destroy();