@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.
- package/CHANGES.txt +2 -1
- package/cjs/evaluator/Engine.js +1 -1
- package/cjs/evaluator/index.js +1 -1
- package/cjs/readiness/readinessManager.js +13 -2
- package/cjs/sdkClient/sdkClientMethodCS.js +0 -1
- package/cjs/sdkFactory/index.js +26 -8
- package/cjs/storages/{AbstractSegmentsCacheSync.js → AbstractMySegmentsCacheSync.js} +15 -17
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +5 -5
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +3 -2
- package/cjs/storages/inLocalStorage/index.js +1 -1
- package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -2
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -5
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +13 -27
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -1
- package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +2 -1
- package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +2 -1
- package/cjs/storages/inRedis/RedisAdapter.js +2 -1
- package/cjs/storages/inRedis/SegmentsCacheInRedis.js +13 -19
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +2 -1
- package/cjs/storages/pluggable/SegmentsCachePluggable.js +11 -32
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +2 -1
- package/cjs/storages/pluggable/inMemoryWrapper.js +2 -1
- package/cjs/sync/offline/syncManagerOffline.js +18 -11
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +12 -28
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -1
- package/cjs/sync/syncManagerOnline.js +20 -21
- package/cjs/trackers/eventTracker.js +12 -10
- package/cjs/trackers/impressionsTracker.js +16 -14
- package/cjs/trackers/uniqueKeysTracker.js +5 -3
- package/cjs/utils/lang/sets.js +12 -2
- package/esm/evaluator/Engine.js +1 -1
- package/esm/evaluator/index.js +2 -2
- package/esm/readiness/readinessManager.js +13 -2
- package/esm/sdkClient/sdkClientMethodCS.js +0 -1
- package/esm/sdkFactory/index.js +26 -8
- package/esm/storages/{AbstractSegmentsCacheSync.js → AbstractMySegmentsCacheSync.js} +14 -16
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +5 -5
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +3 -2
- package/esm/storages/inLocalStorage/index.js +1 -1
- package/esm/storages/inMemory/InMemoryStorageCS.js +2 -2
- package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -5
- package/esm/storages/inMemory/SegmentsCacheInMemory.js +13 -27
- package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -1
- package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +2 -1
- package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +2 -1
- package/esm/storages/inRedis/RedisAdapter.js +2 -1
- package/esm/storages/inRedis/SegmentsCacheInRedis.js +13 -19
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +2 -1
- package/esm/storages/pluggable/SegmentsCachePluggable.js +11 -32
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +2 -1
- package/esm/storages/pluggable/inMemoryWrapper.js +2 -1
- package/esm/sync/offline/syncManagerOffline.js +18 -11
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +12 -28
- package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -1
- package/esm/sync/syncManagerOnline.js +20 -21
- package/esm/trackers/eventTracker.js +12 -10
- package/esm/trackers/impressionsTracker.js +16 -14
- package/esm/trackers/uniqueKeysTracker.js +5 -3
- package/esm/utils/lang/sets.js +10 -1
- package/package.json +1 -1
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/index.ts +2 -2
- package/src/readiness/readinessManager.ts +12 -3
- package/src/readiness/types.ts +3 -0
- package/src/sdkClient/sdkClientMethodCS.ts +0 -2
- package/src/sdkFactory/index.ts +28 -9
- package/src/sdkFactory/types.ts +2 -0
- package/src/storages/{AbstractSegmentsCacheSync.ts → AbstractMySegmentsCacheSync.ts} +13 -28
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +5 -5
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +3 -2
- package/src/storages/inLocalStorage/index.ts +1 -1
- package/src/storages/inMemory/InMemoryStorageCS.ts +2 -2
- package/src/storages/inMemory/MySegmentsCacheInMemory.ts +5 -5
- package/src/storages/inMemory/SegmentsCacheInMemory.ts +12 -26
- package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -1
- package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +2 -1
- package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +2 -1
- package/src/storages/inRedis/RedisAdapter.ts +2 -1
- package/src/storages/inRedis/SegmentsCacheInRedis.ts +13 -22
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +2 -1
- package/src/storages/pluggable/SegmentsCachePluggable.ts +11 -35
- package/src/storages/pluggable/UniqueKeysCachePluggable.ts +2 -1
- package/src/storages/pluggable/inMemoryWrapper.ts +2 -1
- package/src/storages/types.ts +3 -9
- package/src/sync/offline/syncManagerOffline.ts +21 -13
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +13 -29
- package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -1
- package/src/sync/syncManagerOnline.ts +17 -17
- package/src/sync/types.ts +1 -1
- package/src/trackers/eventTracker.ts +11 -8
- package/src/trackers/impressionsTracker.ts +13 -10
- package/src/trackers/types.ts +1 -0
- package/src/trackers/uniqueKeysTracker.ts +6 -4
- package/src/utils/lang/sets.ts +11 -1
- package/types/readiness/types.d.ts +3 -0
- package/types/sdkFactory/types.d.ts +1 -0
- package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +5 -5
- package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +5 -5
- package/types/storages/inMemory/SegmentsCacheInMemory.d.ts +5 -7
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +0 -1
- package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +6 -3
- package/types/storages/pluggable/SegmentsCachePluggable.d.ts +4 -16
- package/types/storages/types.d.ts +3 -9
- package/types/sync/types.d.ts +1 -1
- package/types/trackers/eventTracker.d.ts +1 -1
- package/types/trackers/impressionsTracker.d.ts +1 -1
- package/types/trackers/types.d.ts +1 -0
- 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 =
|
|
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(
|
|
111
|
+
if (set instanceof Set) return Promise.resolve(setToArray(set));
|
|
111
112
|
return Promise.reject('key is not a set');
|
|
112
113
|
},
|
|
113
114
|
|
package/src/storages/types.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
79
|
-
const updaters
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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 (
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
}
|
package/src/trackers/types.ts
CHANGED
|
@@ -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
|
}
|
package/src/utils/lang/sets.ts
CHANGED
|
@@ -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(
|
|
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 {
|
|
2
|
+
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
|
|
3
3
|
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
4
|
-
export declare class MySegmentsCacheInLocal extends
|
|
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
|
-
|
|
9
|
-
|
|
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(
|
|
13
|
+
protected setChangeNumber(changeNumber?: number): void;
|
|
14
14
|
getChangeNumber(): number;
|
|
15
15
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
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
|
|
6
|
+
export declare class MySegmentsCacheInMemory extends AbstractMySegmentsCacheSync {
|
|
7
7
|
private segmentCache;
|
|
8
8
|
private cn?;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
protected addSegment(name: string): boolean;
|
|
10
|
+
protected removeSegment(name: string): boolean;
|
|
11
11
|
isInSegment(name: string): boolean;
|
|
12
|
-
setChangeNumber(
|
|
12
|
+
protected setChangeNumber(changeNumber?: number): void;
|
|
13
13
|
getChangeNumber(): number;
|
|
14
14
|
getRegisteredSegments(): string[];
|
|
15
15
|
getKeysCount(): number;
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ISegmentsCacheSync } from '../types';
|
|
2
2
|
/**
|
|
3
|
-
* Default ISplitsCacheSync implementation that stores
|
|
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
|
|
5
|
+
export declare class SegmentsCacheInMemory implements ISegmentsCacheSync {
|
|
7
6
|
private segmentCache;
|
|
8
7
|
private segmentChangeNumber;
|
|
9
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
*
|
|
14
|
-
* The returned promise is resolved
|
|
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
|
-
|
|
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) */
|
package/types/sync/types.d.ts
CHANGED
|
@@ -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;
|