@splitsoftware/splitio-commons 1.5.1-rc.0 → 1.5.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 (67) hide show
  1. package/CHANGES.txt +1 -2
  2. package/cjs/integrations/ga/GaToSplit.js +11 -12
  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 +11 -12
  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 -1
  29. package/src/integrations/ga/GaToSplit.ts +8 -14
  30. package/src/integrations/ga/types.ts +2 -13
  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/types.d.ts +2 -13
  53. package/types/services/types.d.ts +2 -2
  54. package/types/sync/polling/fetchers/types.d.ts +2 -2
  55. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -2
  56. package/types/sync/polling/types.d.ts +11 -6
  57. package/types/sync/polling/updaters/segmentChangesUpdater.d.ts +1 -1
  58. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  59. package/types/sync/streaming/SSEHandler/types.d.ts +0 -4
  60. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +3 -24
  61. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +3 -23
  62. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +6 -33
  63. package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -2
  64. package/types/sync/streaming/types.d.ts +2 -2
  65. package/types/sync/syncTask.d.ts +2 -3
  66. package/types/utils/Backoff.d.ts +2 -0
  67. package/src/integrations/ga/autoRequire.js +0 -33
@@ -1,58 +1,63 @@
1
+ import { ILogger } from '../../../logger/types';
1
2
  import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
2
3
  import { ISplitsEventEmitter } from '../../../readiness/types';
3
4
  import { ISplitsCacheSync } from '../../../storages/types';
4
5
  import { Backoff } from '../../../utils/Backoff';
5
6
  import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
6
7
  import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
8
+ import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
7
9
  import { IUpdateWorker } from './types';
8
10
 
9
11
  /**
10
- * SplitsUpdateWorker class
12
+ * SplitsUpdateWorker factory
11
13
  */
