@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
package/CHANGES.txt CHANGED
@@ -1,3 +1,6 @@
1
+ 1.2.0 (January 19, 2022)
2
+ - Added support to SDK clients on browser to optionally bind attributes to the client, keeping these loaded within the SDK along with the user ID, for easier usage when requesting flag.
3
+
1
4
  1.1.0 (January 11, 2022)
2
5
  - Added support for the SDK to run in "consumer" and "partial consumer" modes, with a pluggable implementation of it's internal storage, enabling
3
6
  customers to implement this caching with any storage technology of choice and connect it to the SDK instance to be used instead of its default in-memory storage.
@@ -5,9 +5,9 @@ var constants_1 = require("../../logger/constants");
5
5
  var lang_1 = require("../../utils/lang");
6
6
  function endsWithMatcherContext(log, ruleAttr) {
7
7
  return function endsWithMatcher(runtimeAttr) {
8
- var endsWith = ruleAttr.some(function (e) { return lang_1.endsWith(runtimeAttr, e); });
9
- log.debug(constants_1.ENGINE_MATCHER_ENDS_WITH, [runtimeAttr, ruleAttr, endsWith]);
10
- return endsWith;
8
+ var strEndsWith = ruleAttr.some(function (e) { return lang_1.endsWith(runtimeAttr, e); });
9
+ log.debug(constants_1.ENGINE_MATCHER_ENDS_WITH, [runtimeAttr, ruleAttr, strEndsWith]);
10
+ return strEndsWith;
11
11
  };
12
12
  }
13
13
  exports.endsWithMatcherContext = endsWithMatcherContext;
@@ -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;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clientAttributesDecoration = void 0;
4
+ var AttributesCacheInMemory_1 = require("../storages/inMemory/AttributesCacheInMemory");
5
+ var attributes_1 = require("../utils/inputValidation/attributes");
6
+ var objectAssign_1 = require("../utils/lang/objectAssign");
7
+ /**
8
+ * Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
9
+ */
10
+ function clientAttributesDecoration(log, client) {
11
+ var attributeStorage = new AttributesCacheInMemory_1.AttributesCacheInMemory();
12
+ // Keep a reference to the original methods
13
+ var clientGetTreatment = client.getTreatment;
14
+ var clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
15
+ var clientGetTreatments = client.getTreatments;
16
+ var clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
17
+ var clientTrack = client.track;
18
+ function getTreatment(maybeKey, maybeSplit, maybeAttributes) {
19
+ return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
20
+ }
21
+ function getTreatmentWithConfig(maybeKey, maybeSplit, maybeAttributes) {
22
+ return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
23
+ }
24
+ function getTreatments(maybeKey, maybeSplits, maybeAttributes) {
25
+ return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
26
+ }
27
+ function getTreatmentsWithConfig(maybeKey, maybeSplits, maybeAttributes) {
28
+ return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
29
+ }
30
+ function track(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties) {
31
+ return clientTrack(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties);
32
+ }
33
+ function combineAttributes(maybeAttributes) {
34
+ var storedAttributes = attributeStorage.getAll();
35
+ if (Object.keys(storedAttributes).length > 0) {
36
+ return objectAssign_1.objectAssign({}, storedAttributes, maybeAttributes);
37
+ }
38
+ return maybeAttributes;
39
+ }
40
+ return objectAssign_1.objectAssign(client, {
41
+ getTreatment: getTreatment,
42
+ getTreatmentWithConfig: getTreatmentWithConfig,
43
+ getTreatments: getTreatments,
44
+ getTreatmentsWithConfig: getTreatmentsWithConfig,
45
+ track: track,
46
+ /**
47
+ * Add an attribute to client's in memory attributes storage
48
+ *
49
+ * @param {string} attributeName Attrinute name
50
+ * @param {string, number, boolean, list} attributeValue Attribute value
51
+ * @returns {boolean} true if the attribute was stored and false otherways
52
+ */
53
+ setAttribute: function (attributeName, attributeValue) {
54
+ var attribute = {};
55
+ attribute[attributeName] = attributeValue;
56
+ if (!attributes_1.validateAttributesDeep(log, attribute, 'setAttribute'))
57
+ return false;
58
+ log.debug("stored " + attributeValue + " for attribute " + attributeName);
59
+ return attributeStorage.setAttribute(attributeName, attributeValue);
60
+ },
61
+ /**
62
+ * Returns the attribute with the given key
63
+ *
64
+ * @param {string} attributeName Attribute name
65
+ * @returns {Object} Attribute with the given key
66
+ */
67
+ getAttribute: function (attributeName) {
68
+ log.debug("retrieved attribute " + attributeName);
69
+ return attributeStorage.getAttribute(attributeName + '');
70
+ },
71
+ /**
72
+ * Add to client's in memory attributes storage the attributes in 'attributes'
73
+ *
74
+ * @param {Object} attributes Object with attributes to store
75
+ * @returns true if attributes were stored an false otherways
76
+ */
77
+ setAttributes: function (attributes) {
78
+ if (!attributes_1.validateAttributesDeep(log, attributes, 'setAttributes'))
79
+ return false;
80
+ return attributeStorage.setAttributes(attributes);
81
+ },
82
+ /**
83
+ * Return all the attributes stored in client's in memory attributes storage
84
+ *
85
+ * @returns {Object} returns all the stored attributes
86
+ */
87
+ getAttributes: function () {
88
+ return attributeStorage.getAll();
89
+ },
90
+ /**
91
+ * Removes from client's in memory attributes storage the attribute with the given key
92
+ *
93
+ * @param {string} attributeName
94
+ * @returns {boolean} true if attribute was removed and false otherways
95
+ */
96
+ removeAttribute: function (attributeName) {
97
+ log.debug("removed attribute " + attributeName);
98
+ return attributeStorage.removeAttribute(attributeName + '');
99
+ },
100
+ /**
101
+ * Remove all the stored attributes in the client's in memory attribute storage
102
+ */
103
+ clearAttributes: function () {
104
+ return attributeStorage.clear();
105
+ }
106
+ });
107
+ }
108
+ exports.clientAttributesDecoration = clientAttributesDecoration;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.clientCSDecorator = void 0;
4
4
  var objectAssign_1 = require("../utils/lang/objectAssign");
