@splitsoftware/splitio-commons 1.16.1-rc.10 → 1.16.1-rc.12

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 (77) hide show
  1. package/cjs/logger/constants.js +2 -2
  2. package/cjs/logger/messages/warn.js +1 -1
  3. package/cjs/services/splitApi.js +3 -3
  4. package/cjs/storages/AbstractSegmentsCacheSync.js +41 -7
  5. package/cjs/storages/dataLoader.js +1 -1
  6. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +19 -63
  7. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +5 -40
  8. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +1 -1
  9. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
  10. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +12 -21
  11. package/cjs/sync/streaming/AuthClient/index.js +1 -1
  12. package/cjs/sync/streaming/SSEHandler/index.js +5 -7
  13. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +106 -63
  14. package/cjs/sync/streaming/constants.js +3 -3
  15. package/cjs/sync/streaming/pushManager.js +25 -31
  16. package/cjs/utils/constants/index.js +3 -4
  17. package/esm/logger/constants.js +1 -1
  18. package/esm/logger/messages/warn.js +1 -1
  19. package/esm/services/splitApi.js +4 -4
  20. package/esm/storages/AbstractSegmentsCacheSync.js +41 -7
  21. package/esm/storages/dataLoader.js +1 -1
  22. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +19 -63
  23. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +5 -40
  24. package/esm/storages/inMemory/TelemetryCacheInMemory.js +1 -1
  25. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
  26. package/esm/sync/polling/updaters/mySegmentsUpdater.js +12 -21
  27. package/esm/sync/streaming/AuthClient/index.js +1 -1
  28. package/esm/sync/streaming/SSEHandler/index.js +6 -8
  29. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +106 -63
  30. package/esm/sync/streaming/constants.js +2 -2
  31. package/esm/sync/streaming/pushManager.js +28 -34
  32. package/esm/utils/constants/index.js +1 -2
  33. package/package.json +1 -1
  34. package/src/dtos/types.ts +9 -12
  35. package/src/logger/constants.ts +1 -1
  36. package/src/logger/messages/warn.ts +1 -1
  37. package/src/services/splitApi.ts +4 -4
  38. package/src/services/types.ts +1 -1
  39. package/src/storages/AbstractSegmentsCacheSync.ts +52 -7
  40. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  41. package/src/storages/dataLoader.ts +1 -1
  42. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +15 -69
  43. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +6 -46
  44. package/src/storages/inMemory/TelemetryCacheInMemory.ts +1 -1
  45. package/src/storages/types.ts +6 -5
  46. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +2 -1
  47. package/src/sync/polling/fetchers/types.ts +1 -0
  48. package/src/sync/polling/types.ts +9 -10
  49. package/src/sync/polling/updaters/mySegmentsUpdater.ts +15 -19
  50. package/src/sync/streaming/AuthClient/index.ts +1 -1
  51. package/src/sync/streaming/SSEHandler/index.ts +9 -11
  52. package/src/sync/streaming/SSEHandler/types.ts +6 -6
  53. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +114 -65
  54. package/src/sync/streaming/constants.ts +2 -2
  55. package/src/sync/streaming/parseUtils.ts +2 -2
  56. package/src/sync/streaming/pushManager.ts +30 -39
  57. package/src/sync/streaming/types.ts +6 -6
  58. package/src/sync/submitters/types.ts +4 -5
  59. package/src/utils/constants/index.ts +1 -2
  60. package/types/dtos/types.d.ts +8 -12
  61. package/types/logger/constants.d.ts +1 -1
  62. package/types/services/types.d.ts +1 -1
  63. package/types/storages/AbstractSegmentsCacheSync.d.ts +8 -6
  64. package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
  65. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +1 -12
  66. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +1 -9
  67. package/types/storages/types.d.ts +6 -5
  68. package/types/sync/polling/fetchers/types.d.ts +1 -1
  69. package/types/sync/polling/types.d.ts +9 -7
  70. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +1 -1
  71. package/types/sync/streaming/SSEHandler/types.d.ts +6 -6
  72. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +3 -2
  73. package/types/sync/streaming/constants.d.ts +2 -2
  74. package/types/sync/streaming/parseUtils.d.ts +2 -2
  75. package/types/sync/streaming/types.d.ts +5 -5
  76. package/types/sync/submitters/types.d.ts +4 -5
  77. package/types/utils/constants/index.d.ts +1 -2
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ENGINE_VALUE_INVALID = exports.USER_CONSENT_INITIAL = exports.USER_CONSENT_NOT_UPDATED = exports.USER_CONSENT_UPDATED = exports.IMPRESSIONS_TRACKER_SUCCESS = exports.EVENTS_TRACKER_SUCCESS = exports.SYNC_STOP_POLLING = exports.SYNC_CONTINUE_POLLING = exports.SYNC_START_POLLING = exports.SUBMITTERS_PUSH = exports.SUBMITTERS_PUSH_FULL_QUEUE = exports.STREAMING_DISCONNECTING = exports.STREAMING_DISABLED = exports.STREAMING_CONNECTING = exports.STREAMING_RECONNECT = exports.STREAMING_REFRESH_TOKEN = exports.SYNC_SPLITS_FETCH_RETRY = exports.POLLING_STOP = exports.POLLING_START = exports.POLLING_SMART_PAUSING = exports.NEW_FACTORY = exports.NEW_SHARED_CLIENT = exports.IMPRESSION_QUEUEING = exports.IMPRESSION = exports.CLIENT_READY = exports.CLIENT_READY_FROM_CACHE = exports.ENGINE_MATCHER_RESULT = exports.SETTINGS_SPLITS_FILTER = exports.SYNC_TASK_STOP = exports.SYNC_TASK_EXECUTE = exports.SYNC_TASK_START = exports.STREAMING_NEW_MESSAGE = exports.SYNC_SPLITS_SEGMENTS = exports.SYNC_SPLITS_REMOVED = exports.SYNC_SPLITS_NEW = exports.SYNC_SPLITS_FETCH = exports.SYNC_OFFLINE_DATA = exports.RETRIEVE_MANAGER = exports.RETRIEVE_CLIENT_EXISTING = exports.RETRIEVE_CLIENT_DEFAULT = exports.CLEANUP_DEREGISTERING = exports.CLEANUP_REGISTERING = exports.ENGINE_SANITIZE = exports.ENGINE_VALUE = exports.ENGINE_MATCHER_DEPENDENCY_PRE = exports.ENGINE_MATCHER_DEPENDENCY = exports.ENGINE_BUCKET = exports.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT = exports.ENGINE_COMBINER_IFELSEIF = exports.ENGINE_COMBINER_AND = void 0;
4
- exports.ERROR_INVALID_CONFIG_PARAM = exports.ERROR_EMPTY_ARRAY = exports.ERROR_EMPTY = exports.ERROR_INVALID = exports.ERROR_INVALID_KEY_OBJECT = exports.ERROR_TOO_LONG = exports.ERROR_NULL = exports.ERROR_CLIENT_DESTROYED = exports.ERROR_NOT_FINITE = exports.ERROR_SIZE_EXCEEDED = exports.ERROR_NOT_PLAIN_OBJECT = exports.ERROR_EVENT_TYPE_FORMAT = exports.ERROR_EVENTS_TRACKER = exports.ERROR_IMPRESSIONS_LISTENER = exports.ERROR_IMPRESSIONS_TRACKER = exports.ERROR_STREAMING_AUTH = exports.ERROR_STREAMING_SSE = exports.ERROR_SYNC_OFFLINE_LOADING = exports.ERROR_CLIENT_CANNOT_GET_READY = exports.ERROR_CLIENT_LISTENER = exports.ERROR_LOGLEVEL_INVALID = exports.ERROR_ENGINE_COMBINER_IFELSEIF = exports.WARN_FLAGSET_WITHOUT_FLAGS = exports.WARN_FLAGSET_NOT_CONFIGURED = exports.WARN_LOWERCASE_FLAGSET = exports.WARN_INVALID_FLAGSET = exports.STREAMING_PARSING_SPLIT_UPDATE = exports.STREAMING_PARSING_MY_SEGMENTS_UPDATE = exports.WARN_SDK_KEY = exports.WARN_SPLITS_FILTER_EMPTY = exports.WARN_SPLITS_FILTER_INVALID = exports.WARN_SPLITS_FILTER_IGNORED = exports.WARN_INTEGRATION_INVALID = exports.WARN_NOT_EXISTENT_TT = exports.WARN_LOWERCASE_TRAFFIC_TYPE = exports.WARN_NOT_EXISTENT_SPLIT = exports.WARN_TRIMMING = exports.WARN_CONVERTING = exports.WARN_TRIMMING_PROPERTIES = exports.WARN_SETTING_NULL = exports.SUBMITTERS_PUSH_RETRY = exports.SUBMITTERS_PUSH_FAILS = exports.STREAMING_FALLBACK = exports.STREAMING_PARSING_MESSAGE_FAILS = exports.STREAMING_PARSING_ERROR_FAILS = exports.SYNC_SPLITS_FETCH_FAILS = exports.SYNC_MYSEGMENTS_FETCH_RETRY = exports.CLIENT_NOT_READY = exports.CLIENT_NO_LISTENER = exports.ENGINE_VALUE_NO_ATTRIBUTES = void 0;
4
+ exports.ERROR_INVALID_CONFIG_PARAM = exports.ERROR_EMPTY_ARRAY = exports.ERROR_EMPTY = exports.ERROR_INVALID = exports.ERROR_INVALID_KEY_OBJECT = exports.ERROR_TOO_LONG = exports.ERROR_NULL = exports.ERROR_CLIENT_DESTROYED = exports.ERROR_NOT_FINITE = exports.ERROR_SIZE_EXCEEDED = exports.ERROR_NOT_PLAIN_OBJECT = exports.ERROR_EVENT_TYPE_FORMAT = exports.ERROR_EVENTS_TRACKER = exports.ERROR_IMPRESSIONS_LISTENER = exports.ERROR_IMPRESSIONS_TRACKER = exports.ERROR_STREAMING_AUTH = exports.ERROR_STREAMING_SSE = exports.ERROR_SYNC_OFFLINE_LOADING = exports.ERROR_CLIENT_CANNOT_GET_READY = exports.ERROR_CLIENT_LISTENER = exports.ERROR_LOGLEVEL_INVALID = exports.ERROR_ENGINE_COMBINER_IFELSEIF = exports.WARN_FLAGSET_WITHOUT_FLAGS = exports.WARN_FLAGSET_NOT_CONFIGURED = exports.WARN_LOWERCASE_FLAGSET = exports.WARN_INVALID_FLAGSET = exports.STREAMING_PARSING_SPLIT_UPDATE = exports.STREAMING_PARSING_MEMBERSHIPS_UPDATE = exports.WARN_SDK_KEY = exports.WARN_SPLITS_FILTER_EMPTY = exports.WARN_SPLITS_FILTER_INVALID = exports.WARN_SPLITS_FILTER_IGNORED = exports.WARN_INTEGRATION_INVALID = exports.WARN_NOT_EXISTENT_TT = exports.WARN_LOWERCASE_TRAFFIC_TYPE = exports.WARN_NOT_EXISTENT_SPLIT = exports.WARN_TRIMMING = exports.WARN_CONVERTING = exports.WARN_TRIMMING_PROPERTIES = exports.WARN_SETTING_NULL = exports.SUBMITTERS_PUSH_RETRY = exports.SUBMITTERS_PUSH_FAILS = exports.STREAMING_FALLBACK = exports.STREAMING_PARSING_MESSAGE_FAILS = exports.STREAMING_PARSING_ERROR_FAILS = exports.SYNC_SPLITS_FETCH_FAILS = exports.SYNC_MYSEGMENTS_FETCH_RETRY = exports.CLIENT_NOT_READY = exports.CLIENT_NO_LISTENER = exports.ENGINE_VALUE_NO_ATTRIBUTES = void 0;
5
5
  exports.LOG_PREFIX_CLEANUP = exports.LOG_PREFIX_UNIQUE_KEYS_TRACKER = exports.LOG_PREFIX_EVENTS_TRACKER = exports.LOG_PREFIX_IMPRESSIONS_TRACKER = exports.LOG_PREFIX_SYNC_SUBMITTERS = exports.LOG_PREFIX_SYNC_POLLING = exports.LOG_PREFIX_SYNC_MYSEGMENTS = exports.LOG_PREFIX_SYNC_SEGMENTS = exports.LOG_PREFIX_SYNC_SPLITS = exports.LOG_PREFIX_SYNC_STREAMING = exports.LOG_PREFIX_SYNC_OFFLINE = exports.LOG_PREFIX_SYNC_MANAGER = exports.LOG_PREFIX_SYNC = exports.LOG_PREFIX_ENGINE_VALUE = exports.LOG_PREFIX_ENGINE_MATCHER = exports.LOG_PREFIX_ENGINE_COMBINER = exports.LOG_PREFIX_ENGINE = exports.LOG_PREFIX_CLIENT_INSTANTIATION = exports.LOG_PREFIX_INSTANTIATION = exports.LOG_PREFIX_SETTINGS = exports.ENGINE_MATCHER_ERROR = exports.ERROR_SETS_FILTER_EXCLUSIVE = exports.ERROR_TOO_MANY_SETS = exports.ERROR_MIN_CONFIG_PARAM = exports.ERROR_NOT_BOOLEAN = exports.ERROR_STORAGE_INVALID = exports.ERROR_LOCALHOST_MODULE_REQUIRED = exports.ERROR_HTTP = void 0;
