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

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 (176) hide show
  1. package/CHANGES.txt +6 -1
  2. package/cjs/consent/sdkUserConsent.js +2 -2
  3. package/cjs/listeners/browser.js +11 -12
  4. package/cjs/logger/constants.js +2 -1
  5. package/cjs/sdkClient/sdkClient.js +3 -1
  6. package/cjs/sdkFactory/index.js +26 -26
  7. package/cjs/services/splitApi.js +20 -0
  8. package/cjs/storages/AbstractSplitsCacheAsync.js +1 -1
  9. package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
  10. package/cjs/storages/KeyBuilderSS.js +10 -43
  11. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
  12. package/cjs/storages/inLocalStorage/index.js +17 -9
  13. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  14. package/cjs/storages/inMemory/InMemoryStorage.js +13 -6
  15. package/cjs/storages/inMemory/InMemoryStorageCS.js +13 -6
  16. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +60 -35
  17. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +72 -0
  18. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +76 -0
  19. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +85 -0
  20. package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
  21. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +4 -4
  22. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +71 -0
  23. package/cjs/storages/inRedis/constants.js +4 -1
  24. package/cjs/storages/inRedis/index.js +20 -3
  25. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +81 -0
  26. package/cjs/storages/pluggable/ImpressionsCachePluggable.js +2 -19
  27. package/cjs/storages/pluggable/TelemetryCachePluggable.js +4 -4
  28. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +61 -0
  29. package/cjs/storages/pluggable/inMemoryWrapper.js +8 -6
  30. package/cjs/storages/pluggable/index.js +38 -9
  31. package/cjs/storages/utils.js +73 -0
  32. package/cjs/sync/submitters/submitterManager.js +3 -0
  33. package/cjs/sync/submitters/telemetrySubmitter.js +5 -40
  34. package/cjs/sync/submitters/uniqueKeysSubmitter.js +27 -0
  35. package/cjs/trackers/impressionObserver/utils.js +1 -17
  36. package/cjs/trackers/impressionsTracker.js +22 -41
  37. package/cjs/trackers/strategy/strategyDebug.js +25 -0
  38. package/cjs/trackers/strategy/strategyNone.js +29 -0
  39. package/cjs/trackers/strategy/strategyOptimized.js +35 -0
  40. package/cjs/trackers/uniqueKeysTracker.js +38 -0
  41. package/cjs/utils/constants/index.js +4 -2
  42. package/cjs/utils/redis/RedisMock.js +31 -0
  43. package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
  44. package/cjs/utils/settingsValidation/index.js +9 -3
  45. package/esm/consent/sdkUserConsent.js +2 -2
  46. package/esm/listeners/browser.js +12 -13
  47. package/esm/logger/constants.js +1 -0
  48. package/esm/sdkClient/sdkClient.js +3 -1
  49. package/esm/sdkFactory/index.js +26 -26
  50. package/esm/services/splitApi.js +20 -0
  51. package/esm/storages/AbstractSplitsCacheAsync.js +1 -1
  52. package/esm/storages/AbstractSplitsCacheSync.js +1 -1
  53. package/esm/storages/KeyBuilderSS.js +7 -37
  54. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
  55. package/esm/storages/inLocalStorage/index.js +18 -10
  56. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  57. package/esm/storages/inMemory/InMemoryStorage.js +14 -7
  58. package/esm/storages/inMemory/InMemoryStorageCS.js +14 -7
  59. package/esm/storages/inMemory/TelemetryCacheInMemory.js +61 -36
  60. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +68 -0
  61. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +73 -0
  62. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +82 -0
  63. package/esm/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
  64. package/esm/storages/inRedis/TelemetryCacheInRedis.js +1 -1
  65. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +68 -0
  66. package/esm/storages/inRedis/constants.js +3 -0
  67. package/esm/storages/inRedis/index.js +21 -4
  68. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +78 -0
  69. package/esm/storages/pluggable/ImpressionsCachePluggable.js +2 -19
  70. package/esm/storages/pluggable/TelemetryCachePluggable.js +1 -1
  71. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +58 -0
  72. package/esm/storages/pluggable/inMemoryWrapper.js +8 -6
  73. package/esm/storages/pluggable/index.js +39 -10
  74. package/esm/storages/utils.js +65 -0
  75. package/esm/sync/submitters/submitterManager.js +3 -0
  76. package/esm/sync/submitters/telemetrySubmitter.js +5 -39
  77. package/esm/sync/submitters/uniqueKeysSubmitter.js +23 -0
  78. package/esm/trackers/impressionObserver/utils.js +1 -15
  79. package/esm/trackers/impressionsTracker.js +22 -41
  80. package/esm/trackers/strategy/strategyDebug.js +21 -0
  81. package/esm/trackers/strategy/strategyNone.js +25 -0
  82. package/esm/trackers/strategy/strategyOptimized.js +31 -0
  83. package/esm/trackers/uniqueKeysTracker.js +34 -0
  84. package/esm/utils/constants/index.js +2 -0
  85. package/esm/utils/redis/RedisMock.js +28 -0
  86. package/esm/utils/settingsValidation/impressionsMode.js +3 -3
  87. package/esm/utils/settingsValidation/index.js +9 -3
  88. package/package.json +1 -2
  89. package/src/consent/sdkUserConsent.ts +2 -2
  90. package/src/listeners/browser.ts +12 -15
  91. package/src/logger/constants.ts +1 -0
  92. package/src/sdkClient/sdkClient.ts +3 -1
  93. package/src/sdkFactory/index.ts +29 -31
  94. package/src/sdkFactory/types.ts +7 -4
  95. package/src/services/splitApi.ts +22 -0
  96. package/src/services/types.ts +6 -0
  97. package/src/storages/AbstractSplitsCacheAsync.ts +1 -1
  98. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  99. package/src/storages/KeyBuilderSS.ts +9 -43
  100. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -1
  101. package/src/storages/inLocalStorage/index.ts +18 -10
  102. package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -7
  103. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
  104. package/src/storages/inMemory/InMemoryStorage.ts +14 -7
  105. package/src/storages/inMemory/InMemoryStorageCS.ts +14 -7
  106. package/src/storages/inMemory/TelemetryCacheInMemory.ts +69 -34
  107. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +80 -0
  108. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +86 -0
  109. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +95 -0
  110. package/src/storages/inRedis/ImpressionsCacheInRedis.ts +2 -22
  111. package/src/storages/inRedis/TelemetryCacheInRedis.ts +3 -2
  112. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +77 -0
  113. package/src/storages/inRedis/constants.ts +3 -0
  114. package/src/storages/inRedis/index.ts +18 -5
  115. package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +92 -0
  116. package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -23
  117. package/src/storages/pluggable/TelemetryCachePluggable.ts +3 -2
  118. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +67 -0
  119. package/src/storages/pluggable/inMemoryWrapper.ts +6 -6
  120. package/src/storages/pluggable/index.ts +41 -9
  121. package/src/storages/types.ts +56 -55
  122. package/src/storages/utils.ts +78 -0
  123. package/src/sync/submitters/submitter.ts +2 -2
  124. package/src/sync/submitters/submitterManager.ts +2 -0
  125. package/src/sync/submitters/telemetrySubmitter.ts +8 -43
  126. package/src/sync/submitters/types.ts +29 -27
  127. package/src/sync/submitters/uniqueKeysSubmitter.ts +36 -0
  128. package/src/trackers/impressionObserver/utils.ts +1 -16
  129. package/src/trackers/impressionsTracker.ts +25 -46
  130. package/src/trackers/strategy/strategyDebug.ts +28 -0
  131. package/src/trackers/strategy/strategyNone.ts +34 -0
  132. package/src/trackers/strategy/strategyOptimized.ts +42 -0
  133. package/src/trackers/types.ts +28 -0
  134. package/src/trackers/uniqueKeysTracker.ts +48 -0
  135. package/src/types.ts +1 -1
  136. package/src/utils/constants/index.ts +2 -0
  137. package/src/utils/redis/RedisMock.ts +33 -0
  138. package/src/utils/settingsValidation/impressionsMode.ts +3 -3
  139. package/src/utils/settingsValidation/index.ts +7 -3
  140. package/types/logger/constants.d.ts +1 -0
  141. package/types/sdkFactory/types.d.ts +4 -2
  142. package/types/services/types.d.ts +4 -0
  143. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  144. package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
  145. package/types/storages/KeyBuilderSS.d.ts +3 -3
  146. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +0 -1
  147. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
  148. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +20 -9
  149. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
  150. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +35 -0
  151. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +16 -0
  152. package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +0 -1
  153. package/types/storages/inRedis/constants.d.ts +3 -0
  154. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +21 -0
  155. package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +16 -0
  156. package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +1 -2
  157. package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +20 -0
  158. package/types/storages/types.d.ts +39 -38
  159. package/types/storages/utils.d.ts +8 -0
  160. package/types/sync/submitters/submitter.d.ts +2 -2
  161. package/types/sync/submitters/telemetrySubmitter.d.ts +2 -10
  162. package/types/sync/submitters/types.d.ts +27 -7
  163. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +5 -0
  164. package/types/trackers/impressionObserver/utils.d.ts +0 -8
  165. package/types/trackers/impressionsTracker.d.ts +4 -6
  166. package/types/trackers/strategy/strategyDebug.d.ts +9 -0
  167. package/types/trackers/strategy/strategyNone.d.ts +10 -0
  168. package/types/trackers/strategy/strategyOptimized.d.ts +11 -0
  169. package/types/trackers/types.d.ts +23 -0
  170. package/types/trackers/uniqueKeysTracker.d.ts +13 -0
  171. package/types/types.d.ts +1 -1
  172. package/types/utils/constants/index.d.ts +2 -0
  173. package/types/utils/redis/RedisMock.d.ts +4 -0
  174. package/cjs/storages/metadataBuilder.js +0 -12
  175. package/esm/storages/metadataBuilder.js +0 -8
  176. package/src/storages/metadataBuilder.ts +0 -11
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ImpressionCountsCachePluggable = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var ImpressionCountsCacheInMemory_1 = require("../inMemory/ImpressionCountsCacheInMemory");
6
+ var constants_1 = require("../inRedis/constants");
7
+ var constants_2 = require("./constants");
8
+ var ImpressionCountsCachePluggable = /** @class */ (function (_super) {
9
+ (0, tslib_1.__extends)(ImpressionCountsCachePluggable, _super);
10
+ function ImpressionCountsCachePluggable(log, key, wrapper, impressionCountsCacheSize, refreshRate) {
11
+ if (refreshRate === void 0) { refreshRate = constants_1.REFRESH_RATE; }
12
+ var _this = _super.call(this, impressionCountsCacheSize) || this;
13
+ _this.log = log;
14
+ _this.key = key;
15
+ _this.wrapper = wrapper;
16
+ _this.refreshRate = refreshRate;
17
+ _this.onFullQueue = function () { _this.storeImpressionCounts(); };
18
+ return _this;
19
+ }
20
+ ImpressionCountsCachePluggable.prototype.storeImpressionCounts = function () {
21
+ var _this = this;
22
+ var counts = this.pop();
23
+ var keys = Object.keys(counts);
24
+ if (!keys.length)
25
+ return Promise.resolve(false);
26
+ var pipeline = keys.reduce(function (pipeline, key) {
27
+ return pipeline.then(function () { return _this.wrapper.incr(_this.key + "::" + key, counts[key]); });
28
+ }, Promise.resolve());
29
+ return pipeline.catch(function (err) {
30
+ _this.log.error(constants_2.LOG_PREFIX + "Error in impression counts pipeline: " + err + ".");
31
+ return false;
32
+ });
33
+ };
34
+ ImpressionCountsCachePluggable.prototype.start = function () {
35
+ this.intervalId = setInterval(this.storeImpressionCounts.bind(this), this.refreshRate);
36
+ };
37
+ ImpressionCountsCachePluggable.prototype.stop = function () {
38
+ clearInterval(this.intervalId);
39
+ return this.storeImpressionCounts();
40
+ };
41
+ // Async consumer API, used by synchronizer
42
+ ImpressionCountsCachePluggable.prototype.getImpressionsCount = function () {
43
+ var _this = this;
44
+ return this.wrapper.getKeysByPrefix(this.key)
45
+ .then(function (keys) {
46
+ return keys.length ? Promise.all(keys.map(function (key) { return _this.wrapper.get(key); }))
47
+ .then(function (counts) {
48
+ keys.forEach(function (key) { return _this.wrapper.del(key).catch(function () { }); });
49
+ var pf = [];
50
+ for (var i = 0; i < keys.length; i++) {
51
+ var key = keys[i];
52
+ var count = counts[i];
53
+ var keyFeatureNameAndTime = key.split('::');
54
+ if (keyFeatureNameAndTime.length !== 3) {
55
+ _this.log.error(constants_2.LOG_PREFIX + "Error spliting key " + key);
56
+ continue;
57
+ }
58
+ var timeFrame = parseInt(keyFeatureNameAndTime[2]);
59
+ if (isNaN(timeFrame)) {
60
+ _this.log.error(constants_2.LOG_PREFIX + "Error parsing time frame " + keyFeatureNameAndTime[2]);
61
+ continue;
62
+ }
63
+ // @ts-ignore
64
+ var rawCount = parseInt(count);
65
+ if (isNaN(rawCount)) {
66
+ _this.log.error(constants_2.LOG_PREFIX + "Error parsing raw count " + count);
67
+ continue;
68
+ }
69
+ pf.push({
70
+ f: keyFeatureNameAndTime[1],
71
+ m: timeFrame,
72
+ rc: rawCount,
73
+ });
74
+ }
75
+ return { pf: pf };
76
+ }) : undefined;
77
+ });
78
+ };
79
+ return ImpressionCountsCachePluggable;
80
+ }(ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory));
81
+ exports.ImpressionCountsCachePluggable = ImpressionCountsCachePluggable;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ImpressionsCachePluggable = void 0;
4
+ var utils_1 = require("../utils");
4
5
  var ImpressionsCachePluggable = /** @class */ (function () {
5
6
  function ImpressionsCachePluggable(log, key, wrapper, metadata) {
6
7
  this.log = log;
@@ -15,25 +16,7 @@ var ImpressionsCachePluggable = /** @class */ (function () {
15
16
  * or rejected if the wrapper operation fails.
16
17
  */
17
18
  ImpressionsCachePluggable.prototype.track = function (impressions) {
18
- return this.wrapper.pushItems(this.key, this._toJSON(impressions));
19
- };
20
- ImpressionsCachePluggable.prototype._toJSON = function (impressions) {
21
- var _this = this;
22
- return impressions.map(function (impression) {
23
- var keyName = impression.keyName, bucketingKey = impression.bucketingKey, feature = impression.feature, treatment = impression.treatment, label = impression.label, time = impression.time, changeNumber = impression.changeNumber;
24
- return JSON.stringify({
25
- m: _this.metadata,
26
- i: {
27
- k: keyName,
28
- b: bucketingKey,
29
- f: feature,
30
- t: treatment,
31
- r: label,
32
- c: changeNumber,
33
- m: time
34
- }
35
- });
36
- });
19
+ return this.wrapper.pushItems(this.key, (0, utils_1.impressionsToJSON)(impressions, this.metadata));
37
20
  };
38
21
  /**
39
22
  * Returns a promise that resolves with the count of stored impressions, or 0 if there was some error.
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TelemetryCachePluggable = void 0;
4
- var KeyBuilderSS_1 = require("../KeyBuilderSS");
5
4
  var findLatencyIndex_1 = require("../findLatencyIndex");
6
5
  var telemetrySubmitter_1 = require("../../sync/submitters/telemetrySubmitter");
7
6
  var constants_1 = require("../../utils/constants");
8
7
  var lang_1 = require("../../utils/lang");
9
8
  var maps_1 = require("../../utils/lang/maps");
10
9
  var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
10
+ var utils_1 = require("../utils");
11
11
  var TelemetryCachePluggable = /** @class */ (function () {
12
12
  /**
13
13
  * Create a Telemetry cache that uses a storage wrapper.
@@ -44,7 +44,7 @@ var TelemetryCachePluggable = /** @class */ (function () {
44
44
  var result = new maps_1._Map();
45
45
  for (var i = 0; i < latencyKeys.length; i++) {
46
46
  var field = latencyKeys[i].split('::')[1];
47
- var parsedField = (0, KeyBuilderSS_1.parseLatencyField)(field);
47
+ var parsedField = (0, utils_1.parseLatencyField)(field);
48
48
  if ((0, lang_1.isString)(parsedField)) {
49
49
  _this.log.error("Ignoring invalid latency field: " + field + ": " + parsedField);
50
50
  continue;
@@ -88,7 +88,7 @@ var TelemetryCachePluggable = /** @class */ (function () {
88
88
  var result = new maps_1._Map();
89
89
  for (var i = 0; i < exceptionKeys.length; i++) {
90
90
  var field = exceptionKeys[i].split('::')[1];
91
- var parsedField = (0, KeyBuilderSS_1.parseExceptionField)(field);
91
+ var parsedField = (0, utils_1.parseExceptionField)(field);
92
92
  if ((0, lang_1.isString)(parsedField)) {
93
93
  _this.log.error("Ignoring invalid exception field: " + field + ": " + parsedField);
94
94
  continue;
@@ -128,7 +128,7 @@ var TelemetryCachePluggable = /** @class */ (function () {
128
128
  var result = new maps_1._Map();
129
129
  for (var i = 0; i < configKeys.length; i++) {
130
130
  var field = configKeys[i].split('::')[1];
131
- var parsedField = (0, KeyBuilderSS_1.parseMetadata)(field);
131
+ var parsedField = (0, utils_1.parseMetadata)(field);
132
132
  if ((0, lang_1.isString)(parsedField)) {
133
133
  _this.log.error("Ignoring invalid config field: " + field + ": " + parsedField);
134
134
  continue;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UniqueKeysCachePluggable = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var UniqueKeysCacheInMemory_1 = require("../inMemory/UniqueKeysCacheInMemory");
6
+ var sets_1 = require("../../utils/lang/sets");
7
+ var constants_1 = require("../inRedis/constants");
8
+ var constants_2 = require("./constants");
9
+ var UniqueKeysCachePluggable = /** @class */ (function (_super) {
10
+ (0, tslib_1.__extends)(UniqueKeysCachePluggable, _super);
11
+ function UniqueKeysCachePluggable(log, key, wrapper, uniqueKeysQueueSize, refreshRate) {
12
+ if (uniqueKeysQueueSize === void 0) { uniqueKeysQueueSize = constants_1.DEFAULT_CACHE_SIZE; }
13
+ if (refreshRate === void 0) { refreshRate = constants_1.REFRESH_RATE; }
14
+ var _this = _super.call(this, uniqueKeysQueueSize) || this;
15
+ _this.log = log;
16
+ _this.key = key;
17
+ _this.wrapper = wrapper;
18
+ _this.refreshRate = refreshRate;
19
+ _this.onFullQueue = function () { _this.storeUniqueKeys(); };
20
+ return _this;
21
+ }
22
+ UniqueKeysCachePluggable.prototype.storeUniqueKeys = function () {
23
+ var _this = this;
24
+ var featureNames = Object.keys(this.uniqueKeysTracker);
25
+ if (!featureNames.length)
26
+ return Promise.resolve(false);
27
+ var pipeline = featureNames.reduce(function (pipeline, featureName) {
28
+ var featureKeys = (0, sets_1.setToArray)(_this.uniqueKeysTracker[featureName]);
29
+ var uniqueKeysPayload = {
30
+ f: featureName,
31
+ ks: featureKeys
32
+ };
33
+ return pipeline.then(function () { return _this.wrapper.pushItems(_this.key, [JSON.stringify(uniqueKeysPayload)]); });
34
+ }, Promise.resolve());
35
+ this.clear();
36
+ return pipeline.catch(function (err) {
37
+ _this.log.error(constants_2.LOG_PREFIX + "Error in uniqueKeys pipeline: " + err + ".");
38
+ return false;
39
+ });
40
+ };
41
+ UniqueKeysCachePluggable.prototype.start = function () {
42
+ this.intervalId = setInterval(this.storeUniqueKeys.bind(this), this.refreshRate);
43
+ };
44
+ UniqueKeysCachePluggable.prototype.stop = function () {
45
+ clearInterval(this.intervalId);
46
+ return this.storeUniqueKeys();
47
+ };
48
+ /**
49
+ * Async consumer API, used by synchronizer.
50
+ * @param count number of items to pop from the queue. If not provided or equal 0, all items will be popped.
51
+ */
52
+ UniqueKeysCachePluggable.prototype.popNRaw = function (count) {
53
+ var _this = this;
54
+ if (count === void 0) { count = 0; }
55
+ return Promise.resolve(count || this.wrapper.getItemsCount(this.key))
56
+ .then(function (count) { return _this.wrapper.popItems(_this.key, count); })
57
+ .then(function (uniqueKeyItems) { return uniqueKeyItems.map(function (uniqueKeyItem) { return JSON.parse(uniqueKeyItem); }); });
58
+ };
59
+ return UniqueKeysCachePluggable;
60
+ }(UniqueKeysCacheInMemory_1.UniqueKeysCacheInMemory));
61
+ exports.UniqueKeysCachePluggable = UniqueKeysCachePluggable;
@@ -37,29 +37,31 @@ function inMemoryWrapperFactory(connDelay) {
37
37
  getKeysByPrefix: function (prefix) {
38
38
  return Promise.resolve(Object.keys(_cache).filter(function (key) { return (0, lang_1.startsWith)(key, prefix); }));
39
39
  },
40
- incr: function (key) {
40
+ incr: function (key, increment) {
41
+ if (increment === void 0) { increment = 1; }
41
42
  if (key in _cache) {
42
- var count = (0, lang_1.toNumber)(_cache[key]) + 1;
43
+ var count = (0, lang_1.toNumber)(_cache[key]) + increment;
43
44
  if (isNaN(count))
44
45
  return Promise.reject('Given key is not a number');
45
46
  _cache[key] = count + '';
46
47
  return Promise.resolve(count);
47
48
  }
48
49
  else {
49
- _cache[key] = '1';
50
+ _cache[key] = '' + increment;
50
51
  return Promise.resolve(1);
51
52
  }
52
53
  },
53
- decr: function (key) {
54
+ decr: function (key, decrement) {
55
+ if (decrement === void 0) { decrement = 1; }
54
56
  if (key in _cache) {
55
- var count = (0, lang_1.toNumber)(_cache[key]) - 1;
57
+ var count = (0, lang_1.toNumber)(_cache[key]) - decrement;
56
58
  if (isNaN(count))
57
59
  return Promise.reject('Given key is not a number');
58
60
  _cache[key] = count + '';
59
61
  return Promise.resolve(count);
60
62
  }
61
63
  else {
62
- _cache[key] = '-1';
64
+ _cache[key] = '-' + decrement;
63
65
  return Promise.resolve(-1);
64
66
  }
65
67
  },
@@ -16,6 +16,11 @@ var EventsCacheInMemory_1 = require("../inMemory/EventsCacheInMemory");
16
16
  var ImpressionCountsCacheInMemory_1 = require("../inMemory/ImpressionCountsCacheInMemory");
17
17
  var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
18
18
  var TelemetryCachePluggable_1 = require("./TelemetryCachePluggable");
19
+ var ImpressionCountsCachePluggable_1 = require("./ImpressionCountsCachePluggable");
20
+ var UniqueKeysCachePluggable_1 = require("./UniqueKeysCachePluggable");
21
+ var UniqueKeysCacheInMemory_1 = require("../inMemory/UniqueKeysCacheInMemory");
22
+ var UniqueKeysCacheInMemoryCS_1 = require("../inMemory/UniqueKeysCacheInMemoryCS");
23
+ var utils_1 = require("../utils");
19
24
  var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
20
25
  var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
21
26
  /**
@@ -48,19 +53,39 @@ function PluggableStorage(options) {
48
53
  validatePluggableStorageOptions(options);
49
54
  var prefix = (0, KeyBuilder_1.validatePrefix)(options.prefix);
50
55
  function PluggableStorageFactory(params) {
51
- var log = params.log, metadata = params.metadata, onReadyCb = params.onReadyCb, mode = params.mode, eventsQueueSize = params.eventsQueueSize, impressionsQueueSize = params.impressionsQueueSize, optimize = params.optimize;
56
+ var onReadyCb = params.onReadyCb, settings = params.settings, _a = params.settings, log = _a.log, mode = _a.mode, impressionsMode = _a.sync.impressionsMode, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize;
57
+ var metadata = (0, utils_1.metadataBuilder)(settings);
52
58
  var keys = new KeyBuilderSS_1.KeyBuilderSS(prefix, metadata);
53
59
  var wrapper = (0, wrapperAdapter_1.wrapperAdapter)(log, options.wrapper);
60
+ var isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
54
61
  var isPartialConsumer = mode === constants_1.CONSUMER_PARTIAL_MODE;
55
- var telemetry = (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ?
56
- isPartialConsumer ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory() : new TelemetryCachePluggable_1.TelemetryCachePluggable(log, keys, wrapper) :
62
+ var telemetry = (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) || isSyncronizer ?
63
+ isPartialConsumer ?
64
+ new TelemetryCacheInMemory_1.TelemetryCacheInMemory() :
65
+ new TelemetryCachePluggable_1.TelemetryCachePluggable(log, keys, wrapper) :
66
+ undefined;
67
+ var impressionCountsCache = impressionsMode !== constants_1.DEBUG || isSyncronizer ?
68
+ isPartialConsumer ?
69
+ new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() :
70
+ new ImpressionCountsCachePluggable_1.ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
71
+ undefined;
72
+ var uniqueKeysCache = impressionsMode === constants_1.NONE || isSyncronizer ?
73
+ isPartialConsumer ?
74
+ settings.core.key === undefined ? new UniqueKeysCacheInMemory_1.UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() :
75
+ new UniqueKeysCachePluggable_1.UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
57
76
  undefined;
58
77
  // Connects to wrapper and emits SDK_READY event on main client
59
78
  var connectPromise = wrapper.connect().then(function () {
60
79
  onReadyCb();
61
- // If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
62
- if (telemetry && telemetry.recordConfig && mode)
63
- telemetry.recordConfig();
80
+ // Start periodic flush of async storages if not running synchronizer (producer mode)
81
+ if (!isSyncronizer) {
82
+ if (impressionCountsCache && impressionCountsCache.start)
83
+ impressionCountsCache.start();
84
+ if (uniqueKeysCache && uniqueKeysCache.start)
85
+ uniqueKeysCache.start();
86
+ if (telemetry && telemetry.recordConfig)
87
+ telemetry.recordConfig();
88
+ }
64
89
  }).catch(function (e) {
65
90
  e = e || new Error('Error connecting wrapper');
66
91
  onReadyCb(e);
@@ -70,12 +95,16 @@ function PluggableStorage(options) {
70
95
  splits: new SplitsCachePluggable_1.SplitsCachePluggable(log, keys, wrapper),
71
96
  segments: new SegmentsCachePluggable_1.SegmentsCachePluggable(log, keys, wrapper),
72
97
  impressions: isPartialConsumer ? new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable_1.ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
73
- impressionCounts: optimize ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
98
+ impressionCounts: impressionCountsCache,
74
99
  events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable_1.EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
75
100
  telemetry: telemetry,
76
- // Disconnect the underlying storage
101
+ uniqueKeys: uniqueKeysCache,
102
+ // Stop periodic flush and disconnect the underlying storage
77
103
  destroy: function () {
78
- return wrapper.disconnect();
104
+ return Promise.all(isSyncronizer ? [] : [
105
+ impressionCountsCache && impressionCountsCache.stop && impressionCountsCache.stop(),
106
+ uniqueKeysCache && uniqueKeysCache.stop && uniqueKeysCache.stop(),
107
+ ]).then(function () { return wrapper.disconnect(); });
79
108
  },
80
109
  // emits SDK_READY event on shared clients and returns a reference to the storage
81
110
  shared: function (_, onReadyCb) {
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ // Shared utils for Redis and Pluggable storage
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.parseLatencyField = exports.parseExceptionField = exports.parseMetadata = exports.impressionsToJSON = exports.metadataBuilder = void 0;
5
+ var constants_1 = require("../utils/constants");
6
+ var TelemetryCacheInMemory_1 = require("./inMemory/TelemetryCacheInMemory");
7
+ var KeyBuilderSS_1 = require("./KeyBuilderSS");
8
+ function metadataBuilder(settings) {
9
+ return {
10
+ s: settings.version,
11
+ i: settings.runtime.ip || constants_1.UNKNOWN,
12
+ n: settings.runtime.hostname || constants_1.UNKNOWN,
13
+ };
14
+ }
15
+ exports.metadataBuilder = metadataBuilder;
16
+ // Converts impressions to be stored in Redis or pluggable storage.
17
+ function impressionsToJSON(impressions, metadata) {
18
+ return impressions.map(function (impression) {
19
+ var impressionWithMetadata = {
20
+ m: metadata,
21
+ i: {
22
+ k: impression.keyName,
23
+ b: impression.bucketingKey,
24
+ f: impression.feature,
25
+ t: impression.treatment,
26
+ r: impression.label,
27
+ c: impression.changeNumber,
28
+ m: impression.time,
29
+ pt: impression.pt,
30
+ }
31
+ };
32
+ return JSON.stringify(impressionWithMetadata);
33
+ });
34
+ }
35
+ exports.impressionsToJSON = impressionsToJSON;
36
+ // Utilities used by TelemetryCacheInRedis and TelemetryCachePluggable
37
+ var REVERSE_METHOD_NAMES = Object.keys(KeyBuilderSS_1.METHOD_NAMES).reduce(function (acc, key) {
38
+ acc[KeyBuilderSS_1.METHOD_NAMES[key]] = key;
39
+ return acc;
40
+ }, {});
41
+ function parseMetadata(field) {
42
+ var parts = field.split('/');
43
+ if (parts.length !== 3)
44
+ return "invalid subsection count. Expected 3, got: " + parts.length;
45
+ var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */;
46
+ return [JSON.stringify({ s: s, n: n, i: i })];
47
+ }
48
+ exports.parseMetadata = parseMetadata;
49
+ function parseExceptionField(field) {
50
+ var parts = field.split('/');
51
+ if (parts.length !== 4)
52
+ return "invalid subsection count. Expected 4, got: " + parts.length;
53
+ var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3];
54
+ var method = REVERSE_METHOD_NAMES[m];
55
+ if (!method)
56
+ return "unknown method '" + m + "'";
57
+ return [JSON.stringify({ s: s, n: n, i: i }), method];
58
+ }
59
+ exports.parseExceptionField = parseExceptionField;
60
+ function parseLatencyField(field) {
61
+ var parts = field.split('/');
62
+ if (parts.length !== 5)
63
+ return "invalid subsection count. Expected 5, got: " + parts.length;
64
+ var s = parts[0] /* metadata.s */, n = parts[1] /* metadata.n */, i = parts[2] /* metadata.i */, m = parts[3], b = parts[4];
65
+ var method = REVERSE_METHOD_NAMES[m];
66
+ if (!method)
67
+ return "unknown method '" + m + "'";
68
+ var bucket = parseInt(b);
69
+ if (isNaN(bucket) || bucket >= TelemetryCacheInMemory_1.MAX_LATENCY_BUCKET_COUNT)
70
+ return "invalid bucket. Expected a number between 0 and " + (TelemetryCacheInMemory_1.MAX_LATENCY_BUCKET_COUNT - 1) + ", got: " + b;
71
+ return [JSON.stringify({ s: s, n: n, i: i }), method, bucket];
72
+ }
73
+ exports.parseLatencyField = parseLatencyField;
@@ -5,6 +5,7 @@ var eventsSubmitter_1 = require("./eventsSubmitter");
5
5
  var impressionsSubmitter_1 = require("./impressionsSubmitter");
6
6
  var impressionCountsSubmitter_1 = require("./impressionCountsSubmitter");
7
7
  var telemetrySubmitter_1 = require("./telemetrySubmitter");
8
+ var uniqueKeysSubmitter_1 = require("./uniqueKeysSubmitter");
8
9
  function submitterManagerFactory(params) {
9
10
  var submitters = [
10
11
  (0, impressionsSubmitter_1.impressionsSubmitterFactory)(params),
@@ -14,6 +15,8 @@ function submitterManagerFactory(params) {
14
15
  if (impressionCountsSubmitter)
15
16
  submitters.push(impressionCountsSubmitter);
16
17
  var telemetrySubmitter = (0, telemetrySubmitter_1.telemetrySubmitterFactory)(params);
18
+ if (params.storage.uniqueKeys)
19
+ submitters.push((0, uniqueKeysSubmitter_1.uniqueKeysSubmitterFactory)(params));
17
20
  return {
18
21
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
19
22
  start: function (onlyTelemetry) {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a, _b, _c;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.telemetrySubmitterFactory = exports.telemetryCacheConfigAdapter = exports.getTelemetryConfigStats = exports.telemetryCacheStatsAdapter = void 0;
4
+ exports.telemetrySubmitterFactory = exports.telemetryCacheConfigAdapter = exports.getTelemetryConfigStats = void 0;
5
5
  var submitter_1 = require("./submitter");
6
6
  var constants_1 = require("../../utils/constants");
7
7
  var constants_2 = require("../../readiness/constants");
@@ -9,40 +9,6 @@ var settingsValidation_1 = require("../../utils/settingsValidation");
9
9
  var apiKey_1 = require("../../utils/inputValidation/apiKey");
10
10
  var timer_1 = require("../../utils/timeTracker/timer");
11
11
  var objectAssign_1 = require("../../utils/lang/objectAssign");
12
- var utils_1 = require("../../trackers/impressionObserver/utils");
13
- /**
14
- * Converts data from telemetry cache into /metrics/usage request payload.
15
- */
16
- function telemetryCacheStatsAdapter(telemetry, splits, segments) {
17
- return {
18
- isEmpty: function () { return false; },
19
- clear: function () { },
20
- // @TODO consider moving inside telemetry cache for code size reduction
21
- pop: function () {
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(constants_1.QUEUED),
31
- iDe: telemetry.getImpressionStats(constants_1.DEDUPED),
32
- iDr: telemetry.getImpressionStats(constants_1.DROPPED),
33
- spC: splits && splits.getSplitNames().length,
34
- seC: segments && segments.getRegisteredSegments().length,
35
- skC: segments && segments.getKeysCount(),
36
- sL: telemetry.getSessionLength(),
37
- eQ: telemetry.getEventStats(constants_1.QUEUED),
38
- eD: telemetry.getEventStats(constants_1.DROPPED),
39
- sE: telemetry.popStreamingEvents(),
40
- t: telemetry.popTags(),
41
- };
42
- }
43
- };
44
- }
45
- exports.telemetryCacheStatsAdapter = telemetryCacheStatsAdapter;
46
12
  var OPERATION_MODE_MAP = (_a = {},
47
13
  _a[constants_1.STANDALONE_MODE] = constants_1.STANDALONE_ENUM,
48
14
  _a[constants_1.CONSUMER_MODE] = constants_1.CONSUMER_ENUM,
@@ -51,6 +17,7 @@ var OPERATION_MODE_MAP = (_a = {},
51
17
  var IMPRESSIONS_MODE_MAP = (_b = {},
52
18
  _b[constants_1.OPTIMIZED] = constants_1.OPTIMIZED_ENUM,
53
19
  _b[constants_1.DEBUG] = constants_1.DEBUG_ENUM,
20
+ _b[constants_1.NONE] = constants_1.NONE_ENUM,
54
21
  _b);
55
22
  var USER_CONSENT_MAP = (_c = {},
56
23
  _c[constants_1.CONSENT_UNKNOWN] = 1,
@@ -121,14 +88,12 @@ exports.telemetryCacheConfigAdapter = telemetryCacheConfigAdapter;
121
88
  * Submitter that periodically posts telemetry data
122
89
  */
123
90
  function telemetrySubmitterFactory(params) {
124
- var _a = params.storage, splits = _a.splits, segments = _a.segments, telemetry = _a.telemetry, now = params.platform.now;
91
+ var telemetry = params.storage.telemetry, now = params.platform.now;
125
92
  if (!telemetry || !now)
126
93
  return; // No submitter created if telemetry cache is not defined
127
- var settings = params.settings, _b = params.settings, log = _b.log, telemetryRefreshRate = _b.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
94
+ var settings = params.settings, _a = params.settings, log = _a.log, telemetryRefreshRate = _a.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
128
95
  var startTime = (0, timer_1.timer)(now);
129
- var submitter = (0, submitter_1.firstPushWindowDecorator)((0, submitter_1.submitterFactory)(log, splitApi.postMetricsUsage,
130
- // @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
131
- (0, utils_1.isStorageSync)(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
96
+ var submitter = (0, submitter_1.firstPushWindowDecorator)((0, submitter_1.submitterFactory)(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
132
97
  readiness.gate.once(constants_2.SDK_READY_FROM_CACHE, function () {
133
98
  telemetry.recordTimeUntilReadyFromCache(startTime());
134
99
  });
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uniqueKeysSubmitterFactory = void 0;
4
+ var constants_1 = require("../../logger/constants");
5
+ var submitter_1 = require("./submitter");
6
+ var DATA_NAME = 'unique keys';
7
+ var UNIQUE_KEYS_RATE = 900000; // 15 minutes
8
+ /**
9
+ * Submitter that periodically posts impression counts
10
+ */
11
+ function uniqueKeysSubmitterFactory(params) {
12
+ var _a = params.settings, log = _a.log, key = _a.core.key, _b = params.splitApi, postUniqueKeysBulkCs = _b.postUniqueKeysBulkCs, postUniqueKeysBulkSs = _b.postUniqueKeysBulkSs, uniqueKeys = params.storage.uniqueKeys;
13
+ var isClientSide = key !== undefined;
14
+ var postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
15
+ var syncTask = (0, submitter_1.submitterFactory)(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE, DATA_NAME);
16
+ // register unique keys submitter to be executed when uniqueKeys cache is full
17
+ uniqueKeys.setOnFullQueueCb(function () {
18
+ if (syncTask.isRunning()) {
19
+ log.info(constants_1.SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
20
+ syncTask.execute();
21
+ }
22
+ // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
23
+ // Data will be sent when submitter is resumed.
24
+ });
25
+ return syncTask;
26
+ }
27
+ exports.uniqueKeysSubmitterFactory = uniqueKeysSubmitterFactory;
@@ -1,23 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isStorageSync = exports.shouldBeOptimized = exports.shouldAddPt = void 0;
3
+ exports.isStorageSync = void 0;
4
4
  var constants_1 = require("../../utils/constants");
5
- /**
6
- * Checks if impressions previous time should be added or not.
7
- */
8
- function shouldAddPt(settings) {
9
- return [constants_1.PRODUCER_MODE, constants_1.STANDALONE_MODE, constants_1.CONSUMER_PARTIAL_MODE].indexOf(settings.mode) > -1 ? true : false;
10
- }
11
- exports.shouldAddPt = shouldAddPt;
12
- /**
13
- * Checks if it should dedupe impressions or not.
14
- */
15
- function shouldBeOptimized(settings) {
16
- if (!shouldAddPt(settings))
17
- return false;
18
- return settings.sync.impressionsMode === constants_1.OPTIMIZED ? true : false;
19
- }
20
- exports.shouldBeOptimized = shouldBeOptimized;
21
5
  /**
22
6
  * Storage is async if mode is consumer or partial consumer
23
7
  */