@splitsoftware/splitio-commons 2.0.0-rc.0 → 2.0.0-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 (108) hide show
  1. package/CHANGES.txt +2 -1
  2. package/cjs/evaluator/Engine.js +1 -1
  3. package/cjs/evaluator/index.js +1 -1
  4. package/cjs/readiness/readinessManager.js +13 -2
  5. package/cjs/sdkClient/sdkClientMethodCS.js +0 -1
  6. package/cjs/sdkFactory/index.js +26 -8
  7. package/cjs/storages/{AbstractSegmentsCacheSync.js → AbstractMySegmentsCacheSync.js} +15 -17
  8. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +5 -5
  9. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +3 -2
  10. package/cjs/storages/inLocalStorage/index.js +1 -1
  11. package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -2
  12. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -5
  13. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +13 -27
  14. package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -1
  15. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +2 -1
  16. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +2 -1
  17. package/cjs/storages/inRedis/RedisAdapter.js +2 -1
  18. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +13 -19
  19. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +2 -1
  20. package/cjs/storages/pluggable/SegmentsCachePluggable.js +11 -32
  21. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +2 -1
  22. package/cjs/storages/pluggable/inMemoryWrapper.js +2 -1
  23. package/cjs/sync/offline/syncManagerOffline.js +18 -11
  24. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +12 -28
  25. package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -1
  26. package/cjs/sync/syncManagerOnline.js +20 -21
  27. package/cjs/trackers/eventTracker.js +12 -10
  28. package/cjs/trackers/impressionsTracker.js +16 -14
  29. package/cjs/trackers/uniqueKeysTracker.js +5 -3
  30. package/cjs/utils/lang/sets.js +12 -2
  31. package/esm/evaluator/Engine.js +1 -1
  32. package/esm/evaluator/index.js +2 -2
  33. package/esm/readiness/readinessManager.js +13 -2
  34. package/esm/sdkClient/sdkClientMethodCS.js +0 -1
  35. package/esm/sdkFactory/index.js +26 -8
  36. package/esm/storages/{AbstractSegmentsCacheSync.js → AbstractMySegmentsCacheSync.js} +14 -16
  37. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +5 -5
  38. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +3 -2
  39. package/esm/storages/inLocalStorage/index.js +1 -1
  40. package/esm/storages/inMemory/InMemoryStorageCS.js +2 -2
  41. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -5
  42. package/esm/storages/inMemory/SegmentsCacheInMemory.js +13 -27
  43. package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -1
  44. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +2 -1
  45. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +2 -1
  46. package/esm/storages/inRedis/RedisAdapter.js +2 -1
  47. package/esm/storages/inRedis/SegmentsCacheInRedis.js +13 -19
  48. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +2 -1
  49. package/esm/storages/pluggable/SegmentsCachePluggable.js +11 -32
  50. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +2 -1
  51. package/esm/storages/pluggable/inMemoryWrapper.js +2 -1
  52. package/esm/sync/offline/syncManagerOffline.js +18 -11
  53. package/esm/sync/polling/updaters/segmentChangesUpdater.js +12 -28
  54. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -1
  55. package/esm/sync/syncManagerOnline.js +20 -21
  56. package/esm/trackers/eventTracker.js +12 -10
  57. package/esm/trackers/impressionsTracker.js +16 -14
  58. package/esm/trackers/uniqueKeysTracker.js +5 -3
  59. package/esm/utils/lang/sets.js +10 -1
  60. package/package.json +1 -1
  61. package/src/evaluator/Engine.ts +1 -1
  62. package/src/evaluator/index.ts +2 -2
  63. package/src/readiness/readinessManager.ts +12 -3
  64. package/src/readiness/types.ts +3 -0
  65. package/src/sdkClient/sdkClientMethodCS.ts +0 -2
  66. package/src/sdkFactory/index.ts +28 -9
  67. package/src/sdkFactory/types.ts +2 -0
  68. package/src/storages/{AbstractSegmentsCacheSync.ts → AbstractMySegmentsCacheSync.ts} +13 -28
  69. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +5 -5
  70. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +3 -2
  71. package/src/storages/inLocalStorage/index.ts +1 -1
  72. package/src/storages/inMemory/InMemoryStorageCS.ts +2 -2
  73. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +5 -5
  74. package/src/storages/inMemory/SegmentsCacheInMemory.ts +12 -26
  75. package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -1
  76. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +2 -1
  77. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +2 -1
  78. package/src/storages/inRedis/RedisAdapter.ts +2 -1
  79. package/src/storages/inRedis/SegmentsCacheInRedis.ts +13 -22
  80. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +2 -1
  81. package/src/storages/pluggable/SegmentsCachePluggable.ts +11 -35
  82. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +2 -1
  83. package/src/storages/pluggable/inMemoryWrapper.ts +2 -1
  84. package/src/storages/types.ts +3 -9
  85. package/src/sync/offline/syncManagerOffline.ts +21 -13
  86. package/src/sync/polling/updaters/segmentChangesUpdater.ts +13 -29
  87. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -1
  88. package/src/sync/syncManagerOnline.ts +17 -17
  89. package/src/sync/types.ts +1 -1
  90. package/src/trackers/eventTracker.ts +11 -8
  91. package/src/trackers/impressionsTracker.ts +13 -10
  92. package/src/trackers/types.ts +1 -0
  93. package/src/trackers/uniqueKeysTracker.ts +6 -4
  94. package/src/utils/lang/sets.ts +11 -1
  95. package/types/readiness/types.d.ts +3 -0
  96. package/types/sdkFactory/types.d.ts +1 -0
  97. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +5 -5
  98. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +5 -5
  99. package/types/storages/inMemory/SegmentsCacheInMemory.d.ts +5 -7
  100. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +0 -1
  101. package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +6 -3
  102. package/types/storages/pluggable/SegmentsCachePluggable.d.ts +4 -16
  103. package/types/storages/types.d.ts +3 -9
  104. package/types/sync/types.d.ts +1 -1
  105. package/types/trackers/eventTracker.d.ts +1 -1
  106. package/types/trackers/impressionsTracker.d.ts +1 -1
  107. package/types/trackers/types.d.ts +1 -0
  108. package/types/utils/lang/sets.d.ts +1 -0