12
- export class SplitsUpdateWorker implements IUpdateWorker {
14
+ export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker & { killSplit(event: ISplitKillData): void } {
13
15
 
14
- private readonly splitsCache: ISplitsCacheSync;
15
- private readonly splitsSyncTask: ISplitsSyncTask;
16
- private readonly splitsEventEmitter: ISplitsEventEmitter;
17
- private readonly segmentsSyncTask?: ISegmentsSyncTask;
18
- private maxChangeNumber: number;
19
- private handleNewEvent: boolean;
20
- readonly backoff: Backoff;
16
+ let maxChangeNumber = 0;
17
+ let handleNewEvent = false;
18
+ let isHandlingEvent: boolean;
19
+ let cdnBypass: boolean;
20
+ const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
21
21
 
22
- /**
23
- * @param {Object} splitsCache splits data cache
24
- * @param {Object} splitsSyncTask task for syncing splits data
25
- * @param {Object} splitsEventEmitter emitter for splits data events
26
- */
27
- constructor(splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask) {
28
- this.splitsCache = splitsCache;
29
- this.splitsSyncTask = splitsSyncTask;
30
- this.splitsEventEmitter = splitsEventEmitter;
31
- this.segmentsSyncTask = segmentsSyncTask;
32
- this.maxChangeNumber = 0;
33
- this.handleNewEvent = false;
34
- this.put = this.put.bind(this);
35
- this.killSplit = this.killSplit.bind(this);
36
- this.__handleSplitUpdateCall = this.__handleSplitUpdateCall.bind(this);
37
- this.backoff = new Backoff(this.__handleSplitUpdateCall);
38
- }
39
-
40
- // Private method
41
- // Preconditions: this.splitsSyncTask.isSynchronizingSplits === false
42
- __handleSplitUpdateCall() {
43
- if (this.maxChangeNumber > this.splitsCache.getChangeNumber()) {
44
- this.handleNewEvent = false;
22
+ function __handleSplitUpdateCall() {
23
+ isHandlingEvent = true;
24
+ if (maxChangeNumber > splitsCache.getChangeNumber()) {
25
+ handleNewEvent = false;
45
26
 
46
27
  // fetch splits revalidating data if cached
47
- this.splitsSyncTask.execute(true).then(() => {
48
- if (this.handleNewEvent) {
49
- this.__handleSplitUpdateCall();
28
+ splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined).then(() => {
29
+ if (!isHandlingEvent) return; // halt if `stop` has been called
30
+ if (handleNewEvent) {
31
+ __handleSplitUpdateCall();
50
32
  } else {
51
33
  // fetch new registered segments for server-side API. Not retrying on error
52
- if (this.segmentsSyncTask) this.segmentsSyncTask.execute(undefined, false, true);
53
- this.backoff.scheduleCall();
34
+ if (segmentsSyncTask) segmentsSyncTask.execute(true);
35
+
36
+ const attempts = backoff.attempts + 1;
37
+
38
+ if (maxChangeNumber <= splitsCache.getChangeNumber()) {
39
+ log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
40
+ isHandlingEvent = false;
41
+ return;
42
+ }
43
+
44
+ if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
45
+ backoff.scheduleCall();
46
+ return;
47
+ }
48
+
49
+ if (cdnBypass) {
50
+ log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
51
+ isHandlingEvent = false;
52
+ } else {
53
+ backoff.reset();
54
+ cdnBypass = true;
55
+ __handleSplitUpdateCall();
56
+ }
54
57
  }
55
58
  });
59
+ } else {
60
+ isHandlingEvent = false;
56
61
  }
57
62
  }
58
63
 
@@ -61,34 +66,41 @@ export class SplitsUpdateWorker implements IUpdateWorker {
61
66
  *
62
67
  * @param {number} changeNumber change number of the SPLIT_UPDATE notification
63
68
  */
64
- put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
65
- const currentChangeNumber = this.splitsCache.getChangeNumber();
66
-
67
- if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
69
+ function put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
70
+ const currentChangeNumber = splitsCache.getChangeNumber();
68
71
 
69
- this.maxChangeNumber = changeNumber;
70
- this.handleNewEvent = true;
71
- this.backoff.reset();
72
+ if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
72
73
 
73
- if (this.splitsSyncTask.isExecuting()) return;
74
+ maxChangeNumber = changeNumber;
75
+ handleNewEvent = true;
76
+ cdnBypass = false;
74
77
 
75
- this.__handleSplitUpdateCall();
78
+ if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
79
+ backoff.reset();
76
80
  }
77
81
 
78
- /**
79
- * Invoked by NotificationProcessor on SPLIT_KILL event
80
- *
81
- * @param {number} changeNumber change number of the SPLIT_UPDATE notification
82
- * @param {string} splitName name of split to kill
83
- * @param {string} defaultTreatment default treatment value
84
- */
85
- killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
86
- if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
87
- // trigger an SDK_UPDATE if Split was killed locally
88
- this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
89
- }
90
- // queues the SplitChanges fetch (only if changeNumber is newer)
91
- this.put({ changeNumber });
92
- }
82
+ return {
83
+ put,
93
84
 
85
+ /**
86
+ * Invoked by NotificationProcessor on SPLIT_KILL event
87
+ *
88
+ * @param {number} changeNumber change number of the SPLIT_UPDATE notification
89
+ * @param {string} splitName name of split to kill
90
+ * @param {string} defaultTreatment default treatment value
91
+ */
92
+ killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
93
+ if (splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
94
+ // trigger an SDK_UPDATE if Split was killed locally
95
+ splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
96
+ }
97
+ // queues the SplitChanges fetch (only if changeNumber is newer)
98
+ put({ changeNumber });
99
+ },
100
+
101
+ stop() {
102
+ isHandlingEvent = false;
103
+ backoff.reset();
104
+ }
105
+ };
94
106
  }
@@ -0,0 +1,3 @@
1
+ export const FETCH_BACKOFF_BASE = 10000; // backoff base starting at 10 seconds
2
+ export const FETCH_BACKOFF_MAX_WAIT = 60000; // don't wait for more than 1 minute
3
+ export const FETCH_BACKOFF_MAX_RETRIES = 10; // max retries
@@ -1,6 +1,4 @@
1
- import { Backoff } from '../../../utils/Backoff';
2
-
3
1
  export interface IUpdateWorker {
4
- readonly backoff: Backoff,
5
- put(...args: any[]): void
2
+ stop(): void // clear scheduled tasks (backoff)
3
+ put(...args: any[]): void // handle new update event
6
4
  }
@@ -1,6 +1,6 @@
1
1
  import { IPushEventEmitter, IPushManager } from './types';
2
2
  import { ISSEClient } from './SSEClient/types';
3
- import { ISegmentsSyncTask, IPollingManager } from '../polling/types';
3
+ import { IMySegmentsSyncTask, IPollingManager, ISegmentsSyncTask } from '../polling/types';
4
4
  import { objectAssign } from '../../utils/lang/objectAssign';
5
5
  import { Backoff } from '../../utils/Backoff';
6
6
  import { SSEHandlerFactory } from './SSEHandler';
@@ -20,6 +20,7 @@ import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
20
20
  import { IAuthTokenPushEnabled } from './AuthClient/types';
21
21
  import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
22
22
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
23
+ import { IUpdateWorker } from './UpdateWorkers/types';
23
24
 
24
25
  /**
25
26
  * PushManager factory:
@@ -55,15 +56,15 @@ export function pushManagerFactory(
55
56
 
56
57
  // init workers
57
58
  // MySegmentsUpdateWorker (client-side) are initiated in `add` method
58
- const segmentsUpdateWorker = userKey ? undefined : new SegmentsUpdateWorker(pollingManager.segmentsSyncTask, storage.segments);
59
+ const segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask as ISegmentsSyncTask, storage.segments);
59
60
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
60
- const splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
61
+ const splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
61
62
 
62
63
  // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
63
64
  const userKeyHashes: Record<string, string> = {};
64
65
  // [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
65
66
  // Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
66
- const clients: Record<string, { hash64: Hash64, worker: MySegmentsUpdateWorker }> = {};
67
+ const clients: Record<string, { hash64: Hash64, worker: IUpdateWorker }> = {};
67
68
 
68
69
  // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
69
70
  let connectForNewClient = false;
@@ -169,9 +170,9 @@ export function pushManagerFactory(
169
170
 
170
171
  // cancel scheduled fetch retries of Splits, Segments, and MySegments Update Workers
171
172
  function stopWorkers() {
172
- splitsUpdateWorker.backoff.reset();
173
- if (userKey) forOwn(clients, ({ worker }) => worker.backoff.reset());
174
- else (segmentsUpdateWorker as SegmentsUpdateWorker).backoff.reset();
173
+ splitsUpdateWorker.stop();
174
+ if (userKey) forOwn(clients, ({ worker }) => worker.stop());
175
+ else segmentsUpdateWorker!.stop();
175
176
  }
176
177
 
177
178
  pushEmitter.on(PUSH_SUBSYSTEM_DOWN, stopWorkers);
@@ -180,7 +181,6 @@ export function pushManagerFactory(
180
181
  // Otherwise it is unnecessary (e.g, STREAMING_RESUMED).
181
182
  pushEmitter.on(PUSH_SUBSYSTEM_UP, () => {
182
183
  connectPushRetryBackoff.reset();
183
- stopWorkers();
184
184
  });
185
185
 
186
186
  /** Fallback to polling without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */
@@ -294,7 +294,7 @@ export function pushManagerFactory(
294
294
  });
