@splitsoftware/splitio-commons 1.6.2-rc.1 → 1.6.2-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/cjs/sdkFactory/index.js +4 -3
  2. package/cjs/services/splitApi.js +2 -2
  3. package/cjs/storages/inLocalStorage/index.js +4 -0
  4. package/cjs/storages/inMemory/InMemoryStorage.js +5 -1
  5. package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -1
  6. package/cjs/storages/inMemory/uniqueKeysCacheInMemory.js +73 -0
  7. package/cjs/storages/inMemory/uniqueKeysCacheInMemoryCS.js +78 -0
  8. package/cjs/sync/submitters/telemetrySubmitter.js +1 -0
  9. package/cjs/sync/submitters/uniqueKeysSubmitter.js +14 -58
  10. package/cjs/trackers/strategy/strategyNone.js +1 -1
  11. package/cjs/trackers/uniqueKeysTracker.js +5 -43
  12. package/cjs/utils/constants/index.js +3 -2
  13. package/esm/sdkFactory/index.js +4 -3
  14. package/esm/services/splitApi.js +2 -2
  15. package/esm/storages/inLocalStorage/index.js +5 -1
  16. package/esm/storages/inMemory/InMemoryStorage.js +6 -2
  17. package/esm/storages/inMemory/InMemoryStorageCS.js +6 -2
  18. package/esm/storages/inMemory/uniqueKeysCacheInMemory.js +70 -0
  19. package/esm/storages/inMemory/uniqueKeysCacheInMemoryCS.js +75 -0
  20. package/esm/sync/submitters/telemetrySubmitter.js +2 -1
  21. package/esm/sync/submitters/uniqueKeysSubmitter.js +13 -55
  22. package/esm/trackers/strategy/strategyNone.js +1 -1
  23. package/esm/trackers/uniqueKeysTracker.js +5 -43
  24. package/esm/utils/constants/index.js +1 -0
  25. package/package.json +1 -1
  26. package/src/sdkFactory/index.ts +4 -3
  27. package/src/services/splitApi.ts +2 -2
  28. package/src/storages/inLocalStorage/index.ts +4 -1
  29. package/src/storages/inMemory/InMemoryStorage.ts +5 -2
  30. package/src/storages/inMemory/InMemoryStorageCS.ts +6 -2
  31. package/src/storages/inMemory/uniqueKeysCacheInMemory.ts +83 -0
  32. package/src/storages/inMemory/uniqueKeysCacheInMemoryCS.ts +89 -0
  33. package/src/storages/types.ts +9 -6
  34. package/src/sync/submitters/telemetrySubmitter.ts +4 -3
  35. package/src/sync/submitters/uniqueKeysSubmitter.ts +15 -59
  36. package/src/trackers/impressionsTracker.ts +0 -1
  37. package/src/trackers/strategy/strategyNone.ts +1 -1
  38. package/src/trackers/types.ts +3 -7
  39. package/src/trackers/uniqueKeysTracker.ts +6 -49
  40. package/src/types.ts +1 -0
  41. package/src/utils/constants/index.ts +1 -0
  42. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +32 -0
  43. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +37 -0
  44. package/types/storages/types.d.ts +8 -11
  45. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -14
  46. package/types/trackers/types.d.ts +3 -11
  47. package/types/trackers/uniqueKeysTracker.d.ts +3 -3
  48. package/types/types.d.ts +1 -0
  49. package/types/utils/constants/index.d.ts +1 -0