5
+ var clientAttributesDecoration_1 = require("./clientAttributesDecoration");
5
6
  /**
6
7
  * Decorator that binds a key and (optionally) a traffic type to client methods
7
8
  *
@@ -9,15 +10,17 @@ var objectAssign_1 = require("../utils/lang/objectAssign");
9
10
  * @param key validated split key
10
11
  * @param trafficType validated traffic type
11
12
  */
12
- function clientCSDecorator(client, key, trafficType) {
13
- return objectAssign_1.objectAssign(client, {
13
+ function clientCSDecorator(log, client, key, trafficType) {
14
+ var clientCS = clientAttributesDecoration_1.clientAttributesDecoration(log, client);
15
+ return objectAssign_1.objectAssign(clientCS, {
14
16
  // In the client-side API, we bind a key to the client `getTreatment*` methods
15
- getTreatment: client.getTreatment.bind(client, key),
16
- getTreatmentWithConfig: client.getTreatmentWithConfig.bind(client, key),
17
- getTreatments: client.getTreatments.bind(client, key),
18
- getTreatmentsWithConfig: client.getTreatmentsWithConfig.bind(client, key),
17
+ getTreatment: clientCS.getTreatment.bind(clientCS, key),
18
+ getTreatmentWithConfig: clientCS.getTreatmentWithConfig.bind(clientCS, key),
19
+ getTreatments: clientCS.getTreatments.bind(clientCS, key),
20
+ getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
19
21
  // Key is bound to the `track` method. Same thing happens with trafficType but only if provided
20
- track: trafficType ? client.track.bind(client, key, trafficType) : client.track.bind(client, key)
22
+ track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
23
+ isBrowserClient: true
21
24
  });
22
25
  }
23
26
  exports.clientCSDecorator = clientCSDecorator;
@@ -23,7 +23,7 @@ function sdkClientMethodCSFactory(params) {
23
23
  // `false` value is used as binded key of the default client, but trafficType is ignored
24
24
  // @TODO handle as a non-recoverable error
25
25
  var validKey = key_1.validateKey(log, key, method);
26
- var mainClientInstance = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(params), // @ts-ignore
26
+ var mainClientInstance = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(params), // @ts-ignore
27
27
  validKey);
28
28
  var parsedDefaultKey = key_2.keyParser(key);
29
29
  var defaultInstanceId = buildInstanceId(parsedDefaultKey);
@@ -58,7 +58,7 @@ function sdkClientMethodCSFactory(params) {
58
58
  var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
59
59
  // As shared clients reuse all the storage information, we don't need to check here if we
60
60
  // will use offline or online mode. We should stick with the original decision.
61
- clientInstances[instanceId] = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
61
+ clientInstances[instanceId] = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
62
62
  sdkReadinessManager: sharedSdkReadiness_1,
63
63
  storage: sharedStorage || storage,
64
64
  syncManager: sharedSyncManager,
@@ -29,7 +29,7 @@ function sdkClientMethodCSFactory(params) {
29
29
  if (trafficType !== undefined) {
30
30
  validTrafficType = trafficType_1.validateTrafficType(log, trafficType, method);
31
31
  }
32
- var mainClientInstance = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(params), // @ts-ignore
32
+ var mainClientInstance = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(params), // @ts-ignore
33
33
  validKey, validTrafficType);
34
34
  var parsedDefaultKey = key_2.keyParser(key);
35
35
  var defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
@@ -71,7 +71,7 @@ function sdkClientMethodCSFactory(params) {
71
71
  var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
72
72
  // As shared clients reuse all the storage information, we don't need to check here if we
73
73
  // will use offline or online mode. We should stick with the original decision.
74
- clientInstances[instanceId] = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
74
+ clientInstances[instanceId] = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
75
75
  sdkReadinessManager: sharedSdkReadiness_1,
76
76
  storage: sharedStorage || storage,
77
77
  syncManager: sharedSyncManager,
@@ -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) {
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AttributesCacheInMemory = void 0;
4
+ var objectAssign_1 = require("../../utils/lang/objectAssign");
5
+ var AttributesCacheInMemory = /** @class */ (function () {
6
+ function AttributesCacheInMemory() {
7
+ this.attributesCache = {};
8
+ }
9
+ /**
10
+ * Create or update the value for the given attribute
11
+ *
12
+ * @param {string} attributeName attribute name
13
+ * @param {Object} attributeValue attribute value
14
+ * @returns {boolean} the attribute was stored
15
+ */
16
+ AttributesCacheInMemory.prototype.setAttribute = function (attributeName, attributeValue) {
17
+ this.attributesCache[attributeName] = attributeValue;
18
+ return true;
19
+ };
20
+ /**
21
+ * Retrieves the value of a given attribute
22
+ *
23
+ * @param {string} attributeName attribute name
24
+ * @returns {Object?} stored attribute value
25
+ */
26
+ AttributesCacheInMemory.prototype.getAttribute = function (attributeName) {
27
+ return this.attributesCache[attributeName];
28
+ };
29
+ /**
30
+ * Create or update all the given attributes
31
+ *
32
+ * @param {[string, Object]} attributes attributes to create or update
33
+ * @returns {boolean} attributes were stored
34
+ */
35
+ AttributesCacheInMemory.prototype.setAttributes = function (attributes) {
36
+ this.attributesCache = objectAssign_1.objectAssign(this.attributesCache, attributes);
37
+ return true;
38
+ };
39
+ /**
40
+ * Retrieve the full attributes map
41
+ *
42
+ * @returns {Map<string, Object>} stored attributes
43
+ */
44
+ AttributesCacheInMemory.prototype.getAll = function () {
45
+ return this.attributesCache;
46
+ };
47
+ /**
48
+ * Removes a given attribute from the map
49
+ *
50
+ * @param {string} attributeName attribute to remove
51
+ * @returns {boolean} attribute removed
52
+ */
53
+ AttributesCacheInMemory.prototype.removeAttribute = function (attributeName) {
54
+ if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
55
+ delete this.attributesCache[attributeName];
56
+ return true;
57
+ }
58
+ return false;
59
+ };
60
+ /**
61
+ * Clears all attributes stored in the SDK
62
+ *
63
+ */
64
+ AttributesCacheInMemory.prototype.clear = function () {
65
+ this.attributesCache = {};
66
+ return true;
67
+ };
68
+ return AttributesCacheInMemory;
69
+ }());
70
+ exports.AttributesCacheInMemory = AttributesCacheInMemory;
@@ -5,8 +5,8 @@ exports.mySegmentsFetcherFactory = void 0;
5
5
  * Factory of MySegments fetcher.
6
6
  * MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors.
7
7
  */
8
- function mySegmentsFetcherFactory(fetchMySegments, userMatchingKey) {
9
- return function mySegmentsFetcher(noCache,
8
+ function mySegmentsFetcherFactory(fetchMySegments) {
9
+ return function mySegmentsFetcher(userMatchingKey, noCache,
10
10
  // Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
11
11
  decorator) {
12
12
  var mySegmentsPromise = fetchMySegments(userMatchingKey, noCache);
@@ -11,7 +11,8 @@ var constants_2 = require("../../logger/constants");
11
11
  * Expose start / stop mechanism for polling data from services.
12
12
  * For client-side API with multiple clients.
13
13
  */
14
- function pollingManagerCSFactory(splitApi, storage, readiness, settings) {
14
+ function pollingManagerCSFactory(params) {
15
+ var splitApi = params.splitApi, storage = params.storage, readiness = params.readiness, settings = params.settings;
15
16
  var log = settings.log;
16
17
  var splitsSyncTask = splitsSyncTask_1.splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
17
18
  // Map of matching keys to their corresponding MySegmentsSyncTask.
@@ -8,7 +8,8 @@ var constants_1 = require("../../logger/constants");
8
8
  /**
9
9
  * Expose start / stop mechanism for pulling data from services.
10
10
  */
11
- function pollingManagerSSFactory(splitApi, storage, readiness, settings) {
11
+ function pollingManagerSSFactory(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 = splitsSyncTask_1.splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
14
15
  var segmentsSyncTask = segmentsSyncTask_1.segmentsSyncTaskFactory(splitApi.fetchSegmentChanges, storage, readiness, settings);
@@ -8,6 +8,6 @@ var mySegmentsUpdater_1 = require("../updaters/mySegmentsUpdater");
8
8
  * Creates a sync task that periodically executes a `mySegmentsUpdater` task
9
9
  */
10
10
  function mySegmentsSyncTaskFactory(fetchMySegments, storage, readiness, settings, matchingKey) {
11
- return syncTask_1.syncTaskFactory(settings.log, mySegmentsUpdater_1.mySegmentsUpdaterFactory(settings.log, mySegmentsFetcher_1.mySegmentsFetcherFactory(fetchMySegments, matchingKey), storage.splits, storage.segments, readiness.segments, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady), settings.scheduler.segmentsRefreshRate, 'mySegmentsUpdater');
11
+ return syncTask_1.syncTaskFactory(settings.log, mySegmentsUpdater_1.mySegmentsUpdaterFactory(settings.log, mySegmentsFetcher_1.mySegmentsFetcherFactory(fetchMySegments), storage.splits, storage.segments, readiness.segments, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, matchingKey), settings.scheduler.segmentsRefreshRate, 'mySegmentsUpdater');
12
12
  }
13
13
  exports.mySegmentsSyncTaskFactory = mySegmentsSyncTaskFactory;
@@ -10,7 +10,7 @@ var constants_2 = require("../../../logger/constants");
10
10
  * - updates `mySegmentsCache`
11
11
  * - uses `segmentsEventEmitter` to emit events related to segments data updates
12
12
  */
13
- function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, mySegmentsCache, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
13
+ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, mySegmentsCache, segmentsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, matchingKey) {
14
14
  var readyOnAlreadyExistentState = true;
15
15
  var startingUp = true;
16
16
  /** timeout and telemetry decorator for `splitChangesFetcher` promise */
@@ -54,7 +54,7 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, splitsCache, mySegment
54
54
  // If segmentsData is provided, there is no need to fetch mySegments
55
55
  new Promise(function (res) { updateSegments(segmentsData); res(true); }) :
56
56
  // If not provided, fetch mySegments
57
- mySegmentsFetcher(noCache, _promiseDecorator).then(function (segments) {
57
+ mySegmentsFetcher(matchingKey, noCache, _promiseDecorator).then(function (segments) {
58
58
  // Only when we have downloaded segments completely, we should not keep retrying anymore
59
59
  startingUp = false;
60
60
  updateSegments(segments);
@@ -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`
@@ -10,7 +10,7 @@ var SegmentsUpdateWorker = /** @class */ (function () {
10
10
  * @param {Object} segmentsCache segments data cache
11
11
  * @param {Object} segmentsSyncTask task for syncing segments data
12
12
  */
13
- function SegmentsUpdateWorker(segmentsCache, segmentsSyncTask) {
13
+ function SegmentsUpdateWorker(segmentsSyncTask, segmentsCache) {
14
14
  this.segmentsCache = segmentsCache;
15
15
  this.segmentsSyncTask = segmentsSyncTask;
16
16
  this.maxChangeNumbers = {};
@@ -22,10 +22,11 @@ var murmur3_64_1 = require("../../utils/murmur3/murmur3_64");
22
22
  * - for server-side if key is not provided in settings.
23
23
  * - for client-side, with support for multiple clients, if key is provided in settings
24
24
  */
25
- function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platform, settings) {
25
+ function pushManagerFactory(params, pollingManager) {
26
+ var settings = params.settings, storage = params.storage, splitApi = params.splitApi, readiness = params.readiness, platform = params.platform;
26
27
  // `userKey` is the matching key of main client in client-side SDK.
27
28
  // 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; //
29
+ var userKey = settings.core.key ? key_1.getMatching(settings.core.key) : undefined;
29
30
  var log = settings.log;
30
31
  var sseClient;
31
32
  try {
@@ -36,13 +37,14 @@ function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platf
36
37
  log.warn(constants_2.STREAMING_FALLBACK, [e]);
37
38
  return;
38
39
  }
39
- var authenticate = AuthClient_1.authenticateFactory(fetchAuth);
40
+ var authenticate = AuthClient_1.authenticateFactory(splitApi.fetchAuth);
40
41
  // init feedback loop
41
42
  var pushEmitter = new platform.EventEmitter();
42
43
  var sseHandler = SSEHandler_1.SSEHandlerFactory(log, pushEmitter);
43
44
  sseClient.setEventHandler(sseHandler);
44
45
  // init workers
45
- var segmentsUpdateWorker = userKey ? new MySegmentsUpdateWorker_1.MySegmentsUpdateWorker(pollingManager.segmentsSyncTask) : new SegmentsUpdateWorker_1.SegmentsUpdateWorker(storage.segments, pollingManager.segmentsSyncTask);
46
+ // MySegmentsUpdateWorker (client-side) are initiated in `add` method
47
+ var segmentsUpdateWorker = userKey ? undefined : new SegmentsUpdateWorker_1.SegmentsUpdateWorker(pollingManager.segmentsSyncTask, storage.segments);
46
48
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
47
49
  var splitsUpdateWorker = new SplitsUpdateWorker_1.SplitsUpdateWorker(storage.splits, pollingManager.splitsSyncTask, readiness.splits, userKey ? undefined : pollingManager.segmentsSyncTask);
48
50
  // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
@@ -50,11 +52,6 @@ function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platf
50
52
  // [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
51
53
  // Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
52
54
  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
55
  // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
59
56
  var connectForNewClient = false;
60
57
  // 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 +258,40 @@ function pushManagerFactory(pollingManager, storage, readiness, fetchAuth, platf
261
258
  return objectAssign_1.objectAssign(
262
259
  // Expose Event Emitter functionality and Event constants
263
260
  Object.create(pushEmitter), {
264
- // Expose functionality for starting and stoping push mode:
265
- stop: disconnectPush,
261
+ // Stop/pause push mode
262
+ stop: function () {
263
+ disconnectPush(); // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
264
+ if (userKey)
265
+ this.remove(userKey); // Necessary to properly resume streaming in client-side (e.g., RN SDK transition to foreground).
266
+ },
267
+ // Start/resume push mode
266
268
  start: function () {
267
269
  // Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
268
270
  if (disabled || disconnected === false)
269
271
  return;
270
272
  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);
273
+ if (userKey)
274
+ this.add(userKey, pollingManager.segmentsSyncTask); // client-side
275
+ else
276
+ setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
273
277
  },
274
278
  // [Only for client-side]
275
279
  add: function (userKey, mySegmentsSyncTask) {
276
- clients[userKey] = { hash64: murmur3_64_1.hash64(userKey), worker: new MySegmentsUpdateWorker_1.MySegmentsUpdateWorker(mySegmentsSyncTask) };
277
280
  var hash = AuthClient_1.hashUserKey(userKey);
278
281
  if (!userKeyHashes[hash]) {
279
282
  userKeyHashes[hash] = userKey;
283
+ clients[userKey] = { hash64: murmur3_64_1.hash64(userKey), worker: new MySegmentsUpdateWorker_1.MySegmentsUpdateWorker(mySegmentsSyncTask) };
280
284
  connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
285
+ // Reconnects in case of a new client.
286
+ // Run in next event-loop cycle to save authentication calls
287
+ // in case multiple clients are created in the current cycle.
288
+ setTimeout(function checkForReconnect() {
289
+ if (connectForNewClient) {
290
+ connectForNewClient = false;
291
+ connectPush();
292
+ }
293
+ }, 0);
281
294
  }
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
295
  },
292
296
  // [Only for client-side]
293
297
  remove: function (userKey) {
@@ -5,7 +5,8 @@ var syncTaskComposite_1 = require("../syncTaskComposite");
5
5
  var eventsSyncTask_1 = require("./eventsSyncTask");
6
6
  var impressionsSyncTask_1 = require("./impressionsSyncTask");
7
7
  var impressionCountsSyncTask_1 = require("./impressionCountsSyncTask");
8
- function submitterManagerFactory(settings, storage, splitApi) {
8
+ function submitterManagerFactory(params) {
9
+ var settings = params.settings, storage = params.storage, splitApi = params.splitApi;
9
10
  var log = settings.log;
10
11
  var submitters = [
11
12
  impressionsSyncTask_1.impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled),
@@ -16,26 +16,25 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
16
16
  /**
17
17
  * SyncManager factory for modular SDK
18
18
  */
19
- return function (_a) {
20
- var settings = _a.settings, platform = _a.platform, splitApi = _a.splitApi, storage = _a.storage, readiness = _a.readiness;
21
- var log = settings.log;
19
+ return function (params) {
20
+ var _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled;
22
21
  /** Polling Manager */
23
- var pollingManager = pollingManagerFactory && pollingManagerFactory(splitApi, storage, readiness, settings);
22
+ var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
24
23
  /** Push Manager */
25
- var pushManager = settings.streamingEnabled && pollingManager && pushManagerFactory ?
26
- pushManagerFactory(pollingManager, storage, readiness, splitApi.fetchAuth, platform, settings) :
24
+ var pushManager = streamingEnabled && pollingManager && pushManagerFactory ?
25
+ pushManagerFactory(params, pollingManager) :
27
26
  undefined;
28
27
  /** Submitter Manager */
29
28
  // It is not inyected as push and polling managers, because at the moment it is required
30
- var submitter = submitterManager_1.submitterManagerFactory(settings, storage, splitApi);
29
+ var submitter = submitterManager_1.submitterManagerFactory(params);
31
30
  /** Sync Manager logic */
32
31
  function startPolling() {
33
- if (!pollingManager.isRunning()) {
34
- log.info(constants_2.SYNC_START_POLLING);
35
- pollingManager.start();
32
+ if (pollingManager.isRunning()) {
33
+ log.info(constants_2.SYNC_CONTINUE_POLLING);
36
34
  }
37
35
  else {
38
- log.info(constants_2.SYNC_CONTINUE_POLLING);
36
+ log.info(constants_2.SYNC_START_POLLING);
37
+ pollingManager.start();
39
38
  }
40
39
  }
41
40
  function stopPollingAndSyncAll() {
@@ -73,7 +72,8 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
73
72
  }
74
73
  }
75
74
  // start periodic data recording (events, impressions, telemetry).
76
- submitter && submitter.start();
75
+ if (submitter)
76
+ submitter.start();
77
77
  running = true;
78
78
  },
79
79
  /**
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateAttribute = void 0;
4
+ var lang_1 = require("../../utils/lang");
5
+ function validateAttribute(log, attributeKey, attributeValue, method) {
6
+ if (!lang_1.isString(attributeKey) || attributeKey.length === 0) {
7
+ log.warn(method + ": you passed an invalid attribute name, attribute name must be a non-empty string.");
8
+ return false;
9
+ }
10
+ var isStringVal = lang_1.isString(attributeValue);
11
+ var isFiniteVal = lang_1.isFiniteNumber(attributeValue);
12
+ var isBoolVal = lang_1.isBoolean(attributeValue);
13
+ var isArrayVal = Array.isArray(attributeValue);
14
+ if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) { // If it's not of valid type.
15
+ log.warn(method + ": you passed an invalid attribute value for " + attributeKey + ". Acceptable types are: string, number, boolean and array of strings.");
16
+ return false;
17
+ }
18
+ return true;
19
+ }
20
+ exports.validateAttribute = validateAttribute;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateAttributes = void 0;
3
+ exports.validateAttributesDeep = exports.validateAttributes = void 0;
4
4
  var lang_1 = require("../lang");
5
+ var attribute_1 = require("./attribute");
5
6
  var constants_1 = require("../../logger/constants");
6
7
  function validateAttributes(log, maybeAttrs, method) {
7
8
  // Attributes are optional
@@ -11,3 +12,14 @@ function validateAttributes(log, maybeAttrs, method) {
11
12
  return false;
12
13
  }
13
14
  exports.validateAttributes = validateAttributes;
15
+ function validateAttributesDeep(log, maybeAttributes, method) {
16
+ if (!validateAttributes(log, maybeAttributes, method))
17
+ return false;
18
+ var result = true;
19
+ Object.keys(maybeAttributes).forEach(function (attributeKey) {
20
+ if (!attribute_1.validateAttribute(log, attributeKey, maybeAttributes[attributeKey], method))
21
+ result = false;
22
+ });
23
+ return result;
24
+ }
25
+ exports.validateAttributesDeep = validateAttributesDeep;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateEventProperties = void 0;
4
4
  var lang_1 = require("../lang");
5
+ var objectAssign_1 = require("../lang/objectAssign");
5
6
  var constants_1 = require("../../logger/constants");
6
7
  var ECMA_SIZES = {
7
8
  NULL: 0,
@@ -20,7 +21,8 @@ function validateEventProperties(log, maybeProperties, method) {
20
21
  return { properties: false, size: BASE_EVENT_SIZE };
21
22
  }
22
23
  var keys = Object.keys(maybeProperties);
23
- var clone = lang_1.shallowClone(maybeProperties);
24
+ // Shallow clone
25
+ var clone = objectAssign_1.objectAssign({}, maybeProperties);
24
26
  // To avoid calculating the size twice we'll return it from here.
25
27
  var output = {
26
28
  properties: clone,