@splitsoftware/splitio-commons 1.1.0 → 1.2.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGES.txt +3 -0
  2. package/cjs/evaluator/matchers/ew.js +3 -3
  3. package/cjs/logger/messages/info.js +3 -3
  4. package/cjs/sdkClient/client.js +2 -1
  5. package/cjs/sdkClient/clientAttributesDecoration.js +108 -0
  6. package/cjs/sdkClient/clientCS.js +10 -7
  7. package/cjs/sdkClient/sdkClientMethodCS.js +2 -2
  8. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +2 -2
  9. package/cjs/services/splitHttpClient.js +1 -1
  10. package/cjs/storages/inMemory/AttributesCacheInMemory.js +70 -0
  11. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
  12. package/cjs/sync/polling/pollingManagerCS.js +2 -1
  13. package/cjs/sync/polling/pollingManagerSS.js +2 -1
  14. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +1 -1
  15. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  16. package/cjs/sync/streaming/AuthClient/index.js +1 -2
  17. package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +1 -1
  18. package/cjs/sync/streaming/pushManager.js +27 -23
  19. package/cjs/sync/submitters/submitterManager.js +2 -1
  20. package/cjs/sync/syncManagerOnline.js +12 -12
  21. package/cjs/utils/inputValidation/attribute.js +20 -0
  22. package/cjs/utils/inputValidation/attributes.js +13 -1
  23. package/cjs/utils/inputValidation/eventProperties.js +3 -1
  24. package/cjs/utils/lang/index.js +1 -13
  25. package/cjs/utils/murmur3/legacy.js +44 -0
  26. package/cjs/utils/promise/timeout.js +1 -1
  27. package/esm/evaluator/matchers/ew.js +4 -4
  28. package/esm/logger/messages/info.js +3 -3
  29. package/esm/sdkClient/client.js +2 -1
  30. package/esm/sdkClient/clientAttributesDecoration.js +104 -0
  31. package/esm/sdkClient/clientCS.js +10 -7
  32. package/esm/sdkClient/sdkClientMethodCS.js +2 -2
  33. package/esm/sdkClient/sdkClientMethodCSWithTT.js +2 -2
  34. package/esm/services/splitHttpClient.js +1 -1
  35. package/esm/storages/inMemory/AttributesCacheInMemory.js +67 -0
  36. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
  37. package/esm/sync/polling/pollingManagerCS.js +2 -1
  38. package/esm/sync/polling/pollingManagerSS.js +2 -1
  39. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +1 -1
  40. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  41. package/esm/sync/streaming/AuthClient/index.js +1 -2
  42. package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +1 -1
  43. package/esm/sync/streaming/pushManager.js +27 -23
  44. package/esm/sync/submitters/submitterManager.js +2 -1
  45. package/esm/sync/syncManagerOnline.js +12 -12
  46. package/esm/utils/inputValidation/attribute.js +16 -0
  47. package/esm/utils/inputValidation/attributes.js +11 -0
  48. package/esm/utils/inputValidation/eventProperties.js +4 -2
  49. package/esm/utils/lang/index.js +0 -11
  50. package/esm/utils/murmur3/legacy.js +39 -0
  51. package/esm/utils/promise/timeout.js +1 -1
  52. package/package.json +4 -4
  53. package/src/evaluator/matchers/ew.ts +4 -4
  54. package/src/logger/messages/info.ts +3 -3
  55. package/src/sdkClient/client.ts +2 -1
  56. package/src/sdkClient/clientAttributesDecoration.ts +122 -0
  57. package/src/sdkClient/clientCS.ts +14 -7
  58. package/src/sdkClient/sdkClientMethodCS.ts +2 -0
  59. package/src/sdkClient/sdkClientMethodCSWithTT.ts +2 -0
  60. package/src/services/splitHttpClient.ts +1 -1
  61. package/src/storages/inMemory/AttributesCacheInMemory.ts +73 -0
  62. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +2 -1
  63. package/src/sync/polling/fetchers/types.ts +1 -0
  64. package/src/sync/polling/pollingManagerCS.ts +3 -6
  65. package/src/sync/polling/pollingManagerSS.ts +3 -8
  66. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +2 -1
  67. package/src/sync/polling/types.ts +0 -12
  68. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -1
  69. package/src/sync/streaming/AuthClient/index.ts +1 -2
  70. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
  71. package/src/sync/streaming/pushManager.ts +31 -38
  72. package/src/sync/streaming/types.ts +5 -25
  73. package/src/sync/submitters/submitterManager.ts +4 -8
  74. package/src/sync/syncManagerOnline.ts +16 -22
  75. package/src/types.ts +43 -0
  76. package/src/utils/inputValidation/attribute.ts +21 -0
  77. package/src/utils/inputValidation/attributes.ts +14 -0
  78. package/src/utils/inputValidation/eventProperties.ts +4 -2
  79. package/src/utils/lang/index.ts +0 -14
  80. package/src/utils/murmur3/legacy.ts +48 -0
  81. package/src/utils/promise/timeout.ts +1 -1
  82. package/types/sdkClient/clientAttributesDecoration.d.ts +51 -0
  83. package/types/sdkClient/clientCS.d.ts +2 -1
  84. package/types/storages/inMemory/AttributesCacheInMemory.d.ts +43 -0
  85. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +51 -0
  86. package/types/storages/inMemory/index.d.ts +10 -0
  87. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +0 -0
  88. package/types/storages/parseSegments.d.ts +6 -0
  89. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +2 -0
  90. package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +1 -1
  91. package/types/sync/polling/fetchers/types.d.ts +1 -1
  92. package/types/sync/polling/pollingManagerCS.d.ts +2 -5
  93. package/types/sync/polling/pollingManagerSS.d.ts +2 -5
  94. package/types/sync/polling/types.d.ts +0 -11
  95. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +1 -1
  96. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +1 -1
  97. package/types/sync/streaming/pushManager.d.ts +3 -7
  98. package/types/sync/streaming/pushManagerCS.d.ts +1 -12
  99. package/types/sync/streaming/pushManagerSS.d.ts +1 -11
  100. package/types/sync/streaming/types.d.ts +3 -23
  101. package/types/sync/submitters/submitterManager.d.ts +2 -4
  102. package/types/sync/submitters/telemetrySyncTask.d.ts +17 -0
  103. package/types/sync/syncManagerOnline.d.ts +3 -3
  104. package/types/trackers/telemetryRecorder.d.ts +0 -0
  105. package/types/types.d.ts +40 -0
  106. package/types/utils/EventEmitter.d.ts +4 -0
  107. package/types/utils/inputValidation/attribute.d.ts +2 -0
  108. package/types/utils/inputValidation/attributes.d.ts +1 -0
  109. package/types/utils/lang/index.d.ts +0 -4
  110. package/types/utils/murmur3/legacy.d.ts +2 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uniqueId = exports.uniqAsStrings = exports.uniq = exports.toString = exports.toNumber = exports.startsWith = exports.shallowClone = exports.merge = exports.isString = exports.isObject = exports.isNaNNumber = exports.isIntegerNumber = exports.isFiniteNumber = exports.isBoolean = exports.groupBy = exports.get = exports.forOwn = exports.findIndex = exports.find = exports.endsWith = void 0;