@@ -4,6 +4,7 @@ import { DEFAULT_CACHE_SIZE, REFRESH_RATE } from '../inRedis/constants';
4
4
  import { LOG_PREFIX } from './constants';
5
5
  import { ILogger } from '../../logger/types';
6
6
  import { UniqueKeysItemSs } from '../../sync/submitters/types';
7
+ import { setToArray } from '../../utils/lang/sets';
7
8
 
8
9
  export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
9
10
 
@@ -27,7 +28,7 @@ export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements
27
28
  if (!featureNames.length) return Promise.resolve(false);
28
29
 
29
30
  const uniqueKeysArray = featureNames.map((featureName) => {
30
- const featureKeys = Array.from(this.uniqueKeysTracker[featureName]);
31
+ const featureKeys = setToArray(this.uniqueKeysTracker[featureName]);
31
32
  const uniqueKeysPayload = {
32
33
  f: featureName,
33
34
  ks: featureKeys
@@ -1,5 +1,6 @@
1
1
  import { IPluggableStorageWrapper } from '../types';
2
2
  import { startsWith, toNumber } from '../../utils/lang';
3
+ import { setToArray } from '../../utils/lang/sets';
3
4
 
4
5
  /**
5
6
  * Creates a IPluggableStorageWrapper implementation that stores items in memory.
@@ -107,7 +108,7 @@ export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWra
107
108
  getItems(key: string) {
108
109
  const set = _cache[key];
109
110
  if (!set) return Promise.resolve([]);
110
- if (set instanceof Set) return Promise.resolve(Array.from(set));
111
+ if (set instanceof Set) return Promise.resolve(setToArray(set));
111
112
  return Promise.reject('key is not a set');
112
113
  },
113
114
 
@@ -250,38 +250,32 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
250
250
  /** Segments cache */
251
251
 
252
252
  export interface ISegmentsCacheBase {
253
- addToSegment(name: string, segmentKeys: string[]): MaybeThenable<boolean | void> // different signature on Server and Client-Side
254
- removeFromSegment(name: string, segmentKeys: string[]): MaybeThenable<boolean | void> // different signature on Server and Client-Side
255
253
  isInSegment(name: string, key?: string): MaybeThenable<boolean> // different signature on Server and Client-Side
256
254
  registerSegments(names: string[]): MaybeThenable<boolean | void> // only for Server-Side
257
255
  getRegisteredSegments(): MaybeThenable<string[]> // only for Server-Side
258
- setChangeNumber(name: string, changeNumber: number): MaybeThenable<boolean | void> // only for Server-Side
259
256
  getChangeNumber(name: string): MaybeThenable<number> // only for Server-Side
257
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): MaybeThenable<boolean> // only for Server-Side
260
258
  clear(): MaybeThenable<boolean | void>
261
259
  }
262
260
 
263
261
  // Same API for both variants: SegmentsCache and MySegmentsCache (client-side API)
264
262
  export interface ISegmentsCacheSync extends ISegmentsCacheBase {
265
- addToSegment(name: string, segmentKeys?: string[]): boolean
266
- removeFromSegment(name: string, segmentKeys?: string[]): boolean
267
263
  isInSegment(name: string, key?: string): boolean
268
264
  registerSegments(names: string[]): boolean
269
265
  getRegisteredSegments(): string[]
270
266
  getKeysCount(): number // only used for telemetry
271
- setChangeNumber(name: string, changeNumber: number): boolean | void
272
267
  getChangeNumber(name?: string): number
268
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): boolean // only for Server-Side
273
269
  resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean // only for Sync Client-Side
274
270
  clear(): void
275
271
  }
276
272
 
277
273
  export interface ISegmentsCacheAsync extends ISegmentsCacheBase {
278
- addToSegment(name: string, segmentKeys: string[]): Promise<boolean | void>
279
- removeFromSegment(name: string, segmentKeys: string[]): Promise<boolean | void>
280
274
  isInSegment(name: string, key: string): Promise<boolean>
281
275
  registerSegments(names: string[]): Promise<boolean | void>
282
276
  getRegisteredSegments(): Promise<string[]>
283
- setChangeNumber(name: string, changeNumber: number): Promise<boolean | void>
284
277
  getChangeNumber(name: string): Promise<number>
278
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): Promise<boolean>
285
279
  clear(): Promise<boolean | void>
286
280
  }
