@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.
Files changed (82) hide show
  1. package/cjs/logger/messages/info.js +3 -3
  2. package/cjs/sdkClient/client.js +2 -1
  3. package/cjs/sdkClient/clientCS.js +2 -1
  4. package/cjs/services/splitHttpClient.js +1 -1
  5. package/cjs/sync/streaming/AuthClient/index.js +1 -2
  6. package/cjs/sync/streaming/pushManager.js +24 -21
  7. package/cjs/sync/syncManagerOnline.js +12 -7
  8. package/cjs/utils/murmur3/legacy.js +44 -0
  9. package/cjs/utils/promise/timeout.js +1 -1
  10. package/esm/logger/messages/info.js +3 -3
  11. package/esm/sdkClient/client.js +2 -1
  12. package/esm/sdkClient/clientCS.js +2 -1
  13. package/esm/services/splitHttpClient.js +1 -1
  14. package/esm/sync/streaming/AuthClient/index.js +1 -2
  15. package/esm/sync/streaming/pushManager.js +24 -21
  16. package/esm/sync/syncManagerOnline.js +12 -7
  17. package/esm/utils/murmur3/legacy.js +39 -0
  18. package/esm/utils/promise/timeout.js +1 -1
  19. package/package.json +2 -2
  20. package/src/logger/.DS_Store +0 -0
  21. package/src/logger/messages/info.ts +3 -3
  22. package/src/sdkClient/client.ts +2 -1
  23. package/src/sdkClient/clientCS.ts +3 -1
  24. package/src/services/splitHttpClient.ts +1 -1
  25. package/src/sync/streaming/AuthClient/index.ts +1 -2
  26. package/src/sync/streaming/pushManager.ts +24 -25
  27. package/src/sync/syncManagerOnline.ts +10 -6
  28. package/src/types.ts +5 -1
  29. package/src/utils/murmur3/legacy.ts +48 -0
  30. package/src/utils/promise/timeout.ts +1 -1
  31. package/types/integrations/ga/GaToSplitPlugin.d.ts +3 -0
  32. package/types/integrations/ga/SplitToGaPlugin.d.ts +4 -0
  33. package/types/logger/browser/{DebugLogger.d.ts → debugLogger.d.ts} +0 -0
  34. package/types/logger/browser/{ErrorLogger.d.ts → errorLogger.d.ts} +0 -0
  35. package/types/logger/browser/{InfoLogger.d.ts → infoLogger.d.ts} +0 -0
  36. package/types/logger/browser/{WarnLogger.d.ts → warnLogger.d.ts} +0 -0
  37. package/types/logger/codes.d.ts +2 -0
  38. package/types/logger/codesConstants.d.ts +117 -0
  39. package/types/logger/codesConstantsBrowser.d.ts +2 -0
  40. package/types/logger/codesConstantsNode.d.ts +14 -0
  41. package/types/logger/codesDebug.d.ts +1 -0
  42. package/types/logger/codesDebugBrowser.d.ts +1 -0
  43. package/types/logger/codesDebugNode.d.ts +1 -0
  44. package/types/logger/codesError.d.ts +1 -0
  45. package/types/logger/codesErrorNode.d.ts +1 -0
  46. package/types/logger/codesInfo.d.ts +1 -0
  47. package/types/logger/codesWarn.d.ts +1 -0
  48. package/types/logger/codesWarnNode.d.ts +1 -0
  49. package/types/logger/debugLogger.d.ts +2 -0
  50. package/types/logger/errorLogger.d.ts +2 -0
  51. package/types/logger/infoLogger.d.ts +2 -0
  52. package/types/logger/messages/debugBrowser.d.ts +1 -0
  53. package/types/logger/messages/debugNode.d.ts +1 -0
  54. package/types/logger/messages/errorNode.d.ts +1 -0
  55. package/types/logger/messages/warnNode.d.ts +1 -0
  56. package/types/logger/noopLogger.d.ts +2 -0
  57. package/types/logger/warnLogger.d.ts +2 -0
  58. package/types/sdkManager/sdkManagerMethod.d.ts +6 -0
  59. package/types/storages/getRegisteredSegments.d.ts +10 -0
  60. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +51 -0
  61. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +0 -0
  62. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +2 -0
  63. package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +35 -0
  64. package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +35 -0
  65. package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
  66. package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
  67. package/types/sync/streaming/pushManagerCS.d.ts +12 -0
  68. package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
  69. package/types/sync/streaming/pushManagerSS.d.ts +11 -0
  70. package/types/sync/submitters/telemetrySyncTask.d.ts +17 -0
  71. package/types/sync/syncManagerFromFile.d.ts +2 -0
  72. package/types/sync/syncManagerFromObject.d.ts +2 -0
  73. package/types/sync/syncManagerOffline.d.ts +9 -0
  74. package/types/trackers/telemetryRecorder.d.ts +0 -0
  75. package/types/types.d.ts +1 -0
  76. package/types/utils/EventEmitter.d.ts +4 -0
  77. package/types/utils/lang/errors.d.ts +10 -0
  78. package/types/utils/murmur3/commons.d.ts +12 -0
  79. package/types/utils/murmur3/legacy.d.ts +2 -0
  80. package/types/utils/settingsValidation/buildMetadata.d.ts +3 -0
  81. package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
  82. 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 to streaming.'],
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 from streaming.'],
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.'],
@@ -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
- var authPromise = fetchAuth(userKeys); // errors handled by fetchAuth service
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
- var segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker_1.MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker_1.SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
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
- // Expose functionality for starting and stoping push mode:
265
- stop: disconnectPush,
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
- // Run in next event-loop cycle for optimization on client-side: if multiple clients are created in the same cycle than the factory, only one authentication is performed.
272
- setTimeout(connectPush);
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 (!pollingManager.isRunning()) {
34
- log.info(constants_2.SYNC_START_POLLING);
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.SYNC_CONTINUE_POLLING);
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
- submitter && submitter.start();
77
- running = true;
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 to streaming.'],
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 from streaming.'],
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.'],
@@ -95,6 +95,7 @@ export function clientFactory(params) {
95
95
  getTreatmentWithConfig: getTreatmentWithConfig,
96
96
  getTreatments: getTreatments,
97
97
  getTreatmentsWithConfig: getTreatmentsWithConfig,
98
- track: track
98
+ track: track,
99
+ isBrowserClient: false
99
100
  };