295
295
  });
296
296
  } else {
297
- pushEmitter.on(SEGMENT_UPDATE, (segmentsUpdateWorker as SegmentsUpdateWorker).put);
297
+ pushEmitter.on(SEGMENT_UPDATE, segmentsUpdateWorker!.put);
298
298
  }
299
299
 
300
300
  return objectAssign(
@@ -315,7 +315,7 @@ export function pushManagerFactory(
315
315
  if (disabled || disconnected === false) return;
316
316
  disconnected = false;
317
317
 
318
- if (userKey) this.add(userKey, pollingManager.segmentsSyncTask); // client-side
318
+ if (userKey) this.add(userKey, pollingManager.segmentsSyncTask as IMySegmentsSyncTask); // client-side
319
319
  else setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
320
320
  },
321
321
 
@@ -325,12 +325,12 @@ export function pushManagerFactory(
325
325
  },
326
326
 
327
327
  // [Only for client-side]
328
- add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
328
+ add(userKey: string, mySegmentsSyncTask: IMySegmentsSyncTask) {
329
329
  const hash = hashUserKey(userKey);
330
330
 
331
331
  if (!userKeyHashes[hash]) {
332
332
  userKeyHashes[hash] = userKey;
333
- clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
333
+ clients[userKey] = { hash64: hash64(userKey), worker: MySegmentsUpdateWorker(mySegmentsSyncTask) };
334
334
  connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
335
335
 
336
336
  // Reconnects in case of a new client.
@@ -1,6 +1,6 @@
1
1
  import { IMySegmentsUpdateData, IMySegmentsUpdateV2Data, ISegmentUpdateData, ISplitUpdateData, ISplitKillData } from './SSEHandler/types';
2
2
  import { ITask } from '../types';
3
- import { ISegmentsSyncTask } from '../polling/types';
3
+ import { IMySegmentsSyncTask } from '../polling/types';
4
4
  import { IEventEmitter } from '../../types';
5
5
  import { ControlType } from './constants';
6
6
 
@@ -45,6 +45,6 @@ export interface IPushEventEmitter extends IEventEmitter {
45
45
  */
46
46
  export interface IPushManager extends ITask, IPushEventEmitter {
47
47
  // Methods used in client-side, to support multiple clients
48
- add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask): void,
48
+ add(userKey: string, mySegmentsSyncTask: IMySegmentsSyncTask): void,
49
49
  remove(userKey: string): void
50
50
  }
@@ -3,9 +3,8 @@ import { ILogger } from '../logger/types';
3
3
  import { ISyncTask } from './types';
4
4
 
5
5
  /**
6
- * Creates a syncTask that handles the periodic execution of a given task ("start" and "stop" methods).
7
- * The task can be also executed by calling the "execute" method. Multiple execute calls are chained to run secuentially and avoid race conditions.
8
- * For example, submitters executed on SDK destroy or full queue, while periodic execution is pending.
6
+ * Creates an object that handles the periodic execution of a given task via "start" and "stop" methods.
7
+ * 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).
9
8
  *
10
9
  * @param log Logger instance.
11
10
  * @param task Task to execute that returns a promise that NEVER REJECTS. Otherwise, periodic execution can result in Unhandled Promise Rejections.
@@ -15,8 +14,10 @@ import { ISyncTask } from './types';
15
14
  */
16
15
  export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger, task: (...args: Input) => Promise<Output>, period: number, taskName = 'task'): ISyncTask<Input, Output> {
17
16
 
18
- // Task promise while it is pending. Undefined once the promise is resolved
19
- let pendingTask: Promise<Output> | undefined;
17
+ // Flag that indicates if the task is executing
18
+ let executing = 0;
19
+ // Promise chain to resolve tasks sequentially
20
+ let promiseChain: Promise<Output> | undefined;
20
21
  // flag that indicates if the task periodic execution has been started/stopped.
21
22
  let running = false;
22
23
  // Auxiliar counter used to avoid race condition when calling `start` & `stop` intermittently
@@ -27,20 +28,17 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
27
28
  let timeoutID: any;
28
29
 
29
30
  function execute(...args: Input): Promise<Output> {
30
- // If task is executing, chain the new execution
31
- if (pendingTask) {
32
- return pendingTask.then(() => {
33
- return execute(...args);
31
+ executing++;
32
+ log.debug(SYNC_TASK_EXECUTE, [taskName]);
33
+
34
+ // Update `promiseChain` with last promise, to run tasks serially
35
+ promiseChain = (promiseChain ? promiseChain.then(() => task(...args)) : task(...args))
36
+ .then(result => {
37
+ executing--;
38
+ return result;
34
39
  });
35
- }
36
40
 
37
- // Execute task
38
- log.debug(SYNC_TASK_EXECUTE, [taskName]);
39
- pendingTask = task(...args).then(result => {
40
- pendingTask = undefined;
41
- return result;
42
- });
43
- return pendingTask;
41
+ return promiseChain;
44
42
  }
45
43
 
46
44
  function periodicExecute(currentRunningId: number) {
@@ -56,7 +54,7 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
56
54
  execute,
57
55
 
58
56
  isExecuting() {
59
- return pendingTask !== undefined;
57
+ return executing > 0;
60
58
  },
61
59
 
62
60
  start(...args: Input) {
@@ -1,5 +1,9 @@
1
1
  export class Backoff {
2
2
 
3
+ // For testing purposes, assign to overwrite the provided value by param
4
+ static __TEST__BASE_MILLIS?: number;
5
+ static __TEST__MAX_MILLIS?: number;
6
+
3
7
  static DEFAULT_BASE_MILLIS = 1000; // 1 second
4
8
  static DEFAULT_MAX_MILLIS = 1800000; // 30 minutes
5
9
 
@@ -17,8 +21,8 @@ export class Backoff {
17
21
  * @param {number} maxMillis
18
22
  */
19
23
  constructor(cb: (...args: any[]) => any, baseMillis?: number, maxMillis?: number) {
20
- this.baseMillis = baseMillis || Backoff.DEFAULT_BASE_MILLIS;
21
- this.maxMillis = maxMillis || Backoff.DEFAULT_MAX_MILLIS;
24
+ this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
25
+ this.maxMillis = Backoff.__TEST__MAX_MILLIS || maxMillis || Backoff.DEFAULT_MAX_MILLIS;
22
26
  this.attempts = 0;
23
27
  this.cb = cb;
24
28
  }
@@ -32,6 +36,7 @@ export class Backoff {
32
36
 
33
37
  if (this.timeoutID) clearTimeout(this.timeoutID);
34
38
  this.timeoutID = setTimeout(() => {
39
+ this.timeoutID = undefined;
35
40
  this.cb();
36
41
  }, delayInMillis);
37
42
  this.attempts++;
@@ -52,23 +52,12 @@ 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 log an error if the `auto-require` script is not detected.
57
- * The auto-require script automatically requires the `splitTracker` plugin for created trackers,
58
- * and should be placed right after your Google Analytics, Google Tag Manager or gtag.js script tag.
59
- *
60
- * @see {@link https://help.split.io/hc/en-us/articles/360040838752#set-up-with-gtm-and-gtag.js}
61
- *
62
- * @property {boolean} autoRequire
63
- * @default false
64
- */
65
- autoRequire?: boolean;
66
55
  }
67
56
  /**
68
57
  * Enable 'Google Analytics to Split' integration, to track Google Analytics hits as Split events.
69
58
  * Used by the browser variant of the isomorphic JS SDK.
70
59
  *
71
- * @see {@link https://help.split.io/hc/en-us/articles/360040838752#google-analytics-to-split}
60
+ * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#google-analytics-to-split}
72
61
  */
73
62
  export interface IGoogleAnalyticsToSplitConfig extends GoogleAnalyticsToSplitOptions {
74
63
  type: 'GOOGLE_ANALYTICS_TO_SPLIT';
@@ -136,7 +125,7 @@ export interface SplitToGoogleAnalyticsOptions {
136
125
  * Enable 'Split to Google Analytics' integration, to track Split impressions and events as Google Analytics hits.
137
126
  * Used by the browser variant of the isomorphic JS SDK.
138
127
  *
139
- * @see {@link https://help.split.io/hc/en-us/articles/360040838752#split-to-google-analytics}
128
+ * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#split-to-google-analytics}
140
129
  */
141
130
  export interface ISplitToGoogleAnalyticsConfig extends SplitToGoogleAnalyticsOptions {
142
131
  type: 'SPLIT_TO_GOOGLE_ANALYTICS';
@@ -16,8 +16,8 @@ export declare type IFetch = (url: string, options?: IRequestOptions) => Promise
16
16
  export declare type IHealthCheckAPI = () => Promise<boolean>;
17
17
  export declare type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean) => Promise<IResponse>;
18
18
  export declare type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>;
19
- export declare type IFetchSplitChanges = (since: number, noCache?: boolean) => Promise<IResponse>;
20
- export declare type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean) => Promise<IResponse>;
19
+ export declare type IFetchSplitChanges = (since: number, noCache?: boolean, till?: number) => Promise<IResponse>;
20
+ export declare type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>;
21
21
  export declare type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>;
22
22
  export declare type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
23
23
  export declare type IPostTestImpressionsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
@@ -1,5 +1,5 @@
1
1
  import { ISplitChangesResponse, ISegmentChangesResponse } from '../../../dtos/types';
2
2
  import { IResponse } from '../../../services/types';
3
- export declare type ISplitChangesFetcher = (since: number, noCache?: boolean, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<ISplitChangesResponse>;
4
- export declare type ISegmentChangesFetcher = (since: number, segmentName: string, noCache?: boolean, decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>;
3
+ export declare type ISplitChangesFetcher = (since: number, noCache?: boolean, till?: number, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<ISplitChangesResponse>;
4
+ export declare type ISegmentChangesFetcher = (since: number, segmentName: string, noCache?: boolean, till?: number, decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>;
5
5
  export declare type IMySegmentsFetcher = (userMatchingKey: string, noCache?: boolean, decorator?: (promise: Promise<IResponse>) => Promise<IResponse>) => Promise<string[]>;
@@ -1,9 +1,9 @@
1
1
  import { IStorageSync } from '../../../storages/types';
2
2
  import { IReadinessManager } from '../../../readiness/types';
3
- import { ISegmentsSyncTask } from '../types';
3
+ import { IMySegmentsSyncTask } from '../types';
4
4
  import { IFetchMySegments } from '../../../services/types';
5
5
  import { ISettings } from '../../../types';
6
6
  /**
7
7
  * Creates a sync task that periodically executes a `mySegmentsUpdater` task
8
8
  */
9
- export declare function mySegmentsSyncTaskFactory(fetchMySegments: IFetchMySegments, storage: IStorageSync, readiness: IReadinessManager, settings: ISettings, matchingKey: string): ISegmentsSyncTask;
9
+ export declare function mySegmentsSyncTaskFactory(fetchMySegments: IFetchMySegments, storage: IStorageSync, readiness: IReadinessManager, settings: ISettings, matchingKey: string): IMySegmentsSyncTask;
@@ -1,21 +1,26 @@
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
- export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean], boolean> {
4
+ export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number], boolean> {
6
5
  }
7
- export interface ISegmentsSyncTask extends ISyncTask<[segmentNames?: SegmentsData, noCache?: boolean, fetchOnlyNew?: boolean], boolean> {
6
+ export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> {
7
+ }
8
+ export declare type MySegmentsData = string[] | {
9
+ name: string;
10
+ add: boolean;
11
+ };
12
+ export interface IMySegmentsSyncTask extends ISyncTask<[segmentsData?: MySegmentsData, noCache?: boolean], boolean> {
8
13
  }
9
14
  export interface IPollingManager extends ITask {
10
15
  syncAll(): Promise<any>;
11
16
  splitsSyncTask: ISplitsSyncTask;
12
- segmentsSyncTask: ISegmentsSyncTask;
17
+ segmentsSyncTask: ISyncTask;
13
18
  }
14
19
  /**
15
20
  * PollingManager for client-side with support for multiple clients
16
21
  */
17
22
  export interface IPollingManagerCS extends IPollingManager {
18
- add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): ISegmentsSyncTask;
23
+ add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): IMySegmentsSyncTask;
19
24
  remove(matchingKey: string): void;
20
- get(matchingKey: string): ISegmentsSyncTask | undefined;
25
+ get(matchingKey: string): IMySegmentsSyncTask | undefined;
21
26
  }
@@ -2,7 +2,7 @@ import { ISegmentChangesFetcher } from '../fetchers/types';
2
2
  import { ISegmentsCacheBase } from '../../../storages/types';
3
3
  import { IReadinessManager } from '../../../readiness/types';
4
4
  import { ILogger } from '../../../logger/types';
5
- declare type ISegmentChangesUpdater = (segmentNames?: string[], noCache?: boolean, fetchOnlyNew?: boolean) => Promise<boolean>;
5
+ declare type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>;
6
6
  /**
7
7
  * Factory of SegmentChanges updater, a task that:
8
8
  * - fetches segment changes using `segmentChangesFetcher`
@@ -4,7 +4,7 @@ import { ISplitChangesFetcher } from '../fetchers/types';
4
4
  import { ISplit } from '../../../dtos/types';
5
5
  import { ISplitsEventEmitter } from '../../../readiness/types';
6
6
  import { ILogger } from '../../../logger/types';
7
- declare type ISplitChangesUpdater = (noCache?: boolean) => Promise<boolean>;
7
+ declare type ISplitChangesUpdater = (noCache?: boolean, till?: number) => Promise<boolean>;
8
8
  /**
9
9
  * Collect segments from a raw split definition.
10
10
  * Exported for testing purposes.
@@ -65,7 +65,3 @@ export declare type INotificationError = Event & {
65
65
  parsedData?: any;
66
66
  message?: string;
67
67
  };
68
- export declare type SegmentsData = string[] | {
69
- name: string;
70
- add: boolean;
71
- };
@@ -1,27 +1,6 @@
1
- import { ISegmentsSyncTask } from '../../polling/types';
2
- import { Backoff } from '../../../utils/Backoff';
1
+ import { IMySegmentsSyncTask } from '../../polling/types';
3
2
  import { IUpdateWorker } from './types';
4
- import { SegmentsData } from '../SSEHandler/types';
5
3
  /**
6
- * MySegmentsUpdateWorker class
4
+ * MySegmentsUpdateWorker factory
7
5
  */
8
- export declare class MySegmentsUpdateWorker implements IUpdateWorker {
9
- private readonly mySegmentsSyncTask;
10
- private maxChangeNumber;
11
- private handleNewEvent;
12
- private segmentsData?;
13
- private currentChangeNumber;
14
- readonly backoff: Backoff;
15
- /**
16
- * @param {Object} mySegmentsSyncTask task for syncing mySegments data
17
- */
18
- constructor(mySegmentsSyncTask: ISegmentsSyncTask);
19
- __handleMySegmentsUpdateCall(): void;
20
- /**
21
- * Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
22
- *
23
- * @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
24
- * @param {SegmentsData | undefined} segmentsData might be undefined
25
- */
26
- put(changeNumber: number, segmentsData?: SegmentsData): void;
27
- }
6
+ export declare function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask): IUpdateWorker;
@@ -1,28 +1,8 @@
1
+ import { ILogger } from '../../../logger/types';
1
2
  import { ISegmentsCacheSync } from '../../../storages/types';
2
- import { Backoff } from '../../../utils/Backoff';
3
3
  import { ISegmentsSyncTask } from '../../polling/types';
4
- import { ISegmentUpdateData } from '../SSEHandler/types';
5
4
  import { IUpdateWorker } from './types';
6
5
  /**
7
- * SegmentUpdateWorker class
6
+ * SegmentsUpdateWorker factory
8
7
  */
9
- export declare class SegmentsUpdateWorker implements IUpdateWorker {
10
- private readonly segmentsCache;
11
- private readonly segmentsSyncTask;
12
- private readonly maxChangeNumbers;
13
- private handleNewEvent;
14
- readonly backoff: Backoff;
15
- /**
16
- * @param {Object} segmentsCache segments data cache
17
- * @param {Object} segmentsSyncTask task for syncing segments data
18
- */
19
- constructor(segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync);
20
- __handleSegmentUpdateCall(): void;
21
- /**
22
- * Invoked by NotificationProcessor on SEGMENT_UPDATE event
23
- *
24
- * @param {number} changeNumber change number of the SEGMENT_UPDATE notification
25
- * @param {string} segmentName segment name of the SEGMENT_UPDATE notification
26
- */
27
- put({ changeNumber, segmentName }: ISegmentUpdateData): void;
28
- }
8
+ export declare function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync): IUpdateWorker;
@@ -1,39 +1,12 @@
1
+ import { ILogger } from '../../../logger/types';
1
2
  import { ISplitsEventEmitter } from '../../../readiness/types';
2
3
  import { ISplitsCacheSync } from '../../../storages/types';
3
- import { Backoff } from '../../../utils/Backoff';
4
4
  import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
5
- import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
5
+ import { ISplitKillData } from '../SSEHandler/types';
6
6
  import { IUpdateWorker } from './types';
7
7
  /**
8
- * SplitsUpdateWorker class
8
+ * SplitsUpdateWorker factory
9
9
  */
10
- export declare class SplitsUpdateWorker implements IUpdateWorker {
11
- private readonly splitsCache;
12
- private readonly splitsSyncTask;
13
- private readonly splitsEventEmitter;
14
- private readonly segmentsSyncTask?;
15
- private maxChangeNumber;
16
- private handleNewEvent;
17
- readonly backoff: Backoff;
18
- /**
19
- * @param {Object} splitsCache splits data cache
20
- * @param {Object} splitsSyncTask task for syncing splits data
21
- * @param {Object} splitsEventEmitter emitter for splits data events
22
- */
23
- constructor(splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask);
24
- __handleSplitUpdateCall(): void;
25
- /**
26
- * Invoked by NotificationProcessor on SPLIT_UPDATE event
27
- *
28
- * @param {number} changeNumber change number of the SPLIT_UPDATE notification
29
- */
30
- put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>): void;
31
- /**
32
- * Invoked by NotificationProcessor on SPLIT_KILL event
33
- *
34
- * @param {number} changeNumber change number of the SPLIT_UPDATE notification
35
- * @param {string} splitName name of split to kill
36
- * @param {string} defaultTreatment default treatment value
37
- */
38
- killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData): void;
39
- }
10
+ export declare function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker & {
11
+ killSplit(event: ISplitKillData): void;
12
+ };
@@ -1,5 +1,4 @@
1
- import { Backoff } from '../../../utils/Backoff';
2
1
  export interface IUpdateWorker {
3
- readonly backoff: Backoff;
2
+ stop(): void;
4
3
  put(...args: any[]): void;
5
4
  }