@splitsoftware/splitio-commons 1.2.0 → 1.2.1-rc.0
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/cjs/logger/messages/info.js +3 -3
- package/cjs/sdkClient/client.js +2 -1
- package/cjs/sdkClient/clientCS.js +2 -1
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/sync/streaming/AuthClient/index.js +1 -2
- package/cjs/sync/streaming/pushManager.js +24 -21
- package/cjs/sync/syncManagerOnline.js +12 -7
- package/cjs/utils/murmur3/legacy.js +44 -0
- package/cjs/utils/promise/timeout.js +1 -1
- package/esm/logger/messages/info.js +3 -3
- package/esm/sdkClient/client.js +2 -1
- package/esm/sdkClient/clientCS.js +2 -1
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/sync/streaming/AuthClient/index.js +1 -2
- package/esm/sync/streaming/pushManager.js +24 -21
- package/esm/sync/syncManagerOnline.js +12 -7
- package/esm/utils/murmur3/legacy.js +39 -0
- package/esm/utils/promise/timeout.js +1 -1
- package/package.json +2 -2
- package/src/logger/.DS_Store +0 -0
- package/src/logger/messages/info.ts +3 -3
- package/src/sdkClient/client.ts +2 -1
- package/src/sdkClient/clientCS.ts +3 -1
- package/src/services/splitHttpClient.ts +1 -1
- package/src/sync/streaming/AuthClient/index.ts +1 -2
- package/src/sync/streaming/pushManager.ts +24 -25
- package/src/sync/syncManagerOnline.ts +10 -6
- package/src/types.ts +5 -1
- package/src/utils/murmur3/legacy.ts +48 -0
- package/src/utils/promise/timeout.ts +1 -1
- package/types/integrations/ga/GaToSplitPlugin.d.ts +3 -0
- package/types/integrations/ga/SplitToGaPlugin.d.ts +4 -0
- package/types/logger/browser/{DebugLogger.d.ts → debugLogger.d.ts} +0 -0
- package/types/logger/browser/{ErrorLogger.d.ts → errorLogger.d.ts} +0 -0
- package/types/logger/browser/{InfoLogger.d.ts → infoLogger.d.ts} +0 -0
- package/types/logger/browser/{WarnLogger.d.ts → warnLogger.d.ts} +0 -0
- package/types/logger/codes.d.ts +2 -0
- package/types/logger/codesConstants.d.ts +117 -0
- package/types/logger/codesConstantsBrowser.d.ts +2 -0
- package/types/logger/codesConstantsNode.d.ts +14 -0
- package/types/logger/codesDebug.d.ts +1 -0
- package/types/logger/codesDebugBrowser.d.ts +1 -0
- package/types/logger/codesDebugNode.d.ts +1 -0
- package/types/logger/codesError.d.ts +1 -0
- package/types/logger/codesErrorNode.d.ts +1 -0
- package/types/logger/codesInfo.d.ts +1 -0
- package/types/logger/codesWarn.d.ts +1 -0
- package/types/logger/codesWarnNode.d.ts +1 -0
- package/types/logger/debugLogger.d.ts +2 -0
- package/types/logger/errorLogger.d.ts +2 -0
- package/types/logger/infoLogger.d.ts +2 -0
- package/types/logger/messages/debugBrowser.d.ts +1 -0
- package/types/logger/messages/debugNode.d.ts +1 -0
- package/types/logger/messages/errorNode.d.ts +1 -0
- package/types/logger/messages/warnNode.d.ts +1 -0
- package/types/logger/noopLogger.d.ts +2 -0
- package/types/logger/warnLogger.d.ts +2 -0
- package/types/sdkManager/sdkManagerMethod.d.ts +6 -0
- package/types/storages/getRegisteredSegments.d.ts +10 -0
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +51 -0
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +0 -0
- package/types/storages/pluggable/TelemetryCachePluggable.d.ts +2 -0
- package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +35 -0
- package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +35 -0
- package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
- package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
- package/types/sync/streaming/pushManagerCS.d.ts +12 -0
- package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
- package/types/sync/streaming/pushManagerSS.d.ts +11 -0
- package/types/sync/submitters/telemetrySyncTask.d.ts +17 -0
- package/types/sync/syncManagerFromFile.d.ts +2 -0
- package/types/sync/syncManagerFromObject.d.ts +2 -0
- package/types/sync/syncManagerOffline.d.ts +9 -0
- package/types/trackers/telemetryRecorder.d.ts +0 -0
- package/types/types.d.ts +1 -0
- package/types/utils/EventEmitter.d.ts +4 -0
- package/types/utils/lang/errors.d.ts +10 -0
- package/types/utils/murmur3/commons.d.ts +12 -0
- package/types/utils/murmur3/legacy.d.ts +2 -0
- package/types/utils/settingsValidation/buildMetadata.d.ts +3 -0
- package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
- package/types/utils/settingsValidation/logger.d.ts +11 -0
|
@@ -24,10 +24,10 @@ exports.codesInfo = warn_1.codesWarn.concat([
|
|
|
24
24
|
[c.SUBMITTERS_PUSH_FULL_EVENTS_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full events queue and reseting timer.'],
|
|
25
25
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s %s.'],
|
|
26
26
|
[c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
|
|
27
|
-
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect in %s seconds.'],
|
|
28
|
-
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting
|
|
27
|
+
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
|
|
28
|
+
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'],
|
|
29
29
|
[c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given Api key. Switching to polling mode.'],
|
|
30
|
-
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting
|
|
30
|
+
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting streaming.'],
|
|
31
31
|
[c.SYNC_START_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming not available. Starting polling.'],
|
|
32
32
|
[c.SYNC_CONTINUE_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming couldn\'t connect. Continue polling.'],
|
|
33
33
|
[c.SYNC_STOP_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming (re)connected. Syncing and stopping polling.'],
|
package/cjs/sdkClient/client.js
CHANGED
|
@@ -98,7 +98,8 @@ function clientFactory(params) {
|
|
|
98
98
|
getTreatmentWithConfig: getTreatmentWithConfig,
|
|
99
99
|
getTreatments: getTreatments,
|
|
100
100
|
getTreatmentsWithConfig: getTreatmentsWithConfig,
|
|
101
|
-
track: track
|
|
101
|
+
track: track,
|
|
102
|
+
isBrowserClient: false
|
|
102
103
|
};
|
|
103
104
|
}
|
|
104
105
|
exports.clientFactory = clientFactory;
|
|
@@ -19,7 +19,8 @@ function clientCSDecorator(log, client, key, trafficType) {
|
|
|
19
19
|
getTreatments: clientCS.getTreatments.bind(clientCS, key),
|
|
20
20
|
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
21
21
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
22
|
-
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key)
|
|
22
|
+
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
|
|
23
|
+
isBrowserClient: true
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
exports.clientCSDecorator = clientCSDecorator;
|
|
@@ -46,7 +46,7 @@ function splitHttpClientFactory(settings, getFetch, getOptions) {
|
|
|
46
46
|
return response;
|
|
47
47
|
})
|
|
48
48
|
.catch(function (error) {
|
|
49
|
-
var resp = error.response;
|
|
49
|
+
var resp = error && error.response;
|
|
50
50
|
var msg = '';
|
|
51
51
|
if (resp) { // An HTTP error
|
|
52
52
|
switch (resp.status) {
|
|
@@ -16,8 +16,7 @@ function authenticateFactory(fetchAuth) {
|
|
|
16
16
|
* @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
|
|
17
17
|
*/
|
|
18
18
|
return function authenticate(userKeys) {
|
|
19
|
-
|
|
20
|
-
return authPromise
|
|
19
|
+
return fetchAuth(userKeys)
|
|
21
20
|
.then(function (resp) { return resp.json(); })
|
|
22
21
|
.then(function (json) {
|
|
23
22
|
if (json.token) { // empty token when `"pushEnabled": false`
|
|
@@ -25,7 +25,7 @@ var murmur3_64_1 = require("../../utils/murmur3/murmur3_64");
|
|
|
25
25
|
function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platform, settings) {
|
|
26
26
|
// `userKey` is the matching key of main client in client-side SDK.
|
|
27
27
|
// It can be used to check if running on client-side or server-side SDK.
|
|
28
|
-
var userKey = settings.core.key ? key_1.getMatching(settings.core.key) : undefined;
|
|
28
|
+
var userKey = settings.core.key ? key_1.getMatching(settings.core.key) : undefined;
|
|
29
29
|
var log = settings.log;
|
|
30
30
|
var sseClient;
|
|
31
31
|
try {
|
|
@@ -42,7 +42,8 @@ function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platf
|
|
|
42
42
|
var sseHandler = SSEHandler_1.SSEHandlerFactory(log, pushEmitter);
|
|
43
43
|
sseClient.setEventHandler(sseHandler);
|
|
44
44
|
// init workers
|
|
45
|
-
|
|
45
|
+
// MySegmentsUpdateWorker (client-side) are initiated in `add` method
|
|
46
|
+
var segmentsUpdateWorker = userKey ? undefined : new SegmentsUpdateWorker_1.SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
|
|
46
47
|
// For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
47
48
|
var splitsUpdateWorker = new SplitsUpdateWorker_1.SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
|
|
48
49
|
// [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
|
|
@@ -50,11 +51,6 @@ function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platf
|
|
|
50
51
|
// [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
|
|
51
52
|
// Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
|
|
52
53
|
var clients = {};
|
|
53
|
-
if (userKey) {
|
|
54
|
-
var hash = AuthClient_1.hashUserKey(userKey);
|
|
55
|
-
userKeyHashes[hash] = userKey;
|
|
56
|
-
clients[userKey] = { hash64: murmur3_64_1.hash64(userKey), worker: segmentsUpdateWorker };
|
|
57
|
-
}
|
|
58
54
|
// [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
|
|
59
55
|
var connectForNewClient = false;
|
|
60
56
|
// flag that indicates if `stop/disconnectPush` was called, either by the SyncManager, when the client is destroyed, or due to a PUSH_NONRETRYABLE_ERROR error.
|
|
@@ -261,33 +257,40 @@ function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platf
|
|
|
261
257
|
return objectAssign_1.objectAssign(
|
|
262
258
|
// Expose Event Emitter functionality and Event constants
|
|
263
259
|
Object.create(pushEmitter), {
|
|
264
|
-
//
|
|
265
|
-
stop:
|
|
260
|
+
// Stop/pause push mode
|
|
261
|
+
stop: function () {
|
|
262
|
+
disconnectPush(); // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
|
|
263
|
+
if (userKey)
|
|
264
|
+
this.remove(userKey); // Necessary to properly resume streaming in client-side (e.g., RN SDK transition to foreground).
|
|
265
|
+
},
|
|
266
|
+
// Start/resume push mode
|
|
266
267
|
start: function () {
|
|
267
268
|
// Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
|
|
268
269
|
if (disabled || disconnected === false)
|
|
269
270
|
return;
|
|
270
271
|
disconnected = false;
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
if (userKey)
|
|
273
|
+
this.add(userKey, pollingManager.segmentsSyncTask); // client-side
|
|
274
|
+
else
|
|
275
|
+
setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
|
|
273
276
|
},
|
|
274
277
|
// [Only for client-side]
|
|
275
278
|
add: function (userKey, mySegmentsSyncTask) {
|
|
276
|
-
clients[userKey] = { hash64: murmur3_64_1.hash64(userKey), worker: new MySegmentsUpdateWorker_1.MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
277
279
|
var hash = AuthClient_1.hashUserKey(userKey);
|
|
278
280
|
if (!userKeyHashes[hash]) {
|
|
279
281
|
userKeyHashes[hash] = userKey;
|
|
282
|
+
clients[userKey] = { hash64: murmur3_64_1.hash64(userKey), worker: new MySegmentsUpdateWorker_1.MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
280
283
|
connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
|
|
284
|
+
// Reconnects in case of a new client.
|
|
285
|
+
// Run in next event-loop cycle to save authentication calls
|
|
286
|
+
// in case multiple clients are created in the current cycle.
|
|
287
|
+
setTimeout(function checkForReconnect() {
|
|
288
|
+
if (connectForNewClient) {
|
|
289
|
+
connectForNewClient = false;
|
|
290
|
+
connectPush();
|
|
291
|
+
}
|
|
292
|
+
}, 0);
|
|
281
293
|
}
|
|
282
|
-
// Reconnects in case of a new client.
|
|
283
|
-
// Run in next event-loop cycle to save authentication calls
|
|
284
|
-
// in case the user is creating several clients in the current cycle.
|
|
285
|
-
setTimeout(function checkForReconnect() {
|
|
286
|
-
if (connectForNewClient) {
|
|
287
|
-
connectForNewClient = false;
|
|
288
|
-
connectPush();
|
|
289
|
-
}
|
|
290
|
-
}, 0);
|
|
291
294
|
},
|
|
292
295
|
// [Only for client-side]
|
|
293
296
|
remove: function (userKey) {
|
|
@@ -30,12 +30,12 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
30
30
|
var submitter = submitterManager_1.submitterManagerFactory(settings, storage, splitApi);
|
|
31
31
|
/** Sync Manager logic */
|
|
32
32
|
function startPolling() {
|
|
33
|
-
if (
|
|
34
|
-
log.info(constants_2.
|
|
35
|
-
pollingManager.start();
|
|
33
|
+
if (pollingManager.isRunning()) {
|
|
34
|
+
log.info(constants_2.SYNC_CONTINUE_POLLING);
|
|
36
35
|
}
|
|
37
36
|
else {
|
|
38
|
-
log.info(constants_2.
|
|
37
|
+
log.info(constants_2.SYNC_START_POLLING);
|
|
38
|
+
pollingManager.start();
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
function stopPollingAndSyncAll() {
|
|
@@ -58,6 +58,9 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
58
58
|
* Method used to start the syncManager for the first time, or resume it after being stopped.
|
|
59
59
|
*/
|
|
60
60
|
start: function () {
|
|
61
|
+
if (running)
|
|
62
|
+
return;
|
|
63
|
+
running = true;
|
|
61
64
|
// start syncing splits and segments
|
|
62
65
|
if (pollingManager) {
|
|
63
66
|
if (pushManager) {
|
|
@@ -73,13 +76,16 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
73
76
|
}
|
|
74
77
|
}
|
|
75
78
|
// start periodic data recording (events, impressions, telemetry).
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
if (submitter)
|
|
80
|
+
submitter.start();
|
|
78
81
|
},
|
|
79
82
|
/**
|
|
80
83
|
* Method used to stop/pause the syncManager.
|
|
81
84
|
*/
|
|
82
85
|
stop: function () {
|
|
86
|
+
if (!running)
|
|
87
|
+
return;
|
|
88
|
+
running = false;
|
|
83
89
|
// stop syncing
|
|
84
90
|
if (pushManager)
|
|
85
91
|
pushManager.stop();
|
|
@@ -88,7 +94,6 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
88
94
|
// stop periodic data recording (events, impressions, telemetry).
|
|
89
95
|
if (submitter)
|
|
90
96
|
submitter.stop();
|
|
91
|
-
running = false;
|
|
92
97
|
},
|
|
93
98
|
isRunning: function () {
|
|
94
99
|
return running;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Deprecated hashing function, used for split bucketing. Replaced by murmur3
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.bucket = exports.hash = void 0;
|
|
5
|
+
//
|
|
6
|
+
// JAVA reference implementation for the hashing function.
|
|
7
|
+
//
|
|
8
|
+
// int h = 0;
|
|
9
|
+
// for (int i = 0; i < key.length(); i++) {
|
|
10
|
+
// h = 31 * h + key.charAt(i);
|
|
11
|
+
// }
|
|
12
|
+
// return h ^ seed; // XOR the hash and seed
|
|
13
|
+
//
|
|
14
|
+
function ToInteger(x) {
|
|
15
|
+
x = Number(x);
|
|
16
|
+
return x < 0 ? Math.ceil(x) : Math.floor(x);
|
|
17
|
+
}
|
|
18
|
+
function modulo(a, b) {
|
|
19
|
+
return a - Math.floor(a / b) * b;
|
|
20
|
+
}
|
|
21
|
+
function ToUint32(x) {
|
|
22
|
+
return modulo(ToInteger(x), Math.pow(2, 32));
|
|
23
|
+
}
|
|
24
|
+
function ToInt32(x) {
|
|
25
|
+
var uint32 = ToUint32(x);
|
|
26
|
+
if (uint32 >= Math.pow(2, 31)) {
|
|
27
|
+
return uint32 - Math.pow(2, 32);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return uint32;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function hash(str, seed) {
|
|
34
|
+
var h = 0;
|
|
35
|
+
for (var i = 0; i < str.length; i++) {
|
|
36
|
+
h = ToInt32(ToInt32(31 * h) + str.charCodeAt(i));
|
|
37
|
+
}
|
|
38
|
+
return ToInt32(h ^ seed);
|
|
39
|
+
}
|
|
40
|
+
exports.hash = hash;
|
|
41
|
+
function bucket(str, seed) {
|
|
42
|
+
return Math.abs(hash(str, seed) % 100) + 1;
|
|
43
|
+
}
|
|
44
|
+
exports.bucket = bucket;
|
|
@@ -6,7 +6,7 @@ function timeout(ms, promise) {
|
|
|
6
6
|
return promise;
|
|
7
7
|
return new Promise(function (resolve, reject) {
|
|
8
8
|
var tid = setTimeout(function () {
|
|
9
|
-
reject(new Error("Operation timed out because it exceeded the configured time limit of " + ms + "ms."));
|
|
9
|
+
reject(new Error("Operation timed out because it exceeded the configured time limit of " + ms + " ms."));
|
|
10
10
|
}, ms);
|
|
11
11
|
promise.then(function (res) {
|
|
12
12
|
clearTimeout(tid);
|
|
@@ -20,10 +20,10 @@ export var codesInfo = codesWarn.concat([
|
|
|
20
20
|
[c.SUBMITTERS_PUSH_FULL_EVENTS_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full events queue and reseting timer.'],
|
|
21
21
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s %s.'],
|
|
22
22
|
[c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
|
|
23
|
-
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect in %s seconds.'],
|
|
24
|
-
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting
|
|
23
|
+
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
|
|
24
|
+
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'],
|
|
25
25
|
[c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given Api key. Switching to polling mode.'],
|
|
26
|
-
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting
|
|
26
|
+
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting streaming.'],
|
|
27
27
|
[c.SYNC_START_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming not available. Starting polling.'],
|
|
28
28
|
[c.SYNC_CONTINUE_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming couldn\'t connect. Continue polling.'],
|
|
29
29
|
[c.SYNC_STOP_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming (re)connected. Syncing and stopping polling.'],
|
package/esm/sdkClient/client.js
CHANGED
|
@@ -16,6 +16,7 @@ export function clientCSDecorator(log, client, key, trafficType) {
|
|
|
16
16
|
getTreatments: clientCS.getTreatments.bind(clientCS, key),
|
|
17
17
|
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
18
18
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
19
|
-
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key)
|
|
19
|
+
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
|
|
20
|
+
isBrowserClient: true
|
|
20
21
|
});
|
|
21
22
|
}
|
|
@@ -43,7 +43,7 @@ export function splitHttpClientFactory(settings, getFetch, getOptions) {
|
|
|
43
43
|
return response;
|
|
44
44
|
})
|
|
45
45
|
.catch(function (error) {
|
|
46
|
-
var resp = error.response;
|
|
46
|
+
var resp = error && error.response;
|
|
47
47
|
var msg = '';
|
|
48
48
|
if (resp) { // An HTTP error
|
|
49
49
|
switch (resp.status) {
|
|
@@ -13,8 +13,7 @@ export function authenticateFactory(fetchAuth) {
|
|
|
13
13
|
* @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
|
|
14
14
|
*/
|
|
15
15
|
return function authenticate(userKeys) {
|
|
16
|
-
|
|
17
|
-
return authPromise
|
|
16
|
+
return fetchAuth(userKeys)
|
|
18
17
|
.then(function (resp) { return resp.json(); })
|
|
19
18
|
.then(function (json) {
|
|
20
19
|
if (json.token) { // empty token when `"pushEnabled": false`
|
|
@@ -22,7 +22,7 @@ import { hash64 } from '../../utils/murmur3/murmur3_64';
|
|
|
22
22
|
export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platform, settings) {
|
|
23
23
|
// `userKey` is the matching key of main client in client-side SDK.
|
|
24
24
|
// It can be used to check if running on client-side or server-side SDK.
|
|
25
|
-
var userKey = settings.core.key ? getMatching(settings.core.key) : undefined;
|
|
25
|
+
var userKey = settings.core.key ? getMatching(settings.core.key) : undefined;
|
|
26
26
|
var log = settings.log;
|
|
27
27
|
var sseClient;
|
|
28
28
|
try {
|
|
@@ -39,7 +39,8 @@ export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth
|
|
|
39
39
|
var sseHandler = SSEHandlerFactory(log, pushEmitter);
|
|
40
40
|
sseClient.setEventHandler(sseHandler);
|
|
41
41
|
// init workers
|
|
42
|
-
|
|
42
|
+
// MySegmentsUpdateWorker (client-side) are initiated in `add` method
|
|
43
|
+
var segmentsUpdateWorker = userKey ? undefined : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
|
|
43
44
|
// For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
44
45
|
var splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
|
|
45
46
|
// [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
|
|
@@ -47,11 +48,6 @@ export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth
|
|
|
47
48
|
// [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
|
|
48
49
|
// Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
|
|
49
50
|
var clients = {};
|
|
50
|
-
if (userKey) {
|
|
51
|
-
var hash = hashUserKey(userKey);
|
|
52
|
-
userKeyHashes[hash] = userKey;
|
|
53
|
-
clients[userKey] = { hash64: hash64(userKey), worker: segmentsUpdateWorker };
|
|
54
|
-
}
|
|
55
51
|
// [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
|
|
56
52
|
var connectForNewClient = false;
|
|
57
53
|
// flag that indicates if `stop/disconnectPush` was called, either by the SyncManager, when the client is destroyed, or due to a PUSH_NONRETRYABLE_ERROR error.
|
|
@@ -258,33 +254,40 @@ export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth
|
|
|
258
254
|
return objectAssign(
|
|
259
255
|
// Expose Event Emitter functionality and Event constants
|
|
260
256
|
Object.create(pushEmitter), {
|
|
261
|
-
//
|
|
262
|
-
stop:
|
|
257
|
+
// Stop/pause push mode
|
|
258
|
+
stop: function () {
|
|
259
|
+
disconnectPush(); // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
|
|
260
|
+
if (userKey)
|
|
261
|
+
this.remove(userKey); // Necessary to properly resume streaming in client-side (e.g., RN SDK transition to foreground).
|
|
262
|
+
},
|
|
263
|
+
// Start/resume push mode
|
|
263
264
|
start: function () {
|
|
264
265
|
// Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
|
|
265
266
|
if (disabled || disconnected === false)
|
|
266
267
|
return;
|
|
267
268
|
disconnected = false;
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
if (userKey)
|
|
270
|
+
this.add(userKey, pollingManager.segmentsSyncTask); // client-side
|
|
271
|
+
else
|
|
272
|
+
setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
|
|
270
273
|
},
|
|
271
274
|
// [Only for client-side]
|
|
272
275
|
add: function (userKey, mySegmentsSyncTask) {
|
|
273
|
-
clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
274
276
|
var hash = hashUserKey(userKey);
|
|
275
277
|
if (!userKeyHashes[hash]) {
|
|
276
278
|
userKeyHashes[hash] = userKey;
|
|
279
|
+
clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
|
|
277
280
|
connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
|
|
281
|
+
// Reconnects in case of a new client.
|
|
282
|
+
// Run in next event-loop cycle to save authentication calls
|
|
283
|
+
// in case multiple clients are created in the current cycle.
|
|
284
|
+
setTimeout(function checkForReconnect() {
|
|
285
|
+
if (connectForNewClient) {
|
|
286
|
+
connectForNewClient = false;
|
|
287
|
+
connectPush();
|
|
288
|
+
}
|
|
289
|
+
}, 0);
|
|
278
290
|
}
|
|
279
|
-
// Reconnects in case of a new client.
|
|
280
|
-
// Run in next event-loop cycle to save authentication calls
|
|
281
|
-
// in case the user is creating several clients in the current cycle.
|
|
282
|
-
setTimeout(function checkForReconnect() {
|
|
283
|
-
if (connectForNewClient) {
|
|
284
|
-
connectForNewClient = false;
|
|
285
|
-
connectPush();
|
|
286
|
-
}
|
|
287
|
-
}, 0);
|
|
288
291
|
},
|
|
289
292
|
// [Only for client-side]
|
|
290
293
|
remove: function (userKey) {
|
|
@@ -27,12 +27,12 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
27
27
|
var submitter = submitterManagerFactory(settings, storage, splitApi);
|
|
28
28
|
/** Sync Manager logic */
|
|
29
29
|
function startPolling() {
|
|
30
|
-
if (
|
|
31
|
-
log.info(
|
|
32
|
-
pollingManager.start();
|
|
30
|
+
if (pollingManager.isRunning()) {
|
|
31
|
+
log.info(SYNC_CONTINUE_POLLING);
|
|
33
32
|
}
|
|
34
33
|
else {
|
|
35
|
-
log.info(
|
|
34
|
+
log.info(SYNC_START_POLLING);
|
|
35
|
+
pollingManager.start();
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
function stopPollingAndSyncAll() {
|
|
@@ -55,6 +55,9 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
55
55
|
* Method used to start the syncManager for the first time, or resume it after being stopped.
|
|
56
56
|
*/
|
|
57
57
|
start: function () {
|
|
58
|
+
if (running)
|
|
59
|
+
return;
|
|
60
|
+
running = true;
|
|
58
61
|
// start syncing splits and segments
|
|
59
62
|
if (pollingManager) {
|
|
60
63
|
if (pushManager) {
|
|
@@ -70,13 +73,16 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
// start periodic data recording (events, impressions, telemetry).
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
if (submitter)
|
|
77
|
+
submitter.start();
|
|
75
78
|
},
|
|
76
79
|
/**
|
|
77
80
|
* Method used to stop/pause the syncManager.
|
|
78
81
|
*/
|
|
79
82
|
stop: function () {
|
|
83
|
+
if (!running)
|
|
84
|
+
return;
|
|
85
|
+
running = false;
|
|
80
86
|
// stop syncing
|
|
81
87
|
if (pushManager)
|
|
82
88
|
pushManager.stop();
|
|
@@ -85,7 +91,6 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
85
91
|
// stop periodic data recording (events, impressions, telemetry).
|
|
86
92
|
if (submitter)
|
|
87
93
|
submitter.stop();
|
|
88
|
-
running = false;
|
|
89
94
|
},
|
|
90
95
|
isRunning: function () {
|
|
91
96
|
return running;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Deprecated hashing function, used for split bucketing. Replaced by murmur3
|
|
2
|
+
//
|
|
3
|
+
// JAVA reference implementation for the hashing function.
|
|
4
|
+
//
|
|
5
|
+
// int h = 0;
|
|
6
|
+
// for (int i = 0; i < key.length(); i++) {
|
|
7
|
+
// h = 31 * h + key.charAt(i);
|
|
8
|
+
// }
|
|
9
|
+
// return h ^ seed; // XOR the hash and seed
|
|
10
|
+
//
|
|
11
|
+
function ToInteger(x) {
|
|
12
|
+
x = Number(x);
|
|
13
|
+
return x < 0 ? Math.ceil(x) : Math.floor(x);
|
|
14
|
+
}
|
|
15
|
+
function modulo(a, b) {
|
|
16
|
+
return a - Math.floor(a / b) * b;
|
|
17
|
+
}
|
|
18
|
+
function ToUint32(x) {
|
|
19
|
+
return modulo(ToInteger(x), Math.pow(2, 32));
|
|
20
|
+
}
|
|
21
|
+
function ToInt32(x) {
|
|
22
|
+
var uint32 = ToUint32(x);
|
|
23
|
+
if (uint32 >= Math.pow(2, 31)) {
|
|
24
|
+
return uint32 - Math.pow(2, 32);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return uint32;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function hash(str, seed) {
|
|
31
|
+
var h = 0;
|
|
32
|
+
for (var i = 0; i < str.length; i++) {
|
|
33
|
+
h = ToInt32(ToInt32(31 * h) + str.charCodeAt(i));
|
|
34
|
+
}
|
|
35
|
+
return ToInt32(h ^ seed);
|
|
36
|
+
}
|
|
37
|
+
export function bucket(str, seed) {
|
|
38
|
+
return Math.abs(hash(str, seed) % 100) + 1;
|
|
39
|
+
}
|
|
@@ -3,7 +3,7 @@ export function timeout(ms, promise) {
|
|
|
3
3
|
return promise;
|
|
4
4
|
return new Promise(function (resolve, reject) {
|
|
5
5
|
var tid = setTimeout(function () {
|
|
6
|
-
reject(new Error("Operation timed out because it exceeded the configured time limit of " + ms + "ms."));
|
|
6
|
+
reject(new Error("Operation timed out because it exceeded the configured time limit of " + ms + " ms."));
|
|
7
7
|
}, ms);
|
|
8
8
|
promise.then(function (res) {
|
|
9
9
|
clearTimeout(tid);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.2.0",
|
|
3
|
+
"version": "1.2.1-rc.0",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"jest-localstorage-mock": "^2.4.3",
|
|
65
65
|
"js-yaml": "^3.14.0",
|
|
66
66
|
"lodash": "^4.17.21",
|
|
67
|
-
"node-fetch": "^2.6.
|
|
67
|
+
"node-fetch": "^2.6.7",
|
|
68
68
|
"redis-server": "1.2.2",
|
|
69
69
|
"rimraf": "^3.0.2",
|
|
70
70
|
"ts-jest": "^27.0.5",
|
|
Binary file
|
|
@@ -23,10 +23,10 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
23
23
|
[c.SUBMITTERS_PUSH_FULL_EVENTS_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full events queue and reseting timer.'],
|
|
24
24
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s %s.'],
|
|
25
25
|
[c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
|
|
26
|
-
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect in %s seconds.'],
|
|
27
|
-
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting
|
|
26
|
+
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
|
|
27
|
+
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'],
|
|
28
28
|
[c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given Api key. Switching to polling mode.'],
|
|
29
|
-
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting
|
|
29
|
+
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting streaming.'],
|
|
30
30
|
[c.SYNC_START_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming not available. Starting polling.'],
|
|
31
31
|
[c.SYNC_CONTINUE_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming couldn\'t connect. Continue polling.'],
|
|
32
32
|
[c.SYNC_STOP_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming (re)connected. Syncing and stopping polling.'],
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -123,6 +123,7 @@ export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | S
|
|
|
123
123
|
getTreatmentWithConfig,
|
|
124
124
|
getTreatments,
|
|
125
125
|
getTreatmentsWithConfig,
|
|
126
|
-
track
|
|
126
|
+
track,
|
|
127
|
+
isBrowserClient: false
|
|
127
128
|
} as SplitIO.IClient | SplitIO.IAsyncClient;
|
|
128
129
|
}
|
|
@@ -23,6 +23,8 @@ export function clientCSDecorator(log: ILogger, client: SplitIO.IClient, key: Sp
|
|
|
23
23
|
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
24
24
|
|
|
25
25
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
26
|
-
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key)
|
|
26
|
+
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
|
|
27
|
+
|
|
28
|
+
isBrowserClient: true
|
|
27
29
|
}) as SplitIO.ICsClient;
|
|
28
30
|
}
|
|
@@ -17,8 +17,7 @@ export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
|
|
|
17
17
|
* @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
|
|
18
18
|
*/
|
|
19
19
|
return function authenticate(userKeys?: string[]): Promise<IAuthToken> {
|
|
20
|
-
|
|
21
|
-
return authPromise
|
|
20
|
+
return fetchAuth(userKeys)
|
|
22
21
|
.then(resp => resp.json())
|
|
23
22
|
.then(json => {
|
|
24
23
|
if (json.token) { // empty token when `"pushEnabled": false`
|