@splitsoftware/splitio-commons 1.5.1-rc.0 → 1.5.1-rc.3

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 (71) hide show
  1. package/CHANGES.txt +4 -2
  2. package/cjs/integrations/ga/GaToSplit.js +1 -1
  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/integrations/ga/GaToSplit.js +1 -1
  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/integrations/ga/GaToSplit.ts +1 -1
  30. package/src/integrations/ga/autoRequire.js +16 -16
  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/services/types.d.ts +2 -2
  53. package/types/sync/polling/fetchers/types.d.ts +2 -2
  54. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -2
  55. package/types/sync/polling/types.d.ts +11 -6
  56. package/types/sync/polling/updaters/segmentChangesUpdater.d.ts +1 -1
  57. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  58. package/types/sync/streaming/SSEHandler/types.d.ts +0 -4
  59. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +3 -24
  60. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +3 -23
  61. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +6 -33
  62. package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -2
  63. package/types/sync/streaming/types.d.ts +2 -2
  64. package/types/sync/syncTask.d.ts +2 -3
  65. package/types/utils/Backoff.d.ts +2 -0
  66. package/cjs/sync/offline/LocalhostFromFile.js +0 -13
  67. package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +0 -151
  68. package/esm/sync/offline/LocalhostFromFile.js +0 -9
  69. package/esm/sync/offline/splitsParser/splitsParserFromFile.js +0 -146
  70. package/src/sync/offline/LocalhostFromFile.ts +0 -12
  71. package/src/sync/offline/splitsParser/splitsParserFromFile.ts +0 -182
@@ -1,79 +1,94 @@
1
1
  import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
2
2
  import { Backoff } from '../../../utils/Backoff';
3
+ import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
3
4
  /**
4
- * SplitsUpdateWorker class
5
+ * SplitsUpdateWorker factory
5
6
  */