@@ -24,7 +24,6 @@ var constants_3 = require("../utils/constants");
24
24
  function sdkFactory(params) {
25
25
  var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory, splitApiFactory = params.splitApiFactory, extraProps = params.extraProps, syncManagerFactory = params.syncManagerFactory, SignalListener = params.SignalListener, impressionsObserverFactory = params.impressionsObserverFactory, integrationsManagerFactory = params.integrationsManagerFactory, sdkManagerFactory = params.sdkManagerFactory, sdkClientMethodFactory = params.sdkClientMethodFactory, filterAdapterFactory = params.filterAdapterFactory;
26
26
  var log = settings.log;
27
- var impressionsMode = settings.sync.impressionsMode;
28
27
  // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid API Key, etc.
29
28
  // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
30
29
  // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
@@ -35,12 +34,14 @@ function sdkFactory(params) {
35
34
  var storageFactoryParams = {
36
35
  impressionsQueueSize: settings.scheduler.impressionsQueueSize,
37
36
  eventsQueueSize: settings.scheduler.eventsQueueSize,
37
+ uniqueKeysCacheSize: settings.scheduler.uniqueKeysCacheSize,
38
38
  optimize: (0, utils_1.shouldBeOptimized)(settings),
39
39
  // ATM, only used by InLocalStorage
40
40
  matchingKey: (0, key_1.getMatching)(settings.core.key),
41
41
  splitFiltersValidation: settings.sync.__splitFiltersValidation,
42
42
  // ATM, only used by PluggableStorage
43
43
  mode: settings.mode,
44
+ impressionsMode: settings.sync.impressionsMode,
44
45
  // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined,
45
46
  // or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events.
46
47
  onReadyCb: function (error) {
@@ -56,9 +57,9 @@ function sdkFactory(params) {
56
57
  // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
57
58
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage });
58
59
  var observer = impressionsObserverFactory();
59
- var uniqueKeysTracker = impressionsMode === constants_3.NONE ? (0, uniqueKeysTracker_1.uniqueKeysTrackerFactory)(log, filterAdapterFactory && filterAdapterFactory()) : undefined;
60
+ var uniqueKeysTracker = storageFactoryParams.impressionsMode === constants_3.NONE ? (0, uniqueKeysTracker_1.uniqueKeysTrackerFactory)(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory()) : undefined;
60
61
  var strategy = (storageFactoryParams.optimize) ? (0, strategyOptimized_1.strategyOptimizedFactory)(observer, storage.impressionCounts) :
61
- (impressionsMode === constants_3.NONE) ? (0, strategyNone_1.strategyNoneFactory)(storage.impressionCounts, uniqueKeysTracker) : (0, strategyDebug_1.strategyDebugFactory)(observer);
62
+ (storageFactoryParams.impressionsMode === constants_3.NONE) ? (0, strategyNone_1.strategyNoneFactory)(storage.impressionCounts, uniqueKeysTracker) : (0, strategyDebug_1.strategyDebugFactory)(observer);
62
63
  var impressionsTracker = (0, impressionsTracker_1.impressionsTrackerFactory)(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
63
64
  var eventTracker = (0, eventTracker_1.eventTrackerFactory)(settings, storage.events, integrationsManager, storage.telemetry);
64
65
  var telemetryTracker = (0, telemetryTracker_1.telemetryTrackerFactory)(storage.telemetry, platform.now);
@@ -97,7 +97,7 @@ function splitApiFactory(settings, platform, telemetryTracker) {
97
97
  * @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
98
98
  */
99
99
  postUniqueKeysBulkCs: function (body, headers) {
100
- var url = urls.telemetry + "/api/v1/keys/cs";
100
+ var url = urls.telemetry + "/v1/keys/cs";
101
101
  return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.TELEMETRY));
102
102
  },
103
103
  /**
@@ -107,7 +107,7 @@ function splitApiFactory(settings, platform, telemetryTracker) {
107
107
  * @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
108
108
  */
109
109
  postUniqueKeysBulkSs: function (body, headers) {
110
- var url = urls.telemetry + "/api/v1/keys/ss";
110
+ var url = urls.telemetry + "/v1/keys/ss";
111
111
  return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.TELEMETRY));
112
112
  },
113
113
  postMetricsConfig: function (body) {
@@ -16,6 +16,7 @@ var InMemoryStorageCS_1 = require("../inMemory/InMemoryStorageCS");
16
16
  var constants_1 = require("./constants");
17
17
  var constants_2 = require("../../utils/constants");
18
18
  var TelemetryCacheInMemory_1 = require("../inMemory/TelemetryCacheInMemory");
19
+ var uniqueKeysCacheInMemoryCS_1 = require("../inMemory/uniqueKeysCacheInMemoryCS");
19
20
  /**
20
21
  * InLocal storage factory for standalone client-side SplitFactory
21
22
  */
@@ -38,12 +39,15 @@ function InLocalStorage(options) {
38
39
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
39
40
  events: new EventsCacheInMemory_1.EventsCacheInMemory(params.eventsQueueSize),
40
41
  telemetry: params.mode !== constants_2.LOCALHOST_MODE && (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)() ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory() : undefined,
42
+ uniqueKeys: params.impressionsMode === constants_2.NONE ? new uniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() : undefined,
41
43
  destroy: function () {
44
+ var _a;
42
45
  this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory();
43
46
  this.segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
44
47
  this.impressions.clear();
45
48
  this.impressionCounts && this.impressionCounts.clear();
46
49
  this.events.clear();
50
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
47
51
  },
48
52
  // When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).
49
53
  shared: function (matchingKey) {
@@ -8,6 +8,7 @@ var EventsCacheInMemory_1 = require("./EventsCacheInMemory");
8
8
  var ImpressionCountsCacheInMemory_1 = require("./ImpressionCountsCacheInMemory");
9
9
  var constants_1 = require("../../utils/constants");
10
10
  var TelemetryCacheInMemory_1 = require("./TelemetryCacheInMemory");
11
+ var uniqueKeysCacheInMemory_1 = require("./uniqueKeysCacheInMemory");
11
12
  /**
12
13
  * InMemory storage factory for standalone server-side SplitFactory
13
14
  *
@@ -18,16 +19,19 @@ function InMemoryStorageFactory(params) {
18
19
  splits: new SplitsCacheInMemory_1.SplitsCacheInMemory(),
19
20
  segments: new SegmentsCacheInMemory_1.SegmentsCacheInMemory(),
20
21
  impressions: new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(params.impressionsQueueSize),
21
- impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
22
+ impressionCounts: params.impressionsMode !== constants_1.DEBUG ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
22
23
  events: new EventsCacheInMemory_1.EventsCacheInMemory(params.eventsQueueSize),
23
24
  telemetry: params.mode !== constants_1.LOCALHOST_MODE ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory() : undefined,
25
+ uniqueKeys: params.impressionsMode === constants_1.NONE ? new uniqueKeysCacheInMemory_1.UniqueKeysCacheInMemory(params.uniqueKeysCacheSize) : undefined,
24
26
  // When using MEMORY we should clean all the caches to leave them empty
25
27
  destroy: function () {
28
+ var _a;
26
29
  this.splits.clear();
27
30
  this.segments.clear();
28
31
  this.impressions.clear();
29
32
  this.impressionCounts && this.impressionCounts.clear();
30
33
  this.events.clear();
34
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
31
35
  }
32
36
  };
33
37
  }
@@ -8,6 +8,7 @@ var EventsCacheInMemory_1 = require("./EventsCacheInMemory");
8
8
  var ImpressionCountsCacheInMemory_1 = require("./ImpressionCountsCacheInMemory");
9
9
  var constants_1 = require("../../utils/constants");
10
10
  var TelemetryCacheInMemory_1 = require("./TelemetryCacheInMemory");
11
+ var uniqueKeysCacheInMemoryCS_1 = require("./uniqueKeysCacheInMemoryCS");
11
12
  /**
12
13
  * InMemory storage factory for standalone client-side SplitFactory
13
14
  *
@@ -18,16 +19,19 @@ function InMemoryStorageCSFactory(params) {
18
19
  splits: new SplitsCacheInMemory_1.SplitsCacheInMemory(),
19
20
  segments: new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory(),
20
21
  impressions: new ImpressionsCacheInMemory_1.ImpressionsCacheInMemory(params.impressionsQueueSize),
21
- impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
22
+ impressionCounts: params.impressionsMode !== constants_1.DEBUG ? new ImpressionCountsCacheInMemory_1.ImpressionCountsCacheInMemory() : undefined,
22
23
  events: new EventsCacheInMemory_1.EventsCacheInMemory(params.eventsQueueSize),
23
24
  telemetry: params.mode !== constants_1.LOCALHOST_MODE && (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)() ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory() : undefined,
25
+ uniqueKeys: params.impressionsMode === constants_1.NONE ? new uniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS(params.uniqueKeysCacheSize) : undefined,
24
26
  // When using MEMORY we should clean all the caches to leave them empty
25
27
  destroy: function () {
28
+ var _a;
26
29
  this.splits.clear();
27
30
  this.segments.clear();
28
31
  this.impressions.clear();
29
32
  this.impressionCounts && this.impressionCounts.clear();
30
33
  this.events.clear();
34
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
31
35
  },
32
36
  // When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
33
37
  shared: function () {
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UniqueKeysCacheInMemory = void 0;
4
+ var sets_1 = require("../../utils/lang/sets");
5
+ var DEFAULT_CACHE_SIZE = 30000;
6
+ var UniqueKeysCacheInMemory = /** @class */ (function () {
7
+ function UniqueKeysCacheInMemory(uniqueKeysQueueSize) {
8
+ if (uniqueKeysQueueSize === void 0) { uniqueKeysQueueSize = DEFAULT_CACHE_SIZE; }
9
+ this.uniqueTrackerSize = 0;
10
+ this.maxStorage = uniqueKeysQueueSize;
11
+ this.uniqueKeysTracker = {};
12
+ }
13
+ UniqueKeysCacheInMemory.prototype.setOnFullQueueCb = function (cb) {
14
+ this.onFullQueue = cb;
15
+ };
16
+ /**
17
+ * Store unique keys in sequential order
18
+ * key: string = feature name.
19
+ * value: Set<string> = set of unique keys.
20
+ */
21
+ UniqueKeysCacheInMemory.prototype.track = function (key, featureName) {
22
+ if (!this.uniqueKeysTracker[featureName])
23
+ this.uniqueKeysTracker[featureName] = new sets_1._Set();
24
+ var tracker = this.uniqueKeysTracker[featureName];
25
+ if (!tracker.has(key)) {
26
+ tracker.add(key);
27
+ this.uniqueTrackerSize++;
28
+ }
29
+ if (this.uniqueTrackerSize >= this.maxStorage && this.onFullQueue) {
30
+ this.uniqueTrackerSize = 0;
31
+ this.onFullQueue();
32
+ }
33
+ };
34
+ /**
35
+ * Clear the data stored on the cache.
36
+ */
37
+ UniqueKeysCacheInMemory.prototype.clear = function () {
38
+ this.uniqueKeysTracker = {};
39
+ };
40
+ /**
41
+ * Pop the collected data, used as payload for posting.
42
+ */
43
+ UniqueKeysCacheInMemory.prototype.pop = function () {
44
+ var data = this.uniqueKeysTracker;
45
+ this.uniqueKeysTracker = {};
46
+ return this.fromUniqueKeysCollector(data);
47
+ };
48
+ /**
49
+ * Check if the cache is empty.
50
+ */
51
+ UniqueKeysCacheInMemory.prototype.isEmpty = function () {
52
+ return Object.keys(this.uniqueKeysTracker).length === 0;
53
+ };
54
+ /**
55
+ * Converts `uniqueKeys` data from cache into request payload for SS.
56
+ */
57
+ UniqueKeysCacheInMemory.prototype.fromUniqueKeysCollector = function (uniqueKeys) {
58
+ var payload = [];
59
+ var featureNames = Object.keys(uniqueKeys);
60
+ for (var i = 0; i < featureNames.length; i++) {
61
+ var featureName = featureNames[i];
62
+ var featureKeys = (0, sets_1.setToArray)(uniqueKeys[featureName]);
63
+ var uniqueKeysPayload = {
64
+ f: featureName,
65
+ ks: featureKeys
66
+ };
67
+ payload.push(uniqueKeysPayload);
68
+ }
69
+ return { keys: payload };
70
+ };
71
+ return UniqueKeysCacheInMemory;
72
+ }());
73
+ exports.UniqueKeysCacheInMemory = UniqueKeysCacheInMemory;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UniqueKeysCacheInMemoryCS = void 0;
4
+ var sets_1 = require("../../utils/lang/sets");
5
+ var DEFAULT_CACHE_SIZE = 30000;
6
+ var UniqueKeysCacheInMemoryCS = /** @class */ (function () {
7
+ /**
8
+ *
9
+ * @param impressionsQueueSize number of queued impressions to call onFullQueueCb.
10
+ * Default value is 0, that means no maximum value, in case we want to avoid this being triggered.
11
+ */
12
+ function UniqueKeysCacheInMemoryCS(uniqueKeysQueueSize) {
13
+ if (uniqueKeysQueueSize === void 0) { uniqueKeysQueueSize = DEFAULT_CACHE_SIZE; }
14
+ this.uniqueTrackerSize = 0;
15
+ this.maxStorage = uniqueKeysQueueSize;
16
+ this.uniqueKeysTracker = {};
17
+ }
18
+ UniqueKeysCacheInMemoryCS.prototype.setOnFullQueueCb = function (cb) {
19
+ this.onFullQueue = cb;
20
+ };
21
+ /**
22
+ * Store unique keys in sequential order
23
+ * key: string = key.
24
+ * value: HashSet<string> = set of split names.
25
+ */
26
+ UniqueKeysCacheInMemoryCS.prototype.track = function (key, featureName) {
27
+ if (!this.uniqueKeysTracker[key])
28
+ this.uniqueKeysTracker[key] = new sets_1._Set();
29
+ var tracker = this.uniqueKeysTracker[key];
30
+ if (!tracker.has(featureName)) {
31
+ tracker.add(featureName);
32
+ this.uniqueTrackerSize++;
33
+ }
34
+ if (this.uniqueTrackerSize >= this.maxStorage && this.onFullQueue) {
35
+ this.uniqueTrackerSize = 0;
36
+ this.onFullQueue();
37
+ }
38
+ };
39
+ /**
40
+ * Clear the data stored on the cache.
41
+ */
42
+ UniqueKeysCacheInMemoryCS.prototype.clear = function () {
43
+ this.uniqueKeysTracker = {};
44
+ };
45
+ /**
46
+ * Pop the collected data, used as payload for posting.
47
+ */
48
+ UniqueKeysCacheInMemoryCS.prototype.pop = function () {
49
+ var data = this.uniqueKeysTracker;
50
+ this.uniqueKeysTracker = {};
51
+ return this.fromUniqueKeysCollector(data);
52
+ };
53
+ /**
54
+ * Check if the cache is empty.
55
+ */
56
+ UniqueKeysCacheInMemoryCS.prototype.isEmpty = function () {
57
+ return Object.keys(this.uniqueKeysTracker).length === 0;
58
+ };
59
+ /**
60
+ * Converts `uniqueKeys` data from cache into request payload.
61
+ */
62
+ UniqueKeysCacheInMemoryCS.prototype.fromUniqueKeysCollector = function (uniqueKeys) {
63
+ var payload = [];
64
+ var featureKeys = Object.keys(uniqueKeys);
65
+ for (var k = 0; k < featureKeys.length; k++) {
66
+ var featureKey = featureKeys[k];
67
+ var featureNames = (0, sets_1.setToArray)(uniqueKeys[featureKey]);
68
+ var uniqueKeysPayload = {
69
+ k: featureKey,
70
+ fs: featureNames
71
+ };
72
+ payload.push(uniqueKeysPayload);
73
+ }
74
+ return { keys: payload };
75
+ };
76
+ return UniqueKeysCacheInMemoryCS;
77
+ }());
78
+ exports.UniqueKeysCacheInMemoryCS = UniqueKeysCacheInMemoryCS;
@@ -50,6 +50,7 @@ var OPERATION_MODE_MAP = (_a = {},
50
50
  var IMPRESSIONS_MODE_MAP = (_b = {},
51
51
  _b[constants_1.OPTIMIZED] = constants_1.OPTIMIZED_ENUM,
52
52
  _b[constants_1.DEBUG] = constants_1.DEBUG_ENUM,
53
+ _b[constants_1.NONE] = constants_1.NONE_ENUM,
53
54
  _b);
54
55
  var USER_CONSENT_MAP = (_c = {},
55
56
  _c[constants_1.CONSENT_UNKNOWN] = 1,
@@ -1,62 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uniqueKeysSubmitterFactory = exports.fromUniqueKeysCollectorSs = exports.fromUniqueKeysCollectorCs = void 0;
4
- var sets_1 = require("../../utils/lang/sets");
3
+ exports.uniqueKeysSubmitterFactory = void 0;
4
+ var constants_1 = require("../../logger/constants");
5
5
  var submitter_1 = require("./submitter");
6
- /**
7
- * Invert keys for feature to features for key
8
- */
9
- function invertUniqueKeys(uniqueKeys) {
10
- var featureNames = Object.keys(uniqueKeys);
11
- var inverted = {};
12
- for (var i = 0; i < featureNames.length; i++) {
13
- var featureName = featureNames[i];
14
- var featureKeys = (0, sets_1.setToArray)(uniqueKeys[featureName]);
15
- for (var j = 0; j < featureKeys.length; j++) {
16
- var featureKey = featureKeys[j];
17
- if (!inverted[featureKey])
18
- inverted[featureKey] = [];
19
- inverted[featureKey].push(featureName);
20
- }
21
- }
22
- return inverted;
23
- }
24
- /**
25
- * Converts `uniqueKeys` data from cache into request payload for CS.
26
- */
27
- function fromUniqueKeysCollectorCs(uniqueKeys) {
28
- var payload = [];
29
- var featuresPerKey = invertUniqueKeys(uniqueKeys);
30
- var keys = Object.keys(featuresPerKey);
31
- for (var k = 0; k < keys.length; k++) {
32
- var key = keys[k];
33
- var uniqueKeysPayload = {
34
- k: key,
35
- fs: featuresPerKey[key]
36
- };
37
- payload.push(uniqueKeysPayload);
38
- }
39
- return { keys: payload };
40
- }
41
- exports.fromUniqueKeysCollectorCs = fromUniqueKeysCollectorCs;
42
- /**
43
- * Converts `uniqueKeys` data from cache into request payload for SS.
44
- */
45
- function fromUniqueKeysCollectorSs(uniqueKeys) {
46
- var payload = [];
47
- var featureNames = Object.keys(uniqueKeys);
48
- for (var i = 0; i < featureNames.length; i++) {
49
- var featureName = featureNames[i];
50
- var featureKeys = (0, sets_1.setToArray)(uniqueKeys[featureName]);
51
- var uniqueKeysPayload = {
52
- f: featureName,
53
- ks: featureKeys
54
- };
55
- payload.push(uniqueKeysPayload);
56
- }
57
- return { keys: payload };
58
- }
59
- exports.fromUniqueKeysCollectorSs = fromUniqueKeysCollectorSs;
6
+ var DATA_NAME = 'uniqueKeys';
60
7
  /**
61
8
  * Submitter that periodically posts impression counts
62
9
  */
@@ -64,7 +11,16 @@ function uniqueKeysSubmitterFactory(params) {
64
11
  var _a = params.settings, log = _a.log, uniqueKeysRefreshRate = _a.scheduler.uniqueKeysRefreshRate, key = _a.core.key, _b = params.splitApi, postUniqueKeysBulkCs = _b.postUniqueKeysBulkCs, postUniqueKeysBulkSs = _b.postUniqueKeysBulkSs, uniqueKeys = params.storage.uniqueKeys;
65
12
  var isClientSide = key !== undefined;
66
13
  var postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
67
- var fromUniqueKeysCollector = isClientSide ? fromUniqueKeysCollectorCs : fromUniqueKeysCollectorSs;
68
- return (0, submitter_1.submitterFactory)(log, postUniqueKeysBulk, uniqueKeys, uniqueKeysRefreshRate, 'unique keys', fromUniqueKeysCollector);
14
+ var syncTask = (0, submitter_1.submitterFactory)(log, postUniqueKeysBulk, uniqueKeys, uniqueKeysRefreshRate, 'unique keys');
15
+ // register unique keys submitter to be executed when uniqueKeys cache is full
16
+ uniqueKeys.setOnFullQueueCb(function () {
17
+ if (syncTask.isRunning()) {
18
+ log.info(constants_1.SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
19
+ syncTask.execute();
20
+ }
21
+ // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
22
+ // Data will be sent when submitter is resumed.
23
+ });
24
+ return syncTask;
69
25
  }
70
26
  exports.uniqueKeysSubmitterFactory = uniqueKeysSubmitterFactory;
@@ -16,7 +16,7 @@ function strategyNoneFactory(impressionsCounter, uniqueKeysTracker) {
16
16
  // Increments impression counter per featureName
17
17
  impressionsCounter.track(impression.feature, now, 1);
18
18
  // Keep track by unique key
19
- uniqueKeysTracker.track(impression.feature, impression.keyName);
19
+ uniqueKeysTracker.track(impression.keyName, impression.feature);
20
20
  });
21
21
  return {
22
22
  impressionsToStore: [],
@@ -2,13 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uniqueKeysTrackerFactory = void 0;
4
4
  var constants_1 = require("../logger/constants");
5
- var sets_1 = require("../utils/lang/sets");
6
5
  var noopFilterAdapter = {
7
6
  add: function () { return true; },
8
7
  contains: function () { return true; },
9
8
  clear: function () { }
10
9
  };
11
- var DEFAULT_CACHE_SIZE = 30000;
12
10
  /**
13
11
  * Trackes uniques keys
14
12
  * Unique Keys Tracker will be in charge of checking if the MTK was already sent to the BE in the last period
@@ -16,53 +14,17 @@ var DEFAULT_CACHE_SIZE = 30000;
16
14
  *
17
15
  * @param log Logger instance
18
16
  * @param filterAdapter filter adapter
19
- * @param cacheSize optional internal cache size
20
- * @param maxBulkSize optional max MTKs bulk size
17
+ * @param uniqueKeysCache cache to save unique keys
21
18
  */
22
- function uniqueKeysTrackerFactory(log, filterAdapter, cacheSize) {
19
+ function uniqueKeysTrackerFactory(log, uniqueKeysCache, filterAdapter) {
23
20
  if (filterAdapter === void 0) { filterAdapter = noopFilterAdapter; }
24
- if (cacheSize === void 0) { cacheSize = DEFAULT_CACHE_SIZE; }
25
- var uniqueKeysTracker = {};
26
- var uniqueTrackerSize = 0;
27
21
  return {
28
- track: function (featureName, key) {
29
- if (!filterAdapter.add(featureName, key)) {
22
+ track: function (key, featureName) {
23
+ if (!filterAdapter.add(key, featureName)) {
30
24
  log.debug(constants_1.LOG_PREFIX_UNIQUE_KEYS_TRACKER + "The feature " + featureName + " and key " + key + " exist in the filter");
31
25
  return;
32
26
  }
33
- if (!uniqueKeysTracker[featureName])
34
- uniqueKeysTracker[featureName] = new sets_1._Set();
35
- var tracker = uniqueKeysTracker[featureName];
36
- if (!tracker.has(key)) {
37
- tracker.add(key);
38
- log.debug(constants_1.LOG_PREFIX_UNIQUE_KEYS_TRACKER + "Key " + key + " added to feature " + featureName);
39
- uniqueTrackerSize++;
40
- }
41
- if (uniqueTrackerSize >= cacheSize) {
42
- log.warn(constants_1.LOG_PREFIX_UNIQUE_KEYS_TRACKER + "The UniqueKeysTracker size reached the maximum limit");
43
- // @TODO trigger event to submitter to send mtk
44
- uniqueTrackerSize = 0;
45
- }
46
- },
47
- /**
48
- * Pop the collected data, used as payload for posting.
49
- */
50
- pop: function () {
51
- var data = uniqueKeysTracker;
52
- uniqueKeysTracker = {};
53
- return data;
54
- },
55
- /**
56
- * Clear the data stored on the cache.
57
- */
58
- clear: function () {
59
- uniqueKeysTracker = {};
60
- },
61
- /**
62
- * Check if the cache is empty.
63
- */
64
- isEmpty: function () {
65
- return Object.keys(uniqueKeysTracker).length === 0;
27
+ uniqueKeysCache.track(key, featureName);
66
28
  }
67
29
  };
68
30
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SYNC_MODE_UPDATE = exports.ABLY_ERROR = exports.TOKEN_REFRESH = exports.SSE_CONNECTION_ERROR = exports.STREAMING_STATUS = exports.OCCUPANCY_SEC = exports.OCCUPANCY_PRI = exports.CONNECTION_ESTABLISHED = exports.TRACK = exports.TREATMENTS_WITH_CONFIG = exports.TREATMENT_WITH_CONFIG = exports.TREATMENTS = exports.TREATMENT = exports.MY_SEGMENT = exports.SEGMENT = exports.TOKEN = exports.TELEMETRY = exports.EVENTS = exports.IMPRESSIONS_COUNT = exports.IMPRESSIONS = exports.SPLITS = exports.DEBUG_ENUM = exports.OPTIMIZED_ENUM = exports.CONSUMER_PARTIAL_ENUM = exports.CONSUMER_ENUM = exports.STANDALONE_ENUM = exports.DEDUPED = exports.DROPPED = exports.QUEUED = exports.CONSENT_UNKNOWN = exports.CONSENT_DECLINED = exports.CONSENT_GRANTED = exports.STORAGE_PLUGGABLE = exports.STORAGE_REDIS = exports.STORAGE_LOCALSTORAGE = exports.STORAGE_MEMORY = exports.CONSUMER_PARTIAL_MODE = exports.CONSUMER_MODE = exports.PRODUCER_MODE = exports.STANDALONE_MODE = exports.LOCALHOST_MODE = exports.NONE = exports.OPTIMIZED = exports.DEBUG = exports.SPLIT_EVENT = exports.SPLIT_IMPRESSION = exports.NA = exports.UNKNOWN = exports.CONTROL_WITH_CONFIG = exports.CONTROL = void 0;
4
- exports.PAUSED = exports.ENABLED = exports.DISABLED = exports.NON_REQUESTED = exports.REQUESTED = exports.POLLING = exports.STREAMING = exports.AUTH_REJECTION = void 0;
3
+ exports.ABLY_ERROR = exports.TOKEN_REFRESH = exports.SSE_CONNECTION_ERROR = exports.STREAMING_STATUS = exports.OCCUPANCY_SEC = exports.OCCUPANCY_PRI = exports.CONNECTION_ESTABLISHED = exports.TRACK = exports.TREATMENTS_WITH_CONFIG = exports.TREATMENT_WITH_CONFIG = exports.TREATMENTS = exports.TREATMENT = exports.MY_SEGMENT = exports.SEGMENT = exports.TOKEN = exports.TELEMETRY = exports.EVENTS = exports.IMPRESSIONS_COUNT = exports.IMPRESSIONS = exports.SPLITS = exports.NONE_ENUM = exports.DEBUG_ENUM = exports.OPTIMIZED_ENUM = exports.CONSUMER_PARTIAL_ENUM = exports.CONSUMER_ENUM = exports.STANDALONE_ENUM = exports.DEDUPED = exports.DROPPED = exports.QUEUED = exports.CONSENT_UNKNOWN = exports.CONSENT_DECLINED = exports.CONSENT_GRANTED = exports.STORAGE_PLUGGABLE = exports.STORAGE_REDIS = exports.STORAGE_LOCALSTORAGE = exports.STORAGE_MEMORY = exports.CONSUMER_PARTIAL_MODE = exports.CONSUMER_MODE = exports.PRODUCER_MODE = exports.STANDALONE_MODE = exports.LOCALHOST_MODE = exports.NONE = exports.OPTIMIZED = exports.DEBUG = exports.SPLIT_EVENT = exports.SPLIT_IMPRESSION = exports.NA = exports.UNKNOWN = exports.CONTROL_WITH_CONFIG = exports.CONTROL = void 0;
4
+ exports.PAUSED = exports.ENABLED = exports.DISABLED = exports.NON_REQUESTED = exports.REQUESTED = exports.POLLING = exports.STREAMING = exports.AUTH_REJECTION = exports.SYNC_MODE_UPDATE = void 0;
5
5
  // Special treatments
6
6
  exports.CONTROL = 'control';
7
7
  exports.CONTROL_WITH_CONFIG = {
@@ -42,6 +42,7 @@ exports.CONSUMER_ENUM = 1;
42
42
  exports.CONSUMER_PARTIAL_ENUM = 2;
43
43
  exports.OPTIMIZED_ENUM = 0;
44
44
  exports.DEBUG_ENUM = 1;
45
+ exports.NONE_ENUM = 2;
45
46
  exports.SPLITS = 'sp';
46
47
  exports.IMPRESSIONS = 'im';
47
48
  exports.IMPRESSIONS_COUNT = 'ic';
@@ -21,7 +21,6 @@ import { NONE } from '../utils/constants';
21
21
  export function sdkFactory(params) {
22
22
  var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory, splitApiFactory = params.splitApiFactory, extraProps = params.extraProps, syncManagerFactory = params.syncManagerFactory, SignalListener = params.SignalListener, impressionsObserverFactory = params.impressionsObserverFactory, integrationsManagerFactory = params.integrationsManagerFactory, sdkManagerFactory = params.sdkManagerFactory, sdkClientMethodFactory = params.sdkClientMethodFactory, filterAdapterFactory = params.filterAdapterFactory;
23
23
  var log = settings.log;
24
- var impressionsMode = settings.sync.impressionsMode;
25
24
  // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid API Key, etc.
26
25
  // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
27
26
  // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
@@ -32,12 +31,14 @@ export function sdkFactory(params) {
32
31
  var storageFactoryParams = {
33
32
  impressionsQueueSize: settings.scheduler.impressionsQueueSize,
34
33
  eventsQueueSize: settings.scheduler.eventsQueueSize,
34
+ uniqueKeysCacheSize: settings.scheduler.uniqueKeysCacheSize,
35
35
  optimize: shouldBeOptimized(settings),
36
36
  // ATM, only used by InLocalStorage
37
37
  matchingKey: getMatching(settings.core.key),
38
38
  splitFiltersValidation: settings.sync.__splitFiltersValidation,
39
39
  // ATM, only used by PluggableStorage
40
40
  mode: settings.mode,
41
+ impressionsMode: settings.sync.impressionsMode,
41
42
  // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined,
42
43
  // or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events.
43
44
  onReadyCb: function (error) {
@@ -53,9 +54,9 @@ export function sdkFactory(params) {
53
54
  // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
54
55
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage });
55
56
  var observer = impressionsObserverFactory();
56
- var uniqueKeysTracker = impressionsMode === NONE ? uniqueKeysTrackerFactory(log, filterAdapterFactory && filterAdapterFactory()) : undefined;
57
+ var uniqueKeysTracker = storageFactoryParams.impressionsMode === NONE ? uniqueKeysTrackerFactory(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory()) : undefined;
57
58
  var strategy = (storageFactoryParams.optimize) ? strategyOptimizedFactory(observer, storage.impressionCounts) :
58
- (impressionsMode === NONE) ? strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker) : strategyDebugFactory(observer);
59
+ (storageFactoryParams.impressionsMode === NONE) ? strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker) : strategyDebugFactory(observer);
59
60
  var impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
60
61
  var eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
61
62
  var telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
@@ -94,7 +94,7 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
94
94
  * @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
95
95
  */
96
96
  postUniqueKeysBulkCs: function (body, headers) {
97
- var url = urls.telemetry + "/api/v1/keys/cs";
97
+ var url = urls.telemetry + "/v1/keys/cs";
98
98
  return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY));
99
99
  },
100
100
  /**
@@ -104,7 +104,7 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
104
104
  * @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
105
105
  */
106
106
  postUniqueKeysBulkSs: function (body, headers) {
107
- var url = urls.telemetry + "/api/v1/keys/ss";
107
+ var url = urls.telemetry + "/v1/keys/ss";
108
108
  return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY));
109
109
  },
110
110
  postMetricsConfig: function (body) {
@@ -11,8 +11,9 @@ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
11
11
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
12
12
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
13
13
  import { LOG_PREFIX } from './constants';
14
- import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
14
+ import { LOCALHOST_MODE, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
15
15
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
16
+ import { UniqueKeysCacheInMemoryCS } from '../inMemory/uniqueKeysCacheInMemoryCS';
16
17
  /**
17
18
  * InLocal storage factory for standalone client-side SplitFactory
18
19
  */
@@ -35,12 +36,15 @@ export function InLocalStorage(options) {
35
36
  impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
36
37
  events: new EventsCacheInMemory(params.eventsQueueSize),
37
38
  telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
39
+ uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
38
40
  destroy: function () {
41
+ var _a;
39
42
  this.splits = new SplitsCacheInMemory();
40
43
  this.segments = new MySegmentsCacheInMemory();
41
44
  this.impressions.clear();
42
45
  this.impressionCounts && this.impressionCounts.clear();
43
46
  this.events.clear();
47
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
44
48
  },
45
49
  // When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).
46
50
  shared: function (matchingKey) {
@@ -3,8 +3,9 @@ import { SegmentsCacheInMemory } from './SegmentsCacheInMemory';
3
3
  import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
6
- import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
6
+ import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
7
7
  import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
8
+ import { UniqueKeysCacheInMemory } from './uniqueKeysCacheInMemory';
8
9
  /**
9
10
  * InMemory storage factory for standalone server-side SplitFactory
10
11
  *
@@ -15,16 +16,19 @@ export function InMemoryStorageFactory(params) {
15
16
  splits: new SplitsCacheInMemory(),
16
17
  segments: new SegmentsCacheInMemory(),
17
18
  impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
18
- impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
19
+ impressionCounts: params.impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
19
20
  events: new EventsCacheInMemory(params.eventsQueueSize),
20
21
  telemetry: params.mode !== LOCALHOST_MODE ? new TelemetryCacheInMemory() : undefined,
22
+ uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemory(params.uniqueKeysCacheSize) : undefined,
21
23
  // When using MEMORY we should clean all the caches to leave them empty
22
24
  destroy: function () {
25
+ var _a;
23
26
  this.splits.clear();
24
27
  this.segments.clear();
25
28
  this.impressions.clear();
26
29
  this.impressionCounts && this.impressionCounts.clear();
27
30
  this.events.clear();
31
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
28
32
  }
29
33
  };
30
34
  }
@@ -3,8 +3,9 @@ import { MySegmentsCacheInMemory } from './MySegmentsCacheInMemory';
3
3
  import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
6
- import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
6
+ import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
7
7
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
8
+ import { UniqueKeysCacheInMemoryCS } from './uniqueKeysCacheInMemoryCS';
8
9
  /**
9
10
  * InMemory storage factory for standalone client-side SplitFactory
10
11
  *
@@ -15,16 +16,19 @@ export function InMemoryStorageCSFactory(params) {
15
16
  splits: new SplitsCacheInMemory(),
16
17
  segments: new MySegmentsCacheInMemory(),
17
18
  impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
18
- impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
19
+ impressionCounts: params.impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
19
20
  events: new EventsCacheInMemory(params.eventsQueueSize),
20
21
  telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
22
+ uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS(params.uniqueKeysCacheSize) : undefined,
21
23
  // When using MEMORY we should clean all the caches to leave them empty
22
24
  destroy: function () {
25
+ var _a;
23
26
  this.splits.clear();
24
27
  this.segments.clear();
25
28
  this.impressions.clear();
26
29
  this.impressionCounts && this.impressionCounts.clear();
27
30
  this.events.clear();
31
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
28
32
  },
29
33
  // When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
30
34
  shared: function () {