@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
package/CHANGES.txt
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
1.5.0 (June 24, 2022)
|
|
2
|
+
- Added a new config option to control the tasks that listen or poll for updates on feature flags and segments, via the new config sync.enabled . Running online Split will always pull the most recent updates upon initialization, this only affects updates fetching on a running instance. Useful when a consistent session experience is a must or to save resources when updates are not being used.
|
|
3
|
+
- Updated telemetry logic to track the anonymous config for user consent flag set to declined or unknown.
|
|
4
|
+
- Updated submitters logic, to avoid duplicating the post of impressions to Split cloud when the SDK is destroyed while its periodic post of impressions is running.
|
|
5
|
+
|
|
1
6
|
1.4.1 (June 13, 2022)
|
|
2
7
|
- Bugfixing - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending.
|
|
3
8
|
|
|
@@ -31,10 +31,10 @@ function createUserConsentAPI(params) {
|
|
|
31
31
|
log.info(constants_1.USER_CONSENT_UPDATED, [settings.userConsent, newConsentStatus]); // @ts-ignore, modify readonly prop
|
|
32
32
|
settings.userConsent = newConsentStatus;
|
|
33
33
|
if (consent) { // resumes submitters if transitioning to GRANTED
|
|
34
|
-
(_a = syncManager === null || syncManager === void 0 ? void 0 : syncManager.
|
|
34
|
+
(_a = syncManager === null || syncManager === void 0 ? void 0 : syncManager.submitterManager) === null || _a === void 0 ? void 0 : _a.start();
|
|
35
35
|
}
|
|
36
|
-
else { // pauses submitters and drops tracked data if transitioning to DECLINED
|
|
37
|
-
(_b = syncManager === null || syncManager === void 0 ? void 0 : syncManager.
|
|
36
|
+
else { // pauses submitters (except telemetry), and drops tracked data if transitioning to DECLINED
|
|
37
|
+
(_b = syncManager === null || syncManager === void 0 ? void 0 : syncManager.submitterManager) === null || _b === void 0 ? void 0 : _b.stop(true);
|
|
38
38
|
// @ts-ignore, clear method is present in storage for standalone and partial consumer mode
|
|
39
39
|
if (events.clear)
|
|
40
40
|
events.clear(); // @ts-ignore
|
package/cjs/listeners/browser.js
CHANGED
|
@@ -53,7 +53,7 @@ var BrowserSignalListener = /** @class */ (function () {
|
|
|
53
53
|
BrowserSignalListener.prototype.flushData = function () {
|
|
54
54
|
if (!this.syncManager)
|
|
55
55
|
return; // In consumer mode there is not sync manager and data to flush
|
|
56
|
-
// Flush data if there is user consent
|
|
56
|
+
// Flush impressions & events data if there is user consent
|
|
57
57
|
if ((0, consent_1.isConsentGranted)(this.settings)) {
|
|
58
58
|
var eventsUrl = this.settings.urls.events;
|
|
59
59
|
var extraMetadata = {
|
|
@@ -64,11 +64,12 @@ var BrowserSignalListener = /** @class */ (function () {
|
|
|
64
64
|
this._flushData(eventsUrl + '/events/beacon', this.storage.events, this.serviceApi.postEventsBulk);
|
|
65
65
|
if (this.storage.impressionCounts)
|
|
66
66
|
this._flushData(eventsUrl + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, impressionCountsSubmitter_1.fromImpressionCountsCollector);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
}
|
|
68
|
+
// Flush telemetry data
|
|
69
|
+
if (this.storage.telemetry) {
|
|
70
|
+
var telemetryUrl = this.settings.urls.telemetry;
|
|
71
|
+
var telemetryCacheAdapter = (0, telemetrySubmitter_1.telemetryCacheStatsAdapter)(this.storage.telemetry, this.storage.splits, this.storage.segments);
|
|
72
|
+
this._flushData(telemetryUrl + '/v1/metrics/usage/beacon', telemetryCacheAdapter, this.serviceApi.postMetricsUsage);
|
|
72
73
|
}
|
|
73
74
|
// Close streaming connection
|
|
74
75
|
if (this.syncManager.pushManager)
|
package/cjs/sdkFactory/index.js
CHANGED
|
@@ -25,13 +25,14 @@ function sdkFactory(params) {
|
|
|
25
25
|
(0, apiKey_1.validateAndTrackApiKey)(log, settings.core.authorizationKey);
|
|
26
26
|
var sdkReadinessManager = (0, sdkReadinessManager_1.sdkReadinessManagerFactory)(log, platform.EventEmitter, settings.startup.readyTimeout);
|
|
27
27
|
var readiness = sdkReadinessManager.readinessManager;
|
|
28
|
+
var matchingKey = (0, key_1.getMatching)(settings.core.key);
|
|
28
29
|
// @TODO consider passing the settings object, so that each storage access only what it needs
|
|
29
30
|
var storageFactoryParams = {
|
|
30
31
|
impressionsQueueSize: settings.scheduler.impressionsQueueSize,
|
|
31
32
|
eventsQueueSize: settings.scheduler.eventsQueueSize,
|
|
32
33
|
optimize: (0, utils_1.shouldBeOptimized)(settings),
|
|
33
34
|
// ATM, only used by InLocalStorage
|
|
34
|
-
matchingKey:
|
|
35
|
+
matchingKey: matchingKey,
|
|
35
36
|
splitFiltersValidation: settings.sync.__splitFiltersValidation,
|
|
36
37
|
// ATM, only used by PluggableStorage
|
|
37
38
|
mode: settings.mode,
|
|
@@ -47,7 +48,21 @@ function sdkFactory(params) {
|
|
|
47
48
|
log: log
|
|
48
49
|
};
|
|
49
50
|
var storage = storageFactory(storageFactoryParams);
|
|
50
|
-
// @TODO
|
|
51
|
+
// @TODO dataLoader requires validation
|
|
52
|
+
if (settings.dataLoader) {
|
|
53
|
+
settings.dataLoader(storage, matchingKey);
|
|
54
|
+
Promise.resolve(storage.splits.checkCache()).then(function (cacheReady) {
|
|
55
|
+
if (cacheReady) {
|
|
56
|
+
if (settings.sync.onlySubmitters) { // emit SDK_READY to not timeout when not synchronizing splits & segments
|
|
57
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED);
|
|
58
|
+
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
|
|
59
|
+
}
|
|
60
|
+
else { // emit SDK_READY_FROM_CACHE
|
|
61
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
51
66
|
var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage });
|
|
52
67
|
// trackers
|
|
53
68
|
var observer = impressionsObserverFactory && impressionsObserverFactory();
|
|
@@ -79,6 +94,9 @@ function sdkFactory(params) {
|
|
|
79
94
|
// Logger wrapper API
|
|
80
95
|
Logger: (0, sdkLogger_1.createLoggerAPI)(settings.log),
|
|
81
96
|
settings: settings,
|
|
97
|
+
// @TODO remove
|
|
98
|
+
__storage: storage,
|
|
99
|
+
__ctx: ctx
|
|
82
100
|
}, extraProps && extraProps(ctx));
|
|
83
101
|
}
|
|
84
102
|
exports.sdkFactory = sdkFactory;
|
|
@@ -33,7 +33,7 @@ var AbstractSplitsCacheSync = /** @class */ (function () {
|
|
|
33
33
|
* It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
|
|
34
34
|
*/
|
|
35
35
|
AbstractSplitsCacheSync.prototype.checkCache = function () {
|
|
36
|
-
return
|
|
36
|
+
return this.getChangeNumber() > -1;
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
39
|
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.DataLoaderFactory = void 0;
|
|
4
4
|
var browser_1 = require("../utils/constants/browser");
|
|
5
5
|
/**
|
|
6
|
-
* Factory of
|
|
6
|
+
* Factory of storage loader
|
|
7
7
|
*
|
|
8
8
|
* @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
|
|
9
9
|
* and extended with a `mySegmentsData` property.
|
|
10
10
|
* @returns function to preload the storage
|
|
11
11
|
*/
|
|
12
|
-
function
|
|
12
|
+
function DataLoaderFactory(preloadedData) {
|
|
13
13
|
/**
|
|
14
14
|
* Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
|
|
15
15
|
* (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
|
|
16
16
|
*
|
|
17
17
|
* @param storage object containing `splits` and `segments` cache (client-side variant)
|
|
18
|
-
* @param
|
|
18
|
+
* @param userKey user key (matching key) of the provided MySegmentsCache
|
|
19
19
|
*
|
|
20
|
-
* @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
|
|
21
20
|
* @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
|
|
21
|
+
* @TODO add logs, and input validation in this module, in favor of size reduction.
|
|
22
|
+
* @TODO unit tests
|
|
22
23
|
*/
|
|
23
|
-
return function loadData(storage,
|
|
24
|
+
return function loadData(storage, userKey) {
|
|
24
25
|
// Do not load data if current preloadedData is empty
|
|
25
26
|
if (Object.keys(preloadedData).length === 0)
|
|
26
27
|
return;
|
|
@@ -36,16 +37,23 @@ function dataLoaderFactory(preloadedData) {
|
|
|
36
37
|
storage.splits.setChangeNumber(since);
|
|
37
38
|
// splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
|
|
38
39
|
storage.splits.addSplits(Object.keys(splitsData).map(function (splitName) { return [splitName, splitsData[splitName]]; }));
|
|
39
|
-
// add mySegments data
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
if (userKey) { // add mySegments data (client-side)
|
|
41
|
+
var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userKey];
|
|
42
|
+
if (!mySegmentsData) {
|
|
43
|
+
// segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
|
|
44
|
+
mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
|
|
45
|
+
var userKeys = segmentsData[segmentName];
|
|
46
|
+
return userKeys.indexOf(userKey) > -1;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
storage.segments.resetSegments(mySegmentsData);
|
|
50
|
+
}
|
|
51
|
+
else { // add segments data (server-side)
|
|
52
|
+
Object.keys(segmentsData).filter(function (segmentName) {
|
|
53
|
+
var userKeys = segmentsData[segmentName];
|
|
54
|
+
storage.segments.addToSegment(segmentName, userKeys);
|
|
46
55
|
});
|
|
47
56
|
}
|
|
48
|
-
storage.segments.resetSegments(mySegmentsData);
|
|
49
57
|
};
|
|
50
58
|
}
|
|
51
|
-
exports.
|
|
59
|
+
exports.DataLoaderFactory = DataLoaderFactory;
|
|
@@ -8,6 +8,7 @@ var EventsCacheInMemory_1 = require("./EventsCacheInMemory");
|
|
|
8
8
|
var ImpressionCountsCacheInMemory_1 = require("./ImpressionCountsCacheInMemory");
|
|
9
9
|
var constants_1 = require("../../utils/constants");
|
|
10
10
|
var TelemetryCacheInMemory_1 = require("./TelemetryCacheInMemory");
|
|
11
|
+
var sets_1 = require("../../utils/lang/sets");
|
|
11
12
|
/**
|
|
12
13
|
* InMemory storage factory for standalone server-side SplitFactory
|
|
13
14
|
*
|
|
@@ -28,7 +29,20 @@ function InMemoryStorageFactory(params) {
|
|
|
28
29
|
this.impressions.clear();
|
|
29
30
|
this.impressionCounts && this.impressionCounts.clear();
|
|
30
31
|
this.events.clear();
|
|
31
|
-
}
|
|
32
|
+
},
|
|
33
|
+
// @ts-ignore, private method, for POC
|
|
34
|
+
getSnapshot: function () {
|
|
35
|
+
var _this = this;
|
|
36
|
+
return {
|
|
37
|
+
lastUpdated: Date.now(),
|
|
38
|
+
since: this.splits.changeNumber,
|
|
39
|
+
splitsData: this.splits.splitsCache,
|
|
40
|
+
segmentsData: Object.keys(this.segments.segmentCache).reduce(function (prev, cur) {
|
|
41
|
+
prev[cur] = (0, sets_1.setToArray)(_this.segments.segmentCache[cur]);
|
|
42
|
+
return prev;
|
|
43
|
+
}, {})
|
|
44
|
+
};
|
|
45
|
+
},
|
|
32
46
|
};
|
|
33
47
|
}
|
|
34
48
|
exports.InMemoryStorageFactory = InMemoryStorageFactory;
|
|
@@ -29,6 +29,18 @@ function InMemoryStorageCSFactory(params) {
|
|
|
29
29
|
this.impressionCounts && this.impressionCounts.clear();
|
|
30
30
|
this.events.clear();
|
|
31
31
|
},
|
|
32
|
+
// @ts-ignore, private method, for POC
|
|
33
|
+
getSnapshot: function () {
|
|
34
|
+
var _a;
|
|
35
|
+
return {
|
|
36
|
+
lastUpdated: Date.now(),
|
|
37
|
+
since: this.splits.changeNumber,
|
|
38
|
+
splitsData: this.splits.splitsCache,
|
|
39
|
+
mySegmentsData: (_a = {},
|
|
40
|
+
_a[params.matchingKey] = Object.keys(this.segments.segmentCache),
|
|
41
|
+
_a)
|
|
42
|
+
};
|
|
43
|
+
},
|
|
32
44
|
// When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
|
|
33
45
|
shared: function () {
|
|
34
46
|
return {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.submitterManagerFactory = void 0;
|
|
4
|
-
var syncTaskComposite_1 = require("../syncTaskComposite");
|
|
5
4
|
var eventsSubmitter_1 = require("./eventsSubmitter");
|
|
6
5
|
var impressionsSubmitter_1 = require("./impressionsSubmitter");
|
|
7
6
|
var impressionCountsSubmitter_1 = require("./impressionCountsSubmitter");
|
|
@@ -15,8 +14,33 @@ function submitterManagerFactory(params) {
|
|
|
15
14
|
if (impressionCountsSubmitter)
|
|
16
15
|
submitters.push(impressionCountsSubmitter);
|
|
17
16
|
var telemetrySubmitter = (0, telemetrySubmitter_1.telemetrySubmitterFactory)(params);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
return {
|
|
18
|
+
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
19
|
+
start: function (onlyTelemetry) {
|
|
20
|
+
if (!onlyTelemetry)
|
|
21
|
+
submitters.forEach(function (submitter) { return submitter.start(); });
|
|
22
|
+
if (telemetrySubmitter)
|
|
23
|
+
telemetrySubmitter.start();
|
|
24
|
+
},
|
|
25
|
+
// `allExceptTelemetry` true if userConsent is changed to DECLINED
|
|
26
|
+
stop: function (allExceptTelemetry) {
|
|
27
|
+
submitters.forEach(function (submitter) { return submitter.stop(); });
|
|
28
|
+
if (!allExceptTelemetry && telemetrySubmitter)
|
|
29
|
+
telemetrySubmitter.stop();
|
|
30
|
+
},
|
|
31
|
+
isRunning: function () {
|
|
32
|
+
return submitters.some(function (submitter) { return submitter.isRunning(); });
|
|
33
|
+
},
|
|
34
|
+
// Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
|
|
35
|
+
execute: function (onlyTelemetry) {
|
|
36
|
+
var promises = onlyTelemetry ? [] : submitters.map(function (submitter) { return submitter.execute(); });
|
|
37
|
+
if (telemetrySubmitter)
|
|
38
|
+
promises.push(telemetrySubmitter.execute());
|
|
39
|
+
return Promise.all(promises);
|
|
40
|
+
},
|
|
41
|
+
isExecuting: function () {
|
|
42
|
+
return submitters.some(function (submitter) { return submitter.isExecuting(); });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
21
45
|
}
|
|
22
46
|
exports.submitterManagerFactory = submitterManagerFactory;
|
|
@@ -19,16 +19,16 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
19
19
|
* SyncManager factory for modular SDK
|
|
20
20
|
*/
|
|
21
21
|
return function (params) {
|
|
22
|
-
var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, telemetryTracker = params.telemetryTracker;
|
|
22
|
+
var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, _b = _a.sync, syncEnabled = _b.enabled, onlySubmitters = _b.onlySubmitters, telemetryTracker = params.telemetryTracker;
|
|
23
23
|
/** Polling Manager */
|
|
24
|
-
var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
24
|
+
var pollingManager = onlySubmitters ? undefined : pollingManagerFactory && pollingManagerFactory(params);
|
|
25
25
|
/** Push Manager */
|
|
26
|
-
var pushManager = streamingEnabled && pollingManager && pushManagerFactory ?
|
|
26
|
+
var pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ?
|
|
27
27
|
pushManagerFactory(params, pollingManager) :
|
|
28
28
|
undefined;
|
|
29
29
|
/** Submitter Manager */
|
|
30
30
|
// It is not inyected as push and polling managers, because at the moment it is required
|
|
31
|
-
var
|
|
31
|
+
var submitterManager = (0, submitterManager_1.submitterManagerFactory)(params);
|
|
32
32
|
/** Sync Manager logic */
|
|
33
33
|
function startPolling() {
|
|
34
34
|
if (pollingManager.isRunning()) {
|
|
@@ -61,7 +61,7 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
61
61
|
// E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline).
|
|
62
62
|
pollingManager: pollingManager,
|
|
63
63
|
pushManager: pushManager,
|
|
64
|
-
|
|
64
|
+
submitterManager: submitterManager,
|
|
65
65
|
/**
|
|
66
66
|
* Method used to start the syncManager for the first time, or resume it after being stopped.
|
|
67
67
|
*/
|
|
@@ -69,21 +69,29 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
69
69
|
running = true;
|
|
70
70
|
// start syncing splits and segments
|
|
71
71
|
if (pollingManager) {
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
// If synchronization is disabled pushManager and pollingManager should not start
|
|
73
|
+
if (syncEnabled) {
|
|
74
|
+
if (pushManager) {
|
|
75
|
+
// Doesn't call `syncAll` when the syncManager is resuming
|
|
76
|
+
if (startFirstTime) {
|
|
77
|
+
pollingManager.syncAll();
|
|
78
|
+
startFirstTime = false;
|
|
79
|
+
}
|
|
80
|
+
pushManager.start();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
pollingManager.start();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
74
87
|
if (startFirstTime) {
|
|
75
88
|
pollingManager.syncAll();
|
|
76
89
|
startFirstTime = false;
|
|
77
90
|
}
|
|
78
|
-
pushManager.start();
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
pollingManager.start();
|
|
82
91
|
}
|
|
83
92
|
}
|
|
84
93
|
// start periodic data recording (events, impressions, telemetry).
|
|
85
|
-
|
|
86
|
-
submitter.start();
|
|
94
|
+
submitterManager.start(!(0, consent_1.isConsentGranted)(settings));
|
|
87
95
|
},
|
|
88
96
|
/**
|
|
89
97
|
* Method used to stop/pause the syncManager.
|
|
@@ -96,16 +104,13 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
96
104
|
if (pollingManager && pollingManager.isRunning())
|
|
97
105
|
pollingManager.stop();
|
|
98
106
|
// stop periodic data recording (events, impressions, telemetry).
|
|
99
|
-
|
|
107
|
+
submitterManager.stop();
|
|
100
108
|
},
|
|
101
109
|
isRunning: function () {
|
|
102
110
|
return running;
|
|
103
111
|
},
|
|
104
112
|
flush: function () {
|
|
105
|
-
|
|
106
|
-
return submitter.execute();
|
|
107
|
-
else
|
|
108
|
-
return Promise.resolve();
|
|
113
|
+
return submitterManager.execute(!(0, consent_1.isConsentGranted)(settings));
|
|
109
114
|
},
|
|
110
115
|
// [Only used for client-side]
|
|
111
116
|
// If polling and push managers are defined (standalone mode), they implement the interfaces for client-side
|
|
@@ -116,22 +121,28 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
116
121
|
return {
|
|
117
122
|
isRunning: mySegmentsSyncTask.isRunning,
|
|
118
123
|
start: function () {
|
|
119
|
-
if (
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
if (syncEnabled) {
|
|
125
|
+
if (pushManager) {
|
|
126
|
+
if (pollingManager.isRunning()) {
|
|
127
|
+
// if doing polling, we must start the periodic fetch of data
|
|
128
|
+
if (storage.splits.usesSegments())
|
|
129
|
+
mySegmentsSyncTask.start();
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// if not polling, we must execute the sync task for the initial fetch
|
|
133
|
+
// of segments since `syncAll` was already executed when starting the main client
|
|
134
|
+
mySegmentsSyncTask.execute();
|
|
135
|
+
}
|
|
136
|
+
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
124
137
|
}
|
|
125
138
|
else {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
mySegmentsSyncTask.execute();
|
|
139
|
+
if (storage.splits.usesSegments())
|
|
140
|
+
mySegmentsSyncTask.start();
|
|
129
141
|
}
|
|
130
|
-
pushManager.add(matchingKey, mySegmentsSyncTask);
|
|
131
142
|
}
|
|
132
143
|
else {
|
|
133
|
-
if (
|
|
134
|
-
mySegmentsSyncTask.
|
|
144
|
+
if (!readinessManager.isReady())
|
|
145
|
+
mySegmentsSyncTask.execute();
|
|
135
146
|
}
|
|
136
147
|
},
|
|
137
148
|
stop: function () {
|
package/cjs/sync/syncTask.js
CHANGED
|
@@ -4,8 +4,8 @@ exports.syncTaskFactory = void 0;
|
|
|
4
4
|
var constants_1 = require("../logger/constants");
|
|
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 @@ var constants_1 = require("../logger/constants");
|
|
|
15
15
|
*/
|
|
16
16
|
function syncTaskFactory(log, task, period, taskName) {
|
|
17
17
|
if (taskName === void 0) { taskName = 'task'; }
|
|
18
|
-
//
|
|
19
|
-
var
|
|
18
|
+
// Task promise while it is pending. Undefined once the promise is resolved
|
|
19
|
+
var pendingTask;
|
|
20
20
|
// flag that indicates if the task periodic execution has been started/stopped.
|
|
21
21
|
var running = false;
|
|
22
22
|
// Auxiliar counter used to avoid race condition when calling `start` & `stop` intermittently
|
|
@@ -30,13 +30,19 @@ function syncTaskFactory(log, task, period, taskName) {
|
|
|
30
30
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
31
31
|
args[_i] = arguments[_i];
|
|
32
32
|
}
|
|
33
|
-
executing
|
|
33
|
+
// If task is executing, chain the new execution
|
|
34
|
+
if (pendingTask) {
|
|
35
|
+
return pendingTask.then(function () {
|
|
36
|
+
return execute.apply(void 0, args);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Execute task
|
|
34
40
|
log.debug(constants_1.SYNC_TASK_EXECUTE, [taskName]);
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
pendingTask = task.apply(void 0, args).then(function (result) {
|
|
42
|
+
pendingTask = undefined;
|
|
37
43
|
return result;
|
|
38
44
|
});
|
|
39
|
-
|
|
45
|
+
return pendingTask;
|
|
40
46
|
}
|
|
41
47
|
function periodicExecute(currentRunningId) {
|
|
42
48
|
return execute.apply(void 0, runningArgs).then(function (result) {
|
|
@@ -48,10 +54,9 @@ function syncTaskFactory(log, task, period, taskName) {
|
|
|
48
54
|
});
|
|
49
55
|
}
|
|
50
56
|
return {
|
|
51
|
-
// @TODO check if we need to queued `execute` calls, to avoid possible race conditions on submitters and updaters with streaming.
|
|
52
57
|
execute: execute,
|
|
53
58
|
isExecuting: function () {
|
|
54
|
-
return
|
|
59
|
+
return pendingTask !== undefined;
|
|
55
60
|
},
|
|
56
61
|
start: function () {
|
|
57
62
|
var args = [];
|
|
@@ -5,7 +5,7 @@ var ImpressionObserver_1 = require("./ImpressionObserver");
|
|
|
5
5
|
var murmur3_1 = require("../../utils/murmur3/murmur3");
|
|
6
6
|
var buildKey_1 = require("./buildKey");
|
|
7
7
|
function hashImpression32(impression) {
|
|
8
|
-
return (0, murmur3_1.hash)((0, buildKey_1.buildKey)(impression));
|
|
8
|
+
return (0, murmur3_1.hash)((0, buildKey_1.buildKey)(impression)).toString();
|
|
9
9
|
}
|
|
10
10
|
exports.hashImpression32 = hashImpression32;
|
|
11
11
|
var LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
|
|
@@ -73,7 +73,8 @@ exports.base = {
|
|
|
73
73
|
splitFilters: undefined,
|
|
74
74
|
// impressions collection mode
|
|
75
75
|
impressionsMode: constants_1.OPTIMIZED,
|
|
76
|
-
localhostMode: undefined
|
|
76
|
+
localhostMode: undefined,
|
|
77
|
+
enabled: true
|
|
77
78
|
},
|
|
78
79
|
// Logger
|
|
79
80
|
log: undefined
|
|
@@ -126,6 +127,9 @@ function settingsValidation(config, validationParams) {
|
|
|
126
127
|
// ensure a valid SDK mode
|
|
127
128
|
// @ts-ignore, modify readonly prop
|
|
128
129
|
withDefaults.mode = (0, mode_1.mode)(withDefaults.core.authorizationKey, withDefaults.mode);
|
|
130
|
+
if (withDefaults.sync.onlySubmitters && withDefaults.mode === constants_1.STANDALONE_MODE && !withDefaults.dataLoader) {
|
|
131
|
+
throw new Error('To use `onlySubmitters` param in standalone mode, DataLoader is required to preload data into the storage');
|
|
132
|
+
}
|
|
129
133
|
// ensure a valid Storage based on mode defined.
|
|
130
134
|
// @ts-ignore, modify readonly prop
|
|
131
135
|
if (storage)
|
|
@@ -167,6 +171,14 @@ function settingsValidation(config, validationParams) {
|
|
|
167
171
|
// We are not checking if bases are positive numbers. Thus, we might be reauthenticating immediately (`setTimeout` with NaN or negative number)
|
|
168
172
|
scheduler.pushRetryBackoffBase = fromSecondsToMillis(scheduler.pushRetryBackoffBase);
|
|
169
173
|
}
|
|
174
|
+
// validate sync enabled
|
|
175
|
+
if (withDefaults.sync.enabled !== false) {
|
|
176
|
+
withDefaults.sync.enabled = true;
|
|
177
|
+
}
|
|
178
|
+
// validate sync onlySubmitters
|
|
179
|
+
if (withDefaults.sync.onlySubmitters !== true) {
|
|
180
|
+
withDefaults.sync.onlySubmitters = false;
|
|
181
|
+
}
|
|
170
182
|
// validate the `splitFilters` settings and parse splits query
|
|
171
183
|
var splitFiltersValidation = (0, splitFilters_1.validateSplitFilters)(log, withDefaults.sync.splitFilters, withDefaults.mode);
|
|
172
184
|
withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
|
|
@@ -28,10 +28,10 @@ export function createUserConsentAPI(params) {
|
|
|
28
28
|
log.info(USER_CONSENT_UPDATED, [settings.userConsent, newConsentStatus]); // @ts-ignore, modify readonly prop
|
|
29
29
|
settings.userConsent = newConsentStatus;
|
|
30
30
|
if (consent) { // resumes submitters if transitioning to GRANTED
|
|
31
|
-
(_a = syncManager === null || syncManager === void 0 ? void 0 : syncManager.
|
|
31
|
+
(_a = syncManager === null || syncManager === void 0 ? void 0 : syncManager.submitterManager) === null || _a === void 0 ? void 0 : _a.start();
|
|
32
32
|
}
|
|
33
|
-
else { // pauses submitters and drops tracked data if transitioning to DECLINED
|
|
34
|
-
(_b = syncManager === null || syncManager === void 0 ? void 0 : syncManager.
|
|
33
|
+
else { // pauses submitters (except telemetry), and drops tracked data if transitioning to DECLINED
|
|
34
|
+
(_b = syncManager === null || syncManager === void 0 ? void 0 : syncManager.submitterManager) === null || _b === void 0 ? void 0 : _b.stop(true);
|
|
35
35
|
// @ts-ignore, clear method is present in storage for standalone and partial consumer mode
|
|
36
36
|
if (events.clear)
|
|
37
37
|
events.clear(); // @ts-ignore
|
package/esm/listeners/browser.js
CHANGED
|
@@ -50,7 +50,7 @@ var BrowserSignalListener = /** @class */ (function () {
|
|
|
50
50
|
BrowserSignalListener.prototype.flushData = function () {
|
|
51
51
|
if (!this.syncManager)
|
|
52
52
|
return; // In consumer mode there is not sync manager and data to flush
|
|
53
|
-
// Flush data if there is user consent
|
|
53
|
+
// Flush impressions & events data if there is user consent
|
|
54
54
|
if (isConsentGranted(this.settings)) {
|
|
55
55
|
var eventsUrl = this.settings.urls.events;
|
|
56
56
|
var extraMetadata = {
|
|
@@ -61,11 +61,12 @@ var BrowserSignalListener = /** @class */ (function () {
|
|
|
61
61
|
this._flushData(eventsUrl + '/events/beacon', this.storage.events, this.serviceApi.postEventsBulk);
|
|
62
62
|
if (this.storage.impressionCounts)
|
|
63
63
|
this._flushData(eventsUrl + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, fromImpressionCountsCollector);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
}
|
|
65
|
+
// Flush telemetry data
|
|
66
|
+
if (this.storage.telemetry) {
|
|
67
|
+
var telemetryUrl = this.settings.urls.telemetry;
|
|
68
|
+
var telemetryCacheAdapter = telemetryCacheStatsAdapter(this.storage.telemetry, this.storage.splits, this.storage.segments);
|
|
69
|
+
this._flushData(telemetryUrl + '/v1/metrics/usage/beacon', telemetryCacheAdapter, this.serviceApi.postMetricsUsage);
|
|
69
70
|
}
|
|
70
71
|
// Close streaming connection
|
|
71
72
|
if (this.syncManager.pushManager)
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
|
|
|
8
8
|
import { createLoggerAPI } from '../logger/sdkLogger';
|
|
9
9
|
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
|
|
10
10
|
import { metadataBuilder } from '../storages/metadataBuilder';
|
|
11
|
-
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
|
|
11
|
+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
|
|
12
12
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
13
13
|
/**
|
|
14
14
|
* Modular SDK factory
|
|
@@ -22,13 +22,14 @@ export function sdkFactory(params) {
|
|
|
22
22
|
validateAndTrackApiKey(log, settings.core.authorizationKey);
|
|
23
23
|
var sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout);
|
|
24
24
|
var readiness = sdkReadinessManager.readinessManager;
|
|
25
|
+
var matchingKey = getMatching(settings.core.key);
|
|
25
26
|
// @TODO consider passing the settings object, so that each storage access only what it needs
|
|
26
27
|
var storageFactoryParams = {
|
|
27
28
|
impressionsQueueSize: settings.scheduler.impressionsQueueSize,
|
|
28
29
|
eventsQueueSize: settings.scheduler.eventsQueueSize,
|
|
29
30
|
optimize: shouldBeOptimized(settings),
|
|
30
31
|
// ATM, only used by InLocalStorage
|
|
31
|
-
matchingKey:
|
|
32
|
+
matchingKey: matchingKey,
|
|
32
33
|
splitFiltersValidation: settings.sync.__splitFiltersValidation,
|
|
33
34
|
// ATM, only used by PluggableStorage
|
|
34
35
|
mode: settings.mode,
|
|
@@ -44,7 +45,21 @@ export function sdkFactory(params) {
|
|
|
44
45
|
log: log
|
|
45
46
|
};
|
|
46
47
|
var storage = storageFactory(storageFactoryParams);
|
|
47
|
-
// @TODO
|
|
48
|
+
// @TODO dataLoader requires validation
|
|
49
|
+
if (settings.dataLoader) {
|
|
50
|
+
settings.dataLoader(storage, matchingKey);
|
|
51
|
+
Promise.resolve(storage.splits.checkCache()).then(function (cacheReady) {
|
|
52
|
+
if (cacheReady) {
|
|
53
|
+
if (settings.sync.onlySubmitters) { // emit SDK_READY to not timeout when not synchronizing splits & segments
|
|
54
|
+
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
55
|
+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
56
|
+
}
|
|
57
|
+
else { // emit SDK_READY_FROM_CACHE
|
|
58
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
48
63
|
var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage });
|
|
49
64
|
// trackers
|
|
50
65
|
var observer = impressionsObserverFactory && impressionsObserverFactory();
|
|
@@ -76,5 +91,8 @@ export function sdkFactory(params) {
|
|
|
76
91
|
// Logger wrapper API
|
|
77
92
|
Logger: createLoggerAPI(settings.log),
|
|
78
93
|
settings: settings,
|
|
94
|
+
// @TODO remove
|
|
95
|
+
__storage: storage,
|
|
96
|
+
__ctx: ctx
|
|
79
97
|
}, extraProps && extraProps(ctx));
|
|
80
98
|
}
|
|
@@ -30,7 +30,7 @@ var AbstractSplitsCacheSync = /** @class */ (function () {
|
|
|
30
30
|
* It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
|
|
31
31
|
*/
|
|
32
32
|
AbstractSplitsCacheSync.prototype.checkCache = function () {
|
|
33
|
-
return
|
|
33
|
+
return this.getChangeNumber() > -1;
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
36
|
* Kill `name` split and set `defaultTreatment` and `changeNumber`.
|