6
6
  /**
7
7
  * Message codes used to trim string log messages from commons and client-side API modules,
@@ -82,7 +82,7 @@ exports.WARN_SPLITS_FILTER_IGNORED = 219;
82
82
  exports.WARN_SPLITS_FILTER_INVALID = 220;
83
83
  exports.WARN_SPLITS_FILTER_EMPTY = 221;
84
84
  exports.WARN_SDK_KEY = 222;
85
- exports.STREAMING_PARSING_MY_SEGMENTS_UPDATE = 223;
85
+ exports.STREAMING_PARSING_MEMBERSHIPS_UPDATE = 223;
86
86
  exports.STREAMING_PARSING_SPLIT_UPDATE = 224;
87
87
  exports.WARN_INVALID_FLAGSET = 225;
88
88
  exports.WARN_LOWERCASE_FLAGSET = 226;
@@ -34,7 +34,7 @@ exports.codesWarn = error_1.codesError.concat([
34
34
  [c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS + ': feature flag filter at position %s is invalid. It must be an object with a valid filter type ("bySet", "byName" or "byPrefix") and a list of "values".'],
35
35
  [c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS + ': feature flag filter configuration must be a non-empty array of filter objects.'],
36
36
  [c.WARN_SDK_KEY, c.LOG_PREFIX_SETTINGS + ': You already have %s. We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing it throughout your application'],
37
- [c.STREAMING_PARSING_MY_SEGMENTS_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching Memberships due to an error processing %s notification: %s'],
37
+ [c.STREAMING_PARSING_MEMBERSHIPS_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching Memberships due to an error processing %s notification: %s'],
38
38
  [c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing SPLIT_UPDATE notification: %s'],
39
39
  [c.WARN_INVALID_FLAGSET, '%s: you passed %s, flag set must adhere to the regular expressions %s. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. %s was discarded.'],
40
40
  [c.WARN_LOWERCASE_FLAGSET, '%s: flag set %s should be all lowercase - converting string to lowercase.'],
@@ -54,15 +54,15 @@ function splitApiFactory(settings, platform, telemetryTracker) {
54
54
  var url = urls.sdk + "/segmentChanges/" + segmentName + "?since=" + since + (till ? '&till=' + till : '');
55
55
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.SEGMENT));
56
56
  },
57
- fetchMemberships: function (userMatchingKey, noCache) {
57
+ fetchMemberships: function (userMatchingKey, noCache, till) {
58
58
  /**
59
59
  * URI encoding of user keys in order to:
60
60
  * - avoid 400 responses (due to URI malformed). E.g.: '/api/memberships/%'
61
61
  * - avoid 404 responses. E.g.: '/api/memberships/foo/bar'
62
62
  * - match user keys with special characters. E.g.: 'foo%bar', 'foo/bar'
63
63
  */