287
281
 
@@ -1,4 +1,4 @@
1
- import { ISyncManager, ISyncManagerCS } from '../types';
1
+ import { ISyncManagerCS } from '../types';
2
2
  import { fromObjectSyncTaskFactory } from './syncTasks/fromObjectSyncTask';
3
3
  import { objectAssign } from '../../utils/lang/objectAssign';
4
4
  import { ISplitsParser } from './splitsParser/types';
@@ -29,26 +29,34 @@ export function syncManagerOfflineFactory(
29
29
  storage,
30
30
  }: ISdkFactoryContextSync): ISyncManagerCS {
31
31
 
32
+ const mainSyncManager = fromObjectSyncTaskFactory(splitsParserFactory(), storage, readiness, settings);
33
+ const mainStart = mainSyncManager.start;
34
+ const sharedStarts: Array<() => void> = [];
35
+
32
36
  return objectAssign(
33
- fromObjectSyncTaskFactory(splitsParserFactory(), storage, readiness, settings),
37
+ mainSyncManager,
34
38
  {
39
+ start() {
40
+ mainStart();
41
+ sharedStarts.forEach(cb => cb());
42
+ sharedStarts.length = 0;
43
+ },
35
44
  // fake flush, that resolves immediately
36
45
  flush,
37
46
 
38
47
  // [Only used for client-side]
39
- shared(matchingKey: string, readinessManager: IReadinessManager): ISyncManager {
48
+ shared(matchingKey: string, readinessManager: IReadinessManager) {
49
+ // In LOCALHOST mode, shared clients are ready in the next event-loop cycle than created
50
+ // SDK_READY cannot be emitted directly because this will not update the readiness status
51
+ function emitSdkReady() {
52
+ readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // SDK_SPLITS_ARRIVED emitted by main SyncManager
53
+ }
54
+
55
+ if (mainSyncManager.isRunning()) setTimeout(emitSdkReady);
56
+ else sharedStarts.push(emitSdkReady);
57
+
40
58
  return {
41
- start() {
42
- // In LOCALHOST mode, shared clients are ready in the next event-loop cycle than created
43
- // SDK_READY cannot be emitted directly because this will not update the readiness status
44
- setTimeout(() => {
45
- readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // SDK_SPLITS_ARRIVED emitted by main SyncManager
46
- }, 0);
47
- },
48
59
  stop() { },
49
- isRunning() {
50
- return true;
51
- },
52
60
  flush,
53
61
  };
54
62
  }
@@ -1,12 +1,9 @@
1
1
  import { ISegmentChangesFetcher } from '../fetchers/types';
2
2
  import { ISegmentsCacheBase } from '../../../storages/types';
3
3
  import { IReadinessManager } from '../../../readiness/types';
4
- import { MaybeThenable } from '../../../dtos/types';
5
- import { findIndex } from '../../../utils/lang';
6
4
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
7
5
  import { ILogger } from '../../../logger/types';
8
6
  import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
9
- import { thenable } from '../../../utils/promise/thenable';
10
7
 
11
8
  type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>
12
9
 
@@ -30,31 +27,22 @@ export function segmentChangesUpdaterFactory(
30
27
 
31
28
  let readyOnAlreadyExistentState = true;
32
29
 
33
- function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean) {
30
+ function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean): Promise<boolean> {
34
31
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing segment ${segmentName}`);
35
32
  let sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
36
33
 
37
34
  return sincePromise.then(since => {
38
35
  // if fetchOnlyNew flag, avoid processing already fetched segments
39
- if (fetchOnlyNew && since !== -1) return -1;
40
-
41
- return segmentChangesFetcher(since, segmentName, noCache, till).then(function (changes) {
42
- let changeNumber = -1;
43
- const results: MaybeThenable<boolean | void>[] = [];
44
- changes.forEach(x => {
45
- if (x.added.length > 0) results.push(segments.addToSegment(segmentName, x.added));
46
- if (x.removed.length > 0) results.push(segments.removeFromSegment(segmentName, x.removed));
47
- if (x.added.length > 0 || x.removed.length > 0) {
48
- results.push(segments.setChangeNumber(segmentName, x.till));
49
- changeNumber = x.till;
50
- }
51
-
52
- log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processed ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
36
+ return fetchOnlyNew && since !== -1 ?
37
+ false :
38
+ segmentChangesFetcher(since, segmentName, noCache, till).then((changes) => {
39
+ return Promise.all(changes.map(x => {
40
+ log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
41
+ return segments.update(x.name, x.added, x.removed, x.till);
42
+ })).then((updates) => {
43
+ return updates.some(update => update);
44
+ });
53
45
  });
54
- // If at least one storage operation result is a promise, join all in a single promise.
55
- if (results.some(result => thenable(result))) return Promise.all(results).then(() => changeNumber);
56
- return changeNumber;
57
- });
58
46
  });
59
47
  }
60
48
  /**
@@ -75,16 +63,12 @@ export function segmentChangesUpdaterFactory(
75
63
  let segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
76
64
 
77
65
  return segmentsPromise.then(segmentNames => {
78
- // Async fetchers are collected here.
79
- const updaters: Promise<number>[] = [];
80
-
81
- for (let index = 0; index < segmentNames.length; index++) {
82
- updaters.push(updateSegment(segmentNames[index], noCache, till, fetchOnlyNew));
83
- }
66
+ // Async fetchers
67
+ const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew));
84
68
 
85
69
  return Promise.all(updaters).then(shouldUpdateFlags => {
86
70
  // if at least one segment fetch succeeded, mark segments ready
87
- if (findIndex(shouldUpdateFlags, v => v !== -1) !== -1 || readyOnAlreadyExistentState) {
71
+ if (shouldUpdateFlags.some(update => update) || readyOnAlreadyExistentState) {
88
72
  readyOnAlreadyExistentState = false;
89
73
  if (readiness) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
90
74
  }
@@ -8,6 +8,7 @@ import { ILogger } from '../../../logger/types';
8
8
  import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
9
9
  import { startsWith } from '../../../utils/lang';
10
10
  import { IN_SEGMENT } from '../../../utils/constants';
11
+ import { setToArray } from '../../../utils/lang/sets';
11
12
 
12
13
  type ISplitChangesUpdater = (noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }) => Promise<boolean>
13
14
 
@@ -88,7 +89,7 @@ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersV
88
89
  return accum;
89
90
  }, { added: [], removed: [], segments: [] } as ISplitMutations);
90
91
 
91
- computed.segments = Array.from(segments);
92
+ computed.segments = setToArray(segments);
92
93
 
93
94
  return computed;
94
95
  }
@@ -143,27 +143,27 @@ export function syncManagerOnlineFactory(
143
143
 
144
144
  const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage);
145
145
 
146
- return {
147
- isRunning: mySegmentsSyncTask.isRunning,
148
- start() {
149
- if (syncEnabled) {
150
- if (pushManager) {
151
- if (pollingManager!.isRunning()) {
152
- // if doing polling, we must start the periodic fetch of data
153
- if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
154
- } else {
155
- // if not polling, we must execute the sync task for the initial fetch
156
- // of segments since `syncAll` was already executed when starting the main client
157
- mySegmentsSyncTask.execute();
158
- }
159
- pushManager.add(matchingKey, mySegmentsSyncTask);
160
- } else {
146
+ if (running) {
147
+ if (syncEnabled) {
148
+ if (pushManager) {
149
+ if (pollingManager!.isRunning()) {
150
+ // if doing polling, we must start the periodic fetch of data
161
151
  if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
152
+ } else {
153
+ // if not polling, we must execute the sync task for the initial fetch
154
+ // of segments since `syncAll` was already executed when starting the main client
155
+ mySegmentsSyncTask.execute();
162
156
  }
157
+ pushManager.add(matchingKey, mySegmentsSyncTask);
163
158
  } else {
164
- if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
159
+ if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
165
160
  }
166
- },
161
+ } else {
162
+ if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
163
+ }
164
+ }
165
+
166
+ return {
167
167
  stop() {
168
168
  // check in case `client.destroy()` has been invoked more than once for the same client
169
169
  const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).get(matchingKey);
package/src/sync/types.ts CHANGED
@@ -44,5 +44,5 @@ export interface ISyncManager extends ITask {
44
44
  }
45
45
 
46
46
  export interface ISyncManagerCS extends ISyncManager {
47
- shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined
47
+ shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): Pick<ISyncManager, 'stop' | 'flush'> | undefined
48
48
  }
@@ -16,6 +16,7 @@ import { isConsumerMode } from '../utils/settingsValidation/mode';
16
16
  export function eventTrackerFactory(
17
17
  settings: ISettings,
18
18
  eventsCache: IEventsCacheBase,
19
+ whenInit: (cb: () => void) => void,
19
20
  integrationsManager?: IEventsHandler,
20
21
  telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync
21
22
  ): IEventTracker {
@@ -31,14 +32,16 @@ export function eventTrackerFactory(
31
32
  if (tracked) {
32
33
  log.info(EVENTS_TRACKER_SUCCESS, [msg]);
33
34
  if (integrationsManager) {
34
- // Wrap in a timeout because we don't want it to be blocking.
35
- setTimeout(function () {
36
- // copy of event, to avoid unexpected behaviour if modified by integrations
37
- const eventDataCopy = objectAssign({}, eventData);
38
- if (properties) eventDataCopy.properties = objectAssign({}, properties);
39
- // integrationsManager does not throw errors (they are internally handled by each integration module)
40
- integrationsManager.handleEvent(eventDataCopy);
41
- }, 0);
35
+ whenInit(() => {
36
+ // Wrap in a timeout because we don't want it to be blocking.
37
+ setTimeout(() => {
38
+ // copy of event, to avoid unexpected behaviour if modified by integrations
39
+ const eventDataCopy = objectAssign({}, eventData);
40
+ if (properties) eventDataCopy.properties = objectAssign({}, properties);
41
+ // integrationsManager does not throw errors (they are internally handled by each integration module)
42
+ integrationsManager.handleEvent(eventDataCopy);
43
+ });
44
+ });
42
45
  }
43
46
  } else {
44
47
  log.error(ERROR_EVENTS_TRACKER, [msg]);
@@ -19,6 +19,7 @@ export function impressionsTrackerFactory(
19
19
  settings: ISettings,
20
20
  impressionsCache: IImpressionsCacheBase,
21
21
  strategy: IStrategy,
22
+ whenInit: (cb: () => void) => void,
22
23
  integrationsManager?: IImpressionsHandler,
23
24
  telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync,
24
25
  ): IImpressionsTracker {
@@ -66,17 +67,19 @@ export function impressionsTrackerFactory(
66
67
  sdkLanguageVersion: version
67
68
  };
68
69
 
69
- // Wrap in a timeout because we don't want it to be blocking.
70
- setTimeout(function () {
71
- // integrationsManager.handleImpression does not throw errors
72
- if (integrationsManager) integrationsManager.handleImpression(impressionData);
70
+ whenInit(() => {
71
+ // Wrap in a timeout because we don't want it to be blocking.
72
+ setTimeout(() => {
73
+ // integrationsManager.handleImpression does not throw errors
74
+ if (integrationsManager) integrationsManager.handleImpression(impressionData);
73
75
 
74
- try { // @ts-ignore. An exception on the listeners should not break the SDK.
75
- if (impressionListener) impressionListener.logImpression(impressionData);
76
- } catch (err) {
77
- log.error(ERROR_IMPRESSIONS_LISTENER, [err]);
78
- }
79
- }, 0);
76
+ try { // @ts-ignore. An exception on the listeners should not break the SDK.
77
+ if (impressionListener) impressionListener.logImpression(impressionData);
78
+ } catch (err) {
79
+ log.error(ERROR_IMPRESSIONS_LISTENER, [err]);
80
+ }
81
+ });
82
+ });
80
83
  }
81
84
  }
82
85
  }
@@ -65,6 +65,7 @@ export interface IImpressionSenderAdapter {
65
65
 
66
66
  /** Unique keys tracker */
67
67
  export interface IUniqueKeysTracker {
68
+ start(): void;
68
69
  stop(): void;
69
70
  track(key: string, featureName: string): void;
70
71
  }
@@ -25,10 +25,6 @@ export function uniqueKeysTrackerFactory(
25
25
  ): IUniqueKeysTracker {
26
26
  let intervalId: any;
27
27
 
28
- if (filterAdapter.refreshRate) {
29
- intervalId = setInterval(filterAdapter.clear, filterAdapter.refreshRate);
30
- }
31
-
32
28
  return {
33
29
 
34
30
  track(key: string, featureName: string): void {
@@ -39,6 +35,12 @@ export function uniqueKeysTrackerFactory(
39
35
  uniqueKeysCache.track(key, featureName);
40
36
  },
41
37
 
38
+ start(): void {
39
+ if (filterAdapter.refreshRate) {
40
+ intervalId = setInterval(filterAdapter.clear, filterAdapter.refreshRate);
41
+ }
42
+ },
43
+
42
44
  stop(): void {
43
45
  clearInterval(intervalId);
44
46
  }
@@ -1,5 +1,15 @@
1
+ export function setToArray<T>(set: Set<T>): T[] {
2
+ if (Array.from) return Array.from(set);
3
+
4
+ const array: T[] = [];
5
+ set.forEach((value: T) => {
6
+ array.push(value);
7
+ });
8
+ return array;
9
+ }
10
+
1
11
  export function returnSetsUnion<T>(set: Set<T>, set2: Set<T>): Set<T> {
2
- return new Set(Array.from(set).concat(Array.from(set2)));
12
+ return new Set(setToArray(set).concat(setToArray(set2)));
3
13
  }
4
14
 
5
15
  export function returnDifference<T>(list: T[] = [], list2: T[] = []): T[] {
@@ -9,6 +9,8 @@ export interface ISplitsEventEmitter extends IEventEmitter {
9
9
  once(event: ISplitsEvent, listener: (...args: any[]) => void): this;
10
10
  splitsArrived: boolean;
11
11
  splitsCacheLoaded: boolean;
12
+ initialized: boolean;
13
+ initCallbacks: (() => void)[];
12
14
  }
13
15
  /** Segments data emitter */
14
16
  declare type SDK_SEGMENTS_ARRIVED = 'state::segments-arrived';
@@ -46,6 +48,7 @@ export interface IReadinessManager {
46
48
  timeout(): void;
47
49
  setDestroyed(): void;
48
50
  destroy(): void;
51
+ init(): void;
49
52
  /** for client-side */
50
53
  shared(): IReadinessManager;
51
54
  }
@@ -63,6 +63,7 @@ export interface ISdkFactoryContextAsync extends ISdkFactoryContext {
63
63
  * Object parameter with the modules required to create an SDK factory instance
64
64
  */
65
65
  export interface ISdkFactoryParams {
66
+ lazyInit?: boolean;
66
67
  settings: ISettings;
67
68
  platform: IPlatform;
68
69
  storageFactory: (params: IStorageFactoryParams) => IStorageSync | IStorageAsync;
@@ -1,15 +1,15 @@
1
1
  import { ILogger } from '../../logger/types';
2
- import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
2
+ import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
3
3
  import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
4
- export declare class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
4
+ export declare class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
5
5
  private readonly keys;
6
6
  private readonly log;
7
7
  constructor(log: ILogger, keys: MySegmentsKeyBuilder);
8
- addToSegment(name: string): boolean;
9
- removeFromSegment(name: string): boolean;
8
+ protected addSegment(name: string): boolean;
9
+ protected removeSegment(name: string): boolean;
10
10
  isInSegment(name: string): boolean;
11
11
  getRegisteredSegments(): string[];
12
12
  getKeysCount(): number;
13
- setChangeNumber(name?: string, changeNumber?: number): void;
13
+ protected setChangeNumber(changeNumber?: number): void;
14
14
  getChangeNumber(): number;
15
15
  }
@@ -1,15 +1,15 @@
1
- import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
1
+ import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
2
2
  /**
3
3
  * Default MySegmentsCacheInMemory implementation that stores MySegments in memory.
4
4
  * Supported by all JS runtimes.
5
5
  */
6
- export declare class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
6
+ export declare class MySegmentsCacheInMemory extends AbstractMySegmentsCacheSync {
7
7
  private segmentCache;
8
8
  private cn?;
9
- addToSegment(name: string): boolean;
10
- removeFromSegment(name: string): boolean;
9
+ protected addSegment(name: string): boolean;
10
+ protected removeSegment(name: string): boolean;
11
11
  isInSegment(name: string): boolean;
12
- setChangeNumber(name?: string, changeNumber?: number): void;
12
+ protected setChangeNumber(changeNumber?: number): void;
13
13
  getChangeNumber(): number;
14
14
  getRegisteredSegments(): string[];
15
15
  getKeysCount(): number;
@@ -1,19 +1,17 @@
1
- import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
1
+ import { ISegmentsCacheSync } from '../types';
2
2
  /**
3
- * Default ISplitsCacheSync implementation that stores split definitions in memory.
4
- * Supported by all JS runtimes.
3
+ * Default ISplitsCacheSync implementation for server-side that stores segments definitions in memory.
5
4
  */
6
- export declare class SegmentsCacheInMemory extends AbstractSegmentsCacheSync {
5
+ export declare class SegmentsCacheInMemory implements ISegmentsCacheSync {
7
6
  private segmentCache;
8
7
  private segmentChangeNumber;
9
- addToSegment(name: string, segmentKeys: string[]): boolean;
10
- removeFromSegment(name: string, segmentKeys: string[]): boolean;
8
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): boolean;
11
9
  isInSegment(name: string, key: string): boolean;
12
10
  clear(): void;
13
11
  private _registerSegment;
14
12
  registerSegments(names: string[]): boolean;
15
13
  getRegisteredSegments(): string[];
16
14
  getKeysCount(): number;
17
- setChangeNumber(name: string, changeNumber: number): boolean;
18
15
  getChangeNumber(name: string): number;
16
+ resetSegments(): boolean;
19
17
  }
@@ -2,7 +2,6 @@ import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
2
2
  import { AbstractSplitsCacheSync } from '../AbstractSplitsCacheSync';
3
3
  /**
4
4
  * Default ISplitsCacheSync implementation that stores split definitions in memory.
5
- * Supported by all JS runtimes.
6
5
  */
7
6
  export declare class SplitsCacheInMemory extends AbstractSplitsCacheSync {
8
7
  private flagSetsFilter;
@@ -7,10 +7,13 @@ export declare class SegmentsCacheInRedis implements ISegmentsCacheAsync {
7
7
  private readonly redis;
8
8
  private readonly keys;
9
9
  constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter);
10
- addToSegment(name: string, segmentKeys: string[]): Promise<boolean>;
11
- removeFromSegment(name: string, segmentKeys: string[]): Promise<boolean>;
10
+ /**
11
+ * Update the given segment `name` with the lists of `addedKeys`, `removedKeys` and `changeNumber`.
12
+ * The returned promise is resolved if the operation success, with `true` if the segment was updated (i.e., some key was added or removed),
13
+ * or rejected if it fails (e.g., Redis operation fails).
14
+ */
15
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): Promise<boolean>;
12
16
  isInSegment(name: string, key: string): Promise<boolean>;
13
- setChangeNumber(name: string, changeNumber: number): Promise<boolean>;
14
17
  getChangeNumber(name: string): Promise<number>;
15
18
  registerSegments(segments: string[]): Promise<boolean>;
16
19
  getRegisteredSegments(): Promise<string[]>;
@@ -10,28 +10,16 @@ export declare class SegmentsCachePluggable implements ISegmentsCacheAsync {
10
10
  private readonly wrapper;
11
11
  constructor(log: ILogger, keys: KeyBuilderSS, wrapper: IPluggableStorageWrapper);
12
12
  /**
13
- * Add a list of `segmentKeys` to the given segment `name`.
14
- * The returned promise is resolved when the operation success
15
- * or rejected if wrapper operation fails.
16
- */
17
- addToSegment(name: string, segmentKeys: string[]): Promise<boolean | void>;
18
- /**
19
- * Remove a list of `segmentKeys` from the given segment `name`.
20
- * The returned promise is resolved when the operation success
21
- * or rejected if wrapper operation fails.
13
+ * Update the given segment `name` with the lists of `addedKeys`, `removedKeys` and `changeNumber`.
14
+ * The returned promise is resolved if the operation success, with `true` if the segment was updated (i.e., some key was added or removed),
15
+ * or rejected if it fails (e.g., wrapper operation fails).
22
16
  */
23
- removeFromSegment(name: string, segmentKeys: string[]): Promise<boolean | void>;
17
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): Promise<boolean>;
24
18
  /**
25
19
  * Returns a promise that resolves with a boolean value indicating if `key` is part of `name` segment.
26
20
  * Promise can be rejected if wrapper operation fails.
27
21
  */
28
22
  isInSegment(name: string, key: string): Promise<boolean>;
29
- /**
30
- * Set till number for the given segment `name`.
31
- * The returned promise is resolved when the operation success,
32
- * or rejected if it fails (e.g., wrapper operation fails).
33
- */
34
- setChangeNumber(name: string, changeNumber: number): Promise<boolean | void>;
35
23
  /**
36
24
  * Get till number or -1 if it's not defined.
37
25
  * The returned promise is resolved with the changeNumber or -1 if it doesn't exist or a wrapper operation fails.
@@ -229,35 +229,29 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
229
229
  }
230
230
  /** Segments cache */
231
231
  export interface ISegmentsCacheBase {
232
- addToSegment(name: string, segmentKeys: string[]): MaybeThenable<boolean | void>;
233
- removeFromSegment(name: string, segmentKeys: string[]): MaybeThenable<boolean | void>;
234
232
  isInSegment(name: string, key?: string): MaybeThenable<boolean>;
235
233
  registerSegments(names: string[]): MaybeThenable<boolean | void>;
236
234
  getRegisteredSegments(): MaybeThenable<string[]>;
237
- setChangeNumber(name: string, changeNumber: number): MaybeThenable<boolean | void>;
238
235
  getChangeNumber(name: string): MaybeThenable<number>;
236
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): MaybeThenable<boolean>;
239
237
  clear(): MaybeThenable<boolean | void>;
240
238
  }
241
239
  export interface ISegmentsCacheSync extends ISegmentsCacheBase {
242
- addToSegment(name: string, segmentKeys?: string[]): boolean;
243
- removeFromSegment(name: string, segmentKeys?: string[]): boolean;
244
240
  isInSegment(name: string, key?: string): boolean;
245
241
  registerSegments(names: string[]): boolean;
246
242
  getRegisteredSegments(): string[];
247
243
  getKeysCount(): number;
248
- setChangeNumber(name: string, changeNumber: number): boolean | void;
249
244
  getChangeNumber(name?: string): number;
245
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): boolean;
250
246
  resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean;
251
247
  clear(): void;
252
248
  }
253
249
  export interface ISegmentsCacheAsync extends ISegmentsCacheBase {
254
- addToSegment(name: string, segmentKeys: string[]): Promise<boolean | void>;
255
- removeFromSegment(name: string, segmentKeys: string[]): Promise<boolean | void>;
256
250
  isInSegment(name: string, key: string): Promise<boolean>;
257
251
  registerSegments(names: string[]): Promise<boolean | void>;
258
252
  getRegisteredSegments(): Promise<string[]>;
259
- setChangeNumber(name: string, changeNumber: number): Promise<boolean | void>;
260
253
  getChangeNumber(name: string): Promise<number>;
254
+ update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): Promise<boolean>;
261
255
  clear(): Promise<boolean | void>;
262
256
  }
263
257
  /** Recorder storages (impressions, events and telemetry) */
@@ -39,5 +39,5 @@ export interface ISyncManager extends ITask {
39
39
  submitterManager?: ISubmitterManager;
40
40
  }
41
41
  export interface ISyncManagerCS extends ISyncManager {
42
- shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined;
42
+ shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): Pick<ISyncManager, 'stop' | 'flush'> | undefined;
43
43
  }
@@ -7,4 +7,4 @@ import { ISettings } from '../types';
7
7
  * @param eventsCache cache to save events
8
8
  * @param integrationsManager optional event handler used for integrations
9
9
  */
10
- export declare function eventTrackerFactory(settings: ISettings, eventsCache: IEventsCacheBase, integrationsManager?: IEventsHandler, telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync): IEventTracker;
10
+ export declare function eventTrackerFactory(settings: ISettings, eventsCache: IEventsCacheBase, whenInit: (cb: () => void) => void, integrationsManager?: IEventsHandler, telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync): IEventTracker;
@@ -10,4 +10,4 @@ import { ISettings } from '../types';
10
10
  * @param integrationsManager optional integrations manager
11
11
  * @param strategy strategy for impressions tracking.
12
12
  */
13
- export declare function impressionsTrackerFactory(settings: ISettings, impressionsCache: IImpressionsCacheBase, strategy: IStrategy, integrationsManager?: IImpressionsHandler, telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync): IImpressionsTracker;
13
+ export declare function impressionsTrackerFactory(settings: ISettings, impressionsCache: IImpressionsCacheBase, strategy: IStrategy, whenInit: (cb: () => void) => void, integrationsManager?: IImpressionsHandler, telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync): IImpressionsTracker;
@@ -54,6 +54,7 @@ export interface IImpressionSenderAdapter {
54
54
  }
55
55
  /** Unique keys tracker */
56
56
  export interface IUniqueKeysTracker {
57
+ start(): void;
57
58
  stop(): void;
58
59
  track(key: string, featureName: string): void;
59
60
  }
@@ -1,2 +1,3 @@
1
+ export declare function setToArray<T>(set: Set<T>): T[];
1
2
  export declare function returnSetsUnion<T>(set: Set<T>, set2: Set<T>): Set<T>;
2
3
  export declare function returnDifference<T>(list?: T[], list2?: T[]): T[];