@splitsoftware/splitio-commons 2.0.3-rc.0 → 2.1.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 +5 -2
- package/README.md +2 -2
- package/cjs/evaluator/index.js +0 -2
- package/cjs/evaluator/matchers/large_segment.js +0 -6
- package/cjs/evaluator/matchers/segment.js +0 -6
- package/cjs/listeners/browser.js +6 -4
- package/cjs/listeners/node.js +2 -2
- package/cjs/readiness/readinessManager.js +6 -0
- package/cjs/sdkClient/client.js +13 -13
- package/cjs/sdkClient/sdkClient.js +1 -1
- package/cjs/sdkFactory/index.js +14 -9
- package/cjs/sdkManager/index.js +1 -2
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
- package/cjs/storages/AbstractSplitsCacheSync.js +0 -7
- package/cjs/storages/KeyBuilderCS.js +3 -0
- package/cjs/storages/dataLoader.js +3 -2
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
- package/cjs/storages/inLocalStorage/index.js +8 -7
- package/cjs/storages/inLocalStorage/validateCache.js +79 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -3
- package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -4
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/index.js +13 -9
- package/cjs/storages/pluggable/index.js +21 -16
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -10
- package/cjs/sync/streaming/pushManager.js +8 -6
- package/cjs/sync/submitters/impressionCountsSubmitter.js +4 -2
- package/cjs/sync/submitters/submitterManager.js +6 -3
- package/cjs/sync/syncManagerOnline.js +10 -4
- package/cjs/trackers/eventTracker.js +1 -1
- package/cjs/trackers/impressionsTracker.js +19 -18
- package/cjs/trackers/strategy/strategyDebug.js +11 -4
- package/cjs/trackers/strategy/strategyNone.js +16 -11
- package/cjs/trackers/strategy/strategyOptimized.js +21 -11
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/evaluator/index.js +0 -2
- package/esm/evaluator/matchers/large_segment.js +0 -6
- package/esm/evaluator/matchers/segment.js +0 -6
- package/esm/listeners/browser.js +3 -1
- package/esm/listeners/node.js +2 -2
- package/esm/readiness/readinessManager.js +6 -0
- package/esm/sdkClient/client.js +13 -13
- package/esm/sdkClient/sdkClient.js +1 -1
- package/esm/sdkFactory/index.js +15 -10
- package/esm/sdkManager/index.js +1 -2
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
- package/esm/storages/AbstractSplitsCacheSync.js +0 -7
- package/esm/storages/KeyBuilderCS.js +3 -0
- package/esm/storages/dataLoader.js +2 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
- package/esm/storages/inLocalStorage/index.js +9 -8
- package/esm/storages/inLocalStorage/validateCache.js +75 -0
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -5
- package/esm/storages/inRedis/SplitsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/index.js +14 -10
- package/esm/storages/pluggable/index.js +22 -17
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -11
- package/esm/sync/streaming/pushManager.js +8 -6
- package/esm/sync/submitters/impressionCountsSubmitter.js +4 -2
- package/esm/sync/submitters/submitterManager.js +6 -3
- package/esm/sync/syncManagerOnline.js +10 -4
- package/esm/trackers/eventTracker.js +1 -1
- package/esm/trackers/impressionsTracker.js +19 -18
- package/esm/trackers/strategy/strategyDebug.js +11 -4
- package/esm/trackers/strategy/strategyNone.js +16 -11
- package/esm/trackers/strategy/strategyOptimized.js +21 -11
- package/esm/utils/settingsValidation/index.js +1 -1
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +1 -2
- package/src/evaluator/index.ts +0 -2
- package/src/evaluator/matchers/large_segment.ts +0 -7
- package/src/evaluator/matchers/segment.ts +0 -7
- package/src/evaluator/types.ts +1 -1
- package/src/listeners/browser.ts +3 -1
- package/src/listeners/node.ts +2 -2
- package/src/readiness/readinessManager.ts +5 -0
- package/src/sdkClient/client.ts +11 -11
- package/src/sdkClient/sdkClient.ts +1 -1
- package/src/sdkFactory/index.ts +16 -11
- package/src/sdkFactory/types.ts +1 -1
- package/src/sdkManager/index.ts +1 -2
- package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
- package/src/storages/AbstractSplitsCacheSync.ts +0 -8
- package/src/storages/KeyBuilderCS.ts +4 -0
- package/src/storages/dataLoader.ts +3 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -66
- package/src/storages/inLocalStorage/index.ts +12 -13
- package/src/storages/inLocalStorage/validateCache.ts +91 -0
- package/src/storages/inMemory/InMemoryStorage.ts +4 -4
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -5
- package/src/storages/inRedis/SplitsCacheInRedis.ts +1 -1
- package/src/storages/inRedis/index.ts +10 -10
- package/src/storages/pluggable/index.ts +22 -17
- package/src/storages/types.ts +3 -6
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
- package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -11
- package/src/sync/streaming/pushManager.ts +8 -6
- package/src/sync/submitters/impressionCountsSubmitter.ts +4 -2
- package/src/sync/submitters/submitterManager.ts +4 -3
- package/src/sync/submitters/uniqueKeysSubmitter.ts +3 -2
- package/src/sync/syncManagerOnline.ts +11 -5
- package/src/trackers/eventTracker.ts +1 -1
- package/src/trackers/impressionsTracker.ts +19 -18
- package/src/trackers/strategy/strategyDebug.ts +11 -4
- package/src/trackers/strategy/strategyNone.ts +17 -11
- package/src/trackers/strategy/strategyOptimized.ts +20 -10
- package/src/trackers/types.ts +8 -2
- package/src/utils/lang/index.ts +1 -1
- package/src/utils/settingsValidation/index.ts +1 -1
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/splitio.d.ts +26 -6
- package/cjs/utils/constants/browser.js +0 -5
- package/esm/utils/constants/browser.js +0 -2
- package/src/utils/constants/browser.ts +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { timeout } from '../../../utils/promise/timeout';
|
|
2
|
-
import { SDK_SPLITS_ARRIVED
|
|
2
|
+
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
3
3
|
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
4
4
|
import { startsWith } from '../../../utils/lang';
|
|
5
5
|
import { IN_SEGMENT } from '../../../utils/constants';
|
|
@@ -121,7 +121,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, seg
|
|
|
121
121
|
function _splitChangesUpdater(since, retry) {
|
|
122
122
|
if (retry === void 0) { retry = 0; }
|
|
123
123
|
log.debug(SYNC_SPLITS_FETCH, [since]);
|
|
124
|
-
|
|
124
|
+
return Promise.resolve(splitUpdateNotification ?
|
|
125
125
|
{ splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
|
|
126
126
|
splitChangesFetcher(since, noCache, till, _promiseDecorator))
|
|
127
127
|
.then(function (splitChanges) {
|
|
@@ -165,15 +165,6 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, seg
|
|
|
165
165
|
}
|
|
166
166
|
return false;
|
|
167
167
|
});
|
|
168
|
-
// After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
|
|
169
|
-
// Wrapping in a promise since checkCache can be async.
|
|
170
|
-
if (splitsEventEmitter && startingUp) {
|
|
171
|
-
Promise.resolve(splits.checkCache()).then(function (isCacheReady) {
|
|
172
|
-
if (isCacheReady)
|
|
173
|
-
splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
return fetcherPromise;
|
|
177
168
|
}
|
|
178
169
|
var sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
|
|
179
170
|
return sincePromise.then(_splitChangesUpdater);
|
|
@@ -306,12 +306,14 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
306
306
|
// Reconnects in case of a new client.
|
|
307
307
|
// Run in next event-loop cycle to save authentication calls
|
|
308
308
|
// in case multiple clients are created in the current cycle.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
connectForNewClient
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
309
|
+
if (this.isRunning()) {
|
|
310
|
+
setTimeout(function checkForReconnect() {
|
|
311
|
+
if (connectForNewClient) {
|
|
312
|
+
connectForNewClient = false;
|
|
313
|
+
connectPush();
|
|
314
|
+
}
|
|
315
|
+
}, 0);
|
|
316
|
+
}
|
|
315
317
|
}
|
|
316
318
|
},
|
|
317
319
|
// [Only for client-side]
|
|
@@ -26,6 +26,8 @@ var IMPRESSIONS_COUNT_RATE = 1800000; // 30 minutes
|
|
|
26
26
|
*/
|
|
27
27
|
export function impressionCountsSubmitterFactory(params) {
|
|
28
28
|
var log = params.settings.log, postTestImpressionsCount = params.splitApi.postTestImpressionsCount, impressionCounts = params.storage.impressionCounts;
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
if (impressionCounts) {
|
|
30
|
+
// retry impressions counts only once.
|
|
31
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
|
|
32
|
+
}
|
|
31
33
|
}
|
|
@@ -6,11 +6,14 @@ import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
|
|
|
6
6
|
export function submitterManagerFactory(params) {
|
|
7
7
|
var submitters = [
|
|
8
8
|
impressionsSubmitterFactory(params),
|
|
9
|
-
eventsSubmitterFactory(params)
|
|
10
|
-
impressionCountsSubmitterFactory(params),
|
|
11
|
-
uniqueKeysSubmitterFactory(params)
|
|
9
|
+
eventsSubmitterFactory(params)
|
|
12
10
|
];
|
|
11
|
+
var impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
|
|
12
|
+
if (impressionCountsSubmitter)
|
|
13
|
+
submitters.push(impressionCountsSubmitter);
|
|
13
14
|
var telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
15
|
+
if (params.storage.uniqueKeys)
|
|
16
|
+
submitters.push(uniqueKeysSubmitterFactory(params));
|
|
14
17
|
return {
|
|
15
18
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
16
19
|
start: function (onlyTelemetry) {
|
|
@@ -3,6 +3,7 @@ import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
|
|
|
3
3
|
import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
|
|
4
4
|
import { isConsentGranted } from '../consent';
|
|
5
5
|
import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
|
|
6
|
+
import { SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
|
|
6
7
|
/**
|
|
7
8
|
* Online SyncManager factory.
|
|
8
9
|
* Can be used for server-side API, and client-side API with or without multiple clients.
|
|
@@ -16,7 +17,7 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
16
17
|
* SyncManager factory for modular SDK
|
|
17
18
|
*/
|
|
18
19
|
return function (params) {
|
|
19
|
-
var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker;
|
|
20
|
+
var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker, storage = params.storage, readiness = params.readiness;
|
|
20
21
|
/** Polling Manager */
|
|
21
22
|
var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
22
23
|
/** Push Manager */
|
|
@@ -64,6 +65,11 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
64
65
|
*/
|
|
65
66
|
start: function () {
|
|
66
67
|
running = true;
|
|
68
|
+
if (startFirstTime) {
|
|
69
|
+
var isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
|
|
70
|
+
if (isCacheLoaded)
|
|
71
|
+
Promise.resolve().then(function () { readiness.splits.emit(SDK_SPLITS_CACHE_LOADED); });
|
|
72
|
+
}
|
|
67
73
|
// start syncing splits and segments
|
|
68
74
|
if (pollingManager) {
|
|
69
75
|
// If synchronization is disabled pushManager and pollingManager should not start
|
|
@@ -72,7 +78,6 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
72
78
|
// Doesn't call `syncAll` when the syncManager is resuming
|
|
73
79
|
if (startFirstTime) {
|
|
74
80
|
pollingManager.syncAll();
|
|
75
|
-
startFirstTime = false;
|
|
76
81
|
}
|
|
77
82
|
pushManager.start();
|
|
78
83
|
}
|
|
@@ -83,12 +88,12 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
83
88
|
else {
|
|
84
89
|
if (startFirstTime) {
|
|
85
90
|
pollingManager.syncAll();
|
|
86
|
-
startFirstTime = false;
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
// start periodic data recording (events, impressions, telemetry).
|
|
91
95
|
submitterManager.start(!isConsentGranted(settings));
|
|
96
|
+
startFirstTime = false;
|
|
92
97
|
},
|
|
93
98
|
/**
|
|
94
99
|
* Method used to stop/pause the syncManager.
|
|
@@ -115,6 +120,8 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
115
120
|
if (!pollingManager)
|
|
116
121
|
return;
|
|
117
122
|
var mySegmentsSyncTask = pollingManager.add(matchingKey, readinessManager, storage);
|
|
123
|
+
if (syncEnabled && pushManager)
|
|
124
|
+
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
118
125
|
if (running) {
|
|
119
126
|
if (syncEnabled) {
|
|
120
127
|
if (pushManager) {
|
|
@@ -128,7 +135,6 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
128
135
|
// of segments since `syncAll` was already executed when starting the main client
|
|
129
136
|
mySegmentsSyncTask.execute();
|
|
130
137
|
}
|
|
131
|
-
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
132
138
|
}
|
|
133
139
|
else {
|
|
134
140
|
if (storage.splits.usesSegments())
|
|
@@ -22,7 +22,7 @@ export function eventTrackerFactory(settings, eventsCache, whenInit, integration
|
|
|
22
22
|
whenInit(function () {
|
|
23
23
|
// Wrap in a timeout because we don't want it to be blocking.
|
|
24
24
|
setTimeout(function () {
|
|
25
|
-
// copy of event, to avoid unexpected
|
|
25
|
+
// copy of event, to avoid unexpected behavior if modified by integrations
|
|
26
26
|
var eventDataCopy = objectAssign({}, eventData);
|
|
27
27
|
if (properties)
|
|
28
28
|
eventDataCopy.properties = objectAssign({}, properties);
|
|
@@ -4,37 +4,38 @@ import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIO
|
|
|
4
4
|
import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
5
5
|
/**
|
|
6
6
|
* Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
|
|
7
|
+
*
|
|
8
|
+
* @param impressionsCache - cache to save impressions
|
|
9
|
+
* @param metadata - runtime metadata (ip, hostname and version)
|
|
10
|
+
* @param impressionListener - optional impression listener
|
|
11
|
+
* @param integrationsManager - optional integrations manager
|
|
12
|
+
* @param strategy - strategy for impressions tracking.
|
|
7
13
|
*/
|
|
8
|
-
export function impressionsTrackerFactory(settings, impressionsCache,
|
|
14
|
+
export function impressionsTrackerFactory(settings, impressionsCache, strategy, whenInit, integrationsManager, telemetryCache) {
|
|
9
15
|
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
|
|
10
16
|
return {
|
|
11
17
|
track: function (impressions, attributes) {
|
|
12
18
|
if (settings.userConsent === CONSENT_DECLINED)
|
|
13
19
|
return;
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
20
|
-
var impressionsLength = impressions.length;
|
|
21
|
-
var impressionsToStoreLength = impressionsToStore.length;
|
|
22
|
-
if (impressionsToStoreLength) {
|
|
23
|
-
var res = impressionsCache.track(impressionsToStore.map(function (item) { return item[0]; }));
|
|
20
|
+
var impressionsCount = impressions.length;
|
|
21
|
+
var _a = strategy.process(impressions), impressionsToStore = _a.impressionsToStore, impressionsToListener = _a.impressionsToListener, deduped = _a.deduped;
|
|
22
|
+
var impressionsToListenerCount = impressionsToListener.length;
|
|
23
|
+
if (impressionsToStore.length > 0) {
|
|
24
|
+
var res = impressionsCache.track(impressionsToStore);
|
|
24
25
|
// If we're on an async storage, handle error and log it.
|
|
25
26
|
if (thenable(res)) {
|
|
26
27
|
res.then(function () {
|
|
27
|
-
log.info(IMPRESSIONS_TRACKER_SUCCESS, [
|
|
28
|
+
log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
|
|
28
29
|
}).catch(function (err) {
|
|
29
|
-
log.error(ERROR_IMPRESSIONS_TRACKER, [
|
|
30
|
+
log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
else {
|
|
33
34
|
// Record when impressionsCache is sync only (standalone mode)
|
|
34
35
|
// @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
|
|
35
36
|
if (telemetryCache) {
|
|
36
|
-
telemetryCache.recordImpressionStats(QUEUED,
|
|
37
|
-
telemetryCache.recordImpressionStats(DEDUPED,
|
|
37
|
+
telemetryCache.recordImpressionStats(QUEUED, impressionsToStore.length);
|
|
38
|
+
telemetryCache.recordImpressionStats(DEDUPED, deduped);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -42,8 +43,8 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
42
43
|
if (impressionListener || integrationsManager) {
|
|
43
44
|
var _loop_1 = function (i) {
|
|
44
45
|
var impressionData = {
|
|
45
|
-
// copy of impression, to avoid unexpected
|
|
46
|
-
impression: objectAssign({},
|
|
46
|
+
// copy of impression, to avoid unexpected behavior if modified by integrations or impressionListener
|
|
47
|
+
impression: objectAssign({}, impressionsToListener[i]),
|
|
47
48
|
attributes: attributes,
|
|
48
49
|
ip: ip,
|
|
49
50
|
hostname: hostname,
|
|
@@ -65,7 +66,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
65
66
|
});
|
|
66
67
|
});
|
|
67
68
|
};
|
|
68
|
-
for (var i = 0; i <
|
|
69
|
+
for (var i = 0; i < impressionsToListenerCount; i++) {
|
|
69
70
|
_loop_1(i);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
* Debug strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
3
3
|
*
|
|
4
4
|
* @param impressionsObserver - impression observer. Previous time (pt property) is included in impression instances
|
|
5
|
-
* @returns
|
|
5
|
+
* @returns IStrategyResult
|
|
6
6
|
*/
|
|
7
7
|
export function strategyDebugFactory(impressionsObserver) {
|
|
8
8
|
return {
|
|
9
|
-
process: function (
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
process: function (impressions) {
|
|
10
|
+
impressions.forEach(function (impression) {
|
|
11
|
+
// Adds previous time if it is enabled
|
|
12
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
impressionsToStore: impressions,
|
|
16
|
+
impressionsToListener: impressions,
|
|
17
|
+
deduped: 0
|
|
18
|
+
};
|
|
12
19
|
}
|
|
13
20
|
};
|
|
14
21
|
}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* None strategy for impressions tracker.
|
|
3
3
|
*
|
|
4
|
-
* @param
|
|
4
|
+
* @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
5
5
|
* @param uniqueKeysTracker - unique keys tracker in charge of tracking the unique keys per split.
|
|
6
|
-
* @returns
|
|
6
|
+
* @returns IStrategyResult
|
|
7
7
|
*/
|
|
8
|
-
export function strategyNoneFactory(
|
|
8
|
+
export function strategyNoneFactory(impressionsCounter, uniqueKeysTracker) {
|
|
9
9
|
return {
|
|
10
|
-
process: function (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
process: function (impressions) {
|
|
11
|
+
impressions.forEach(function (impression) {
|
|
12
|
+
var now = Date.now();
|
|
13
|
+
// Increments impression counter per featureName
|
|
14
|
+
impressionsCounter.track(impression.feature, now, 1);
|
|
15
|
+
// Keep track by unique key
|
|
16
|
+
uniqueKeysTracker.track(impression.keyName, impression.feature);
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
impressionsToStore: [],
|
|
20
|
+
impressionsToListener: impressions,
|
|
21
|
+
deduped: 0
|
|
22
|
+
};
|
|
18
23
|
}
|
|
19
24
|
};
|
|
20
25
|
}
|
|
@@ -3,19 +3,29 @@ import { truncateTimeFrame } from '../../utils/time';
|
|
|
3
3
|
* Optimized strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
4
4
|
*
|
|
5
5
|
* @param impressionsObserver - impression observer. previous time (pt property) is included in impression instances
|
|
6
|
-
* @param
|
|
7
|
-
* @returns
|
|
6
|
+
* @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
7
|
+
* @returns IStrategyResult
|
|
8
8
|
*/
|
|
9
|
-
export function strategyOptimizedFactory(impressionsObserver,
|
|
9
|
+
export function strategyOptimizedFactory(impressionsObserver, impressionsCounter) {
|
|
10
10
|
return {
|
|
11
|
-
process: function (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
process: function (impressions) {
|
|
12
|
+
var impressionsToStore = [];
|
|
13
|
+
impressions.forEach(function (impression) {
|
|
14
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
15
|
+
var now = Date.now();
|
|
16
|
+
// Increments impression counter per featureName
|
|
17
|
+
if (impression.pt)
|
|
18
|
+
impressionsCounter.track(impression.feature, now, 1);
|
|
19
|
+
// Checks if the impression should be added in queue to be sent
|
|
20
|
+
if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
|
|
21
|
+
impressionsToStore.push(impression);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
impressionsToStore: impressionsToStore,
|
|
26
|
+
impressionsToListener: impressions,
|
|
27
|
+
deduped: impressions.length - impressionsToStore.length
|
|
28
|
+
};
|
|
19
29
|
}
|
|
20
30
|
};
|
|
21
31
|
}
|
|
@@ -136,7 +136,7 @@ export function settingsValidation(config, validationParams) {
|
|
|
136
136
|
withDefaults.core.key = 'localhost_key';
|
|
137
137
|
}
|
|
138
138
|
else {
|
|
139
|
-
// Keeping same
|
|
139
|
+
// Keeping same behavior than JS SDK: if settings key or TT are invalid,
|
|
140
140
|
// `false` value is used as bound key/TT of the default client, which leads to some issues.
|
|
141
141
|
// @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
|
|
142
142
|
withDefaults.core.key = validateKey(log, maybeKey, LOG_PREFIX_CLIENT_INSTANTIATION);
|
|
@@ -3,7 +3,7 @@ import { ERROR_STORAGE_INVALID } from '../../../logger/constants';
|
|
|
3
3
|
import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants';
|
|
4
4
|
export function __InLocalStorageMockFactory(params) {
|
|
5
5
|
var result = InMemoryStorageCSFactory(params);
|
|
6
|
-
result.
|
|
6
|
+
result.validateCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
|
|
7
7
|
return result;
|
|
8
8
|
}
|
|
9
9
|
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
|
package/package.json
CHANGED
package/src/dtos/types.ts
CHANGED
package/src/evaluator/index.ts
CHANGED
|
@@ -156,14 +156,12 @@ function getEvaluation(
|
|
|
156
156
|
return evaluation.then(result => {
|
|
157
157
|
result.changeNumber = split.getChangeNumber();
|
|
158
158
|
result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
|
|
159
|
-
result.track = splitJSON.trackImpressions;
|
|
160
159
|
|
|
161
160
|
return result;
|
|
162
161
|
});
|
|
163
162
|
} else {
|
|
164
163
|
evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
|
|
165
164
|
evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
|
|
166
|
-
evaluation.track = splitJSON.trackImpressions;
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { MaybeThenable } from '../../dtos/types';
|
|
2
2
|
import { ISegmentsCacheBase } from '../../storages/types';
|
|
3
|
-
import { thenable } from '../../utils/promise/thenable';
|
|
4
3
|
|
|
5
4
|
export function largeSegmentMatcherContext(largeSegmentName: string, storage: { largeSegments?: ISegmentsCacheBase }) {
|
|
6
5
|
|
|
7
6
|
return function largeSegmentMatcher(key: string): MaybeThenable<boolean> {
|
|
8
7
|
const isInLargeSegment = storage.largeSegments ? storage.largeSegments.isInSegment(largeSegmentName, key) : false;
|
|
9
8
|
|
|
10
|
-
if (thenable(isInLargeSegment)) {
|
|
11
|
-
isInLargeSegment.then(result => {
|
|
12
|
-
return result;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
9
|
return isInLargeSegment;
|
|
17
10
|
};
|
|
18
11
|
}
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { MaybeThenable } from '../../dtos/types';
|
|
2
2
|
import { ISegmentsCacheBase } from '../../storages/types';
|
|
3
|
-
import { thenable } from '../../utils/promise/thenable';
|
|
4
3
|
|
|
5
4
|
export function segmentMatcherContext(segmentName: string, storage: { segments: ISegmentsCacheBase }) {
|
|
6
5
|
|
|
7
6
|
return function segmentMatcher(key: string): MaybeThenable<boolean> {
|
|
8
7
|
const isInSegment = storage.segments.isInSegment(segmentName, key);
|
|
9
8
|
|
|
10
|
-
if (thenable(isInSegment)) {
|
|
11
|
-
isInSegment.then(result => {
|
|
12
|
-
return result;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
9
|
return isInSegment;
|
|
17
10
|
};
|
|
18
11
|
}
|
package/src/evaluator/types.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface IEvaluation {
|
|
|
25
25
|
config?: string | null
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export type IEvaluationResult = IEvaluation & { treatment: string
|
|
28
|
+
export type IEvaluationResult = IEvaluation & { treatment: string }
|
|
29
29
|
|
|
30
30
|
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
|
|
31
31
|
|
package/src/listeners/browser.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { IResponse, ISplitApi } from '../services/types';
|
|
|
8
8
|
import { ISettings } from '../types';
|
|
9
9
|
import SplitIO from '../../types/splitio';
|
|
10
10
|
import { ImpressionsPayload } from '../sync/submitters/types';
|
|
11
|
+
import { OPTIMIZED, DEBUG, NONE } from '../utils/constants';
|
|
11
12
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
12
13
|
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
|
|
13
14
|
import { ISyncManager } from '../sync/types';
|
|
@@ -77,9 +78,10 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
77
78
|
|
|
78
79
|
// Flush impressions & events data if there is user consent
|
|
79
80
|
if (isConsentGranted(this.settings)) {
|
|
81
|
+
const sim = this.settings.sync.impressionsMode;
|
|
80
82
|
const extraMetadata = {
|
|
81
83
|
// sim stands for Sync/Split Impressions Mode
|
|
82
|
-
sim:
|
|
84
|
+
sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
|
|
83
85
|
};
|
|
84
86
|
|
|
85
87
|
this._flushData(events + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
|
package/src/listeners/node.ts
CHANGED
|
@@ -56,7 +56,7 @@ export class NodeSignalListener implements ISignalListener {
|
|
|
56
56
|
// Cleaned up, remove handlers.
|
|
57
57
|
this.stop();
|
|
58
58
|
|
|
59
|
-
// This handler prevented the default
|
|
59
|
+
// This handler prevented the default behavior, start again.
|
|
60
60
|
// eslint-disable-next-line no-undef
|
|
61
61
|
process.kill(process.pid, SIGTERM);
|
|
62
62
|
};
|
|
@@ -72,7 +72,7 @@ export class NodeSignalListener implements ISignalListener {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (thenable(handlerResult)) {
|
|
75
|
-
// Always exit, even with errors. The promise is returned for UT
|
|
75
|
+
// Always exit, even with errors. The promise is returned for UT purposes.
|
|
76
76
|
return handlerResult.then(wrapUp).catch(wrapUp);
|
|
77
77
|
} else {
|
|
78
78
|
wrapUp();
|
|
@@ -3,6 +3,7 @@ import { ISettings } from '../types';
|
|
|
3
3
|
import SplitIO from '../../types/splitio';
|
|
4
4
|
import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
|
|
5
5
|
import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';
|
|
6
|
+
import { STORAGE_LOCALSTORAGE } from '../utils/constants';
|
|
6
7
|
|
|
7
8
|
function splitsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): ISplitsEventEmitter {
|
|
8
9
|
const splitsEventEmitter = objectAssign(new EventEmitter(), {
|
|
@@ -114,6 +115,10 @@ export function readinessManagerFactory(
|
|
|
114
115
|
isReady = true;
|
|
115
116
|
try {
|
|
116
117
|
syncLastUpdate();
|
|
118
|
+
if (!isReadyFromCache && settings.storage?.type === STORAGE_LOCALSTORAGE) {
|
|
119
|
+
isReadyFromCache = true;
|
|
120
|
+
gate.emit(SDK_READY_FROM_CACHE);
|
|
121
|
+
}
|
|
117
122
|
gate.emit(SDK_READY);
|
|
118
123
|
} catch (e) {
|
|
119
124
|
// throws user callback exceptions in next tick
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -34,11 +34,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
34
34
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
|
|
35
35
|
|
|
36
36
|
const wrapUp = (evaluationResult: IEvaluationResult) => {
|
|
37
|
-
const queue:
|
|
37
|
+
const queue: SplitIO.ImpressionDTO[] = [];
|
|
38
38
|
const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
39
39
|
impressionsTracker.track(queue, attributes);
|
|
40
40
|
|
|
41
|
-
stopTelemetryTracker(queue[0] && queue[0]
|
|
41
|
+
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
42
42
|
return treatment;
|
|
43
43
|
};
|
|
44
44
|
|
|
@@ -59,14 +59,14 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
59
59
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
|
|
60
60
|
|
|
61
61
|
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
62
|
-
const queue:
|
|
62
|
+
const queue: SplitIO.ImpressionDTO[] = [];
|
|
63
63
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
64
64
|
Object.keys(evaluationResults).forEach(featureFlagName => {
|
|
65
65
|
treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
66
66
|
});
|
|
67
67
|
impressionsTracker.track(queue, attributes);
|
|
68
68
|
|
|
69
|
-
stopTelemetryTracker(queue[0] && queue[0]
|
|
69
|
+
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
70
70
|
return treatments;
|
|
71
71
|
};
|
|
72
72
|
|
|
@@ -87,7 +87,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
87
87
|
const stopTelemetryTracker = telemetryTracker.trackEval(method);
|
|
88
88
|
|
|
89
89
|
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
90
|
-
const queue:
|
|
90
|
+
const queue: SplitIO.ImpressionDTO[] = [];
|
|
91
91
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
92
92
|
const evaluations = evaluationResults;
|
|
93
93
|
Object.keys(evaluations).forEach(featureFlagName => {
|
|
@@ -95,7 +95,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
95
95
|
});
|
|
96
96
|
impressionsTracker.track(queue, attributes);
|
|
97
97
|
|
|
98
|
-
stopTelemetryTracker(queue[0] && queue[0]
|
|
98
|
+
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
99
99
|
return treatments;
|
|
100
100
|
};
|
|
101
101
|
|
|
@@ -128,25 +128,25 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
128
128
|
attributes: SplitIO.Attributes | undefined,
|
|
129
129
|
withConfig: boolean,
|
|
130
130
|
invokingMethodName: string,
|
|
131
|
-
queue:
|
|
131
|
+
queue: SplitIO.ImpressionDTO[]
|
|
132
132
|
): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
|
|
133
133
|
const matchingKey = getMatching(key);
|
|
134
134
|
const bucketingKey = getBucketing(key);
|
|
135
135
|
|
|
136
|
-
const { treatment, label, changeNumber, config = null
|
|
136
|
+
const { treatment, label, changeNumber, config = null } = evaluation;
|
|
137
137
|
log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
|
|
138
138
|
|
|
139
139
|
if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
|
|
140
140
|
log.info(IMPRESSION_QUEUEING);
|
|
141
|
-
queue.push(
|
|
141
|
+
queue.push({
|
|
142
142
|
feature: featureFlagName,
|
|
143
143
|
keyName: matchingKey,
|
|
144
144
|
treatment,
|
|
145
145
|
time: Date.now(),
|
|
146
146
|
bucketingKey,
|
|
147
147
|
label,
|
|
148
|
-
changeNumber: changeNumber as number
|
|
149
|
-
}
|
|
148
|
+
changeNumber: changeNumber as number
|
|
149
|
+
});
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
if (withConfig) {
|
|
@@ -61,7 +61,7 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
61
61
|
releaseApiKey(settings.core.authorizationKey);
|
|
62
62
|
telemetryTracker.sessionLength();
|
|
63
63
|
signalListener && signalListener.stop();
|
|
64
|
-
uniqueKeysTracker.stop();
|
|
64
|
+
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// Stop background jobs
|