64
- var url = urls.sdk + "/memberships/" + encodeURIComponent(userMatchingKey);
65
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.MY_SEGMENT));
64
+ var url = urls.sdk + "/memberships/" + encodeURIComponent(userMatchingKey) + (till ? '?till=' + till : '');
65
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.MEMBERSHIPS));
66
66
  },
67
67
  /**
68
68
  * Post events.
@@ -9,20 +9,54 @@ var AbstractSegmentsCacheSync = /** @class */ (function () {
9
9
  function AbstractSegmentsCacheSync() {
10
10
  }
11
11
  /**
12
- * For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
13
- * For client-side synchronizer: the method is not used.
12
+ * clear the cache.
14
13
  */
15
- AbstractSegmentsCacheSync.prototype.registerSegments = function (names) { return false; };
14
+ AbstractSegmentsCacheSync.prototype.clear = function () {
15
+ this.resetSegments({});
16
+ };
16
17
  /**
17
- * For server-side synchronizer: set the change number of `name` segment.
18
+ * For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
18
19
  * For client-side synchronizer: the method is not used.
19
20
  */
20
- AbstractSegmentsCacheSync.prototype.setChangeNumber = function (name, changeNumber) { return true; };
21
+ AbstractSegmentsCacheSync.prototype.registerSegments = function (names) { return false; };
21
22
  /**
22
23
  * For server-side synchronizer: the method is not used.
23
- * For client-side synchronizer: reset the cache with the given list of segments.
24
+ * For client-side synchronizer: it resets or updates the cache.
24
25
  */
25
- AbstractSegmentsCacheSync.prototype.resetSegments = function (names, changeNumber) { return true; };
26
+ AbstractSegmentsCacheSync.prototype.resetSegments = function (segmentsData) {
27
+ var _this = this;
28
+ this.setChangeNumber(undefined, segmentsData.cn);
29
+ var _a = segmentsData, added = _a.added, removed = _a.removed;
30
+ if (added && removed) {
31
+ var isDiff_1 = false;
32
+ added.forEach(function (segment) {
33
+ isDiff_1 = _this.addToSegment(segment) || isDiff_1;
34
+ });
35
+ removed.forEach(function (segment) {
36
+ isDiff_1 = _this.removeFromSegment(segment) || isDiff_1;
37
+ });
38
+ return isDiff_1;
39
+ }
40
+ var names = (segmentsData.k || []).map(function (s) { return s.n; }).sort();
41
+ var storedSegmentKeys = this.getRegisteredSegments().sort();
42
+ // Extreme fast => everything is empty
43
+ if (!names.length && !storedSegmentKeys.length)
44
+ return false;
45
+ var index = 0;
46
+ while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index])
47
+ index++;
48
+ // Quick path => no changes
49
+ if (index === names.length && index === storedSegmentKeys.length)
50
+ return false;
51
+ // Slowest path => add and/or remove segments
52
+ for (var removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
53
+ this.removeFromSegment(storedSegmentKeys[removeIndex]);
54
+ }
55
+ for (var addIndex = index; addIndex < names.length; addIndex++) {
56
+ this.addToSegment(names[addIndex]);
57
+ }
58
+ return true;
59
+ };
26
60
  return AbstractSegmentsCacheSync;
