@splitsoftware/splitio-commons 1.4.1-rc.0 → 1.4.1-rc.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 (35) hide show
  1. package/CHANGES.txt +3 -0
  2. package/cjs/integrations/ga/GaToSplit.js +3 -34
  3. package/cjs/listeners/browser.js +1 -1
  4. package/cjs/logger/messages/warn.js +1 -1
  5. package/cjs/storages/inMemory/EventsCacheInMemory.js +5 -3
  6. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -4
  7. package/cjs/storages/inMemory/ImpressionsCacheInMemory.js +5 -3
  8. package/cjs/sync/submitters/submitter.js +10 -6
  9. package/cjs/sync/submitters/telemetrySubmitter.js +2 -2
  10. package/esm/integrations/ga/GaToSplit.js +2 -32
  11. package/esm/listeners/browser.js +1 -1
  12. package/esm/logger/messages/warn.js +1 -1
  13. package/esm/storages/inMemory/EventsCacheInMemory.js +5 -3
  14. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -4
  15. package/esm/storages/inMemory/ImpressionsCacheInMemory.js +5 -3
  16. package/esm/sync/submitters/submitter.js +10 -6
  17. package/esm/sync/submitters/telemetrySubmitter.js +2 -2
  18. package/package.json +1 -1
  19. package/src/integrations/ga/GaToSplit.ts +2 -38
  20. package/src/integrations/ga/types.ts +0 -7
  21. package/src/listeners/browser.ts +1 -1
  22. package/src/logger/messages/warn.ts +1 -1
  23. package/src/storages/inMemory/EventsCacheInMemory.ts +5 -3
  24. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +14 -4
  25. package/src/storages/inMemory/ImpressionsCacheInMemory.ts +5 -3
  26. package/src/storages/types.ts +3 -4
  27. package/src/sync/submitters/submitter.ts +9 -5
  28. package/src/sync/submitters/telemetrySubmitter.ts +2 -2
  29. package/types/integrations/ga/GaToSplit.d.ts +0 -1
  30. package/types/integrations/ga/types.d.ts +0 -7
  31. package/types/storages/inMemory/EventsCacheInMemory.d.ts +2 -2
  32. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +9 -3
  33. package/types/storages/inMemory/ImpressionsCacheInMemory.d.ts +2 -2
  34. package/types/storages/types.d.ts +2 -3
  35. package/types/sync/submitters/telemetrySubmitter.d.ts +2 -2
package/CHANGES.txt CHANGED
@@ -1,3 +1,6 @@
1
+ 1.4.1 (June 13, 2022)
2
+ - Bugfixing - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending.
3
+
1
4
  1.4.0 (May 24, 2022)
2
5
  - Added `scheduler.telemetryRefreshRate` property to SDK configuration, and deprecated `scheduler.metricsRefreshRate` property.
3
6
  - Updated SDK telemetry storage, metrics and updater to be more effective and send less often.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.autoRequireScript = exports.GaToSplit = exports.fixEventTypeId = exports.validateEventData = exports.validateIdentities = exports.defaultPrefix = exports.defaultMapper = void 0;
3
+ exports.GaToSplit = exports.fixEventTypeId = exports.validateEventData = exports.validateIdentities = exports.defaultPrefix = exports.defaultMapper = void 0;
4
4
  /* eslint-disable no-undef */
5
5
  var objectAssign_1 = require("../../utils/lang/objectAssign");
6
6
  var lang_1 = require("../../utils/lang");
@@ -12,9 +12,8 @@ var logNameMapper = 'ga-to-split:mapper';
12
12
  * that the global command queue has been renamed or not yet defined.
13
13
  * @param {string} pluginName The plugin name identifier.
14
14
  * @param {Function} pluginConstructor The plugin constructor function.
15
- * @param {boolean} autoRequire Whether the integration should automatically queue a 'require' command for each 'create' command.
16
15
  */