6
- var SplitsUpdateWorker = /** @class */ (function () {
7
- /**
8
- * @param {Object} splitsCache splits data cache
9
- * @param {Object} splitsSyncTask task for syncing splits data
10
- * @param {Object} splitsEventEmitter emitter for splits data events
11
- */
12
- function SplitsUpdateWorker(splitsCache, splitsSyncTask, splitsEventEmitter, segmentsSyncTask) {
13
- this.splitsCache = splitsCache;
14
- this.splitsSyncTask = splitsSyncTask;
15
- this.splitsEventEmitter = splitsEventEmitter;
16
- this.segmentsSyncTask = segmentsSyncTask;
17
- this.maxChangeNumber = 0;
18
- this.handleNewEvent = false;
19
- this.put = this.put.bind(this);
20
- this.killSplit = this.killSplit.bind(this);
21
- this.__handleSplitUpdateCall = this.__handleSplitUpdateCall.bind(this);
22
- this.backoff = new Backoff(this.__handleSplitUpdateCall);
23
- }
24
- // Private method
25
- // Preconditions: this.splitsSyncTask.isSynchronizingSplits === false
26
- SplitsUpdateWorker.prototype.__handleSplitUpdateCall = function () {
27
- var _this = this;
28
- if (this.maxChangeNumber > this.splitsCache.getChangeNumber()) {
29
- this.handleNewEvent = false;
7
+ export function SplitsUpdateWorker(log, splitsCache, splitsSyncTask, splitsEventEmitter, segmentsSyncTask) {
8
+ var maxChangeNumber = 0;
9
+ var handleNewEvent = false;
10
+ var isHandlingEvent;
11
+ var cdnBypass;
12
+ var backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
13
+ function __handleSplitUpdateCall() {
14
+ isHandlingEvent = true;
15
+ if (maxChangeNumber > splitsCache.getChangeNumber()) {
16
+ handleNewEvent = false;
30
17
  // fetch splits revalidating data if cached
31
- this.splitsSyncTask.execute(true).then(function () {
32
- if (_this.handleNewEvent) {
33
- _this.__handleSplitUpdateCall();
18
+ splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined).then(function () {
19
+ if (!isHandlingEvent)
20
+ return; // halt if `stop` has been called
21
+ if (handleNewEvent) {
22
+ __handleSplitUpdateCall();
34
23
  }
35
24
  else {
36
25
  // fetch new registered segments for server-side API. Not retrying on error
37
- if (_this.segmentsSyncTask)
38
- _this.segmentsSyncTask.execute(undefined, false, true);
39
- _this.backoff.scheduleCall();
26
+ if (segmentsSyncTask)
27
+ segmentsSyncTask.execute(true);
28
+ var attempts = backoff.attempts + 1;
29
+ if (maxChangeNumber <= splitsCache.getChangeNumber()) {
30
+ log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
31
+ isHandlingEvent = false;
32
+ return;
33
+ }
34
+ if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
35
+ backoff.scheduleCall();
36
+ return;
37
+ }
38
+ if (cdnBypass) {
39
+ log.debug("No changes fetched after " + attempts + " attempts with CDN bypassed.");
40
+ isHandlingEvent = false;
41
+ }
42
+ else {
43
+ backoff.reset();
44
+ cdnBypass = true;
45
+ __handleSplitUpdateCall();
46
+ }
40
47
  }
41
48
  });
42
49
  }
43
- };
50
+ else {
51
+ isHandlingEvent = false;
52
+ }
53
+ }
44
54
  /**
45
55
  * Invoked by NotificationProcessor on SPLIT_UPDATE event
46
56
  *
47
57
  * @param {number} changeNumber change number of the SPLIT_UPDATE notification
48
58
  */
49
- SplitsUpdateWorker.prototype.put = function (_a) {
59
+ function put(_a) {
50
60
  var changeNumber = _a.changeNumber;
51
- var currentChangeNumber = this.splitsCache.getChangeNumber();
52
- if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumber)
53
- return;
54
- this.maxChangeNumber = changeNumber;
55
- this.handleNewEvent = true;
56
- this.backoff.reset();
57
- if (this.splitsSyncTask.isExecuting())
61
+ var currentChangeNumber = splitsCache.getChangeNumber();
62
+ if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
58
63
  return;
59
- this.__handleSplitUpdateCall();
60
- };
61
- /**
62
- * Invoked by NotificationProcessor on SPLIT_KILL event
63
- *
64
- * @param {number} changeNumber change number of the SPLIT_UPDATE notification
65
- * @param {string} splitName name of split to kill
66
- * @param {string} defaultTreatment default treatment value
67
- */
68
- SplitsUpdateWorker.prototype.killSplit = function (_a) {
69
- var changeNumber = _a.changeNumber, splitName = _a.splitName, defaultTreatment = _a.defaultTreatment;
70
- if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
71
- // trigger an SDK_UPDATE if Split was killed locally
72
- this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
64
+ maxChangeNumber = changeNumber;
65
+ handleNewEvent = true;
66
+ cdnBypass = false;
67
+ if (backoff.timeoutID || !isHandlingEvent)
68
+ __handleSplitUpdateCall();
69
+ backoff.reset();
70
+ }
71
+ return {
72
+ put: put,
73
+ /**
74
+ * Invoked by NotificationProcessor on SPLIT_KILL event
75
+ *
76
+ * @param {number} changeNumber change number of the SPLIT_UPDATE notification
77
+ * @param {string} splitName name of split to kill
78
+ * @param {string} defaultTreatment default treatment value
79
+ */
80
+ killSplit: function (_a) {
81
+ var changeNumber = _a.changeNumber, splitName = _a.splitName, defaultTreatment = _a.defaultTreatment;
82
+ if (splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
83
+ // trigger an SDK_UPDATE if Split was killed locally
84
+ splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
85
+ }
86
+ // queues the SplitChanges fetch (only if changeNumber is newer)
87
+ put({ changeNumber: changeNumber });
88
+ },
89
+ stop: function () {
90
+ isHandlingEvent = false;
91
+ backoff.reset();
73
92
  }
74
- // queues the SplitChanges fetch (only if changeNumber is newer)
75
- this.put({ changeNumber: changeNumber });
76
93
  };
77
- return SplitsUpdateWorker;
78
- }());
79
- export { SplitsUpdateWorker };
94
+ }
@@ -0,0 +1,3 @@
1
+ export var FETCH_BACKOFF_BASE = 10000; // backoff base starting at 10 seconds
2
+ export var FETCH_BACKOFF_MAX_WAIT = 60000; // don't wait for more than 1 minute
3
+ export var FETCH_BACKOFF_MAX_RETRIES = 10; // max retries
@@ -42,9 +42,9 @@ export function pushManagerFactory(params, pollingManager) {
42
42
  sseClient.setEventHandler(sseHandler);
43
43
  // init workers
44
44
  // MySegmentsUpdateWorker (client-side) are initiated in `add` method
45
- var segmentsUpdateWorker = userKey ? undefined : new SegmentsUpdateWorker(pollingManager.segmentsSyncTask, storage.segments);
45
+ var segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask, storage.segments);
46
46
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
47
- var splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
47
+ var splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
48
48
  // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
49
49
  var userKeyHashes = {};
50
50
  // [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
@@ -137,21 +137,20 @@ export function pushManagerFactory(params, pollingManager) {
137
137
  }
138
138
  // cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
139
139
  function stopWorkers() {
140
- splitsUpdateWorker.backoff.reset();
140
+ splitsUpdateWorker.stop();
141
141
  if (userKey)
142
142
  forOwn(clients, function (_a) {
143
143
  var worker = _a.worker;
144
- return worker.backoff.reset();
144
+ return worker.stop();
145
145
  });
146
146
  else
147
- segmentsUpdateWorker.backoff.reset();
147
+ segmentsUpdateWorker.stop();
148
148
  }
149
149
  pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
150
150
  // Only required when streaming connects after a PUSH_RETRYABLE_ERROR.
151
151
  // Otherwise it is unnecessary (e.g, STREAMING_RESUMED).
152
152
  pushEmitter.on(PUSH_SUBSYSTEM_UP, function () {
153
153
  connectPushRetryBackoff.reset();
154
- stopWorkers();
155
154
  });
156
155
  /** Fallback to polling without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
157
156
  pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() {
@@ -287,7 +286,7 @@ export function pushManagerFactory(params, pollingManager) {
287
286
  var hash = hashUserKey(userKey);
288
287
  if (!userKeyHashes[hash]) {
289
288
  userKeyHashes[hash] = userKey;
290
- clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
289
+ clients[userKey] = { hash64: hash64(userKey), worker: MySegmentsUpdateWorker(mySegmentsSyncTask) };
291
290
  connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
292
291
  // Reconnects in case of a new client.
293
292
  // Run in next event-loop cycle to save authentication calls
@@ -1,8 +1,7 @@
1
1
  import { SYNC_TASK_EXECUTE, SYNC_TASK_START, SYNC_TASK_STOP } from '../logger/constants';
2
2
  /**
3
- * Creates a syncTask that handles the periodic execution of a given task ("start" and "stop" methods).
4
- * The task can be also executed by calling the "execute" method. Multiple execute calls are chained to run secuentially and avoid race conditions.
5
- * For example, submitters executed on SDK destroy or full queue, while periodic execution is pending.
3
+ * Creates an object that handles the periodic execution of a given task via "start" and "stop" methods.
4
+ * The task can be also executed by calling the "execute" method. Multiple calls run sequentially to avoid race conditions (e.g., submitters executed on SDK destroy or full queue, while periodic execution is pending).
6
5
  *
7
6
  * @param log Logger instance.
8
7
  * @param task Task to execute that returns a promise that NEVER REJECTS. Otherwise, periodic execution can result in Unhandled Promise Rejections.
@@ -12,8 +11,10 @@ import { SYNC_TASK_EXECUTE, SYNC_TASK_START, SYNC_TASK_STOP } from '../logger/co
12
11
  */
13
12
  export function syncTaskFactory(log, task, period, taskName) {
14
13
  if (taskName === void 0) { taskName = 'task'; }
15
- // Task promise while it is pending. Undefined once the promise is resolved
16
- var pendingTask;
14
+ // Flag that indicates if the task is executing
15
+ var executing = 0;
16
+ // Promise chain to resolve tasks sequentially
17
+ var promiseChain;
17
18
  // flag that indicates if the task periodic execution has been started/stopped.
18
19
  var running = false;
19
20
  // Auxiliar counter used to avoid race condition when calling `start` & `stop` intermittently
@@ -27,19 +28,15 @@ export function syncTaskFactory(log, task, period, taskName) {
27
28
  for (var _i = 0; _i < arguments.length; _i++) {
28
29
  args[_i] = arguments[_i];
29
30
  }
30
- // If task is executing, chain the new execution
31
- if (pendingTask) {
32
- return pendingTask.then(function () {
33
- return execute.apply(void 0, args);
34
- });
35
- }
36
- // Execute task
31
+ executing++;
37
32
  log.debug(SYNC_TASK_EXECUTE, [taskName]);
38
- pendingTask = task.apply(void 0, args).then(function (result) {
39
- pendingTask = undefined;
33
+ // Update `promiseChain` with last promise, to run tasks serially
34
+ promiseChain = (promiseChain ? promiseChain.then(function () { return task.apply(void 0, args); }) : task.apply(void 0, args))
35
+ .then(function (result) {
36
+ executing--;
40
37
  return result;
41
38
  });
42
- return pendingTask;
39
+ return promiseChain;
43
40
  }
44
41
  function periodicExecute(currentRunningId) {
45
42
  return execute.apply(void 0, runningArgs).then(function (result) {
@@ -53,7 +50,7 @@ export function syncTaskFactory(log, task, period, taskName) {
53
50
  return {
54
51
  execute: execute,
55
52
  isExecuting: function () {
56
- return pendingTask !== undefined;
53
+ return executing > 0;
57
54
  },
58
55
  start: function () {
59
56
  var args = [];
@@ -7,8 +7,8 @@ var Backoff = /** @class */ (function () {
7
7
  * @param {number} maxMillis
8
8
  */
9
9
  function Backoff(cb, baseMillis, maxMillis) {
10
- this.baseMillis = baseMillis || Backoff.DEFAULT_BASE_MILLIS;
11
- this.maxMillis = maxMillis || Backoff.DEFAULT_MAX_MILLIS;
10
+ this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
11
+ this.maxMillis = Backoff.__TEST__MAX_MILLIS || maxMillis || Backoff.DEFAULT_MAX_MILLIS;
12
12
  this.attempts = 0;
13
13
  this.cb = cb;
14
14
  }
@@ -22,6 +22,7 @@ var Backoff = /** @class */ (function () {
22
22
  if (this.timeoutID)
23
23
  clearTimeout(this.timeoutID);
24
24
  this.timeoutID = setTimeout(function () {
25
+ _this.timeoutID = undefined;
25
26
  _this.cb();
26
27
  }, delayInMillis);
27
28
  this.attempts++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.5.1-rc.0",
3
+ "version": "1.5.1-rc.3",
4
4
  "description": "Split Javascript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -47,13 +47,9 @@
47
47
  "tslib": "^2.3.1"
48
48
  },
49
49
  "peerDependencies": {
50
- "js-yaml": "^3.13.1",
51
50
  "ioredis": "^4.28.0"
52
51
  },
53
52
  "peerDependenciesMeta": {
54
- "js-yaml": {
55
- "optional": true
56
- },
57
53
  "ioredis": {
58
54
  "optional": true
59
55
  }
@@ -37,7 +37,7 @@ function providePlugin(window: any, pluginName: string, pluginConstructor: Funct
37
37
 
38
38
  if (autoRequire && (!window[gaAlias].q || window[gaAlias].q.push === [].push)) {
39
39
  // Expecting spy on ga.q push method but not found
40
- log.error(logPrefix + 'integration is configured to autorequire the splitTracker plugin, but the necessary script does not seem to have run.');
40
+ log.error(logPrefix + 'integration is configured to autorequire the splitTracker plugin, but the necessary script does not seem to have run. Please check the docs.');
41
41
  }
42
42
  }
43
43
 
@@ -2,31 +2,31 @@
2
2
  /**
3
3
  * Auto-require script to use with GoogleAnalyticsToSplit integration
4
4
  */
5
- (function (i, r, s) {
6
- i[s] = i[s] || r;
7
- i[r] = i[r] || function () { i[r].q.push(arguments); };
8
- i[r].q = i[r].q || [];
5
+ (function (w, g, o) {
6
+ w[o] = w[o] || g;
7
+ w[g] = w[g] || function () { w[g].q.push(arguments); };
8
+ w[g].q = w[g].q || [];
9
9
 
10
- var ts = {}; // Tracker names
10
+ var trackerNames = {};
11
11
  function name(arg) { return typeof arg === 'object' && typeof arg.name === 'string' && arg.name; }
12
12
 
13
- function processCommand(v) { // Queue a `require` command if v is a `create` command
14
- if (v && v[0] === 'create') {
15
- var t = name(v[1]) || name(v[2]) || name(v[3]) || (typeof v[3] === 'string' ? v[3] : undefined); // Get tracker name
13
+ function processCommand(command) { // Queue a `require` command if v is a `create` command
14
+ if (command && command[0] === 'create') {
15
+ var trackerName = name(command[1]) || name(command[2]) || name(command[3]) || (typeof command[3] === 'string' ? command[3] : undefined); // Get tracker name
16
16
 
17
- if (!ts[t]) {
18
- ts[t] = true;
19
- i[r]((t ? t + '.' : '') + 'require', 'splitTracker'); // Auto-require
17
+ if (!trackerNames[trackerName]) {
18
+ trackerNames[trackerName] = true;
19
+ w[g]((trackerName ? trackerName + '.' : '') + 'require', 'splitTracker'); // Auto-require
20
20
  }
21
21
  }
22
22
  }
23
23
 
24
- i[r].q.forEach(processCommand); // Process already queued commands
24
+ w[g].q.forEach(processCommand); // Process already queued commands
25
25
 
26
- var o = i[r].q.push;
27
- i[r].q.push = function (v) { // Spy new queued commands
28
- var result = o.apply(this, arguments);
29
- processCommand(v);
26
+ var originalPush = w[g].q.push;
27
+ w[g].q.push = function (command) { // Spy new queued commands
28
+ var result = originalPush.apply(this, arguments);
29
+ processCommand(command);
30
30
  return result;
31
31
  };
32
32
 
@@ -50,13 +50,13 @@ export function splitApiFactory(
50
50
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
51
51
  },
52
52
 
53
- fetchSplitChanges(since: number, noCache?: boolean) {
54
- const url = `${urls.sdk}/splitChanges?since=${since}${filterQueryString || ''}`;
53
+ fetchSplitChanges(since: number, noCache?: boolean, till?: number) {
54
+ const url = `${urls.sdk}/splitChanges?since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
55
55
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS));
56
56
  },
57
57
 
58
- fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean) {
59
- const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}`;
58
+ fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
59
+ const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
60
60
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
61
61
  },
62
62
 
@@ -35,9 +35,9 @@ export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyT
35
35
 
36
36
  export type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>
37
37
 
38
- export type IFetchSplitChanges = (since: number, noCache?: boolean) => Promise<IResponse>
38
+ export type IFetchSplitChanges = (since: number, noCache?: boolean, till?: number) => Promise<IResponse>
39
39
 
40
- export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean) => Promise<IResponse>
40
+ export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>
41
41
 
42
42
  export type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>
43
43
 
@@ -2,15 +2,15 @@ import { IFetchSegmentChanges } from '../../../services/types';
2
2
  import { ISegmentChangesResponse } from '../../../dtos/types';
3
3
  import { ISegmentChangesFetcher } from './types';
4
4
 
5
- function greedyFetch(fetchSegmentChanges: IFetchSegmentChanges, since: number, segmentName: string, noCache?: boolean): Promise<ISegmentChangesResponse[]> {
6
- return fetchSegmentChanges(since, segmentName, noCache)
5
+ function greedyFetch(fetchSegmentChanges: IFetchSegmentChanges, since: number, segmentName: string, noCache?: boolean, targetTill?: number): Promise<ISegmentChangesResponse[]> {
6
+ return fetchSegmentChanges(since, segmentName, noCache, targetTill)
7
7
  .then(resp => resp.json())
8
8
  .then((json: ISegmentChangesResponse) => {
9
9
  let { since, till } = json;
10
10
  if (since === till) {
11
11
  return [json];
12
12
  } else {
13
- return Promise.all([json, greedyFetch(fetchSegmentChanges, till, segmentName, noCache)]).then(flatMe => {
13
+ return Promise.all([json, greedyFetch(fetchSegmentChanges, till, segmentName, noCache, targetTill)]).then(flatMe => {
14
14
  return [flatMe[0], ...flatMe[1]];
15
15
  });
16
16
  }
@@ -27,11 +27,12 @@ export function segmentChangesFetcherFactory(fetchSegmentChanges: IFetchSegmentC
27
27
  since: number,
28
28
  segmentName: string,
29
29
  noCache?: boolean,
30
+ till?: number,
30
31
  // Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
31
32
  decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>
32
33
  ): Promise<ISegmentChangesResponse[]> {
33
34
 
34
- let segmentsPromise = greedyFetch(fetchSegmentChanges, since, segmentName, noCache);
35
+ let segmentsPromise = greedyFetch(fetchSegmentChanges, since, segmentName, noCache, till);
35
36
  if (decorator) segmentsPromise = decorator(segmentsPromise);
36
37
 
37
38
  return segmentsPromise;
@@ -10,11 +10,12 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
10
10
  return function splitChangesFetcher(
11
11
  since: number,
12
12
  noCache?: boolean,
13
+ till?: number,
13
14
  // Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
14
15
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
15
16
  ) {
16
17
 
17
- let splitsPromise = fetchSplitChanges(since, noCache);
18
+ let splitsPromise = fetchSplitChanges(since, noCache, till);
18
19
  if (decorator) splitsPromise = decorator(splitsPromise);
19
20
 
20
21
  return splitsPromise.then(resp => resp.json());
@@ -4,6 +4,7 @@ import { IResponse } from '../../../services/types';
4
4
  export type ISplitChangesFetcher = (
5
5
  since: number,
6
6
  noCache?: boolean,
7
+ till?: number,
7
8
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
8
9
  ) => Promise<ISplitChangesResponse>
9
10
 
@@ -11,6 +12,7 @@ export type ISegmentChangesFetcher = (
11
12
  since: number,
12
13
  segmentName: string,
13
14
  noCache?: boolean,
15
+ till?: number,
14
16
  decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>
15
17
  ) => Promise<ISegmentChangesResponse[]>
16
18
 
@@ -1,4 +1,4 @@
1
- import { ISegmentsSyncTask, ISplitsSyncTask, IPollingManagerCS } from './types';
1
+ import { IMySegmentsSyncTask, IPollingManagerCS } from './types';
2
2
  import { forOwn } from '../../utils/lang';
3
3
  import { IReadinessManager } from '../../readiness/types';
4
4
  import { IStorageSync } from '../../storages/types';
@@ -20,13 +20,13 @@ export function pollingManagerCSFactory(
20
20
  const { splitApi, storage, readiness, settings } = params;
21
21
  const log = settings.log;
22
22
 
23
- const splitsSyncTask: ISplitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true);
23
+ const splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true);
24
24
 
25
25
  // Map of matching keys to their corresponding MySegmentsSyncTask.
26
- const mySegmentsSyncTasks: Record<string, ISegmentsSyncTask> = {};
26
+ const mySegmentsSyncTasks: Record<string, IMySegmentsSyncTask> = {};
27
27
 
28
28
  const matchingKey = getMatching(settings.core.key);
29
- const mySegmentsSyncTask: ISegmentsSyncTask = add(matchingKey, readiness, storage);
29
+ const mySegmentsSyncTask = add(matchingKey, readiness, storage);
30
30
 
31
31
  function startMySegmentsSyncTasks() {
32
32
  forOwn(mySegmentsSyncTasks, function (mySegmentsSyncTask) {
@@ -54,7 +54,7 @@ export function pollingManagerCSFactory(
54
54
  }
55
55
  });
56
56
 
57
- function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): ISegmentsSyncTask {
57
+ function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
58
58
  const mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMySegments, storage, readiness, settings, matchingKey);
59
59
 
60
60
  // smart ready
@@ -1,7 +1,7 @@
1
1
  import { IStorageSync } from '../../../storages/types';
2
2
  import { IReadinessManager } from '../../../readiness/types';
3
3
  import { syncTaskFactory } from '../../syncTask';
4
- import { ISegmentsSyncTask } from '../types';
4
+ import { IMySegmentsSyncTask } from '../types';
5
5
  import { IFetchMySegments } from '../../../services/types';
6
6
  import { mySegmentsFetcherFactory } from '../fetchers/mySegmentsFetcher';
7
7
  import { ISettings } from '../../../types';
@@ -16,7 +16,7 @@ export function mySegmentsSyncTaskFactory(
16
16
  readiness: IReadinessManager,
17
17
  settings: ISettings,
18
18
  matchingKey: string
19
- ): ISegmentsSyncTask {
19
+ ): IMySegmentsSyncTask {
20
20
  return syncTaskFactory(
21
21
  settings.log,
22
22
  mySegmentsUpdaterFactory(
@@ -1,23 +1,31 @@
1
1
  import { IReadinessManager } from '../../readiness/types';
2
2
  import { IStorageSync } from '../../storages/types';
3
- import { SegmentsData } from '../streaming/SSEHandler/types';
4
3
  import { ITask, ISyncTask } from '../types';
5
4
 
6
- export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean], boolean> { }
5
+ export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number], boolean> { }
7
6
 
8
- export interface ISegmentsSyncTask extends ISyncTask<[segmentNames?: SegmentsData, noCache?: boolean, fetchOnlyNew?: boolean], boolean> { }
7
+ export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
8
+
9
+ export type MySegmentsData = string[] | {
10
+ /* segment name */
11
+ name: string,
12
+ /* action: `true` for add, and `false` for delete */
13
+ add: boolean
14
+ }
15
+
16
+ export interface IMySegmentsSyncTask extends ISyncTask<[segmentsData?: MySegmentsData, noCache?: boolean], boolean> { }
9
17
 
10
18
  export interface IPollingManager extends ITask {
11
19
  syncAll(): Promise<any>
12
20
  splitsSyncTask: ISplitsSyncTask
13
- segmentsSyncTask: ISegmentsSyncTask
21
+ segmentsSyncTask: ISyncTask
14
22
  }
15
23
 
16
24
  /**
17
25
  * PollingManager for client-side with support for multiple clients
18
26
  */
19
27
  export interface IPollingManagerCS extends IPollingManager {
20
- add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): ISegmentsSyncTask
28
+ add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): IMySegmentsSyncTask
21
29
  remove(matchingKey: string): void;
22
- get(matchingKey: string): ISegmentsSyncTask | undefined
30
+ get(matchingKey: string): IMySegmentsSyncTask | undefined
23
31
  }
@@ -5,7 +5,7 @@ import { timeout } from '../../../utils/promise/timeout';
5
5
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
6
6
  import { ILogger } from '../../../logger/types';
7
7
  import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
8
- import { SegmentsData } from '../../streaming/SSEHandler/types';
8
+ import { MySegmentsData } from '../types';
9
9
 
10
10
  type IMySegmentsUpdater = (segmentList?: string[], noCache?: boolean) => Promise<boolean>
11
11
 
@@ -36,7 +36,7 @@ export function mySegmentsUpdaterFactory(
36
36
  }
37
37
 
38
38
  // @TODO if allowing pluggable storages, handle async execution
39
- function updateSegments(segmentsData: SegmentsData) {
39
+ function updateSegments(segmentsData: MySegmentsData) {
40
40
 
41
41
  let shouldNotifyUpdate;
42
42
  if (Array.isArray(segmentsData)) {
@@ -61,7 +61,7 @@ export function mySegmentsUpdaterFactory(
61
61
  }
62
62
  }
63
63
 
64
- function _mySegmentsUpdater(retry: number, segmentsData?: SegmentsData, noCache?: boolean): Promise<boolean> {
64
+ function _mySegmentsUpdater(retry: number, segmentsData?: MySegmentsData, noCache?: boolean): Promise<boolean> {
65
65
  const updaterPromise: Promise<boolean> = segmentsData ?
66
66
  // If segmentsData is provided, there is no need to fetch mySegments
67
67
  new Promise((res) => { updateSegments(segmentsData); res(true); }) :
@@ -97,7 +97,7 @@ export function mySegmentsUpdaterFactory(
97
97
  * (3) or `undefined`, for which the updater will fetch mySegments in order to sync the storage.
98
98
  * @param {boolean | undefined} noCache true to revalidate data to fetch
99
99
  */
100
- return function mySegmentsUpdater(segmentsData?: SegmentsData, noCache?: boolean) {
100
+ return function mySegmentsUpdater(segmentsData?: MySegmentsData, noCache?: boolean) {
101
101
  return _mySegmentsUpdater(0, segmentsData, noCache);
102
102
  };
103
103