@splitsoftware/splitio-commons 1.5.0 → 1.5.1-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 (129) hide show
  1. package/CHANGES.txt +5 -0
  2. package/cjs/listeners/browser.js +35 -15
  3. package/cjs/services/splitApi.js +4 -4
  4. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +5 -5
  5. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  6. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +34 -34
  7. package/cjs/sync/polling/updaters/splitChangesUpdater.js +4 -3
  8. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +46 -46
  9. package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +82 -64
  10. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +74 -58
  11. package/cjs/sync/streaming/UpdateWorkers/constants.js +6 -0
  12. package/cjs/sync/streaming/pushManager.js +6 -7
  13. package/cjs/sync/syncTask.js +13 -16
  14. package/cjs/utils/Backoff.js +3 -2
  15. package/esm/listeners/browser.js +35 -15
  16. package/esm/services/splitApi.js +4 -4
  17. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +5 -5
  18. package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  19. package/esm/sync/polling/updaters/segmentChangesUpdater.js +34 -34
  20. package/esm/sync/polling/updaters/splitChangesUpdater.js +4 -3
  21. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +46 -47
  22. package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +82 -65
  23. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +74 -59
  24. package/esm/sync/streaming/UpdateWorkers/constants.js +3 -0
  25. package/esm/sync/streaming/pushManager.js +6 -7
  26. package/esm/sync/syncTask.js +13 -16
  27. package/esm/utils/Backoff.js +3 -2
  28. package/package.json +1 -5
  29. package/src/listeners/browser.ts +34 -14
  30. package/src/logger/.DS_Store +0 -0
  31. package/src/services/splitApi.ts +4 -4
  32. package/src/services/types.ts +2 -2
  33. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +5 -4
  34. package/src/sync/polling/fetchers/splitChangesFetcher.ts +2 -1
  35. package/src/sync/polling/fetchers/types.ts +2 -0
  36. package/src/sync/polling/pollingManagerCS.ts +5 -5
  37. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +2 -2
  38. package/src/sync/polling/types.ts +14 -6
  39. package/src/sync/polling/updaters/mySegmentsUpdater.ts +4 -4
  40. package/src/sync/polling/updaters/segmentChangesUpdater.ts +34 -32
  41. package/src/sync/polling/updaters/splitChangesUpdater.ts +5 -4
  42. package/src/sync/streaming/SSEHandler/types.ts +0 -7
  43. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +45 -54
  44. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +78 -63
  45. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +73 -61
  46. package/src/sync/streaming/UpdateWorkers/constants.ts +3 -0
  47. package/src/sync/streaming/UpdateWorkers/types.ts +2 -4
  48. package/src/sync/streaming/pushManager.ts +12 -12
  49. package/src/sync/streaming/types.ts +2 -2
  50. package/src/sync/syncTask.ts +16 -18
  51. package/src/utils/Backoff.ts +7 -2
  52. package/types/integrations/ga/GaToSplitPlugin.d.ts +3 -0
  53. package/types/integrations/ga/SplitToGaPlugin.d.ts +4 -0
  54. package/types/integrations/ga/autoRequire.d.ts +4 -0
  55. package/types/listeners/browser.d.ts +6 -6
  56. package/types/logger/browser/{DebugLogger.d.ts → debugLogger.d.ts} +0 -0
  57. package/types/logger/browser/{ErrorLogger.d.ts → errorLogger.d.ts} +0 -0
  58. package/types/logger/browser/{InfoLogger.d.ts → infoLogger.d.ts} +0 -0
  59. package/types/logger/browser/{WarnLogger.d.ts → warnLogger.d.ts} +0 -0
  60. package/types/logger/codes.d.ts +2 -0
  61. package/types/logger/codesConstants.d.ts +117 -0
  62. package/types/logger/codesConstantsBrowser.d.ts +2 -0
  63. package/types/logger/codesConstantsNode.d.ts +14 -0
  64. package/types/logger/codesDebug.d.ts +1 -0
  65. package/types/logger/codesDebugBrowser.d.ts +1 -0
  66. package/types/logger/codesDebugNode.d.ts +1 -0
  67. package/types/logger/codesError.d.ts +1 -0
  68. package/types/logger/codesErrorNode.d.ts +1 -0
  69. package/types/logger/codesInfo.d.ts +1 -0
  70. package/types/logger/codesWarn.d.ts +1 -0
  71. package/types/logger/codesWarnNode.d.ts +1 -0
  72. package/types/logger/debugLogger.d.ts +2 -0
  73. package/types/logger/errorLogger.d.ts +2 -0
  74. package/types/logger/infoLogger.d.ts +2 -0
  75. package/types/logger/messages/debugBrowser.d.ts +1 -0
  76. package/types/logger/messages/debugNode.d.ts +1 -0
  77. package/types/logger/messages/errorNode.d.ts +1 -0
  78. package/types/logger/messages/warnNode.d.ts +1 -0
  79. package/types/logger/noopLogger.d.ts +2 -0
  80. package/types/logger/warnLogger.d.ts +2 -0
  81. package/types/sdkFactory/userConsentProps.d.ts +6 -0
  82. package/types/sdkManager/sdkManagerMethod.d.ts +6 -0
  83. package/types/services/types.d.ts +2 -2
  84. package/types/storages/getRegisteredSegments.d.ts +10 -0
  85. package/types/storages/inMemory/index.d.ts +10 -0
  86. package/types/storages/parseSegments.d.ts +6 -0
  87. package/types/sync/polling/fetchers/types.d.ts +2 -2
  88. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -2
  89. package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +35 -0
  90. package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +35 -0
  91. package/types/sync/polling/types.d.ts +11 -6
  92. package/types/sync/polling/updaters/segmentChangesUpdater.d.ts +1 -1
  93. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  94. package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
  95. package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
  96. package/types/sync/streaming/SSEHandler/types.d.ts +0 -4
  97. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +3 -24
  98. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +3 -23
  99. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +6 -33
  100. package/types/sync/streaming/UpdateWorkers/constants.d.ts +3 -0
  101. package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -2
  102. package/types/sync/streaming/pushManagerCS.d.ts +1 -0
  103. package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
  104. package/types/sync/streaming/pushManagerSS.d.ts +1 -0
  105. package/types/sync/streaming/types.d.ts +2 -2
  106. package/types/sync/submitters/telemetrySyncTask.d.ts +0 -0
  107. package/types/sync/syncManagerFromFile.d.ts +2 -0
  108. package/types/sync/syncManagerFromObject.d.ts +2 -0
  109. package/types/sync/syncManagerOffline.d.ts +9 -0
  110. package/types/sync/syncTask.d.ts +2 -3
  111. package/types/trackers/telemetryRecorder.d.ts +0 -0
  112. package/types/utils/Backoff.d.ts +2 -0
  113. package/types/utils/EventEmitter.d.ts +4 -0
  114. package/types/utils/consent.d.ts +2 -0
  115. package/types/utils/lang/errors.d.ts +10 -0
  116. package/types/utils/murmur3/commons.d.ts +12 -0
  117. package/types/utils/settingsValidation/buildMetadata.d.ts +3 -0
  118. package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
  119. package/types/utils/settingsValidation/logger.d.ts +11 -0
  120. package/types/utils/settingsValidation/runtime/browser.d.ts +2 -0
  121. package/types/utils/settingsValidation/runtime/node.d.ts +2 -0
  122. package/types/utils/settingsValidation/userConsent.d.ts +5 -0
  123. package/types/utils/timeTracker/index.d.ts +1 -70
  124. package/cjs/sync/offline/LocalhostFromFile.js +0 -13
  125. package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +0 -151
  126. package/esm/sync/offline/LocalhostFromFile.js +0 -9
  127. package/esm/sync/offline/splitsParser/splitsParserFromFile.js +0 -146
  128. package/src/sync/offline/LocalhostFromFile.ts +0 -12
  129. package/src/sync/offline/splitsParser/splitsParserFromFile.ts +0 -182