17
- function providePlugin(pluginName, pluginConstructor, autoRequire) {
16
+ function providePlugin(pluginName, pluginConstructor) {
18
17
  // get reference to global command queue. Init it if not defined yet.
19
18
  // @ts-expect-error
20
19
  var gaAlias = window.GoogleAnalyticsObject || 'ga';
@@ -28,8 +27,6 @@ function providePlugin(pluginName, pluginConstructor, autoRequire) {
28
27
  // provides the plugin for use with analytics.js.
29
28
  // @ts-expect-error
30
29
  window[gaAlias]('provide', pluginName, pluginConstructor);
31
- if (autoRequire)
32
- autoRequireScript();
33
30
  }
34
31
  // Default mapping: object used for building the default mapper from hits to Split events
35
32
  var defaultMapping = {
@@ -249,34 +246,6 @@ function GaToSplit(sdkOptions, params) {
249
246
  return SplitTracker;
250
247
  }());
251
248
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
252
- providePlugin('splitTracker', SplitTracker, sdkOptions.autoRequire);
249
+ providePlugin('splitTracker', SplitTracker);
253
250
  }
254
251
  exports.GaToSplit = GaToSplit;
255
- function autoRequireScript() {
256
- (function (i, r) {
257
- i['GoogleAnalyticsObject'] = r;
258
- i[r] = i[r] || function () { i[r].q.push(arguments); };
259
- i[r].q = i[r].q || [];
260
- var ts = {}; // Tracker names
261
- var o = i[r].q.push; // Reference to Array.prototype.push
262
- i[r].q.push = function () {
263
- var result = o.apply(this, arguments);
264
- var v = arguments[0];
265
- if (v && v[0] === 'create') {
266
- var t = typeof v[2] === 'object' && typeof v[2].name === 'string' ?
267
- v[2].name : // `ga('create', 'UA-ID', { name: 'trackerName', ... })`
268
- typeof v[3] === 'object' && typeof v[3].name === 'string' ?
269
- v[3].name : // `ga('create', 'UA-ID', 'auto', { name: 'trackerName', ... })`
270
- typeof v[3] === 'string' ?
271
- v[3] : // `ga('create', 'UA-ID', 'auto', 'trackerName')`
272
- undefined; // No name tracker, `ga('create', 'UA-ID', 'auto')`
273
- if (!ts[t]) {
274
- ts[t] = true;
275
- i[r](t ? t + '.require' : 'require', 'splitTracker'); // Auto-require
276
- }
277
- }
278
- return result;
279
- };
280
- })(window, 'ga');
281
- }
282
- exports.autoRequireScript = autoRequireScript;
@@ -77,7 +77,7 @@ var BrowserSignalListener = /** @class */ (function () {
77
77
  BrowserSignalListener.prototype._flushData = function (url, cache, postService, fromCacheToPayload, extraMetadata) {
78
78
  // if there is data in cache, send it to backend
79
79
  if (!cache.isEmpty()) {
80
- var dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.state()) : cache.state();
80
+ var dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.pop()) : cache.pop();
81
81
  if (!this._sendBeacon(url, dataPayload, extraMetadata)) {
82
82
  postService(JSON.stringify(dataPayload)).catch(function () { }); // no-op just to catch a possible exception
83
83
  }
@@ -14,7 +14,7 @@ exports.codesWarn = error_1.codesError.concat([
14
14
  [c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
15
15
  [c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
16
16
  [c.STREAMING_FALLBACK, c.LOG_PREFIX_SYNC_STREAMING + 'Falling back to polling mode. Reason: %s'],
17
- [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s after retry. Reason: %s.'],
17
+ [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Dropping %s after retry. Reason: %s.'],
18
18
  [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s, keeping data to retry on next iteration. Reason: %s.'],
19
19
  // client status
20
20
  [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect. Make sure to wait for SDK readiness before using this method.'],
@@ -35,10 +35,12 @@ var EventsCacheInMemory = /** @class */ (function () {
35
35
  this.queueByteSize = 0;
36
36
  };
37
37
  /**
38
- * Get the collected data, used as payload for posting.
38
+ * Pop the collected data, used as payload for posting.
39
39
  */
40
- EventsCacheInMemory.prototype.state = function () {
41
- return this.queue;
40
+ EventsCacheInMemory.prototype.pop = function () {
41
+ var data = this.queue;
42
+ this.clear();
43
+ return data;
42
44
  };
43
45
  /**
44
46
  * Check if the cache is empty.
@@ -21,14 +21,22 @@ var ImpressionCountsCacheInMemory = /** @class */ (function () {
21
21
  this.cache[key] = currentAmount ? currentAmount + amount : amount;
22
22
  };
23
23
  /**
24
- * Returns all the elements stored in the cache and resets the cache.
25
- */
26
- ImpressionCountsCacheInMemory.prototype.state = function () {
27
- return this.cache;
24
+ * Pop the collected data, used as payload for posting.
25
+ */
26
+ ImpressionCountsCacheInMemory.prototype.pop = function () {
27
+ var data = this.cache;
28
+ this.clear();
29
+ return data;
28
30
  };
31
+ /**
32
+ * Clear the data stored on the cache.
33
+ */
29
34
  ImpressionCountsCacheInMemory.prototype.clear = function () {
30
35
  this.cache = {};
31
36
  };
37
+ /**
38
+ * Check if the cache is empty.
39
+ */
32
40
  ImpressionCountsCacheInMemory.prototype.isEmpty = function () {
33
41
  return Object.keys(this.cache).length === 0;
34
42
  };
@@ -33,10 +33,12 @@ var ImpressionsCacheInMemory = /** @class */ (function () {
33
33
  this.queue = [];
34
34
  };
35
35
  /**
36
- * Get the collected data, used as payload for posting.
36
+ * Pop the collected data, used as payload for posting.
37
37
  */
38
- ImpressionsCacheInMemory.prototype.state = function () {
39
- return this.queue;
38
+ ImpressionsCacheInMemory.prototype.pop = function () {
39
+ var data = this.queue;
40
+ this.clear();
41
+ return data;
40
42
  };
41
43
  /**
42
44
  * Check if the cache is empty.
@@ -10,26 +10,30 @@ function submitterFactory(log, postClient, sourceCache, postRate, dataName, from
10
10
  ) {
11
11
  if (maxRetries === void 0) { maxRetries = 0; }
12
12
  var retries = 0;
13
+ var data;
13
14
  function postData() {
14
- if (sourceCache.isEmpty())
15
- return Promise.resolve();
16
- var data = sourceCache.state();
15
+ if (!data) {
16
+ if (sourceCache.isEmpty())
17
+ return Promise.resolve();
18
+ // we clear the cache to track new items, while `data` is used for retries
19
+ data = sourceCache.pop();
20
+ }
17
21
  // @ts-ignore
18
22
  var dataCountMessage = typeof data.length === 'number' ? data.length + " " + dataName : dataName;
19
23
  log[debugLogs ? 'debug' : 'info'](constants_1.SUBMITTERS_PUSH, [dataCountMessage]);
20
24
  var jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
21
25
  if (!maxRetries)
22
- sourceCache.clear();
26
+ data = undefined;
23
27
  return postClient(jsonPayload).then(function () {
24
28
  retries = 0;
25
- sourceCache.clear(); // we clear the queue if request successes.
29
+ data = undefined;
26
30
  }).catch(function (err) {
27
31
  if (!maxRetries) {
28
32
  log[debugLogs ? 'debug' : 'warn'](constants_1.SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
29
33
  }
30
34
  else if (retries === maxRetries) {
31
35
  retries = 0;
32
- sourceCache.clear(); // we clear the queue if request fails after retries.
36
+ data = undefined;
33
37
  log[debugLogs ? 'debug' : 'warn'](constants_1.SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
34
38
  }
35
39
  else {
@@ -17,7 +17,7 @@ function telemetryCacheStatsAdapter(telemetry, splits, segments) {
17
17
  isEmpty: function () { return false; },
18
18
  clear: function () { },
19
19
  // @TODO consider moving inside telemetry cache for code size reduction
20
- state: function () {
20
+ pop: function () {
21
21
  return {
22
22
  lS: telemetry.getLastSynchronization(),
23
23
  mL: telemetry.popLatencies(),
@@ -80,7 +80,7 @@ function telemetryCacheConfigAdapter(telemetry, settings) {
80
80
  return {
81
81
  isEmpty: function () { return false; },
82
82
  clear: function () { },
83
- state: function () {
83
+ pop: function () {
84
84
  var urls = settings.urls, scheduler = settings.scheduler;
85
85
  var isClientSide = settings.core.key !== undefined;
86
86
  return (0, objectAssign_1.objectAssign)(getTelemetryConfigStats(settings.mode, settings.storage.type), {
@@ -9,9 +9,8 @@ var logNameMapper = 'ga-to-split:mapper';
9
9
  * that the global command queue has been renamed or not yet defined.
10
10
  * @param {string} pluginName The plugin name identifier.
11
11
  * @param {Function} pluginConstructor The plugin constructor function.
12
- * @param {boolean} autoRequire Whether the integration should automatically queue a 'require' command for each 'create' command.
13
12
  */
14
- function providePlugin(pluginName, pluginConstructor, autoRequire) {
13
+ function providePlugin(pluginName, pluginConstructor) {
15
14
  // get reference to global command queue. Init it if not defined yet.
16
15
  // @ts-expect-error
17
16
  var gaAlias = window.GoogleAnalyticsObject || 'ga';
@@ -25,8 +24,6 @@ function providePlugin(pluginName, pluginConstructor, autoRequire) {
25
24
  // provides the plugin for use with analytics.js.
26
25
  // @ts-expect-error
27
26
  window[gaAlias]('provide', pluginName, pluginConstructor);
28
- if (autoRequire)
29
- autoRequireScript();
30
27
  }
31
28
  // Default mapping: object used for building the default mapper from hits to Split events
32
29
  var defaultMapping = {
@@ -243,32 +240,5 @@ export function GaToSplit(sdkOptions, params) {
243
240
  return SplitTracker;
244
241
  }());
245
242
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
246
- providePlugin('splitTracker', SplitTracker, sdkOptions.autoRequire);
247
- }
248
- export function autoRequireScript() {
249
- (function (i, r) {
250
- i['GoogleAnalyticsObject'] = r;
251
- i[r] = i[r] || function () { i[r].q.push(arguments); };
252
- i[r].q = i[r].q || [];
253
- var ts = {}; // Tracker names
254
- var o = i[r].q.push; // Reference to Array.prototype.push
255
- i[r].q.push = function () {
256
- var result = o.apply(this, arguments);
257
- var v = arguments[0];
258
- if (v && v[0] === 'create') {
259
- var t = typeof v[2] === 'object' && typeof v[2].name === 'string' ?
260
- v[2].name : // `ga('create', 'UA-ID', { name: 'trackerName', ... })`
261
- typeof v[3] === 'object' && typeof v[3].name === 'string' ?
262
- v[3].name : // `ga('create', 'UA-ID', 'auto', { name: 'trackerName', ... })`
263
- typeof v[3] === 'string' ?
264
- v[3] : // `ga('create', 'UA-ID', 'auto', 'trackerName')`
265
- undefined; // No name tracker, `ga('create', 'UA-ID', 'auto')`
266
- if (!ts[t]) {
267
- ts[t] = true;
268
- i[r](t ? t + '.require' : 'require', 'splitTracker'); // Auto-require
269
- }
270
- }
271
- return result;
272
- };
273
- })(window, 'ga');
243
+ providePlugin('splitTracker', SplitTracker);
274
244
  }
@@ -74,7 +74,7 @@ var BrowserSignalListener = /** @class */ (function () {
74
74
  BrowserSignalListener.prototype._flushData = function (url, cache, postService, fromCacheToPayload, extraMetadata) {
75
75
  // if there is data in cache, send it to backend
76
76
  if (!cache.isEmpty()) {
77
- var dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.state()) : cache.state();
77
+ var dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.pop()) : cache.pop();
78
78
  if (!this._sendBeacon(url, dataPayload, extraMetadata)) {
79
79
  postService(JSON.stringify(dataPayload)).catch(function () { }); // no-op just to catch a possible exception
80
80
  }
@@ -10,7 +10,7 @@ export var codesWarn = codesError.concat([
10
10
  [c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
11
11
  [c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
12
12
  [c.STREAMING_FALLBACK, c.LOG_PREFIX_SYNC_STREAMING + 'Falling back to polling mode. Reason: %s'],
13
- [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s after retry. Reason: %s.'],
13
+ [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Dropping %s after retry. Reason: %s.'],
14
14
  [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s, keeping data to retry on next iteration. Reason: %s.'],
15
15
  // client status
16
16
  [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect. Make sure to wait for SDK readiness before using this method.'],
@@ -32,10 +32,12 @@ var EventsCacheInMemory = /** @class */ (function () {
32
32
  this.queueByteSize = 0;
33
33
  };
34
34
  /**
35
- * Get the collected data, used as payload for posting.
35
+ * Pop the collected data, used as payload for posting.
36
36
  */
37
- EventsCacheInMemory.prototype.state = function () {
38
- return this.queue;
37
+ EventsCacheInMemory.prototype.pop = function () {
38
+ var data = this.queue;
39
+ this.clear();
40
+ return data;
39
41
  };
40
42
  /**
41
43
  * Check if the cache is empty.
@@ -18,14 +18,22 @@ var ImpressionCountsCacheInMemory = /** @class */ (function () {
18
18
  this.cache[key] = currentAmount ? currentAmount + amount : amount;
19
19
  };
20
20
  /**
21
- * Returns all the elements stored in the cache and resets the cache.
22
- */
23
- ImpressionCountsCacheInMemory.prototype.state = function () {
24
- return this.cache;
21
+ * Pop the collected data, used as payload for posting.
22
+ */
23
+ ImpressionCountsCacheInMemory.prototype.pop = function () {
24
+ var data = this.cache;
25
+ this.clear();
26
+ return data;
25
27
  };
28
+ /**
29
+ * Clear the data stored on the cache.
30
+ */
26
31
  ImpressionCountsCacheInMemory.prototype.clear = function () {
27
32
  this.cache = {};
28
33
  };
34
+ /**
35
+ * Check if the cache is empty.
36
+ */
29
37
  ImpressionCountsCacheInMemory.prototype.isEmpty = function () {
30
38
  return Object.keys(this.cache).length === 0;
31
39
  };
@@ -30,10 +30,12 @@ var ImpressionsCacheInMemory = /** @class */ (function () {
30
30
  this.queue = [];
31
31
  };
32
32
  /**
33
- * Get the collected data, used as payload for posting.
33
+ * Pop the collected data, used as payload for posting.
34
34
  */
35
- ImpressionsCacheInMemory.prototype.state = function () {
36
- return this.queue;
35
+ ImpressionsCacheInMemory.prototype.pop = function () {
36
+ var data = this.queue;
37
+ this.clear();
38
+ return data;
37
39
  };
38
40
  /**
39
41
  * Check if the cache is empty.
@@ -7,26 +7,30 @@ export function submitterFactory(log, postClient, sourceCache, postRate, dataNam
7
7
  ) {
8
8
  if (maxRetries === void 0) { maxRetries = 0; }
9
9
  var retries = 0;
10
+ var data;
10
11
  function postData() {
11
- if (sourceCache.isEmpty())
12
- return Promise.resolve();
13
- var data = sourceCache.state();
12
+ if (!data) {
13
+ if (sourceCache.isEmpty())
14
+ return Promise.resolve();
15
+ // we clear the cache to track new items, while `data` is used for retries
16
+ data = sourceCache.pop();
17
+ }
14
18
  // @ts-ignore
15
19
  var dataCountMessage = typeof data.length === 'number' ? data.length + " " + dataName : dataName;
16
20
  log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCountMessage]);
17
21
  var jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
18
22
  if (!maxRetries)
19
- sourceCache.clear();
23
+ data = undefined;
20
24
  return postClient(jsonPayload).then(function () {
21
25
  retries = 0;
22
- sourceCache.clear(); // we clear the queue if request successes.
26
+ data = undefined;
23
27
  }).catch(function (err) {
24
28
  if (!maxRetries) {
25
29
  log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
26
30
  }
27
31
  else if (retries === maxRetries) {
28
32
  retries = 0;
29
- sourceCache.clear(); // we clear the queue if request fails after retries.
33
+ data = undefined;
30
34
  log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
31
35
  }
32
36
  else {
@@ -14,7 +14,7 @@ export function telemetryCacheStatsAdapter(telemetry, splits, segments) {
14
14
  isEmpty: function () { return false; },
15
15
  clear: function () { },
16
16
  // @TODO consider moving inside telemetry cache for code size reduction
17
- state: function () {
17
+ pop: function () {
18
18
  return {
19
19
  lS: telemetry.getLastSynchronization(),
20
20
  mL: telemetry.popLatencies(),
@@ -75,7 +75,7 @@ export function telemetryCacheConfigAdapter(telemetry, settings) {
75
75
  return {
76
76
  isEmpty: function () { return false; },
77
77
  clear: function () { },
78
- state: function () {
78
+ pop: function () {
79
79
  var urls = settings.urls, scheduler = settings.scheduler;
80
80
  var isClientSide = settings.core.key !== undefined;
81
81
  return objectAssign(getTelemetryConfigStats(settings.mode, settings.storage.type), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.4.1-rc.0",
3
+ "version": "1.4.1-rc.1",
4
4
  "description": "Split Javascript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -21,9 +21,8 @@ const logNameMapper = 'ga-to-split:mapper';
21
21
  * that the global command queue has been renamed or not yet defined.
22
22
  * @param {string} pluginName The plugin name identifier.
23
23
  * @param {Function} pluginConstructor The plugin constructor function.
24
- * @param {boolean} autoRequire Whether the integration should automatically queue a 'require' command for each 'create' command.
25
24
  */
26
- function providePlugin(pluginName: string, pluginConstructor: Function, autoRequire?: boolean) {
25
+ function providePlugin(pluginName: string, pluginConstructor: Function) {
27
26
  // get reference to global command queue. Init it if not defined yet.
28
27
  // @ts-expect-error
29
28
  const gaAlias = window.GoogleAnalyticsObject || 'ga';
@@ -34,8 +33,6 @@ function providePlugin(pluginName: string, pluginConstructor: Function, autoRequ
34
33
  // provides the plugin for use with analytics.js.
35
34
  // @ts-expect-error
36
35
  window[gaAlias]('provide', pluginName, pluginConstructor);
37
-
38
- if (autoRequire) autoRequireScript();
39
36
  }
40
37
 
41
38
  // Default mapping: object used for building the default mapper from hits to Split events
@@ -287,38 +284,5 @@ export function GaToSplit(sdkOptions: GoogleAnalyticsToSplitOptions, params: IIn
287
284
  }
288
285
 
289
286
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
290
- providePlugin('splitTracker', SplitTracker, sdkOptions.autoRequire);
291
- }
292
-
293
- export function autoRequireScript() {
294
- (function (i: any, r: any) {
295
- i['GoogleAnalyticsObject'] = r;
296
- i[r] = i[r] || function () { i[r].q.push(arguments); };
297
- i[r].q = i[r].q || [];
298
-
299
- var ts: any = {}; // Tracker names
300
- var o = i[r].q.push; // Reference to Array.prototype.push
301
- i[r].q.push = function () {
302
- var result = o.apply(this, arguments);
303
-
304
- var v = arguments[0];
305
- if (v && v[0] === 'create') {
306
- var t = typeof v[2] === 'object' && typeof v[2].name === 'string' ?
307
- v[2].name : // `ga('create', 'UA-ID', { name: 'trackerName', ... })`
308
- typeof v[3] === 'object' && typeof v[3].name === 'string' ?
309
- v[3].name : // `ga('create', 'UA-ID', 'auto', { name: 'trackerName', ... })`
310
- typeof v[3] === 'string' ?
311
- v[3] : // `ga('create', 'UA-ID', 'auto', 'trackerName')`
312
- undefined; // No name tracker, `ga('create', 'UA-ID', 'auto')`
313
-
314
- if (!ts[t]) {
315
- ts[t] = true;
316
- i[r](t ? t + '.require' : 'require', 'splitTracker'); // Auto-require
317
- }
318
- }
319
-
320
- return result;
321
- };
322
-
323
- })(window, 'ga');
287
+ providePlugin('splitTracker', SplitTracker);
324
288
  }
@@ -53,13 +53,6 @@ export interface GoogleAnalyticsToSplitOptions {
53
53
  * If not provided, events are sent using the key and traffic type provided at SDK config
54
54
  */
55
55
  identities?: Identity[],
56
- /**
57
- * Optional flag to automatically require the `splitTracker` plugin for all created trackers.
58
- * If true, it will spy on the ga command queue, and run `ga('[trackerName].require', 'splitTracker');` for each 'create' command.
59
- * @property {boolean} autoRequire
60
- * @default false
61
- */
62
- autoRequire?: boolean,
63
56
  }
64
57
 
65
58
  /**
@@ -92,7 +92,7 @@ export class BrowserSignalListener implements ISignalListener {
92
92
  private _flushData<TState>(url: string, cache: IRecorderCacheProducerSync<TState>, postService: (body: string) => Promise<IResponse>, fromCacheToPayload?: (cacheData: TState) => any, extraMetadata?: {}) {
93
93
  // if there is data in cache, send it to backend
94
94
  if (!cache.isEmpty()) {
95
- const dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.state()) : cache.state();
95
+ const dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.pop()) : cache.pop();
96
96
  if (!this._sendBeacon(url, dataPayload, extraMetadata)) {
97
97
  postService(JSON.stringify(dataPayload)).catch(() => { }); // no-op just to catch a possible exception
98
98
  }
@@ -11,7 +11,7 @@ export const codesWarn: [number, string][] = codesError.concat([
11
11
  [c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
12
12
  [c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
13
13
  [c.STREAMING_FALLBACK, c.LOG_PREFIX_SYNC_STREAMING + 'Falling back to polling mode. Reason: %s'],
14
- [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s after retry. Reason: %s.'],
14
+ [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Dropping %s after retry. Reason: %s.'],
15
15
  [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s, keeping data to retry on next iteration. Reason: %s.'],
16
16
  // client status
17
17
  [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect. Make sure to wait for SDK readiness before using this method.'],
@@ -46,10 +46,12 @@ export class EventsCacheInMemory implements IEventsCacheSync {
46
46
  }
47
47
 
48
48
  /**
49
- * Get the collected data, used as payload for posting.
49
+ * Pop the collected data, used as payload for posting.
50
50
  */
51
- state() {
52
- return this.queue;
51
+ pop() {
52
+ const data = this.queue;
53
+ this.clear();
54
+ return data;
53
55
  }
54
56
 
55
57
  /**
@@ -20,17 +20,27 @@ export class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync
20
20
  this.cache[key] = currentAmount ? currentAmount + amount : amount;
21
21
  }
22
22
 
23
+
24
+
23
25
  /**
24
- * Returns all the elements stored in the cache and resets the cache.
25
- */
26
- state() {
27
- return this.cache;
26
+ * Pop the collected data, used as payload for posting.
27
+ */
28
+ pop() {
29
+ const data = this.cache;
30
+ this.clear();
31
+ return data;
28
32
  }
29
33
 
34
+ /**
35
+ * Clear the data stored on the cache.
36
+ */
30
37
  clear() {
31
38
  this.cache = {};
32
39
  }
33
40
 
41
+ /**
42
+ * Check if the cache is empty.
43
+ */
34
44
  isEmpty() {
35
45
  return Object.keys(this.cache).length === 0;
36
46
  }
@@ -41,10 +41,12 @@ export class ImpressionsCacheInMemory implements IImpressionsCacheSync {
41
41
  }
42
42
 
43
43
  /**
44
- * Get the collected data, used as payload for posting.
44
+ * Pop the collected data, used as payload for posting.
45
45
  */
46
- state() {
47
- return this.queue;
46
+ pop() {
47
+ const data = this.queue;
48
+ this.clear();
49
+ return data;
48
50
  }
49
51
 
50
52
  /**
@@ -301,8 +301,8 @@ export interface IRecorderCacheProducerSync<T> {
301
301
  isEmpty(): boolean
302
302
  /* Clears cache data */
303
303
  clear(): void
304
- /* Gets cache data */
305
- state(): T
304
+ /* Pops cache data */
305
+ pop(): T
306
306
  }
307
307
 
308
308
 
@@ -352,8 +352,7 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
352
352
 
353
353
  // Used by impressions count submitter in standalone and producer mode
354
354
  isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
355
- clear(): void // clear cache data
356
- state(): Record<string, number> // get cache data
355
+ pop(): Record<string, number> // pop cache data
357
356
  }
358
357
 
359
358
 
@@ -20,27 +20,31 @@ export function submitterFactory<TState>(
20
20
  ): ISyncTask<[], void> {
21
21
 
22
22
  let retries = 0;
23
+ let data: TState | undefined;
23
24
 
24
25
  function postData(): Promise<any> {
25
- if (sourceCache.isEmpty()) return Promise.resolve();
26
+ if (!data) {
27
+ if (sourceCache.isEmpty()) return Promise.resolve();
28
+ // we clear the cache to track new items, while `data` is used for retries
29
+ data = sourceCache.pop();
30
+ }
26
31
 
27
- const data = sourceCache.state();
28
32
  // @ts-ignore
29
33
  const dataCountMessage = typeof data.length === 'number' ? `${data.length} ${dataName}` : dataName;
30
34
  log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCountMessage]);
31
35
 
32
36
  const jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
33
- if (!maxRetries) sourceCache.clear();
37
+ if (!maxRetries) data = undefined;
34
38
 
35
39
  return postClient(jsonPayload).then(() => {
36
40
  retries = 0;
37
- sourceCache.clear(); // we clear the queue if request successes.
41
+ data = undefined;
38
42
  }).catch(err => {
39
43
  if (!maxRetries) {
40
44
  log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
41
45
  } else if (retries === maxRetries) {
42
46
  retries = 0;
43
- sourceCache.clear(); // we clear the queue if request fails after retries.
47
+ data = undefined;
44
48
  log[debugLogs ? 'debug' : 'warn'](SUBMITTERS_PUSH_FAILS, [dataCountMessage, err]);
45
49
  } else {
46
50
  retries++;
@@ -19,7 +19,7 @@ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, split
19
19
  clear() { }, // No-op
20
20
 
21
21
  // @TODO consider moving inside telemetry cache for code size reduction
22
- state(): TelemetryUsageStatsPayload {
22
+ pop(): TelemetryUsageStatsPayload {
23
23
  return {
24
24
  lS: telemetry.getLastSynchronization(),
25
25
  mL: telemetry.popLatencies(),
@@ -88,7 +88,7 @@ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, sett
88
88
  isEmpty() { return false; },
89
89
  clear() { },
90
90
 
91
- state(): TelemetryConfigStatsPayload {
91
+ pop(): TelemetryConfigStatsPayload {
92
92
  const { urls, scheduler } = settings;
93
93
  const isClientSide = settings.core.key !== undefined;
94
94
 
@@ -38,4 +38,3 @@ export declare function fixEventTypeId(log: ILogger, eventTypeId: any): any;
38
38
  * @param {object} log factory logger
39
39
  */
40
40
  export declare function GaToSplit(sdkOptions: GoogleAnalyticsToSplitOptions, params: IIntegrationFactoryParams): void;
41
- export declare function autoRequireScript(): void;
@@ -52,13 +52,6 @@ export interface GoogleAnalyticsToSplitOptions {
52
52
  * If not provided, events are sent using the key and traffic type provided at SDK config
53
53
  */
54
54
  identities?: Identity[];
55
- /**
56
- * Optional flag to automatically require the `splitTracker` plugin for all created trackers.
57
- * If true, it will spy on the ga command queue, and run `ga('[trackerName].require', 'splitTracker');` for each 'create' command.
58
- * @property {boolean} autoRequire
59
- * @default false
60
- */
61
- autoRequire?: boolean;
62
55
  }
63
56
  /**
64
57
  * Enable 'Google Analytics to Split' integration, to track Google Analytics hits as Split events.
@@ -21,9 +21,9 @@ export declare class EventsCacheInMemory implements IEventsCacheSync {
21
21
  */
22
22
  clear(): void;
23
23
  /**
24
- * Get the collected data, used as payload for posting.
24
+ * Pop the collected data, used as payload for posting.
25
25
  */
26
- state(): SplitIO.EventData[];
26
+ pop(): SplitIO.EventData[];
27
27
  /**
28
28
  * Check if the cache is empty.
29
29
  */
@@ -10,9 +10,15 @@ export declare class ImpressionCountsCacheInMemory implements IImpressionCountsC
10
10
  */
11
11
  track(featureName: string, timeFrame: number, amount: number): void;
12
12
  /**
13
- * Returns all the elements stored in the cache and resets the cache.
14
- */
15
- state(): Record<string, number>;
13
+ * Pop the collected data, used as payload for posting.
14
+ */
15
+ pop(): Record<string, number>;
16
+ /**
17
+ * Clear the data stored on the cache.
18
+ */
16
19
  clear(): void;
20
+ /**
21
+ * Check if the cache is empty.
22
+ */
17
23
  isEmpty(): boolean;
18
24
  }
@@ -20,9 +20,9 @@ export declare class ImpressionsCacheInMemory implements IImpressionsCacheSync {
20
20
  */
21
21
  clear(): void;
22
22
  /**
23
- * Get the collected data, used as payload for posting.
23
+ * Pop the collected data, used as payload for posting.
24
24
  */
25
- state(): ImpressionDTO[];
25
+ pop(): ImpressionDTO[];
26
26
  /**
27
27
  * Check if the cache is empty.
28
28
  */
@@ -266,7 +266,7 @@ export interface IEventsCacheBase {
266
266
  export interface IRecorderCacheProducerSync<T> {
267
267
  isEmpty(): boolean;
268
268
  clear(): void;
269
- state(): T;
269
+ pop(): T;
270
270
  }
271
271
  export interface IImpressionsCacheSync extends IImpressionsCacheBase, IRecorderCacheProducerSync<ImpressionDTO[]> {
272
272
  track(data: ImpressionDTO[]): void;
@@ -295,8 +295,7 @@ export interface IEventsCacheAsync extends IEventsCacheBase, IRecorderCacheProdu
295
295
  export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<Record<string, number>> {
296
296
  track(featureName: string, timeFrame: number, amount: number): void;
297
297
  isEmpty(): boolean;
298
- clear(): void;
299
- state(): Record<string, number>;
298
+ pop(): Record<string, number>;
300
299
  }
301
300
  /**
302
301
  * Telemetry storage interface for standalone and partial consumer modes.
@@ -8,7 +8,7 @@ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
8
8
  export declare function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits: ISplitsCacheSync, segments: ISegmentsCacheSync): {
9
9
  isEmpty(): boolean;
10
10
  clear(): void;
11
- state(): TelemetryUsageStatsPayload;
11
+ pop(): TelemetryUsageStatsPayload;
12
12
  };
13
13
  export declare function getTelemetryConfigStats(mode: SDKMode, storageType: string): TelemetryConfigStats;
14
14
  /**
@@ -17,7 +17,7 @@ export declare function getTelemetryConfigStats(mode: SDKMode, storageType: stri
17
17
  export declare function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, settings: ISettings): {
18
18
  isEmpty(): boolean;
19
19
  clear(): void;
20
- state(): TelemetryConfigStatsPayload;
20
+ pop(): TelemetryConfigStatsPayload;
21
21
  };
22
22
  /**
23
23
  * Submitter that periodically posts telemetry data