3
+ exports.uniqueId = exports.uniqAsStrings = exports.uniq = exports.toString = exports.toNumber = exports.startsWith = exports.merge = exports.isString = exports.isObject = exports.isNaNNumber = exports.isIntegerNumber = exports.isFiniteNumber = exports.isBoolean = exports.groupBy = exports.get = exports.forOwn = exports.findIndex = exports.find = exports.endsWith = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  /**
6
6
  * Checks if the target string ends with the sub string.
@@ -199,18 +199,6 @@ function merge(target, source) {
199
199
  return res;
200
200
  }
201
201
  exports.merge = merge;
202
- /**
203
- * Shallow clone an object
204
- */
205
- function shallowClone(obj) {
206
- var keys = Object.keys(obj);
207
- var output = {};
208
- for (var i = 0; i < keys.length; i++) {
209
- output[keys[i]] = obj[keys[i]];
210
- }
211
- return output;
212
- }
213
- exports.shallowClone = shallowClone;
214
202
  /**
215
203
  * Checks if the target string starts with the sub string.
216
204
  */
@@ -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);
@@ -1,9 +1,9 @@
1
1
  import { ENGINE_MATCHER_ENDS_WITH } from '../../logger/constants';
2
- import { endsWith as strEndsWith } from '../../utils/lang';
2
+ import { endsWith } from '../../utils/lang';
3
3
  export function endsWithMatcherContext(log, ruleAttr) {
4
4
  return function endsWithMatcher(runtimeAttr) {
5
- var endsWith = ruleAttr.some(function (e) { return strEndsWith(runtimeAttr, e); });
6
- log.debug(ENGINE_MATCHER_ENDS_WITH, [runtimeAttr, ruleAttr, endsWith]);
7
- return endsWith;
5
+ var strEndsWith = ruleAttr.some(function (e) { return endsWith(runtimeAttr, e); });
6
+ log.debug(ENGINE_MATCHER_ENDS_WITH, [runtimeAttr, ruleAttr, strEndsWith]);
7
+ return strEndsWith;
8
8
  };
9
9
  }
