@splitsoftware/splitio-commons 1.4.1 → 1.4.2-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +5 -0
- package/cjs/consent/sdkUserConsent.js +3 -3
- package/cjs/listeners/browser.js +7 -6
- package/cjs/sdkFactory/index.js +20 -2
- package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
- package/cjs/storages/dataLoader.js +23 -15
- package/cjs/storages/inMemory/InMemoryStorage.js +15 -1
- package/cjs/storages/inMemory/InMemoryStorageCS.js +12 -0
- package/cjs/sync/submitters/submitterManager.js +28 -4
- package/cjs/sync/syncManagerOnline.js +40 -29
- package/cjs/sync/syncTask.js +15 -10
- package/cjs/trackers/impressionObserver/impressionObserverCS.js +1 -1
- package/cjs/utils/settingsValidation/index.js +13 -1
- package/esm/consent/sdkUserConsent.js +3 -3
- package/esm/listeners/browser.js +7 -6
- package/esm/sdkFactory/index.js +21 -3
- package/esm/storages/AbstractSplitsCacheSync.js +1 -1
- package/esm/storages/dataLoader.js +21 -13
- package/esm/storages/inMemory/InMemoryStorage.js +15 -1
- package/esm/storages/inMemory/InMemoryStorageCS.js +12 -0
- package/esm/sync/submitters/submitterManager.js +28 -4
- package/esm/sync/syncManagerOnline.js +40 -29
- package/esm/sync/syncTask.js +15 -10
- package/esm/trackers/impressionObserver/impressionObserverCS.js +1 -1
- package/esm/utils/settingsValidation/index.js +13 -1
- package/package.json +1 -1
- package/src/consent/sdkUserConsent.ts +4 -3
- package/src/listeners/browser.ts +8 -6
- package/src/sdkClient/clientAttributesDecoration.ts +9 -9
- package/src/sdkFactory/index.ts +23 -4
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/dataLoader.ts +20 -13
- package/src/storages/inMemory/InMemoryStorage.ts +16 -1
- package/src/storages/inMemory/InMemoryStorageCS.ts +13 -0
- package/src/sync/submitters/submitterManager.ts +30 -4
- package/src/sync/submitters/types.ts +7 -0
- package/src/sync/syncManagerOnline.ts +35 -23
- package/src/sync/syncTask.ts +17 -11
- package/src/sync/types.ts +2 -1
- package/src/trackers/impressionObserver/impressionObserverCS.ts +1 -1
- package/src/types.ts +11 -3
- package/src/utils/settingsValidation/index.ts +15 -1
- package/types/integrations/ga/autoRequire.d.ts +4 -0
- package/types/storages/dataLoader.d.ts +2 -2
- package/types/sync/submitters/submitterManager.d.ts +2 -1
- package/types/sync/submitters/types.d.ts +6 -0
- package/types/sync/syncTask.d.ts +2 -2
- package/types/sync/types.d.ts +2 -1
- package/types/trackers/impressionObserver/impressionObserverCS.d.ts +2 -2
- package/types/types.d.ts +10 -2
- package/types/utils/settingsValidation/index.d.ts +1 -0
- package/cjs/sync/syncTaskComposite.js +0 -26
- package/esm/sync/syncTaskComposite.js +0 -22
- package/src/sync/syncTaskComposite.ts +0 -26
|
@@ -6,6 +6,8 @@ import { IStorageFactoryParams, IStorageSync } from '../types';
|
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
7
|
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
|
+
import { SplitIO } from '../../types';
|
|
10
|
+
import { setToArray, ISet } from '../../utils/lang/sets';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* InMemory storage factory for standalone server-side SplitFactory
|
|
@@ -29,7 +31,20 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
29
31
|
this.impressions.clear();
|
|
30
32
|
this.impressionCounts && this.impressionCounts.clear();
|
|
31
33
|
this.events.clear();
|
|
32
|
-
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// @ts-ignore, private method, for POC
|
|
37
|
+
getSnapshot(): SplitIO.PreloadedData {
|
|
38
|
+
return {
|
|
39
|
+
lastUpdated: Date.now(), // @ts-ignore accessing private prop
|
|
40
|
+
since: this.splits.changeNumber, // @ts-ignore accessing private prop
|
|
41
|
+
splitsData: this.splits.splitsCache, // @ts-ignore accessing private prop
|
|
42
|
+
segmentsData: Object.keys(this.segments.segmentCache).reduce((prev, cur) => { // @ts-ignore accessing private prop
|
|
43
|
+
prev[cur] = setToArray(this.segments.segmentCache[cur] as ISet<string>);
|
|
44
|
+
return prev;
|
|
45
|
+
}, {})
|
|
46
|
+
};
|
|
47
|
+
},
|
|
33
48
|
};
|
|
34
49
|
}
|
|
35
50
|
|
|
@@ -6,6 +6,7 @@ import { IStorageSync, IStorageFactoryParams } from '../types';
|
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
7
|
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
|
+
import { SplitIO } from '../../types';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* InMemory storage factory for standalone client-side SplitFactory
|
|
@@ -31,6 +32,18 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
31
32
|
this.events.clear();
|
|
32
33
|
},
|
|
33
34
|
|
|
35
|
+
// @ts-ignore, private method, for POC
|
|
36
|
+
getSnapshot(): SplitIO.PreloadedData {
|
|
37
|
+
return {
|
|
38
|
+
lastUpdated: Date.now(), // @ts-ignore accessing private prop
|
|
39
|
+
since: this.splits.changeNumber, // @ts-ignore accessing private prop
|
|
40
|
+
splitsData: this.splits.splitsCache,
|
|
41
|
+
mySegmentsData: { // @ts-ignore accessing private prop
|
|
42
|
+
[params.matchingKey as string]: Object.keys(this.segments.segmentCache)
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
|
|
34
47
|
// When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
|
|
35
48
|
shared() {
|
|
36
49
|
return {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { syncTaskComposite } from '../syncTaskComposite';
|
|
2
1
|
import { eventsSubmitterFactory } from './eventsSubmitter';
|
|
3
2
|
import { impressionsSubmitterFactory } from './impressionsSubmitter';
|
|
4
3
|
import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
|
|
5
4
|
import { telemetrySubmitterFactory } from './telemetrySubmitter';
|
|
6
5
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
6
|
+
import { ISubmitterManager } from './types';
|
|
7
7
|
|
|
8
|
-
export function submitterManagerFactory(params: ISdkFactoryContextSync) {
|
|
8
|
+
export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager {
|
|
9
9
|
|
|
10
10
|
const submitters = [
|
|
11
11
|
impressionsSubmitterFactory(params),
|
|
@@ -15,7 +15,33 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync) {
|
|
|
15
15
|
const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
|
|
16
16
|
if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
|
|
17
17
|
const telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
18
|
-
if (telemetrySubmitter) submitters.push(telemetrySubmitter);
|
|
19
18
|
|
|
20
|
-
return
|
|
19
|
+
return {
|
|
20
|
+
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
21
|
+
start(onlyTelemetry?: boolean) {
|
|
22
|
+
if (!onlyTelemetry) submitters.forEach(submitter => submitter.start());
|
|
23
|
+
if (telemetrySubmitter) telemetrySubmitter.start();
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// `allExceptTelemetry` true if userConsent is changed to DECLINED
|
|
27
|
+
stop(allExceptTelemetry?: boolean) {
|
|
28
|
+
submitters.forEach(submitter => submitter.stop());
|
|
29
|
+
if (!allExceptTelemetry && telemetrySubmitter) telemetrySubmitter.stop();
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
isRunning() {
|
|
33
|
+
return submitters.some(submitter => submitter.isRunning());
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
|
|
37
|
+
execute(onlyTelemetry?: boolean) {
|
|
38
|
+
const promises = onlyTelemetry ? [] : submitters.map(submitter => submitter.execute());
|
|
39
|
+
if (telemetrySubmitter) promises.push(telemetrySubmitter.execute());
|
|
40
|
+
return Promise.all(promises);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
isExecuting() {
|
|
44
|
+
return submitters.some(submitter => submitter.isExecuting());
|
|
45
|
+
}
|
|
46
|
+
};
|
|
21
47
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IMetadata } from '../../dtos/types';
|
|
2
2
|
import { SplitIO } from '../../types';
|
|
3
|
+
import { ISyncTask } from '../types';
|
|
3
4
|
|
|
4
5
|
export type ImpressionsPayload = {
|
|
5
6
|
/** Split name */
|
|
@@ -191,3 +192,9 @@ export type TelemetryConfigStatsPayload = TelemetryConfigStats & {
|
|
|
191
192
|
i?: Array<string>, // integrations
|
|
192
193
|
uC: number, // userConsent
|
|
193
194
|
}
|
|
195
|
+
|
|
196
|
+
export interface ISubmitterManager extends ISyncTask {
|
|
197
|
+
start(onlyTelemetry?: boolean): void,
|
|
198
|
+
stop(allExceptTelemetry?: boolean): void,
|
|
199
|
+
execute(onlyTelemetry?: boolean): Promise<any>
|
|
200
|
+
}
|
|
@@ -28,19 +28,19 @@ export function syncManagerOnlineFactory(
|
|
|
28
28
|
*/
|
|
29
29
|
return function (params: ISdkFactoryContextSync): ISyncManagerCS {
|
|
30
30
|
|
|
31
|
-
const { settings, settings: { log, streamingEnabled },
|
|
31
|
+
const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled, onlySubmitters } }, telemetryTracker } = params;
|
|
32
32
|
|
|
33
33
|
/** Polling Manager */
|
|
34
|
-
const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
34
|
+
const pollingManager = onlySubmitters ? undefined : pollingManagerFactory && pollingManagerFactory(params);
|
|
35
35
|
|
|
36
36
|
/** Push Manager */
|
|
37
|
-
const pushManager = streamingEnabled && pollingManager && pushManagerFactory ?
|
|
37
|
+
const pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ?
|
|
38
38
|
pushManagerFactory(params, pollingManager) :
|
|
39
39
|
undefined;
|
|
40
40
|
|
|
41
41
|
/** Submitter Manager */
|
|
42
42
|
// It is not inyected as push and polling managers, because at the moment it is required
|
|
43
|
-
const
|
|
43
|
+
const submitterManager = submitterManagerFactory(params);
|
|
44
44
|
|
|
45
45
|
/** Sync Manager logic */
|
|
46
46
|
|
|
@@ -79,7 +79,7 @@ export function syncManagerOnlineFactory(
|
|
|
79
79
|
// E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline).
|
|
80
80
|
pollingManager,
|
|
81
81
|
pushManager,
|
|
82
|
-
|
|
82
|
+
submitterManager,
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Method used to start the syncManager for the first time, or resume it after being stopped.
|
|
@@ -89,20 +89,29 @@ export function syncManagerOnlineFactory(
|
|
|
89
89
|
|
|
90
90
|
// start syncing splits and segments
|
|
91
91
|
if (pollingManager) {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
|
|
93
|
+
// If synchronization is disabled pushManager and pollingManager should not start
|
|
94
|
+
if (syncEnabled) {
|
|
95
|
+
if (pushManager) {
|
|
96
|
+
// Doesn't call `syncAll` when the syncManager is resuming
|
|
97
|
+
if (startFirstTime) {
|
|
98
|
+
pollingManager.syncAll();
|
|
99
|
+
startFirstTime = false;
|
|
100
|
+
}
|
|
101
|
+
pushManager.start();
|
|
102
|
+
} else {
|
|
103
|
+
pollingManager.start();
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
94
106
|
if (startFirstTime) {
|
|
95
107
|
pollingManager.syncAll();
|
|
96
108
|
startFirstTime = false;
|
|
97
109
|
}
|
|
98
|
-
pushManager.start();
|
|
99
|
-
} else {
|
|
100
|
-
pollingManager.start();
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
// start periodic data recording (events, impressions, telemetry).
|
|
105
|
-
|
|
114
|
+
submitterManager.start(!isConsentGranted(settings));
|
|
106
115
|
},
|
|
107
116
|
|
|
108
117
|
/**
|
|
@@ -116,7 +125,7 @@ export function syncManagerOnlineFactory(
|
|
|
116
125
|
if (pollingManager && pollingManager.isRunning()) pollingManager.stop();
|
|
117
126
|
|
|
118
127
|
// stop periodic data recording (events, impressions, telemetry).
|
|
119
|
-
|
|
128
|
+
submitterManager.stop();
|
|
120
129
|
},
|
|
121
130
|
|
|
122
131
|
isRunning() {
|
|
@@ -124,8 +133,7 @@ export function syncManagerOnlineFactory(
|
|
|
124
133
|
},
|
|
125
134
|
|
|
126
135
|
flush() {
|
|
127
|
-
|
|
128
|
-
else return Promise.resolve();
|
|
136
|
+
return submitterManager.execute(!isConsentGranted(settings));
|
|
129
137
|
},
|
|
130
138
|
|
|
131
139
|
// [Only used for client-side]
|
|
@@ -138,18 +146,22 @@ export function syncManagerOnlineFactory(
|
|
|
138
146
|
return {
|
|
139
147
|
isRunning: mySegmentsSyncTask.isRunning,
|
|
140
148
|
start() {
|
|
141
|
-
if (
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
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);
|
|
145
160
|
} else {
|
|
146
|
-
|
|
147
|
-
// of segments since `syncAll` was already executed when starting the main client
|
|
148
|
-
mySegmentsSyncTask.execute();
|
|
161
|
+
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
149
162
|
}
|
|
150
|
-
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
151
163
|
} else {
|
|
152
|
-
if (
|
|
164
|
+
if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
|
|
153
165
|
}
|
|
154
166
|
},
|
|
155
167
|
stop() {
|
package/src/sync/syncTask.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { ISyncTask } from './types';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates a syncTask that handles the periodic execution of a given task ("start" and "stop" methods).
|
|
7
|
-
* The task can be executed
|
|
8
|
-
*
|
|
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.
|
|
9
9
|
*
|
|
10
10
|
* @param log Logger instance.
|
|
11
11
|
* @param task Task to execute that returns a promise that NEVER REJECTS. Otherwise, periodic execution can result in Unhandled Promise Rejections.
|
|
@@ -15,8 +15,8 @@ import { ISyncTask } from './types';
|
|
|
15
15
|
*/
|
|
16
16
|
export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger, task: (...args: Input) => Promise<Output>, period: number, taskName = 'task'): ISyncTask<Input, Output> {
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
let
|
|
18
|
+
// Task promise while it is pending. Undefined once the promise is resolved
|
|
19
|
+
let pendingTask: Promise<Output> | undefined;
|
|
20
20
|
// flag that indicates if the task periodic execution has been started/stopped.
|
|
21
21
|
let running = false;
|
|
22
22
|
// Auxiliar counter used to avoid race condition when calling `start` & `stop` intermittently
|
|
@@ -26,14 +26,21 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
|
|
|
26
26
|
// Id of the periodic call timeout
|
|
27
27
|
let timeoutID: any;
|
|
28
28
|
|
|
29
|
-
function execute(...args: Input) {
|
|
30
|
-
executing
|
|
29
|
+
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);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Execute task
|
|
31
38
|
log.debug(SYNC_TASK_EXECUTE, [taskName]);
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
pendingTask = task(...args).then(result => {
|
|
40
|
+
pendingTask = undefined;
|
|
34
41
|
return result;
|
|
35
42
|
});
|
|
36
|
-
|
|
43
|
+
return pendingTask;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
function periodicExecute(currentRunningId: number) {
|
|
@@ -46,11 +53,10 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
|
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
return {
|
|
49
|
-
// @TODO check if we need to queued `execute` calls, to avoid possible race conditions on submitters and updaters with streaming.
|
|
50
56
|
execute,
|
|
51
57
|
|
|
52
58
|
isExecuting() {
|
|
53
|
-
return
|
|
59
|
+
return pendingTask !== undefined;
|
|
54
60
|
},
|
|
55
61
|
|
|
56
62
|
start(...args: Input) {
|
package/src/sync/types.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { IReadinessManager } from '../readiness/types';
|
|
|
2
2
|
import { IStorageSync } from '../storages/types';
|
|
3
3
|
import { IPollingManager } from './polling/types';
|
|
4
4
|
import { IPushManager } from './streaming/types';
|
|
5
|
+
import { ISubmitterManager } from './submitters/types';
|
|
5
6
|
|
|
6
7
|
export interface ITask<Input extends any[] = []> {
|
|
7
8
|
/**
|
|
@@ -39,7 +40,7 @@ export interface ISyncManager extends ITask {
|
|
|
39
40
|
flush(): Promise<any>,
|
|
40
41
|
pushManager?: IPushManager,
|
|
41
42
|
pollingManager?: IPollingManager,
|
|
42
|
-
|
|
43
|
+
submitterManager?: ISubmitterManager
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export interface ISyncManagerCS extends ISyncManager {
|
|
@@ -4,7 +4,7 @@ import { buildKey } from './buildKey';
|
|
|
4
4
|
import { ImpressionDTO } from '../../types';
|
|
5
5
|
|
|
6
6
|
export function hashImpression32(impression: ImpressionDTO) {
|
|
7
|
-
return hash(buildKey(impression));
|
|
7
|
+
return hash(buildKey(impression)).toString();
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
|
package/src/types.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { ILogger } from './logger/types';
|
|
|
4
4
|
import { ISdkFactoryContext } from './sdkFactory/types';
|
|
5
5
|
/* eslint-disable no-use-before-define */
|
|
6
6
|
|
|
7
|
-
import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory } from './storages/types';
|
|
7
|
+
import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory, DataLoader } from './storages/types';
|
|
8
8
|
import { ISyncManagerCS } from './sync/types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -98,6 +98,7 @@ export interface ISettings {
|
|
|
98
98
|
eventsFirstPushWindow: number
|
|
99
99
|
},
|
|
100
100
|
readonly storage: IStorageSyncFactory | IStorageAsyncFactory,
|
|
101
|
+
readonly dataLoader?: DataLoader,
|
|
101
102
|
readonly integrations: Array<{
|
|
102
103
|
readonly type: string,
|
|
103
104
|
(params: IIntegrationFactoryParams): IIntegration | void
|
|
@@ -117,7 +118,9 @@ export interface ISettings {
|
|
|
117
118
|
splitFilters: SplitIO.SplitFilter[],
|
|
118
119
|
impressionsMode: SplitIO.ImpressionsMode,
|
|
119
120
|
__splitFiltersValidation: ISplitFiltersValidation,
|
|
120
|
-
localhostMode?: SplitIO.LocalhostFactory
|
|
121
|
+
localhostMode?: SplitIO.LocalhostFactory,
|
|
122
|
+
enabled: boolean,
|
|
123
|
+
onlySubmitters: boolean
|
|
121
124
|
},
|
|
122
125
|
readonly runtime: {
|
|
123
126
|
ip: string | false
|
|
@@ -214,6 +217,11 @@ interface ISharedSettings {
|
|
|
214
217
|
* @default 'OPTIMIZED'
|
|
215
218
|
*/
|
|
216
219
|
impressionsMode?: SplitIO.ImpressionsMode,
|
|
220
|
+
/**
|
|
221
|
+
* Enables synchronization.
|
|
222
|
+
* @property {boolean} enabled
|
|
223
|
+
*/
|
|
224
|
+
enabled: boolean
|
|
217
225
|
}
|
|
218
226
|
}
|
|
219
227
|
/**
|
|
@@ -746,7 +754,7 @@ export namespace SplitIO {
|
|
|
746
754
|
* This property is ignored if `mySegmentsData` was provided.
|
|
747
755
|
*/
|
|
748
756
|
segmentsData?: {
|
|
749
|
-
[segmentName: string]: string
|
|
757
|
+
[segmentName: string]: string[]
|
|
750
758
|
},
|
|
751
759
|
}
|
|
752
760
|
/**
|
|
@@ -83,7 +83,8 @@ export const base = {
|
|
|
83
83
|
splitFilters: undefined,
|
|
84
84
|
// impressions collection mode
|
|
85
85
|
impressionsMode: OPTIMIZED,
|
|
86
|
-
localhostMode: undefined
|
|
86
|
+
localhostMode: undefined,
|
|
87
|
+
enabled: true
|
|
87
88
|
},
|
|
88
89
|
|
|
89
90
|
// Logger
|
|
@@ -146,6 +147,9 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
|
|
|
146
147
|
// ensure a valid SDK mode
|
|
147
148
|
// @ts-ignore, modify readonly prop
|
|
148
149
|
withDefaults.mode = mode(withDefaults.core.authorizationKey, withDefaults.mode);
|
|
150
|
+
if (withDefaults.sync.onlySubmitters && withDefaults.mode === STANDALONE_MODE && !withDefaults.dataLoader) {
|
|
151
|
+
throw new Error('To use `onlySubmitters` param in standalone mode, DataLoader is required to preload data into the storage');
|
|
152
|
+
}
|
|
149
153
|
|
|
150
154
|
// ensure a valid Storage based on mode defined.
|
|
151
155
|
// @ts-ignore, modify readonly prop
|
|
@@ -191,6 +195,16 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
|
|
|
191
195
|
scheduler.pushRetryBackoffBase = fromSecondsToMillis(scheduler.pushRetryBackoffBase);
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
// validate sync enabled
|
|
199
|
+
if (withDefaults.sync.enabled !== false) {
|
|
200
|
+
withDefaults.sync.enabled = true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// validate sync onlySubmitters
|
|
204
|
+
if (withDefaults.sync.onlySubmitters !== true) {
|
|
205
|
+
withDefaults.sync.onlySubmitters = false;
|
|
206
|
+
}
|
|
207
|
+
|
|
194
208
|
// validate the `splitFilters` settings and parse splits query
|
|
195
209
|
const splitFiltersValidation = validateSplitFilters(log, withDefaults.sync.splitFilters, withDefaults.mode);
|
|
196
210
|
withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { SplitIO } from '../types';
|
|
2
2
|
import { DataLoader } from './types';
|
|
3
3
|
/**
|
|
4
|
-
* Factory of
|
|
4
|
+
* Factory of storage loader
|
|
5
5
|
*
|
|
6
6
|
* @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
|
|
7
7
|
* and extended with a `mySegmentsData` property.
|
|
8
8
|
* @returns function to preload the storage
|
|
9
9
|
*/
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function DataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
2
|
-
|
|
2
|
+
import { ISubmitterManager } from './types';
|
|
3
|
+
export declare function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IMetadata } from '../../dtos/types';
|
|
2
2
|
import { SplitIO } from '../../types';
|
|
3
|
+
import { ISyncTask } from '../types';
|
|
3
4
|
export declare type ImpressionsPayload = {
|
|
4
5
|
/** Split name */
|
|
5
6
|
f: string;
|
|
@@ -169,3 +170,8 @@ export declare type TelemetryConfigStatsPayload = TelemetryConfigStats & {
|
|
|
169
170
|
i?: Array<string>;
|
|
170
171
|
uC: number;
|
|
171
172
|
};
|
|
173
|
+
export interface ISubmitterManager extends ISyncTask {
|
|
174
|
+
start(onlyTelemetry?: boolean): void;
|
|
175
|
+
stop(allExceptTelemetry?: boolean): void;
|
|
176
|
+
execute(onlyTelemetry?: boolean): Promise<any>;
|
|
177
|
+
}
|
package/types/sync/syncTask.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { ILogger } from '../logger/types';
|
|
|
2
2
|
import { ISyncTask } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Creates a syncTask that handles the periodic execution of a given task ("start" and "stop" methods).
|
|
5
|
-
* The task can be executed
|
|
6
|
-
*
|
|
5
|
+
* The task can be also executed by calling the "execute" method. Multiple execute calls are chained to run secuentially and avoid race conditions.
|
|
6
|
+
* For example, submitters executed on SDK destroy or full queue, while periodic execution is pending.
|
|
7
7
|
*
|
|
8
8
|
* @param log Logger instance.
|
|
9
9
|
* @param task Task to execute that returns a promise that NEVER REJECTS. Otherwise, periodic execution can result in Unhandled Promise Rejections.
|
package/types/sync/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { IReadinessManager } from '../readiness/types';
|
|
|
2
2
|
import { IStorageSync } from '../storages/types';
|
|
3
3
|
import { IPollingManager } from './polling/types';
|
|
4
4
|
import { IPushManager } from './streaming/types';
|
|
5
|
+
import { ISubmitterManager } from './submitters/types';
|
|
5
6
|
export interface ITask<Input extends any[] = []> {
|
|
6
7
|
/**
|
|
7
8
|
* Start periodic execution of the task
|
|
@@ -35,7 +36,7 @@ export interface ISyncManager extends ITask {
|
|
|
35
36
|
flush(): Promise<any>;
|
|
36
37
|
pushManager?: IPushManager;
|
|
37
38
|
pollingManager?: IPollingManager;
|
|
38
|
-
|
|
39
|
+
submitterManager?: ISubmitterManager;
|
|
39
40
|
}
|
|
40
41
|
export interface ISyncManagerCS extends ISyncManager {
|
|
41
42
|
shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ImpressionObserver } from './ImpressionObserver';
|
|
2
2
|
import { ImpressionDTO } from '../../types';
|
|
3
|
-
export declare function hashImpression32(impression: ImpressionDTO):
|
|
4
|
-
export declare function impressionObserverCSFactory(): ImpressionObserver<
|
|
3
|
+
export declare function hashImpression32(impression: ImpressionDTO): string;
|
|
4
|
+
export declare function impressionObserverCSFactory(): ImpressionObserver<string>;
|
package/types/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ISplitFiltersValidation } from './dtos/types';
|
|
|
2
2
|
import { IIntegration, IIntegrationFactoryParams } from './integrations/types';
|
|
3
3
|
import { ILogger } from './logger/types';
|
|
4
4
|
import { ISdkFactoryContext } from './sdkFactory/types';
|
|
5
|
-
import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory } from './storages/types';
|
|
5
|
+
import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory, DataLoader } from './storages/types';
|
|
6
6
|
import { ISyncManagerCS } from './sync/types';
|
|
7
7
|
/**
|
|
8
8
|
* Reduced version of NodeJS.EventEmitter interface with the minimal methods used by the SDK
|
|
@@ -92,6 +92,7 @@ export interface ISettings {
|
|
|
92
92
|
eventsFirstPushWindow: number;
|
|
93
93
|
};
|
|
94
94
|
readonly storage: IStorageSyncFactory | IStorageAsyncFactory;
|
|
95
|
+
readonly dataLoader?: DataLoader;
|
|
95
96
|
readonly integrations: Array<{
|
|
96
97
|
readonly type: string;
|
|
97
98
|
(params: IIntegrationFactoryParams): IIntegration | void;
|
|
@@ -112,6 +113,8 @@ export interface ISettings {
|
|
|
112
113
|
impressionsMode: SplitIO.ImpressionsMode;
|
|
113
114
|
__splitFiltersValidation: ISplitFiltersValidation;
|
|
114
115
|
localhostMode?: SplitIO.LocalhostFactory;
|
|
116
|
+
enabled: boolean;
|
|
117
|
+
onlySubmitters: boolean;
|
|
115
118
|
};
|
|
116
119
|
readonly runtime: {
|
|
117
120
|
ip: string | false;
|
|
@@ -208,6 +211,11 @@ interface ISharedSettings {
|
|
|
208
211
|
* @default 'OPTIMIZED'
|
|
209
212
|
*/
|
|
210
213
|
impressionsMode?: SplitIO.ImpressionsMode;
|
|
214
|
+
/**
|
|
215
|
+
* Enables synchronization.
|
|
216
|
+
* @property {boolean} enabled
|
|
217
|
+
*/
|
|
218
|
+
enabled: boolean;
|
|
211
219
|
};
|
|
212
220
|
}
|
|
213
221
|
/**
|
|
@@ -743,7 +751,7 @@ export declare namespace SplitIO {
|
|
|
743
751
|
* This property is ignored if `mySegmentsData` was provided.
|
|
744
752
|
*/
|
|
745
753
|
segmentsData?: {
|
|
746
|
-
[segmentName: string]: string;
|
|
754
|
+
[segmentName: string]: string[];
|
|
747
755
|
};
|
|
748
756
|
}
|
|
749
757
|
/**
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.syncTaskComposite = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Composite Sync Task: group of sync tasks that are treated as a single one.
|
|
6
|
-
*/
|
|
7
|
-
function syncTaskComposite(syncTasks) {
|
|
8
|
-
return {
|
|
9
|
-
start: function () {
|
|
10
|
-
syncTasks.forEach(function (syncTask) { return syncTask.start(); });
|
|
11
|
-
},
|
|
12
|
-
stop: function () {
|
|
13
|
-
syncTasks.forEach(function (syncTask) { return syncTask.stop(); });
|
|
14
|
-
},
|
|
15
|
-
isRunning: function () {
|
|
16
|
-
return syncTasks.some(function (syncTask) { return syncTask.isRunning(); });
|
|
17
|
-
},
|
|
18
|
-
execute: function () {
|
|
19
|
-
return Promise.all(syncTasks.map(function (syncTask) { return syncTask.execute(); }));
|
|
20
|
-
},
|
|
21
|
-
isExecuting: function () {
|
|
22
|
-
return syncTasks.some(function (syncTask) { return syncTask.isExecuting(); });
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
exports.syncTaskComposite = syncTaskComposite;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Composite Sync Task: group of sync tasks that are treated as a single one.
|
|
3
|
-
*/
|
|
4
|
-
export function syncTaskComposite(syncTasks) {
|
|
5
|
-
return {
|
|
6
|
-
start: function () {
|
|
7
|
-
syncTasks.forEach(function (syncTask) { return syncTask.start(); });
|
|
8
|
-
},
|
|
9
|
-
stop: function () {
|
|
10
|
-
syncTasks.forEach(function (syncTask) { return syncTask.stop(); });
|
|
11
|
-
},
|
|
12
|
-
isRunning: function () {
|
|
13
|
-
return syncTasks.some(function (syncTask) { return syncTask.isRunning(); });
|
|
14
|
-
},
|
|
15
|
-
execute: function () {
|
|
16
|
-
return Promise.all(syncTasks.map(function (syncTask) { return syncTask.execute(); }));
|
|
17
|
-
},
|
|
18
|
-
isExecuting: function () {
|
|
19
|
-
return syncTasks.some(function (syncTask) { return syncTask.isExecuting(); });
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { ISyncTask } from './types';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Composite Sync Task: group of sync tasks that are treated as a single one.
|
|
5
|
-
*/
|
|
6
|
-
export function syncTaskComposite(syncTasks: ISyncTask[]): ISyncTask {
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
start() {
|
|
10
|
-
syncTasks.forEach(syncTask => syncTask.start());
|
|
11
|
-
},
|
|
12
|
-
stop() {
|
|
13
|
-
syncTasks.forEach(syncTask => syncTask.stop());
|
|
14
|
-
},
|
|
15
|
-
isRunning() {
|
|
16
|
-
return syncTasks.some(syncTask => syncTask.isRunning());
|
|
17
|
-
},
|
|
18
|
-
execute() {
|
|
19
|
-
return Promise.all(syncTasks.map(syncTask => syncTask.execute()));
|
|
20
|
-
},
|
|
21
|
-
isExecuting() {
|
|
22
|
-
return syncTasks.some(syncTask => syncTask.isExecuting());
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
}
|