100
101
  }
@@ -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
- var authPromise = fetchAuth(userKeys); // errors handled by fetchAuth service
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
- var segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
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
- // Expose functionality for starting and stoping push mode:
262
- stop: disconnectPush,
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
- // Run in next event-loop cycle for optimization on client-side: if multiple clients are created in the same cycle than the factory, only one authentication is performed.
269
- setTimeout(connectPush);
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 (!pollingManager.isRunning()) {
31
- log.info(SYNC_START_POLLING);
32
- pollingManager.start();
30
+ if (pollingManager.isRunning()) {
31
+ log.info(SYNC_CONTINUE_POLLING);
33
32
  }
34
33
  else {
35
- log.info(SYNC_CONTINUE_POLLING);
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
- submitter && submitter.start();
74
- running = true;
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.1",
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 to streaming.'],
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 from streaming.'],
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.'],
@@ -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
  }
@@ -49,7 +49,7 @@ export function splitHttpClientFactory(settings: Pick<ISettings, 'log' | 'versio
49
49
  return response;
50
50
  })
51
51
  .catch(error => {
52
- const resp = error.response;
52
+ const resp = error && error.response;
53
53
  let msg = '';
54
54
 
55
55
  if (resp) { // An HTTP error
@@ -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
- let authPromise = fetchAuth(userKeys); // errors handled by fetchAuth service
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`