@@ -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
  }
@@ -0,0 +1,104 @@
1
+ import { AttributesCacheInMemory } from '../storages/inMemory/AttributesCacheInMemory';
2
+ import { validateAttributesDeep } from '../utils/inputValidation/attributes';
3
+ import { objectAssign } from '../utils/lang/objectAssign';
4
+ /**
5
+ * Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
6
+ */
7
+ export function clientAttributesDecoration(log, client) {
8
+ var attributeStorage = new AttributesCacheInMemory();
9
+ // Keep a reference to the original methods
10
+ var clientGetTreatment = client.getTreatment;
11
+ var clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
12
+ var clientGetTreatments = client.getTreatments;
13
+ var clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
14
+ var clientTrack = client.track;
15
+ function getTreatment(maybeKey, maybeSplit, maybeAttributes) {
16
+ return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
17
+ }
18
+ function getTreatmentWithConfig(maybeKey, maybeSplit, maybeAttributes) {
19
+ return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
20
+ }
21
+ function getTreatments(maybeKey, maybeSplits, maybeAttributes) {
22
+ return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
23
+ }
24
+ function getTreatmentsWithConfig(maybeKey, maybeSplits, maybeAttributes) {
25
+ return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
26
+ }
27
+ function track(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties) {
28
+ return clientTrack(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties);
29
+ }
30
+ function combineAttributes(maybeAttributes) {
31
+ var storedAttributes = attributeStorage.getAll();
32
+ if (Object.keys(storedAttributes).length > 0) {
33
+ return objectAssign({}, storedAttributes, maybeAttributes);
34
+ }
35
+ return maybeAttributes;
36
+ }
37
+ return objectAssign(client, {
38
+ getTreatment: getTreatment,
39
+ getTreatmentWithConfig: getTreatmentWithConfig,
40
+ getTreatments: getTreatments,
41
+ getTreatmentsWithConfig: getTreatmentsWithConfig,
42
+ track: track,
43
+ /**
44
+ * Add an attribute to client's in memory attributes storage
45
+ *
46
+ * @param {string} attributeName Attrinute name
47
+ * @param {string, number, boolean, list} attributeValue Attribute value
48
+ * @returns {boolean} true if the attribute was stored and false otherways
49
+ */
50
+ setAttribute: function (attributeName, attributeValue) {
51
+ var attribute = {};
52
+ attribute[attributeName] = attributeValue;
53
+ if (!validateAttributesDeep(log, attribute, 'setAttribute'))
54
+ return false;
55
+ log.debug("stored " + attributeValue + " for attribute " + attributeName);
56
+ return attributeStorage.setAttribute(attributeName, attributeValue);
57
+ },
58
+ /**
59
+ * Returns the attribute with the given key
60
+ *
61
+ * @param {string} attributeName Attribute name
62
+ * @returns {Object} Attribute with the given key
63
+ */
64
+ getAttribute: function (attributeName) {
65
+ log.debug("retrieved attribute " + attributeName);
66
+ return attributeStorage.getAttribute(attributeName + '');
67
+ },
68
+ /**
69
+ * Add to client's in memory attributes storage the attributes in 'attributes'
70
+ *
71
+ * @param {Object} attributes Object with attributes to store
72
+ * @returns true if attributes were stored an false otherways
73
+ */
74
+ setAttributes: function (attributes) {
75
+ if (!validateAttributesDeep(log, attributes, 'setAttributes'))
76
+ return false;
77
+ return attributeStorage.setAttributes(attributes);
78
+ },
79
+ /**
80
+ * Return all the attributes stored in client's in memory attributes storage
81
+ *
82
+ * @returns {Object} returns all the stored attributes
83
+ */
84
+ getAttributes: function () {
85
+ return attributeStorage.getAll();
86
+ },
87
+ /**
88
+ * Removes from client's in memory attributes storage the attribute with the given key
89
+ *
90
+ * @param {string} attributeName
91
+ * @returns {boolean} true if attribute was removed and false otherways
92
+ */
93
+ removeAttribute: function (attributeName) {
94
+ log.debug("removed attribute " + attributeName);
95
+ return attributeStorage.removeAttribute(attributeName + '');
96
+ },
97
+ /**
98
+ * Remove all the stored attributes in the client's in memory attribute storage
99
+ */
100
+ clearAttributes: function () {
101
+ return attributeStorage.clear();
102
+ }
103
+ });
104
+ }
@@ -1,4 +1,5 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
+ import { clientAttributesDecoration } from './clientAttributesDecoration';
2
3
  /**
3
4
  * Decorator that binds a key and (optionally) a traffic type to client methods
4
5
  *
@@ -6,14 +7,16 @@ import { objectAssign } from '../utils/lang/objectAssign';
6
7
  * @param key validated split key
7
8
  * @param trafficType validated traffic type
8
9
  */