27
61
  }());
28
62
  exports.AbstractSegmentsCacheSync = AbstractSegmentsCacheSync;
@@ -45,7 +45,7 @@ function dataLoaderFactory(preloadedData) {
45
45
  return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
46
46
  });
47
47
  }
48
- storage.segments.resetSegments(mySegmentsData);
48
+ storage.segments.resetSegments({ k: mySegmentsData.map(function (s) { return ({ n: s }); }) });
49
49
  };
50
50
  }
51
51
  exports.dataLoaderFactory = dataLoaderFactory;
@@ -14,19 +14,11 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
14
14
  return _this;
15
15
  // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
16
16
  }
17
- /**
18
- * Removes list of segments from localStorage
19
- * @NOTE this method is not being used at the moment.
20
- */
21
- MySegmentsCacheInLocal.prototype.clear = function () {
22
- this.log.info(constants_1.LOG_PREFIX + 'Flushing MySegments data from localStorage');
23
- // We cannot simply call `localStorage.clear()` since that implies removing user items from the storage
24
- // We could optimize next sentence, since it implies iterating over all localStorage items
25
- this.resetSegments([]);
26
- };
27
17
  MySegmentsCacheInLocal.prototype.addToSegment = function (name) {
28
18
  var segmentKey = this.keys.buildSegmentNameKey(name);
29
19
  try {
20
+ if (localStorage.getItem(segmentKey) === constants_1.DEFINED)
21
+ return false;
30
22
  localStorage.setItem(segmentKey, constants_1.DEFINED);
31
23
  return true;
32
24
  }
@@ -38,6 +30,8 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
38
30
  MySegmentsCacheInLocal.prototype.removeFromSegment = function (name) {
39
31
  var segmentKey = this.keys.buildSegmentNameKey(name);
40
32
  try {
33
+ if (localStorage.getItem(segmentKey) !== constants_1.DEFINED)
34
+ return false;
41
35
  localStorage.removeItem(segmentKey);
42
36
  return true;
43
37
  }
@@ -49,41 +43,22 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
49
43
  MySegmentsCacheInLocal.prototype.isInSegment = function (name) {
50
44
  return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === constants_1.DEFINED;
51
45
  };
52
- /**
53
- * Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
54
- *
55
- * @param {string[]} names list of segment names
56
- * @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
57
- */
58
- MySegmentsCacheInLocal.prototype.resetSegments = function (names, changeNumber) {
46
+ MySegmentsCacheInLocal.prototype.getRegisteredSegments = function () {
59
47
  var _this = this;
60
- try {
61
- if (changeNumber) {
62
- localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
63
- }
64
- else {
65
- localStorage.removeItem(this.keys.buildTillKey());
66
- }
67
- }
68
- catch (e) {
69
- this.log.error(e);
70
- }
71
- var isDiff = false;
72
- var index;
73
48
  // Scan current values from localStorage
74
- var storedSegmentNames = Object.keys(localStorage).reduce(function (accum, key) {
49
+ return Object.keys(localStorage).reduce(function (accum, key) {
75
50
  var segmentName = _this.keys.extractSegmentName(key);
76
51
  if (segmentName) {
77
52
  accum.push(segmentName);
78
53
  }
79
54
  else {
80
- // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block and reuse `getRegisteredSegments` method.
55
+ // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block
81
56
  segmentName = _this.keys.extractOldSegmentKey(key);
82
57
  if (segmentName) { // this was an old segment key, let's clean up.
83
58
  var newSegmentKey = _this.keys.buildSegmentNameKey(segmentName);
84
59
  try {
85
60
  // If the new format key is not there, create it.
86
- if (!localStorage.getItem(newSegmentKey) && names.indexOf(segmentName) > -1) {
61
+ if (!localStorage.getItem(newSegmentKey)) {
87
62
  localStorage.setItem(newSegmentKey, constants_1.DEFINED);
88
63
  // we are migrating a segment, let's track it.
89
64
  accum.push(segmentName);
@@ -97,40 +72,21 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
97
72
  }
98
73
  return accum;
99
74
  }, []);
100
- // Extreme fast => everything is empty
101
- if (names.length === 0 && storedSegmentNames.length === names.length)
102
- return isDiff;
103
- // Quick path
104
- if (storedSegmentNames.length !== names.length) {
105
- isDiff = true;
106
- storedSegmentNames.forEach(function (name) { return _this.removeFromSegment(name); });
107
- names.forEach(function (name) { return _this.addToSegment(name); });
108
- }
109
- else {
110
- // Slowest path => we need to find at least 1 difference because
111
- for (index = 0; index < names.length && storedSegmentNames.indexOf(names[index]) !== -1; index++) {
112
- // TODO: why empty statement?
113
- }
114
- if (index < names.length) {
115
- isDiff = true;
116
- storedSegmentNames.forEach(function (name) { return _this.removeFromSegment(name); });
117
- names.forEach(function (name) { return _this.addToSegment(name); });
118
- }
119
- }
120
- return isDiff;
121
- };
122
- MySegmentsCacheInLocal.prototype.getRegisteredSegments = function () {
123
- var _this = this;
124
- return Object.keys(localStorage).reduce(function (accum, key) {
125
- var segmentName = _this.keys.extractSegmentName(key);
126
- if (segmentName)
127
- accum.push(segmentName);
128
- return accum;
129
- }, []);
130
75
  };
131
76
  MySegmentsCacheInLocal.prototype.getKeysCount = function () {
132
77
  return 1;
133
78
  };
79
+ MySegmentsCacheInLocal.prototype.setChangeNumber = function (name, changeNumber) {
80
+ try {
81
+ if (changeNumber)
82
+ localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
83
+ else
84
+ localStorage.removeItem(this.keys.buildTillKey());
85
+ }
86
+ catch (e) {
87
+ this.log.error(e);
88
+ }
89
+ };
134
90
  MySegmentsCacheInLocal.prototype.getChangeNumber = function () {
135
91
  var n = -1;
136
92
  var value = localStorage.getItem(this.keys.buildTillKey());
@@ -14,58 +14,23 @@ var MySegmentsCacheInMemory = /** @class */ (function (_super) {
14
14
  _this.segmentCache = {};
15
15
  return _this;
16
16
  }
17
- MySegmentsCacheInMemory.prototype.clear = function () {
18
- this.segmentCache = {};
19
- };
20
17
  MySegmentsCacheInMemory.prototype.addToSegment = function (name) {
18
+ if (this.segmentCache[name])
19
+ return false;
21
20
  this.segmentCache[name] = true;
22
21
  return true;
23
22
  };
24
23
  MySegmentsCacheInMemory.prototype.removeFromSegment = function (name) {
24
+ if (!this.segmentCache[name])
25
+ return false;
25
26
  delete this.segmentCache[name];
26
27
  return true;
27
28
  };
28
29
  MySegmentsCacheInMemory.prototype.isInSegment = function (name) {
29
30
  return this.segmentCache[name] === true;
30
31
  };
31
- /**
32
- * Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
33
- * @NOTE based on the way we use segments in the browser, this way is the best option
34
- *
35
- * @param {string[]} names list of segment names
36
- * @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
37
- */
38
- MySegmentsCacheInMemory.prototype.resetSegments = function (names, changeNumber) {
39
- var _this = this;
32
+ MySegmentsCacheInMemory.prototype.setChangeNumber = function (name, changeNumber) {
40
33
  this.cn = changeNumber;
41
- var isDiff = false;
42
- var index;
43
- var storedSegmentKeys = Object.keys(this.segmentCache);
44
- // Extreme fast => everything is empty
45
- if (names.length === 0 && storedSegmentKeys.length === names.length)
46
- return isDiff;
47
- // Quick path
48
- if (storedSegmentKeys.length !== names.length) {
49
- isDiff = true;
50
- this.segmentCache = {};
51
- names.forEach(function (s) {
52
- _this.addToSegment(s);
53
- });
54
- }
55
- else {
56
- // Slowest path => we need to find at least 1 difference because
57
- for (index = 0; index < names.length && this.isInSegment(names[index]); index++) {
58
- // TODO: why empty statement?
59
- }
60
- if (index < names.length) {
61
- isDiff = true;
62
- this.segmentCache = {};
63
- names.forEach(function (s) {
64
- _this.addToSegment(s);
65
- });
66
- }
67
- }
68
- return isDiff;
69
34
  };
70
35
  MySegmentsCacheInMemory.prototype.getChangeNumber = function () {
71
36
  return this.cn || -1;
@@ -62,7 +62,7 @@ var TelemetryCacheInMemory = /** @class */ (function () {
62
62
  spC: this.splits && this.splits.getSplitNames().length,
63
63
  seC: this.segments && this.segments.getRegisteredSegments().length,
64
64
  skC: this.segments && this.segments.getKeysCount(),
65
- lseC: this.largeSegments && this.largeSegments.getRegisteredSegments().length,
65
+ lsC: this.largeSegments && this.largeSegments.getRegisteredSegments().length,
66
66
  lskC: this.largeSegments && this.largeSegments.getKeysCount(),
67
67
  sL: this.getSessionLength(),
68
68
  eQ: this.getEventStats(constants_1.QUEUED),
@@ -6,10 +6,10 @@ exports.mySegmentsFetcherFactory = void 0;
6
6
  * MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors.
7
7
  */
8
8
  function mySegmentsFetcherFactory(fetchMemberships) {
9
- return function mySegmentsFetcher(userMatchingKey, noCache,
9
+ return function mySegmentsFetcher(userMatchingKey, noCache, till,
10
10
  // Optional decorator for `fetchMemberships` promise, such as timeout or time tracker
11
11
  decorator) {
12
- var mySegmentsPromise = fetchMemberships(userMatchingKey, noCache);
12
+ var mySegmentsPromise = fetchMemberships(userMatchingKey, noCache, till);
13
13
  if (decorator)
14
14
  mySegmentsPromise = decorator(mySegmentsPromise);
15
15
  return mySegmentsPromise.then(function (resp) { return resp.json(); });
@@ -4,6 +4,7 @@ exports.mySegmentsUpdaterFactory = void 0;
4
4
  var timeout_1 = require("../../../utils/promise/timeout");
5
5
  var constants_1 = require("../../../readiness/constants");
6
6
  var constants_2 = require("../../../logger/constants");
7
+ var constants_3 = require("../../streaming/constants");
7
8
  /**
8
9
  * factory of MySegments updater, a task that:
9
10
  * - fetches mySegments using `mySegmentsFetcher`
@@ -22,26 +23,15 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
22
23
  }
23
24
  // @TODO if allowing pluggable storages, handle async execution
24
25
  function updateSegments(segmentsData) {
25
- var _a, _b, _c, _d;
26
26
  var shouldNotifyUpdate;
27
- if (Array.isArray(segmentsData)) {
28
- // Add/Delete the segment names
29
- segmentsData.forEach(function (_a) {
30
- var isLS = _a.isLS, name = _a.name, add = _a.add;
31
- var cache = isLS ? largeSegments : segments;
32
- if (cache.isInSegment(name) !== add) {
33
- shouldNotifyUpdate = true;
34
- if (add)
35
- cache.addToSegment(name);
36
- else
37
- cache.removeFromSegment(name);
38
- }
39
- });
27
+ if (segmentsData.type !== undefined) {
28
+ shouldNotifyUpdate = segmentsData.type === constants_3.MEMBERSHIPS_LS_UPDATE ?
29
+ largeSegments.resetSegments(segmentsData) :
30
+ segments.resetSegments(segmentsData);
40
31
  }
41
32
  else {
42
- // Reset the list of segment names
43
- shouldNotifyUpdate = segments.resetSegments((((_a = segmentsData.ms) === null || _a === void 0 ? void 0 : _a.k) || []).map(function (segment) { return segment.n; }), (_b = segmentsData.ms) === null || _b === void 0 ? void 0 : _b.cn);
44
- shouldNotifyUpdate = largeSegments.resetSegments((((_c = segmentsData.ls) === null || _c === void 0 ? void 0 : _c.k) || []).map(function (segment) { return segment.n; }), (_d = segmentsData.ls) === null || _d === void 0 ? void 0 : _d.cn) || shouldNotifyUpdate;
33
+ shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
34
+ shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
45
35
  }
46
36
  // Notify update if required
47
37
  if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
@@ -49,12 +39,12 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
49
39
  segmentsEventEmitter.emit(constants_1.SDK_SEGMENTS_ARRIVED);
50
40
  }
51
41
  }
52
- function _mySegmentsUpdater(retry, segmentsData, noCache) {
42
+ function _mySegmentsUpdater(retry, segmentsData, noCache, till) {
53
43
  var updaterPromise = segmentsData ?
54
44
  // If segmentsData is provided, there is no need to fetch mySegments
55
45
  new Promise(function (res) { updateSegments(segmentsData); res(true); }) :
56
46
  // If not provided, fetch mySegments
57
- mySegmentsFetcher(matchingKey, noCache, _promiseDecorator).then(function (segments) {
47
+ mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(function (segments) {
58
48
  // Only when we have downloaded segments completely, we should not keep retrying anymore
59
49
  startingUp = false;
60
50
  updateSegments(segments);
@@ -81,9 +71,10 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
81
71
  * (2) an object with a segment name and action (true: add, or false: delete) to update the storage,
82
72
  * (3) or `undefined`, for which the updater will fetch mySegments in order to sync the storage.
83
73
  * @param {boolean | undefined} noCache true to revalidate data to fetch
74
+ * @param {boolean | undefined} till query param to bypass CDN requests
84
75
  */
85
- return function mySegmentsUpdater(segmentsData, noCache) {
86
- return _mySegmentsUpdater(0, segmentsData, noCache);
76
+ return function mySegmentsUpdater(segmentsData, noCache, till) {
77
+ return _mySegmentsUpdater(0, segmentsData, noCache, till);
87
78
  };
88
79
  }
89
80
  exports.mySegmentsUpdaterFactory = mySegmentsUpdaterFactory;
@@ -13,7 +13,7 @@ var murmur3_1 = require("../../../utils/murmur3/murmur3");
13
13
  function authenticateFactory(fetchAuth) {
14
14
  /**
15
15
  * Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
16
- * @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
16
+ * @param {string[] | undefined} userKeys set of user Keys to track membership updates. It is undefined for server-side API.
17
17
  */
18
18
  return function authenticate(userKeys) {
19
19
  return fetchAuth(userKeys)
@@ -69,18 +69,16 @@ function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
69
69
  var parsedData = messageWithParsedData.parsedData, data = messageWithParsedData.data, channel = messageWithParsedData.channel, timestamp = messageWithParsedData.timestamp;
70
70
  log.debug(constants_2.STREAMING_NEW_MESSAGE, [data]);
71
71
  // we only handle update events if streaming is up
72
- // @ts-expect-error
73
- var type = parsedData.type || parsedData.t;
74
- if (!notificationKeeper.isStreamingUp() && [constants_1.OCCUPANCY, constants_1.CONTROL].indexOf(type) === -1)
72
+ if (!notificationKeeper.isStreamingUp() && [constants_1.OCCUPANCY, constants_1.CONTROL].indexOf(parsedData.type) === -1)
75
73
  return;
76
- switch (type) {
74
+ switch (parsedData.type) {
77
75
  /* update events */
78
76
  case constants_1.SPLIT_UPDATE:
79
77
  case constants_1.SEGMENT_UPDATE:
80
- case constants_1.MY_SEGMENTS_UPDATE_V3:
81
- case constants_1.MY_LARGE_SEGMENTS_UPDATE:
78
+ case constants_1.MEMBERSHIPS_MS_UPDATE:
79
+ case constants_1.MEMBERSHIPS_LS_UPDATE:
82
80
  case constants_1.SPLIT_KILL:
83
- pushEmitter.emit(type, parsedData);
81
+ pushEmitter.emit(parsedData.type, parsedData);
84
82
  break;
85
83
  /* occupancy & control events, handled by NotificationManagerKeeper */
86
84
  case constants_1.OCCUPANCY:
@@ -2,77 +2,120 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MySegmentsUpdateWorker = void 0;
4
4
  var Backoff_1 = require("../../../utils/Backoff");
5
+ var constants_1 = require("../../../utils/constants");
6
+ var constants_2 = require("./constants");
7
+ var constants_3 = require("../constants");
5
8
  /**
6
9
  * MySegmentsUpdateWorker factory
7
10
  */
8
- function MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker, updateType) {
9
- var maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
10
- var currentChangeNumber = -1;
11
- var handleNewEvent = false;
12
- var isHandlingEvent;
13
- var _segmentsData; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
14
- var _delay;
15
- var _delayTimeoutID;
16
- var backoff = new Backoff_1.Backoff(__handleMySegmentsUpdateCall);
17
- function __handleMySegmentsUpdateCall() {
18
- isHandlingEvent = true;
19
- if (maxChangeNumber > currentChangeNumber) {
20
- handleNewEvent = false;
21
- var currentMaxChangeNumber_1 = maxChangeNumber;
22
- // fetch mySegments revalidating data if cached
23
- var syncTask = _delay ?
24
- new Promise(function (res) {
25
- _delayTimeoutID = setTimeout(function () {
26
- _delay = undefined;
27
- mySegmentsSyncTask.execute(_segmentsData, true).then(res);
28
- }, _delay);
29
- }) :
30
- mySegmentsSyncTask.execute(_segmentsData, true);
31
- syncTask.then(function (result) {
32
- if (!isHandlingEvent)
33
- return; // halt if `stop` has been called
34
- if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
35
- if (_segmentsData)
36
- telemetryTracker.trackUpdatesFromSSE(updateType);
37
- currentChangeNumber = Math.max(currentChangeNumber, currentMaxChangeNumber_1); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
38
- }
39
- if (handleNewEvent) {
40
- __handleMySegmentsUpdateCall();
41
- }
42
- else {
43
- backoff.scheduleCall();
44
- }
45
- });
46
- }
47
- else {
48
- isHandlingEvent = false;
11
+ function MySegmentsUpdateWorker(log, storage, mySegmentsSyncTask, telemetryTracker) {
12
+ var _a;
13
+ function createUpdateWorker(mySegmentsCache) {
14
+ var maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
15
+ var currentChangeNumber = -1;
16
+ var handleNewEvent = false;
17
+ var isHandlingEvent;
18
+ var cdnBypass;
19
+ var _segmentsData; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
20
+ var _delay;
21
+ var _delayTimeoutID;
22
+ var backoff = new Backoff_1.Backoff(__handleMySegmentsUpdateCall);
23
+ function __handleMySegmentsUpdateCall() {
24
+ isHandlingEvent = true;
25
+ if (maxChangeNumber > Math.max(currentChangeNumber, mySegmentsCache.getChangeNumber())) {
26
+ handleNewEvent = false;
27
+ var currentMaxChangeNumber_1 = maxChangeNumber;
28
+ // fetch mySegments revalidating data if cached
29
+ var syncTask = _delay ?
30
+ new Promise(function (res) {
31
+ _delayTimeoutID = setTimeout(function () {
32
+ _delay = undefined;
33
+ mySegmentsSyncTask.execute(_segmentsData, true, cdnBypass ? maxChangeNumber : undefined).then(res);
34
+ }, _delay);
35
+ }) :
36
+ mySegmentsSyncTask.execute(_segmentsData, true, cdnBypass ? maxChangeNumber : undefined);
37
+ syncTask.then(function (result) {
38
+ if (!isHandlingEvent)
39
+ return; // halt if `stop` has been called
40
+ if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, `mySegmentsCache.getChangeNumber` can be -1, since `/memberships` change number is optional
41
+ var storageChangeNumber = mySegmentsCache.getChangeNumber();
42
+ currentChangeNumber = storageChangeNumber > -1 ?
43
+ storageChangeNumber :
44
+ Math.max(currentChangeNumber, currentMaxChangeNumber_1); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
45
+ }
46
+ if (handleNewEvent) {
47
+ __handleMySegmentsUpdateCall();
48
+ }
49
+ else {
50
+ if (_segmentsData)
51
+ telemetryTracker.trackUpdatesFromSSE(constants_1.MEMBERSHIPS);
52
+ var attempts = backoff.attempts + 1;
53
+ if (maxChangeNumber <= currentChangeNumber) {
54
+ log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
55
+ isHandlingEvent = false;
56
+ return;
57
+ }
58
+ if (attempts < constants_2.FETCH_BACKOFF_MAX_RETRIES) {
59
+ backoff.scheduleCall();
60
+ return;
61
+ }
62
+ if (cdnBypass) {
63
+ log.debug("No changes fetched after " + attempts + " attempts with CDN bypassed.");
64
+ isHandlingEvent = false;
65
+ }
66
+ else {
67
+ backoff.reset();
68
+ cdnBypass = true;
69
+ __handleMySegmentsUpdateCall();
70
+ }
71
+ }
72
+ });
73
+ }
74
+ else {
75
+ isHandlingEvent = false;
76
+ }
49
77
  }
78
+ return {
79
+ /**
80
+ * Invoked by NotificationProcessor on MY_(LARGE)_SEGMENTS_UPDATE notifications
81
+ *
82
+ * @param changeNumber change number of the notification
83
+ * @param segmentsData data for KeyList or SegmentRemoval instant updates
84
+ * @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
85
+ */
86
+ put: function (mySegmentsData, payload, delay) {
87
+ var type = mySegmentsData.type, cn = mySegmentsData.cn;
88
+ // Ignore event if it is outdated or if there is a pending fetch request (_delay is set)
89
+ if (cn <= Math.max(currentChangeNumber, mySegmentsCache.getChangeNumber()) || cn <= maxChangeNumber || _delay)
90
+ return;
91
+ maxChangeNumber = cn;
92
+ handleNewEvent = true;
93
+ cdnBypass = false;
94
+ _segmentsData = payload && { type: type, cn: cn, added: payload.added, removed: payload.removed };
95
+ _delay = delay;
96
+ if (backoff.timeoutID || !isHandlingEvent)
97
+ __handleMySegmentsUpdateCall();
98
+ backoff.reset();
99
+ },
100
+ stop: function () {
101
+ clearTimeout(_delayTimeoutID);
102
+ _delay = undefined;
103
+ isHandlingEvent = false;
104
+ backoff.reset();
105
+ }
106
+ };
50
107
  }
108
+ var updateWorkers = (_a = {},
109
+ _a[constants_3.MEMBERSHIPS_MS_UPDATE] = createUpdateWorker(storage.segments),
110
+ _a[constants_3.MEMBERSHIPS_LS_UPDATE] = createUpdateWorker(storage.largeSegments),
111
+ _a);
51
112
  return {
52
- /**
53
- * Invoked by NotificationProcessor on MY_(LARGE)_SEGMENTS_UPDATE notifications
54
- *
55
- * @param changeNumber change number of the notification
56
- * @param segmentsData data for KeyList or SegmentRemoval instant updates
57
- * @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
58
- */
59
- put: function (changeNumber, segmentsData, delay) {
60
- // Ignore event if it is outdated or if there is a pending fetch request (_delay is set)
61
- if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber || _delay)
62
- return;
63
- maxChangeNumber = changeNumber;
64
- handleNewEvent = true;
65
- _segmentsData = segmentsData;
66
- _delay = delay;
67
- if (backoff.timeoutID || !isHandlingEvent)
68
- __handleMySegmentsUpdateCall();
69
- backoff.reset();
113
+ put: function (mySegmentsData, payload, delay) {
114
+ updateWorkers[mySegmentsData.type].put(mySegmentsData, payload, delay);
70
115
  },
71
116
  stop: function () {
72
- clearTimeout(_delayTimeoutID);
73
- _delay = undefined;
74
- isHandlingEvent = false;
75
- backoff.reset();
117
+ updateWorkers[constants_3.MEMBERSHIPS_MS_UPDATE].stop();
118
+ updateWorkers[constants_3.MEMBERSHIPS_LS_UPDATE].stop();
76
119
  }
77
120
  };
78
121
  }