package/CHANGES.txt CHANGED
@@ -1,3 +1,8 @@
1
+ 1.6.0 (July XX, 2022)
2
+ - Updated browser listener to push remaining impressions and events on 'visibilitychange' and 'pagehide' DOM events, instead of 'unload', which is not reliable in mobile and modern Web browsers (See https://developer.chrome.com/blog/page-lifecycle-api/#legacy-lifecycle-apis-to-avoid).
3
+ - Updated the synchronization flow to be more reliable in the event of an edge case generating delay in cache purge propagation, keeping the SDK cache properly synced.
4
+ - Bugfixing - Removed js-yaml dependency to avoid issues in Node v14 when installing third-party dependencies that also uses js-yaml as a transitive dependency (Related to issue https://github.com/splitio/javascript-client/issues/662)
5
+
1
6
  1.5.0 (June 29, 2022)
2
7
  - Added a new config option to control the tasks that listen or poll for updates on feature flags and segments, via the new config sync.enabled . Running online Split will always pull the most recent updates upon initialization, this only affects updates fetching on a running instance. Useful when a consistent session experience is a must or to save resources when updates are not being used.
3
8
  - Updated telemetry logic to track the anonymous config for user consent flag set to declined or unknown.
@@ -8,11 +8,12 @@ var objectAssign_1 = require("../utils/lang/objectAssign");
8
8
  var constants_2 = require("../logger/constants");
9
9
  var consent_1 = require("../consent");
10
10
  var telemetrySubmitter_1 = require("../sync/submitters/telemetrySubmitter");
11
- // 'unload' event is used instead of 'beforeunload', since 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
12
- var UNLOAD_DOM_EVENT = 'unload';
11
+ var VISIBILITYCHANGE_EVENT = 'visibilitychange';
12
+ var PAGEHIDE_EVENT = 'pagehide';
13
+ var UNLOAD_EVENT = 'unload';
13
14
  var EVENT_NAME = 'for unload page event.';
14
15
  /**
15
- * We'll listen for 'unload' event over the window object, since it's the standard way to listen page reload and close.
16
+ * We'll listen for events over the window object.
16
17
  */
17
18
  var BrowserSignalListener = /** @class */ (function () {
18
19
  function BrowserSignalListener(syncManager, settings, storage, serviceApi) {
@@ -21,33 +22,50 @@ var BrowserSignalListener = /** @class */ (function () {
21
22
  this.storage = storage;
22
23
  this.serviceApi = serviceApi;
23
24
  this.flushData = this.flushData.bind(this);
25
+ this.flushDataIfHidden = this.flushDataIfHidden.bind(this);
26
+ this.stopSync = this.stopSync.bind(this);
24
27
  this.fromImpressionsCollector = impressionsSubmitter_1.fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled);
25
28
  }
26
29
  /**
27
30
  * start method.
28
- * Called when SplitFactory is initialized.
29
- * We add a handler on unload events. The handler flushes remaining impressions and events to the backend.
31
+ * Called when SplitFactory is initialized, it adds event listeners to close streaming and flush impressions and events.
30
32
  */
31
33
  BrowserSignalListener.prototype.start = function () {
34
+ this.settings.log.debug(constants_2.CLEANUP_REGISTERING, [EVENT_NAME]);
35
+ if (typeof document !== 'undefined' && document.addEventListener) {
36
+ // Flush data whenever the page is hidden or unloaded.
37
+ document.addEventListener(VISIBILITYCHANGE_EVENT, this.flushDataIfHidden);
38
+ }
32
39
  if (typeof window !== 'undefined' && window.addEventListener) {
33
- this.settings.log.debug(constants_2.CLEANUP_REGISTERING, [EVENT_NAME]);
34
- window.addEventListener(UNLOAD_DOM_EVENT, this.flushData);
40
+ // Some browsers like Safari does not fire the `visibilitychange` event when the page is being unloaded. So we also flush data in the `pagehide` event.
41
+ // If both events are triggered, the last one will find the storage empty, so no duplicated data will be submitted.
42
+ window.addEventListener(PAGEHIDE_EVENT, this.flushData);
43
+ // Stop streaming on 'unload' event. Used instead of 'beforeunload', because 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
44
+ window.addEventListener(UNLOAD_EVENT, this.stopSync);
35
45
  }
36
46
  };
37
47
  /**
38
48
  * stop method.
39
- * Called when client is destroyed.
40
- * We need to remove the handler for unload events, since it can break if called when Split context was destroyed.
49
+ * Called when client is destroyed, it removes event listeners.
41
50
  */
42
51
  BrowserSignalListener.prototype.stop = function () {
52
+ this.settings.log.debug(constants_2.CLEANUP_DEREGISTERING, [EVENT_NAME]);
53
+ if (typeof document !== 'undefined' && document.removeEventListener) {
54
+ document.removeEventListener(VISIBILITYCHANGE_EVENT, this.flushDataIfHidden);
55
+ }
43
56
  if (typeof window !== 'undefined' && window.removeEventListener) {
44
- this.settings.log.debug(constants_2.CLEANUP_DEREGISTERING, [EVENT_NAME]);
45
- window.removeEventListener(UNLOAD_DOM_EVENT, this.flushData);
57
+ window.removeEventListener(PAGEHIDE_EVENT, this.flushData);
58
+ window.removeEventListener(UNLOAD_EVENT, this.stopSync);
46
59
  }
47
60
  };
61
+ BrowserSignalListener.prototype.stopSync = function () {
62
+ // Close streaming connection
63
+ if (this.syncManager && this.syncManager.pushManager)
64
+ this.syncManager.pushManager.stop();
65
+ };
48
66
  /**
49
67
  * flushData method.
50
- * Called when unload event is triggered. It flushed remaining impressions and events to the backend,
68
+ * Called when pagehide event is triggered. It flushed remaining impressions and events to the backend,
51
69
  * using beacon API if possible, or falling back to regular post transport.
52
70
  */
53
71
  BrowserSignalListener.prototype.flushData = function () {
@@ -71,9 +89,11 @@ var BrowserSignalListener = /** @class */ (function () {
71
89
  var telemetryCacheAdapter = (0, telemetrySubmitter_1.telemetryCacheStatsAdapter)(this.storage.telemetry, this.storage.splits, this.storage.segments);
72
90
  this._flushData(telemetryUrl + '/v1/metrics/usage/beacon', telemetryCacheAdapter, this.serviceApi.postMetricsUsage);
73
91
  }
74
- // Close streaming connection
75
- if (this.syncManager.pushManager)
76
- this.syncManager.pushManager.stop();
92
+ };
93
+ BrowserSignalListener.prototype.flushDataIfHidden = function () {
94
+ // Precondition: document defined
95
+ if (document.visibilityState === 'hidden')
96
+ this.flushData(); // On a 'visibilitychange' event, flush data if state is hidden
77
97
  };
78
98
  BrowserSignalListener.prototype._flushData = function (url, cache, postService, fromCacheToPayload, extraMetadata) {
79
99
  // if there is data in cache, send it to backend
@@ -37,12 +37,12 @@ function splitApiFactory(settings, platform, telemetryTracker) {
37
37
  }
38
38
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(constants_1.TOKEN));
39
39
  },
40
- fetchSplitChanges: function (since, noCache) {
41
- var url = urls.sdk + "/splitChanges?since=" + since + (filterQueryString || '');
40
+ fetchSplitChanges: function (since, noCache, till) {
41
+ var url = urls.sdk + "/splitChanges?since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
42
42
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.SPLITS));
43
43
  },
44
- fetchSegmentChanges: function (since, segmentName, noCache) {
45
- var url = urls.sdk + "/segmentChanges/" + segmentName + "?since=" + since;
44
+ fetchSegmentChanges: function (since, segmentName, noCache, till) {
45
+ var url = urls.sdk + "/segmentChanges/" + segmentName + "?since=" + since + (till ? '&till=' + till : '');
46
46
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.SEGMENT));
47
47
  },
48
48
  fetchMySegments: function (userMatchingKey, noCache) {
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.segmentChangesFetcherFactory = void 0;
4
4
  var tslib_1 = require("tslib");
5
- function greedyFetch(fetchSegmentChanges, since, segmentName, noCache) {
6
- return fetchSegmentChanges(since, segmentName, noCache)
5
+ function greedyFetch(fetchSegmentChanges, since, segmentName, noCache, targetTill) {
6
+ return fetchSegmentChanges(since, segmentName, noCache, targetTill)
7
7
  .then(function (resp) { return resp.json(); })
8
8
  .then(function (json) {
9
9
  var since = json.since, till = json.till;
@@ -11,7 +11,7 @@ function greedyFetch(fetchSegmentChanges, since, segmentName, noCache) {
11
11
  return [json];
12
12
  }
13
13
  else {
14
- return Promise.all([json, greedyFetch(fetchSegmentChanges, till, segmentName, noCache)]).then(function (flatMe) {
14
+ return Promise.all([json, greedyFetch(fetchSegmentChanges, till, segmentName, noCache, targetTill)]).then(function (flatMe) {
15
15
  return (0, tslib_1.__spreadArray)([flatMe[0]], flatMe[1], true);
16
16
  });
17
17
  }
@@ -22,10 +22,10 @@ function greedyFetch(fetchSegmentChanges, since, segmentName, noCache) {
22
22
  * SegmentChanges fetcher is a wrapper around `segmentChanges` API service that parses the response and handle errors and retries.
23
23
  */
24
24
  function segmentChangesFetcherFactory(fetchSegmentChanges) {
25
- return function segmentChangesFetcher(since, segmentName, noCache,
25
+ return function segmentChangesFetcher(since, segmentName, noCache, till,
26
26
  // Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
27
27
  decorator) {
28
- var segmentsPromise = greedyFetch(fetchSegmentChanges, since, segmentName, noCache);
28
+ var segmentsPromise = greedyFetch(fetchSegmentChanges, since, segmentName, noCache, till);
29
29
  if (decorator)
30
30
  segmentsPromise = decorator(segmentsPromise);
31
31
  return segmentsPromise;
@@ -6,10 +6,10 @@ exports.splitChangesFetcherFactory = void 0;
6
6
  * SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors.
7
7
  */
8
8
  function splitChangesFetcherFactory(fetchSplitChanges) {
9
- return function splitChangesFetcher(since, noCache,
9
+ return function splitChangesFetcher(since, noCache, till,
10
10
  // Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
11
11
  decorator) {
12
- var splitsPromise = fetchSplitChanges(since, noCache);
12
+ var splitsPromise = fetchSplitChanges(since, noCache, till);
13
13
  if (decorator)
14
14
  splitsPromise = decorator(splitsPromise);
15
15
  return splitsPromise.then(function (resp) { return resp.json(); });
@@ -18,54 +18,54 @@ var thenable_1 = require("../../../utils/promise/thenable");
18
18
  */
19
19
  function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness) {
20
20
  var readyOnAlreadyExistentState = true;
21
+ function updateSegment(segmentName, noCache, till, fetchOnlyNew) {
22
+ log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing segment " + segmentName);
23
+ var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
24
+ return sincePromise.then(function (since) {
25
+ // if fetchOnlyNew flag, avoid processing already fetched segments
26
+ if (fetchOnlyNew && since !== -1)
27
+ return -1;
28
+ return segmentChangesFetcher(since, segmentName, noCache, till).then(function (changes) {
29
+ var changeNumber = -1;
30
+ var results = [];
31
+ changes.forEach(function (x) {
32
+ if (x.added.length > 0)
33
+ results.push(segments.addToSegment(segmentName, x.added));
34
+ if (x.removed.length > 0)
35
+ results.push(segments.removeFromSegment(segmentName, x.removed));
36
+ if (x.added.length > 0 || x.removed.length > 0) {
37
+ results.push(segments.setChangeNumber(segmentName, x.till));
38
+ changeNumber = x.till;
39
+ }
40
+ log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processed " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
41
+ });
42
+ // If at least one storage operation result is a promise, join all in a single promise.
43
+ if (results.some(function (result) { return (0, thenable_1.thenable)(result); }))
44
+ return Promise.all(results).then(function () { return changeNumber; });
45
+ return changeNumber;
46
+ });
47
+ });
48
+ }
21
49
  /**
22
50
  * Segments updater returns a promise that resolves with a `false` boolean value if it fails at least to fetch a segment or synchronize it with the storage.
23
51
  * Thus, a false result doesn't imply that SDK_SEGMENTS_ARRIVED was not emitted.
24
52
  * Returned promise will not be rejected.
25
53
  *
26
- * @param {string[] | undefined} segmentNames list of segment names to fetch. By passing `undefined` it fetches the list of segments registered at the storage
27
- * @param {boolean | undefined} noCache true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
28
54
  * @param {boolean | undefined} fetchOnlyNew if true, only fetch the segments that not exists, i.e., which `changeNumber` is equal to -1.
29
55
  * This param is used by SplitUpdateWorker on server-side SDK, to fetch new registered segments on SPLIT_UPDATE notifications.
56
+ * @param {string | undefined} segmentName segment name to fetch. By passing `undefined` it fetches the list of segments registered at the storage
57
+ * @param {boolean | undefined} noCache true to revalidate data to fetch on a SEGMENT_UPDATE notifications.
58
+ * @param {number | undefined} till till target for the provided segmentName, for CDN bypass.
30
59
  */
31
- return function segmentChangesUpdater(segmentNames, noCache, fetchOnlyNew) {
60
+ return function segmentChangesUpdater(fetchOnlyNew, segmentName, noCache, till) {
32
61
  log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Started segments update");
33
62
  // If not a segment name provided, read list of available segments names to be updated.
34
- var segmentsPromise = Promise.resolve(segmentNames ? segmentNames : segments.getRegisteredSegments());
63
+ var segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
35
64
  return segmentsPromise.then(function (segmentNames) {
36
65
  // Async fetchers are collected here.
37
66
  var updaters = [];
38
- var _loop_1 = function (index) {
39
- var segmentName = segmentNames[index];
40
- log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing segment " + segmentName);
41
- var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
42
- updaters.push(sincePromise.then(function (since) {
43
- // if fetchOnlyNew flag, avoid processing already fetched segments
44
- if (fetchOnlyNew && since !== -1)
45
- return -1;
46
- return segmentChangesFetcher(since, segmentName, noCache).then(function (changes) {
47
- var changeNumber = -1;
48
- var results = [];
49
- changes.forEach(function (x) {
50
- if (x.added.length > 0)
51
- results.push(segments.addToSegment(segmentName, x.added));
52
- if (x.removed.length > 0)
53
- results.push(segments.removeFromSegment(segmentName, x.removed));
54
- if (x.added.length > 0 || x.removed.length > 0) {
55
- results.push(segments.setChangeNumber(segmentName, x.till));
56
- changeNumber = x.till;
57
- }
58
- log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processed " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
59
- });
60
- // If at least one storage operation result is a promise, join all in a single promise.
61
- if (results.some(function (result) { return (0, thenable_1.thenable)(result); }))
62
- return Promise.all(results).then(function () { return changeNumber; });
63
- return changeNumber;
64
- });
65
- }));
66
- };
67
67
  for (var index = 0; index < segmentNames.length; index++) {
68
- _loop_1(index);
68
+ updaters.push(updateSegment(segmentNames[index], noCache, till, fetchOnlyNew));
69
69
  }
70
70
  return Promise.all(updaters).then(function (shouldUpdateFlags) {
71
71
  // if at least one segment fetch succeeded, mark segments ready
@@ -84,16 +84,17 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments,
84
84
  * Returned promise will not be rejected.
85
85
  *
86
86
  * @param {boolean | undefined} noCache true to revalidate data to fetch
87
+ * @param {boolean | undefined} till query param to bypass CDN requests
87
88
  */
88
- return function splitChangesUpdater(noCache) {
89
+ return function splitChangesUpdater(noCache, till) {
89
90
  /**
90
91
  * @param {number} since current changeNumber at splitsCache
91
- * @param {number} retry current number of retry attemps
92
+ * @param {number} retry current number of retry attempts
92
93
  */
93
94
  function _splitChangesUpdater(since, retry) {
94
95
  if (retry === void 0) { retry = 0; }
95
96
  log.debug(constants_2.SYNC_SPLITS_FETCH, [since]);
96
- var fetcherPromise = splitChangesFetcher(since, noCache, _promiseDecorator)
97
+ var fetcherPromise = splitChangesFetcher(since, noCache, till, _promiseDecorator)
97
98
  .then(function (splitChanges) {
98
99
  startingUp = false;
99
100
  var mutation = computeSplitsMutation(splitChanges.splits);
@@ -3,59 +3,59 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MySegmentsUpdateWorker = void 0;
4
4
  var Backoff_1 = require("../../../utils/Backoff");
5
5
  /**
6
- * MySegmentsUpdateWorker class
6
+ * MySegmentsUpdateWorker factory
7
7
  */
8
- var MySegmentsUpdateWorker = /** @class */ (function () {
9
- /**
10
- * @param {Object} mySegmentsSyncTask task for syncing mySegments data
11
- */
12
- function MySegmentsUpdateWorker(mySegmentsSyncTask) {
13
- this.mySegmentsSyncTask = mySegmentsSyncTask;
14
- this.maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
15
- this.handleNewEvent = false;
16
- this.segmentsData = undefined; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
17
- this.currentChangeNumber = -1; // @TODO: remove once `/mySegments` endpoint provides the changeNumber
18
- this.put = this.put.bind(this);
19
- this.__handleMySegmentsUpdateCall = this.__handleMySegmentsUpdateCall.bind(this);
20
- this.backoff = new Backoff_1.Backoff(this.__handleMySegmentsUpdateCall);
21
- }
22
- // Private method
23
- // Precondition: this.mySegmentsSyncTask.isSynchronizingMySegments === false
24
- MySegmentsUpdateWorker.prototype.__handleMySegmentsUpdateCall = function () {
25
- var _this = this;
26
- if (this.maxChangeNumber > this.currentChangeNumber) {
27
- this.handleNewEvent = false;
28
- var currentMaxChangeNumber_1 = this.maxChangeNumber;
8
+ function MySegmentsUpdateWorker(mySegmentsSyncTask) {
9
+ var maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
10
+ var currentChangeNumber = -1;
11
+ var handleNewEvent = false;
12
+ var isHandlingEvent;
13
+ var _segmentsData; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
14
+ var backoff = new Backoff_1.Backoff(__handleMySegmentsUpdateCall);
15
+ function __handleMySegmentsUpdateCall() {
16
+ isHandlingEvent = true;
17
+ if (maxChangeNumber > currentChangeNumber) {
18
+ handleNewEvent = false;
19
+ var currentMaxChangeNumber_1 = maxChangeNumber;
29
20
  // fetch mySegments revalidating data if cached
30
- this.mySegmentsSyncTask.execute(this.segmentsData, true).then(function (result) {
21
+ mySegmentsSyncTask.execute(_segmentsData, true).then(function (result) {
22
+ if (!isHandlingEvent)
23
+ return; // halt if `stop` has been called
31
24
  if (result !== false) // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
32
- _this.currentChangeNumber = Math.max(_this.currentChangeNumber, currentMaxChangeNumber_1); // use `currentMaxChangeNumber`, in case that `this.maxChangeNumber` was updated during fetch.
33
- if (_this.handleNewEvent) {
34
- _this.__handleMySegmentsUpdateCall();
25
+ currentChangeNumber = Math.max(currentChangeNumber, currentMaxChangeNumber_1); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
26
+ if (handleNewEvent) {
27
+ __handleMySegmentsUpdateCall();
35
28
  }
36
29
  else {
37
- _this.backoff.scheduleCall();
30
+ backoff.scheduleCall();
38
31
  }
39
32
  });
40
33
  }
34
+ else {
35
+ isHandlingEvent = false;
36
+ }
37
+ }
38
+ return {
39
+ /**
40
+ * Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
41
+ *
42
+ * @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
43
+ * @param {SegmentsData | undefined} segmentsData might be undefined
44
+ */
45
+ put: function (changeNumber, segmentsData) {
46
+ if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
47
+ return;
48
+ maxChangeNumber = changeNumber;
49
+ handleNewEvent = true;
50
+ _segmentsData = segmentsData;
51
+ if (backoff.timeoutID || !isHandlingEvent)
52
+ __handleMySegmentsUpdateCall();
53
+ backoff.reset();
54
+ },
55
+ stop: function () {
56
+ isHandlingEvent = false;
57
+ backoff.reset();
58
+ }
41
59
  };
42
- /**
43
- * Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
44
- *
45
- * @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
46
- * @param {SegmentsData | undefined} segmentsData might be undefined
47
- */
48
- MySegmentsUpdateWorker.prototype.put = function (changeNumber, segmentsData) {
49
- if (changeNumber <= this.currentChangeNumber || changeNumber <= this.maxChangeNumber)
50
- return;
51
- this.maxChangeNumber = changeNumber;
52
- this.handleNewEvent = true;
53
- this.backoff.reset();
54
- this.segmentsData = segmentsData;
55
- if (this.mySegmentsSyncTask.isExecuting())
56
- return;
57
- this.__handleMySegmentsUpdateCall();
58
- };
59
- return MySegmentsUpdateWorker;
60
- }());
60
+ }
61
61
  exports.MySegmentsUpdateWorker = MySegmentsUpdateWorker;
@@ -2,73 +2,91 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SegmentsUpdateWorker = void 0;
4
4
  var Backoff_1 = require("../../../utils/Backoff");
5
+ var constants_1 = require("./constants");
5
6
  /**
6
- * SegmentUpdateWorker class
7
+ * SegmentsUpdateWorker factory
7
8
  */
8
- var SegmentsUpdateWorker = /** @class */ (function () {
9
- /**
10
- * @param {Object} segmentsCache segments data cache
11
- * @param {Object} segmentsSyncTask task for syncing segments data
12
- */
13
- function SegmentsUpdateWorker(segmentsSyncTask, segmentsCache) {
14
- this.segmentsCache = segmentsCache;
15
- this.segmentsSyncTask = segmentsSyncTask;
16
- this.maxChangeNumbers = {};
17
- this.handleNewEvent = false;
18
- this.put = this.put.bind(this);
19
- this.__handleSegmentUpdateCall = this.__handleSegmentUpdateCall.bind(this);
20
- this.backoff = new Backoff_1.Backoff(this.__handleSegmentUpdateCall);
9
+ function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
10
+ // Handles retries with CDN bypass per segment name
11
+ function SegmentUpdateWorker(segment) {
12
+ var maxChangeNumber = 0;
13
+ var handleNewEvent = false;
14
+ var isHandlingEvent;
15
+ var cdnBypass;
16
+ var backoff = new Backoff_1.Backoff(__handleSegmentUpdateCall, constants_1.FETCH_BACKOFF_BASE, constants_1.FETCH_BACKOFF_MAX_WAIT);
17
+ function __handleSegmentUpdateCall() {
18
+ isHandlingEvent = true;
19
+ if (maxChangeNumber > segmentsCache.getChangeNumber(segment)) {
20
+ handleNewEvent = false;
21
+ // fetch segments revalidating data if cached
22
+ segmentsSyncTask.execute(false, segment, true, cdnBypass ? maxChangeNumber : undefined).then(function () {
23
+ if (!isHandlingEvent)
24
+ return; // halt if `stop` has been called
25
+ if (handleNewEvent) {
26
+ __handleSegmentUpdateCall();
27
+ }
28
+ else {
29
+ var attempts = backoff.attempts + 1;
30
+ if (maxChangeNumber <= segmentsCache.getChangeNumber(segment)) {
31
+ log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
32
+ isHandlingEvent = false;
33
+ return;
34
+ }
35
+ if (attempts < constants_1.FETCH_BACKOFF_MAX_RETRIES) {
36
+ backoff.scheduleCall();
37
+ return;
38
+ }
39
+ if (cdnBypass) {
40
+ log.debug("No changes fetched after " + attempts + " attempts with CDN bypassed.");
41
+ isHandlingEvent = false;
42
+ }
43
+ else {
44
+ backoff.reset();
45
+ cdnBypass = true;
46
+ __handleSegmentUpdateCall();
47
+ }
48
+ }
49
+ });
50
+ }
51
+ else {
52
+ isHandlingEvent = false;
53
+ }
54
+ }
55
+ return {
56
+ put: function (changeNumber) {
57
+ var currentChangeNumber = segmentsCache.getChangeNumber(segment);
58
+ if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
59
+ return;
60
+ maxChangeNumber = changeNumber;
61
+ handleNewEvent = true;
62
+ cdnBypass = false;
63
+ if (backoff.timeoutID || !isHandlingEvent)
64
+ __handleSegmentUpdateCall();
65
+ backoff.reset();
66
+ },
67
+ stop: function () {
68
+ isHandlingEvent = false;
69
+ backoff.reset();
70
+ }
71
+ };
21
72
  }
22
- // Private method
23
- // Precondition: this.segmentsSyncTask.isSynchronizingSegments === false
24
- // Approach similar to MySegmentsUpdateWorker due to differences on Segments notifications and endpoint changeNumbers
25
- SegmentsUpdateWorker.prototype.__handleSegmentUpdateCall = function () {
26
- var _this = this;
27
- var segmentsToFetch = Object.keys(this.maxChangeNumbers).filter(function (segmentName) {
28
- return _this.maxChangeNumbers[segmentName] > _this.segmentsCache.getChangeNumber(segmentName);
29
- });
30
- if (segmentsToFetch.length > 0) {
31
- this.handleNewEvent = false;
32
- var currentMaxChangeNumbers_1 = segmentsToFetch.map(function (segmentToFetch) { return _this.maxChangeNumbers[segmentToFetch]; });
33
- // fetch segments revalidating data if cached
34
- this.segmentsSyncTask.execute(segmentsToFetch, true).then(function (result) {
35
- // Unlike `SplitUpdateWorker` where changeNumber is consistent between notification and endpoint, if there is no error,
36
- // we must clean the `maxChangeNumbers` of those segments that didn't receive a new update notification during the fetch.
37
- if (result !== false) {
38
- segmentsToFetch.forEach(function (fetchedSegment, index) {
39
- if (_this.maxChangeNumbers[fetchedSegment] === currentMaxChangeNumbers_1[index])
40
- _this.maxChangeNumbers[fetchedSegment] = -1;
41
- });
42
- }
43
- else {
44
- // recursive invocation with backoff if there was some error
45
- _this.backoff.scheduleCall();
46
- }
47
- // immediate recursive invocation if a new notification was queued during fetch
48
- if (_this.handleNewEvent) {
49
- _this.__handleSegmentUpdateCall();
50
- }
51
- });
73
+ var segments = {};
74
+ return {
75
+ /**
76
+ * Invoked by NotificationProcessor on SEGMENT_UPDATE event
77
+ *
78
+ * @param {number} changeNumber change number of the SEGMENT_UPDATE notification
79
+ * @param {string} segmentName segment name of the SEGMENT_UPDATE notification
80
+ */
81
+ put: function (_a) {
82
+ var changeNumber = _a.changeNumber, segmentName = _a.segmentName;
83
+ if (!segments[segmentName])
84
+ segments[segmentName] = SegmentUpdateWorker(segmentName);
85
+ segments[segmentName].put(changeNumber);
86
+ },
87
+ stop: function () {
88
+ Object.keys(segments).forEach(function (segmentName) { return segments[segmentName].stop(); });
52
89
  }
53
90
  };
54
- /**
55
- * Invoked by NotificationProcessor on SEGMENT_UPDATE event
56
- *
57
- * @param {number} changeNumber change number of the SEGMENT_UPDATE notification
58
- * @param {string} segmentName segment name of the SEGMENT_UPDATE notification
59
- */
60
- SegmentsUpdateWorker.prototype.put = function (_a) {
61
- var changeNumber = _a.changeNumber, segmentName = _a.segmentName;
62
- var currentChangeNumber = this.segmentsCache.getChangeNumber(segmentName);
63
- if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumbers[segmentName])
64
- return;
65
- this.maxChangeNumbers[segmentName] = changeNumber;
66
- this.handleNewEvent = true;
67
- this.backoff.reset();
68
- if (this.segmentsSyncTask.isExecuting())
69
- return;
70
- this.__handleSegmentUpdateCall();
71
- };
72
- return SegmentsUpdateWorker;
73
- }());
91
+ }
74
92
  exports.SegmentsUpdateWorker = SegmentsUpdateWorker;