9
- export function clientCSDecorator(client, key, trafficType) {
10
- return objectAssign(client, {
10
+ export function clientCSDecorator(log, client, key, trafficType) {
11
+ var clientCS = clientAttributesDecoration(log, client);
12
+ return objectAssign(clientCS, {
11
13
  // In the client-side API, we bind a key to the client `getTreatment*` methods
12
- getTreatment: client.getTreatment.bind(client, key),
13
- getTreatmentWithConfig: client.getTreatmentWithConfig.bind(client, key),
14
- getTreatments: client.getTreatments.bind(client, key),
15
- getTreatmentsWithConfig: client.getTreatmentsWithConfig.bind(client, key),
14
+ getTreatment: clientCS.getTreatment.bind(clientCS, key),
15
+ getTreatmentWithConfig: clientCS.getTreatmentWithConfig.bind(clientCS, key),
16
+ getTreatments: clientCS.getTreatments.bind(clientCS, key),
17
+ getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
16
18
  // Key is bound to the `track` method. Same thing happens with trafficType but only if provided
17
- track: trafficType ? client.track.bind(client, key, trafficType) : client.track.bind(client, key)
19
+ track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
20
+ isBrowserClient: true
18
21
  });
19
22
  }
@@ -20,7 +20,7 @@ export function sdkClientMethodCSFactory(params) {
20
20
  // `false` value is used as binded key of the default client, but trafficType is ignored
21
21
  // @TODO handle as a non-recoverable error
22
22
  var validKey = validateKey(log, key, method);
23
- var mainClientInstance = clientCSDecorator(sdkClientFactory(params), // @ts-ignore
23
+ var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), // @ts-ignore
24
24
  validKey);
25
25
  var parsedDefaultKey = keyParser(key);
26
26
  var defaultInstanceId = buildInstanceId(parsedDefaultKey);
@@ -55,7 +55,7 @@ export function sdkClientMethodCSFactory(params) {
55
55
  var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
56
56
  // As shared clients reuse all the storage information, we don't need to check here if we
57
57
  // will use offline or online mode. We should stick with the original decision.
58
- clientInstances[instanceId] = clientCSDecorator(sdkClientFactory(objectAssign({}, params, {
58
+ clientInstances[instanceId] = clientCSDecorator(log, sdkClientFactory(objectAssign({}, params, {
59
59
  sdkReadinessManager: sharedSdkReadiness_1,
60
60
  storage: sharedStorage || storage,
61
61
  syncManager: sharedSyncManager,
@@ -26,7 +26,7 @@ export function sdkClientMethodCSFactory(params) {
26
26
  if (trafficType !== undefined) {
27
27
  validTrafficType = validateTrafficType(log, trafficType, method);
28
28
  }
29
- var mainClientInstance = clientCSDecorator(sdkClientFactory(params), // @ts-ignore
29
+ var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), // @ts-ignore
30
30
  validKey, validTrafficType);
31
31
  var parsedDefaultKey = keyParser(key);
32
32
  var defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
@@ -68,7 +68,7 @@ export function sdkClientMethodCSFactory(params) {
68
68
  var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
69
69
  // As shared clients reuse all the storage information, we don't need to check here if we
70
70
  // will use offline or online mode. We should stick with the original decision.
71
- clientInstances[instanceId] = clientCSDecorator(sdkClientFactory(objectAssign({}, params, {
71
+ clientInstances[instanceId] = clientCSDecorator(log, sdkClientFactory(objectAssign({}, params, {
72
72
  sdkReadinessManager: sharedSdkReadiness_1,
73
73
  storage: sharedStorage || storage,
74
74
  syncManager: sharedSyncManager,
@@ -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) {
@@ -0,0 +1,67 @@
1
+ import { objectAssign } from '../../utils/lang/objectAssign';
2
+ var AttributesCacheInMemory = /** @class */ (function () {
3
+ function AttributesCacheInMemory() {
4
+ this.attributesCache = {};
5
+ }
6
+ /**
7
+ * Create or update the value for the given attribute
8
+ *
9
+ * @param {string} attributeName attribute name
10
+ * @param {Object} attributeValue attribute value
11
+ * @returns {boolean} the attribute was stored
12
+ */
13
+ AttributesCacheInMemory.prototype.setAttribute = function (attributeName, attributeValue) {
14
+ this.attributesCache[attributeName] = attributeValue;
15
+ return true;
16
+ };
17
+ /**
18
+ * Retrieves the value of a given attribute
19
+ *
20
+ * @param {string} attributeName attribute name
21
+ * @returns {Object?} stored attribute value
22
+ */
23
+ AttributesCacheInMemory.prototype.getAttribute = function (attributeName) {
24
+ return this.attributesCache[attributeName];
25
+ };
26
+ /**
27
+ * Create or update all the given attributes
28
+ *
29
+ * @param {[string, Object]} attributes attributes to create or update
30
+ * @returns {boolean} attributes were stored
31
+ */
32
+ AttributesCacheInMemory.prototype.setAttributes = function (attributes) {
33
+ this.attributesCache = objectAssign(this.attributesCache, attributes);
34
+ return true;
35
+ };
36
+ /**
37
+ * Retrieve the full attributes map
38
+ *
39
+ * @returns {Map<string, Object>} stored attributes
40
+ */
41
+ AttributesCacheInMemory.prototype.getAll = function () {
42
+ return this.attributesCache;
43
+ };
44
+ /**
45
+ * Removes a given attribute from the map
46
+ *
47
+ * @param {string} attributeName attribute to remove
48
+ * @returns {boolean} attribute removed
49
+ */
50
+ AttributesCacheInMemory.prototype.removeAttribute = function (attributeName) {
51
+ if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
52
+ delete this.attributesCache[attributeName];
53
+ return true;
54
+ }
55
+ return false;
56
+ };
57
+ /**
58
+ * Clears all attributes stored in the SDK
59
+ *
60
+ */
61
+ AttributesCacheInMemory.prototype.clear = function () {
62
+ this.attributesCache = {};
63
+ return true;
64
+ };
65
+ return AttributesCacheInMemory;
66
+ }());
67
+ export { AttributesCacheInMemory };
@@ -2,8 +2,8 @@
2
2
  * Factory of MySegments fetcher.
3
3
  * MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors.
4
4
  */
5
- export function mySegmentsFetcherFactory(fetchMySegments, userMatchingKey) {
6
- return function mySegmentsFetcher(noCache,
5
+ export function mySegmentsFetcherFactory(fetchMySegments) {
6
+ return function mySegmentsFetcher(userMatchingKey, noCache,
7
7
  // Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
8
8
  decorator) {
9
9
  var mySegmentsPromise = fetchMySegments(userMatchingKey, noCache);
@@ -8,7 +8,8 @@ import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger
8
8
  * Expose start / stop mechanism for polling data from services.
9
9
  * For client-side API with multiple clients.
10
10
  */
11
- export function pollingManagerCSFactory(splitApi, storage, readiness, settings) {
11
+ export function pollingManagerCSFactory(params) {
12
+ var splitApi = params.splitApi, storage = params.storage, readiness = params.readiness, settings = params.settings;
12
13
  var log = settings.log;
13
14
  var splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
14
15
  // Map of matching keys to their corresponding MySegmentsSyncTask.
@@ -5,7 +5,8 @@ import { POLLING_START, POLLING_STOP, LOG_PREFIX_SYNC_POLLING } from '../../logg
5
5
  /**
6
6
  * Expose start / stop mechanism for pulling data from services.
7
7
  */
8
- export function pollingManagerSSFactory(splitApi, storage, readiness, settings) {
8
+ export function pollingManagerSSFactory(params) {
9
+ var splitApi = params.splitApi, storage = params.storage, readiness = params.readiness, settings = params.settings;
9
10
  var log = settings.log;
10
11
  var splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
11
12
  var segmentsSyncTask = segmentsSyncTaskFactory(splitApi.fetchSegmentChanges, storage, readiness, settings);
@@ -5,5 +5,5 @@ import { mySegmentsUpdaterFactory } from '../updaters/mySegmentsUpdater';
5
5
  * Creates a sync task that periodically executes a `mySegmentsUpdater` task
6
6
  */
7
7
  export function mySegmentsSyncTaskFactory(fetchMySegments, storage, readiness, settings, matchingKey) {
8
- return syncTaskFactory(settings.log, mySegmentsUpdaterFactory(settings.log, mySegmentsFetcherFactory(fetchMySegments, matchingKey), storage.splits, storage.segments, readiness.segments, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady), settings.scheduler.segmentsRefreshRate, 'mySegmentsUpdater');
8
+ return syncTaskFactory(settings.log, mySegmentsUpdaterFactory(settings.log, mySegmentsFetcherFactory(fetchMySegments), storage.splits, storage.segments, readiness.segments, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, matchingKey), settings.scheduler.segmentsRefreshRate, 'mySegmentsUpdater');
9
9
  }
@@ -7,7 +7,7 @@ import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
7
7
  * - updates `mySegmentsCache`
8
8
  * - uses `segmentsEventEmitter` to emit events related to segments data updates
9
9
  */
10
- export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, mySegmentsCache, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
10
+ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, mySegmentsCache, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) {
11
11
  var readyOnAlreadyExistentState = true;
12
12
  var startingUp = true;
13
13
  /** timeout and telemetry decorator for `splitChangesFetcher` promise */
@@ -51,7 +51,7 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, my
51
51
  // If segmentsData is provided, there is no need to fetch mySegments
52
52
  new Promise(function (res) { updateSegments(segmentsData); res(true); }) :
53
53
  // If not provided, fetch mySegments
54
- mySegmentsFetcher(noCache, _promiseDecorator).then(function (segments) {
54
+ mySegmentsFetcher(matchingKey, noCache, _promiseDecorator).then(function (segments) {
55
55
  // Only when we have downloaded segments completely, we should not keep retrying anymore
56
56
  startingUp = false;
57
57
  updateSegments(segments);
@@ -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`
@@ -7,7 +7,7 @@ var SegmentsUpdateWorker = /** @class */ (function () {
7
7
  * @param {Object} segmentsCache segments data cache
8
8
  * @param {Object} segmentsSyncTask task for syncing segments data
9
9
  */
10
- function SegmentsUpdateWorker(segmentsCache, segmentsSyncTask) {
10
+ function SegmentsUpdateWorker(segmentsSyncTask, segmentsCache) {
11
11
  this.segmentsCache = segmentsCache;
12
12
  this.segmentsSyncTask = segmentsSyncTask;
13
13
  this.maxChangeNumbers = {};
@@ -19,10 +19,11 @@ import { hash64 } from '../../utils/murmur3/murmur3_64';
19
19
  * - for server-side if key is not provided in settings.
20
20
  * - for client-side, with support for multiple clients, if key is provided in settings
21
21
  */
22
- export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platform, settings) {
22
+ export function pushManagerFactory(params, pollingManager) {
23
+ var settings = params.settings, storage = params.storage, splitApi = params.splitApi, readiness = params.readiness, platform = params.platform;
23
24
  // `userKey` is the matching key of main client in client-side SDK.
24
25
  // 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; //
26
+ var userKey = settings.core.key ? getMatching(settings.core.key) : undefined;
26
27
  var log = settings.log;
27
28
  var sseClient;
28
29
  try {
@@ -33,13 +34,14 @@ export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth
33
34
  log.warn(STREAMING_FALLBACK, [e]);
34
35
  return;
35
36
  }
36
- var authenticate = authenticateFactory(fetchAuth);
37
+ var authenticate = authenticateFactory(splitApi.fetchAuth);
37
38
  // init feedback loop
38
39
  var pushEmitter = new platform.EventEmitter();
39
40
  var sseHandler = SSEHandlerFactory(log, pushEmitter);
40
41
  sseClient.setEventHandler(sseHandler);
41
42
  // init workers
42
- var segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
43
+ // MySegmentsUpdateWorker (client-side) are initiated in `add` method
44
+ var segmentsUpdateWorker = userKey ? undefined : new SegmentsUpdateWorker(pollingManager.segmentsSyncTask, storage.segments);
43
45
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
44
46
  var splitsUpdateWorker = new SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
45
47
  // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
@@ -47,11 +49,6 @@ export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth
47
49
  // [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
48
50
  // Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
49
51
  var clients = {};
50
- if (userKey) {
51
- var hash = hashUserKey(userKey);
52
- userKeyHashes[hash] = userKey;
53
- clients[userKey] = { hash64: hash64(userKey), worker: segmentsUpdateWorker };
54
- }
55
52
  // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
56
53
  var connectForNewClient = false;
57
54
  // 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 +255,40 @@ export function pushManagerFactory(pollingManager, storage, readiness, fetchAuth
258
255
  return objectAssign(
259
256
  // Expose Event Emitter functionality and Event constants
260
257
  Object.create(pushEmitter), {
261
- // Expose functionality for starting and stoping push mode:
262
- stop: disconnectPush,
258
+ // Stop/pause push mode
259
+ stop: function () {
260
+ disconnectPush(); // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
261
+ if (userKey)
262
+ this.remove(userKey); // Necessary to properly resume streaming in client-side (e.g., RN SDK transition to foreground).
263
+ },
264
+ // Start/resume push mode
263
265
  start: function () {
264
266
  // Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
265
267
  if (disabled || disconnected === false)
266
268
  return;
267
269
  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);
270
+ if (userKey)
271
+ this.add(userKey, pollingManager.segmentsSyncTask); // client-side
272
+ else
273
+ setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
270
274
  },
271
275
  // [Only for client-side]
272
276
  add: function (userKey, mySegmentsSyncTask) {
273
- clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
274
277
  var hash = hashUserKey(userKey);
275
278
  if (!userKeyHashes[hash]) {
276
279
  userKeyHashes[hash] = userKey;
280
+ clients[userKey] = { hash64: hash64(userKey), worker: new MySegmentsUpdateWorker(mySegmentsSyncTask) };
277
281
  connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
282
+ // Reconnects in case of a new client.
283
+ // Run in next event-loop cycle to save authentication calls
284
+ // in case multiple clients are created in the current cycle.
285
+ setTimeout(function checkForReconnect() {
286
+ if (connectForNewClient) {
287
+ connectForNewClient = false;
288
+ connectPush();
289
+ }
290
+ }, 0);
278
291
  }
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
292
  },
289
293
  // [Only for client-side]
290
294
  remove: function (userKey) {
@@ -2,7 +2,8 @@ import { syncTaskComposite } from '../syncTaskComposite';
2
2
  import { eventsSyncTaskFactory } from './eventsSyncTask';
3
3
  import { impressionsSyncTaskFactory } from './impressionsSyncTask';
4
4
  import { impressionCountsSyncTaskFactory } from './impressionCountsSyncTask';
5
- export function submitterManagerFactory(settings, storage, splitApi) {
5
+ export function submitterManagerFactory(params) {
6
+ var settings = params.settings, storage = params.storage, splitApi = params.splitApi;
6
7
  var log = settings.log;
7
8
  var submitters = [
8
9
  impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled),
@@ -13,26 +13,25 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
13
13
  /**
14
14
  * SyncManager factory for modular SDK
15
15
  */
16
- return function (_a) {
17
- var settings = _a.settings, platform = _a.platform, splitApi = _a.splitApi, storage = _a.storage, readiness = _a.readiness;
18
- var log = settings.log;
16
+ return function (params) {
17
+ var _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled;
19
18
  /** Polling Manager */
20
- var pollingManager = pollingManagerFactory && pollingManagerFactory(splitApi, storage, readiness, settings);
19
+ var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
21
20
  /** Push Manager */
22
- var pushManager = settings.streamingEnabled && pollingManager && pushManagerFactory ?
23
- pushManagerFactory(pollingManager, storage, readiness, splitApi.fetchAuth, platform, settings) :
21
+ var pushManager = streamingEnabled && pollingManager && pushManagerFactory ?
22
+ pushManagerFactory(params, pollingManager) :
24
23
  undefined;
25
24
  /** Submitter Manager */
26
25
  // It is not inyected as push and polling managers, because at the moment it is required
27
- var submitter = submitterManagerFactory(settings, storage, splitApi);
26
+ var submitter = submitterManagerFactory(params);
28
27
  /** Sync Manager logic */
29
28
  function startPolling() {
30
- if (!pollingManager.isRunning()) {
31
- log.info(SYNC_START_POLLING);
32
- pollingManager.start();
29
+ if (pollingManager.isRunning()) {
30
+ log.info(SYNC_CONTINUE_POLLING);
33
31
  }
34
32
  else {
35
- log.info(SYNC_CONTINUE_POLLING);
33
+ log.info(SYNC_START_POLLING);
34
+ pollingManager.start();
36
35
  }
37
36
  }
38
37
  function stopPollingAndSyncAll() {
@@ -70,7 +69,8 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
70
69
  }
71
70
  }
72
71
  // start periodic data recording (events, impressions, telemetry).
73
- submitter && submitter.start();
72
+ if (submitter)
73
+ submitter.start();
74
74
  running = true;
75
75
  },
76
76
  /**
@@ -0,0 +1,16 @@
1
+ import { isString, isFiniteNumber, isBoolean } from '../../utils/lang';
2
+ export function validateAttribute(log, attributeKey, attributeValue, method) {
3
+ if (!isString(attributeKey) || attributeKey.length === 0) {
4
+ log.warn(method + ": you passed an invalid attribute name, attribute name must be a non-empty string.");
5
+ return false;
6
+ }
7
+ var isStringVal = isString(attributeValue);
8
+ var isFiniteVal = isFiniteNumber(attributeValue);
9
+ var isBoolVal = isBoolean(attributeValue);
10
+ var isArrayVal = Array.isArray(attributeValue);
11
+ if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) { // If it's not of valid type.
12
+ log.warn(method + ": you passed an invalid attribute value for " + attributeKey + ". Acceptable types are: string, number, boolean and array of strings.");
13
+ return false;
14
+ }
15
+ return true;
16
+ }