@splitsoftware/splitio-commons 1.17.0 → 1.17.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/CHANGES.txt +6 -0
  2. package/cjs/evaluator/matchers/index.js +3 -1
  3. package/cjs/evaluator/matchers/large_segment.js +16 -0
  4. package/cjs/evaluator/matchers/matcherTypes.js +1 -0
  5. package/cjs/evaluator/matchersTransform/index.js +4 -1
  6. package/cjs/evaluator/matchersTransform/segment.js +3 -1
  7. package/cjs/logger/constants.js +2 -2
  8. package/cjs/logger/messages/info.js +1 -1
  9. package/cjs/logger/messages/warn.js +1 -1
  10. package/cjs/readiness/readinessManager.js +5 -6
  11. package/cjs/readiness/sdkReadinessManager.js +5 -6
  12. package/cjs/sdkClient/sdkClientMethodCS.js +2 -2
  13. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +2 -2
  14. package/cjs/sdkFactory/index.js +1 -1
  15. package/cjs/services/splitApi.js +5 -5
  16. package/cjs/storages/AbstractSegmentsCacheSync.js +41 -12
  17. package/cjs/storages/AbstractSplitsCacheSync.js +2 -1
  18. package/cjs/storages/KeyBuilderCS.js +23 -5
  19. package/cjs/storages/dataLoader.js +1 -1
  20. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
  21. package/cjs/storages/inLocalStorage/index.js +6 -2
  22. package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -0
  23. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
  24. package/cjs/storages/inMemory/SplitsCacheInMemory.js +8 -8
  25. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +7 -10
  26. package/cjs/storages/pluggable/inMemoryWrapper.js +1 -1
  27. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +5 -8
  28. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  29. package/cjs/sync/polling/pollingManagerCS.js +1 -1
  30. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  31. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +15 -21
  32. package/cjs/sync/streaming/AuthClient/index.js +1 -1
  33. package/cjs/sync/streaming/SSEHandler/index.js +3 -5
  34. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +107 -48
  35. package/cjs/sync/streaming/constants.js +3 -3
  36. package/cjs/sync/streaming/parseUtils.js +14 -9
  37. package/cjs/sync/streaming/pushManager.js +69 -67
  38. package/cjs/utils/constants/index.js +5 -4
  39. package/cjs/utils/settingsValidation/index.js +2 -1
  40. package/esm/evaluator/matchers/index.js +3 -1
  41. package/esm/evaluator/matchers/large_segment.js +12 -0
  42. package/esm/evaluator/matchers/matcherTypes.js +1 -0
  43. package/esm/evaluator/matchersTransform/index.js +4 -1
  44. package/esm/evaluator/matchersTransform/segment.js +3 -1
  45. package/esm/logger/constants.js +1 -1
  46. package/esm/logger/messages/info.js +1 -1
  47. package/esm/logger/messages/warn.js +1 -1
  48. package/esm/readiness/readinessManager.js +5 -6
  49. package/esm/readiness/sdkReadinessManager.js +5 -6
  50. package/esm/sdkClient/sdkClientMethodCS.js +2 -2
  51. package/esm/sdkClient/sdkClientMethodCSWithTT.js +2 -2
  52. package/esm/sdkFactory/index.js +1 -1
  53. package/esm/services/splitApi.js +6 -6
  54. package/esm/storages/AbstractSegmentsCacheSync.js +41 -12
  55. package/esm/storages/AbstractSplitsCacheSync.js +3 -2
  56. package/esm/storages/KeyBuilderCS.js +21 -4
  57. package/esm/storages/dataLoader.js +1 -1
  58. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
  59. package/esm/storages/inLocalStorage/index.js +7 -3
  60. package/esm/storages/inMemory/InMemoryStorageCS.js +5 -0
  61. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
  62. package/esm/storages/inMemory/SplitsCacheInMemory.js +8 -8
  63. package/esm/storages/inMemory/TelemetryCacheInMemory.js +7 -10
  64. package/esm/storages/pluggable/inMemoryWrapper.js +1 -1
  65. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +5 -8
  66. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  67. package/esm/sync/polling/pollingManagerCS.js +1 -1
  68. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  69. package/esm/sync/polling/updaters/mySegmentsUpdater.js +15 -21
  70. package/esm/sync/streaming/AuthClient/index.js +1 -1
  71. package/esm/sync/streaming/SSEHandler/index.js +4 -6
  72. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +108 -49
  73. package/esm/sync/streaming/constants.js +2 -2
  74. package/esm/sync/streaming/parseUtils.js +12 -8
  75. package/esm/sync/streaming/pushManager.js +72 -70
  76. package/esm/utils/constants/index.js +3 -2
  77. package/esm/utils/settingsValidation/index.js +2 -1
  78. package/package.json +1 -1
  79. package/src/dtos/types.ts +21 -7
  80. package/src/evaluator/matchers/index.ts +2 -0
  81. package/src/evaluator/matchers/large_segment.ts +18 -0
  82. package/src/evaluator/matchers/matcherTypes.ts +1 -0
  83. package/src/evaluator/matchersTransform/index.ts +4 -1
  84. package/src/evaluator/matchersTransform/segment.ts +5 -3
  85. package/src/logger/constants.ts +1 -1
  86. package/src/logger/messages/info.ts +1 -1
  87. package/src/logger/messages/warn.ts +1 -1
  88. package/src/readiness/readinessManager.ts +7 -5
  89. package/src/readiness/sdkReadinessManager.ts +7 -7
  90. package/src/readiness/types.ts +2 -2
  91. package/src/sdkClient/sdkClientMethodCS.ts +2 -2
  92. package/src/sdkClient/sdkClientMethodCSWithTT.ts +2 -2
  93. package/src/sdkFactory/index.ts +1 -1
  94. package/src/services/splitApi.ts +7 -7
  95. package/src/services/splitHttpClient.ts +1 -1
  96. package/src/services/types.ts +2 -2
  97. package/src/storages/AbstractSegmentsCacheSync.ts +53 -12
  98. package/src/storages/AbstractSplitsCacheSync.ts +4 -3
  99. package/src/storages/KeyBuilderCS.ts +34 -5
  100. package/src/storages/dataLoader.ts +1 -1
  101. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +29 -59
  102. package/src/storages/inLocalStorage/index.ts +8 -4
  103. package/src/storages/inMemory/InMemoryStorageCS.ts +5 -0
  104. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +10 -44
  105. package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -8
  106. package/src/storages/inMemory/TelemetryCacheInMemory.ts +7 -11
  107. package/src/storages/pluggable/inMemoryWrapper.ts +1 -1
  108. package/src/storages/types.ts +11 -7
  109. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +8 -10
  110. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
  111. package/src/sync/polling/fetchers/types.ts +3 -2
  112. package/src/sync/polling/pollingManagerCS.ts +4 -4
  113. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +4 -5
  114. package/src/sync/polling/types.ts +7 -6
  115. package/src/sync/polling/updaters/mySegmentsUpdater.ts +19 -22
  116. package/src/sync/streaming/AuthClient/index.ts +1 -1
  117. package/src/sync/streaming/SSEClient/index.ts +4 -6
  118. package/src/sync/streaming/SSEHandler/index.ts +5 -8
  119. package/src/sync/streaming/SSEHandler/types.ts +15 -15
  120. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +116 -49
  121. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
  122. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
  123. package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
  124. package/src/sync/streaming/constants.ts +2 -2
  125. package/src/sync/streaming/parseUtils.ts +19 -11
  126. package/src/sync/streaming/pushManager.ts +73 -72
  127. package/src/sync/streaming/types.ts +10 -10
  128. package/src/sync/submitters/types.ts +8 -5
  129. package/src/utils/constants/index.ts +3 -2
  130. package/src/utils/settingsValidation/index.ts +3 -2
  131. package/src/utils/settingsValidation/types.ts +1 -1
  132. package/types/dtos/types.d.ts +18 -7
  133. package/types/evaluator/matchersTransform/segment.d.ts +2 -2
  134. package/types/logger/constants.d.ts +1 -1
  135. package/types/readiness/readinessManager.d.ts +2 -2
  136. package/types/readiness/sdkReadinessManager.d.ts +2 -3
  137. package/types/readiness/types.d.ts +2 -2
  138. package/types/services/splitApi.d.ts +1 -1
  139. package/types/services/splitHttpClient.d.ts +1 -1
  140. package/types/services/types.d.ts +2 -2
  141. package/types/storages/AbstractSegmentsCacheSync.d.ts +9 -11
  142. package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
  143. package/types/storages/KeyBuilderCS.d.ts +9 -2
  144. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +4 -14
  145. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -9
  146. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -1
  147. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +4 -6
  148. package/types/storages/pluggable/inMemoryWrapper.d.ts +1 -1
  149. package/types/storages/types.d.ts +7 -5
  150. package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
  151. package/types/sync/polling/fetchers/types.d.ts +2 -2
  152. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -2
  153. package/types/sync/polling/types.d.ts +7 -4
  154. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +4 -3
  155. package/types/sync/streaming/SSEHandler/types.d.ts +16 -14
  156. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +4 -2
  157. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  158. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  159. package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
  160. package/types/sync/streaming/constants.d.ts +2 -2
  161. package/types/sync/streaming/parseUtils.d.ts +4 -5
  162. package/types/sync/streaming/types.d.ts +8 -8
  163. package/types/sync/submitters/types.d.ts +7 -4
  164. package/types/utils/constants/index.d.ts +3 -2
  165. package/types/utils/settingsValidation/types.d.ts +1 -1
@@ -1,61 +1,120 @@
1
1
  import { Backoff } from '../../../utils/Backoff';
2
- import { MY_SEGMENT } from '../../../utils/constants';
2
+ import { MEMBERSHIPS } from '../../../utils/constants';
3
+ import { FETCH_BACKOFF_MAX_RETRIES } from './constants';
4
+ import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../constants';
3
5
  /**
4
6
  * MySegmentsUpdateWorker factory
5
7
  */
6
- export function MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker) {
7
- var maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
8
- var currentChangeNumber = -1;
9
- var handleNewEvent = false;
10
- var isHandlingEvent;
11
- var _segmentsData; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
12
- var backoff = new Backoff(__handleMySegmentsUpdateCall);
13
- function __handleMySegmentsUpdateCall() {
14
- isHandlingEvent = true;
15
- if (maxChangeNumber > currentChangeNumber) {
16
- handleNewEvent = false;
17
- var currentMaxChangeNumber_1 = maxChangeNumber;
18
- // fetch mySegments revalidating data if cached
19
- mySegmentsSyncTask.execute(_segmentsData, true).then(function (result) {
20
- if (!isHandlingEvent)
21
- return; // halt if `stop` has been called
22
- if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
23
- if (_segmentsData)
24
- telemetryTracker.trackUpdatesFromSSE(MY_SEGMENT);
25
- currentChangeNumber = Math.max(currentChangeNumber, currentMaxChangeNumber_1); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
26
- }
27
- if (handleNewEvent) {
28
- __handleMySegmentsUpdateCall();
29
- }
30
- else {
31
- backoff.scheduleCall();
32
- }
33
- });
34
- }
35
- else {
36
- isHandlingEvent = false;
8
+ export function MySegmentsUpdateWorker(log, storage, mySegmentsSyncTask, telemetryTracker) {
9
+ var _a;
10
+ var _delay;
11
+ var _delayTimeoutID;
12
+ function createUpdateWorker(mySegmentsCache) {
13
+ var maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
14
+ var currentChangeNumber = -1;
15
+ var handleNewEvent = false;
16
+ var isHandlingEvent;
17
+ var cdnBypass;
18
+ var _segmentsData; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
19
+ var backoff = new Backoff(__handleMySegmentsUpdateCall);
20
+ function __handleMySegmentsUpdateCall() {
21
+ isHandlingEvent = true;
22
+ if (maxChangeNumber > Math.max(currentChangeNumber, mySegmentsCache.getChangeNumber())) {
23
+ handleNewEvent = false;
24
+ var currentMaxChangeNumber_1 = maxChangeNumber;
25
+ // fetch mySegments revalidating data if cached
26
+ var syncTask = _delay ?
27
+ new Promise(function (res) {
28
+ _delayTimeoutID = setTimeout(function () {
29
+ _delay = undefined;
30
+ mySegmentsSyncTask.execute(_segmentsData, true, cdnBypass ? maxChangeNumber : undefined).then(res);
31
+ }, _delay);
32
+ }) :
33
+ mySegmentsSyncTask.execute(_segmentsData, true, cdnBypass ? maxChangeNumber : undefined);
34
+ syncTask.then(function (result) {
35
+ if (!isHandlingEvent)
36
+ return; // halt if `stop` has been called
37
+ if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, `mySegmentsCache.getChangeNumber` can be -1, since `/memberships` change number is optional
38
+ var storageChangeNumber = mySegmentsCache.getChangeNumber();
39
+ currentChangeNumber = storageChangeNumber > -1 ?
40
+ storageChangeNumber :
41
+ Math.max(currentChangeNumber, currentMaxChangeNumber_1); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
42
+ }
43
+ if (handleNewEvent) {
44
+ __handleMySegmentsUpdateCall();
45
+ }
46
+ else {
47
+ if (_segmentsData)
48
+ telemetryTracker.trackUpdatesFromSSE(MEMBERSHIPS);
49
+ var attempts = backoff.attempts + 1;
50
+ if (maxChangeNumber <= currentChangeNumber) {
51
+ log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
52
+ isHandlingEvent = false;
53
+ return;
54
+ }
55
+ if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
56
+ backoff.scheduleCall();
57
+ return;
58
+ }
59
+ if (cdnBypass) {
60
+ log.debug("No changes fetched after " + attempts + " attempts with CDN bypassed.");
61
+ isHandlingEvent = false;
62
+ }
63
+ else {
64
+ backoff.reset();
65
+ cdnBypass = true;
66
+ __handleMySegmentsUpdateCall();
67
+ }
68
+ }
69
+ });
70
+ }
71
+ else {
72
+ isHandlingEvent = false;
73
+ }
37
74
  }
75
+ return {
76
+ /**
77
+ * Invoked by NotificationProcessor on MY_(LARGE)_SEGMENTS_UPDATE notifications
78
+ *
79
+ * @param changeNumber change number of the notification
80
+ * @param segmentsData data for KeyList or SegmentRemoval instant updates
81
+ * @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
82
+ */
83
+ put: function (mySegmentsData, payload, delay) {
84
+ var type = mySegmentsData.type, cn = mySegmentsData.cn;
85
+ // Discard event if it is outdated or there is a pending fetch request (_delay is set), but update target change number
86
+ if (cn <= Math.max(currentChangeNumber, mySegmentsCache.getChangeNumber()) || cn <= maxChangeNumber)
87
+ return;
88
+ maxChangeNumber = cn;
89
+ if (_delay)
90
+ return;
91
+ handleNewEvent = true;
92
+ cdnBypass = false;
93
+ _segmentsData = payload && { type: type, cn: cn, added: payload.added, removed: payload.removed };
94
+ _delay = delay;
95
+ if (backoff.timeoutID || !isHandlingEvent)
96
+ __handleMySegmentsUpdateCall();
97
+ backoff.reset();
98
+ },
99
+ stop: function () {
100
+ clearTimeout(_delayTimeoutID);
101
+ _delay = undefined;
102
+ isHandlingEvent = false;
103
+ backoff.reset();
104
+ }
105
+ };
38
106
  }
107
+ var updateWorkers = (_a = {},
108
+ _a[MEMBERSHIPS_MS_UPDATE] = createUpdateWorker(storage.segments),
109
+ _a[MEMBERSHIPS_LS_UPDATE] = createUpdateWorker(storage.largeSegments),
110
+ _a);
39
111
  return {
40
- /**
41
- * Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
42
- *
43
- * @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
44
- * @param {SegmentsData | undefined} segmentsData might be undefined
45
- */
46
- put: function (changeNumber, segmentsData) {
47
- if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
48
- return;
49
- maxChangeNumber = changeNumber;
50
- handleNewEvent = true;
51
- _segmentsData = segmentsData;
52
- if (backoff.timeoutID || !isHandlingEvent)
53
- __handleMySegmentsUpdateCall();
54
- backoff.reset();
112
+ put: function (mySegmentsData, payload, delay) {
113
+ updateWorkers[mySegmentsData.type].put(mySegmentsData, payload, delay);
55
114
  },
56
115
  stop: function () {
57
- isHandlingEvent = false;
58
- backoff.reset();
116
+ updateWorkers[MEMBERSHIPS_MS_UPDATE].stop();
117
+ updateWorkers[MEMBERSHIPS_LS_UPDATE].stop();
59
118
  }
60
119
  };
61
120
  }
@@ -22,8 +22,8 @@ export var PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP';
22
22
  */
23
23
  export var PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN';
24
24
  // Update-type push notifications, handled by NotificationProcessor
25
- export var MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
26
- export var MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
25
+ export var MEMBERSHIPS_MS_UPDATE = 'MEMBERSHIPS_MS_UPDATE';
26
+ export var MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
27
27
  export var SEGMENT_UPDATE = 'SEGMENT_UPDATE';
28
28
  export var SPLIT_KILL = 'SPLIT_KILL';
29
29
  export var SPLIT_UPDATE = 'SPLIT_UPDATE';
@@ -1,5 +1,6 @@
1
1
  import { algorithms } from '../../utils/decompress';
2
2
  import { decodeFromBase64 } from '../../utils/base64';
3
+ import { hash } from '../../utils/murmur3/murmur3';
3
4
  var GZIP = 1;
4
5
  var ZLIB = 2;
5
6
  function Uint8ArrayToString(myUint8Arr) {
@@ -74,14 +75,17 @@ export function isInBitmap(bitmap, hash64hex) {
74
75
  }
75
76
  /**
76
77
  * Parse feature flags notifications for instant feature flag updates
77
- *
78
- * @param {ISplitUpdateData} data
79
- * @returns {KeyList}
80
78
  */
81
79
  export function parseFFUpdatePayload(compression, data) {
82
- var avoidPrecisionLoss = false;
83
- if (compression > 0)
84
- return parseKeyList(data, compression, avoidPrecisionLoss);
85
- else
86
- return JSON.parse(decodeFromBase64(data));
80
+ return compression > 0 ?
81
+ parseKeyList(data, compression, false) :
82
+ JSON.parse(decodeFromBase64(data));
83
+ }
84
+ var DEFAULT_MAX_INTERVAL = 60000;
85
+ export function getDelay(parsedData, matchingKey) {
86
+ if (parsedData.h === 0)
87
+ return 0;
88
+ var interval = parsedData.i || DEFAULT_MAX_INTERVAL;
89
+ var seed = parsedData.s || 0;
90
+ return hash(matchingKey, seed) % interval;
87
91
  }
@@ -8,10 +8,10 @@ import { authenticateFactory, hashUserKey } from './AuthClient';
8
8
  import { forOwn } from '../../utils/lang';
9
9
  import { SSEClient } from './SSEClient';
10
10
  import { getMatching } from '../../utils/key';
11
- import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
12
- import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, STREAMING_PARSING_SPLIT_UPDATE } from '../../logger/constants';
11
+ import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
12
+ import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE, STREAMING_PARSING_SPLIT_UPDATE } from '../../logger/constants';
13
13
  import { UpdateStrategy } from './SSEHandler/types';
14
- import { isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
14
+ import { getDelay, isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
15
15
  import { _Set } from '../../utils/lang/sets';
16
16
  import { hash64 } from '../../utils/murmur3/murmur3_64';
17
17
  import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
@@ -45,10 +45,10 @@ export function pushManagerFactory(params, pollingManager) {
45
45
  var segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask, storage.segments);
46
46
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
47
47
  var splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask);
48
- // [Only for client-side] map of hashes to user keys, to dispatch MY_SEGMENTS_UPDATE events to the corresponding MySegmentsUpdateWorker
48
+ // [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker
49
49
  var userKeyHashes = {};
50
50
  // [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
51
- // Hash64 is used to process MY_SEGMENTS_UPDATE_V2 events and dispatch actions to the corresponding MySegmentsUpdateWorker.
51
+ // Hash64 is used to process membership update events and dispatch actions to the corresponding MySegmentsUpdateWorker.
52
52
  var clients = {};
53
53
  // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming.
54
54
  var connectForNewClient = false;
@@ -195,77 +195,76 @@ export function pushManagerFactory(params, pollingManager) {
195
195
  }
196
196
  splitsUpdateWorker.put(parsedData);
197
197
  });
198
- if (userKey) {
199
- pushEmitter.on(MY_SEGMENTS_UPDATE, function handleMySegmentsUpdate(parsedData, channel) {
200
- var userKeyHash = channel.split('_')[2];
201
- var userKey = userKeyHashes[userKeyHash];
202
- if (userKey && clients[userKey]) { // check existence since it can be undefined if client has been destroyed
203
- clients[userKey].worker.put(parsedData.changeNumber, parsedData.includesPayload ? parsedData.segmentList ? parsedData.segmentList : [] : undefined);
204
- }
205
- });
206
- pushEmitter.on(MY_SEGMENTS_UPDATE_V2, function handleMySegmentsUpdate(parsedData) {
207
- switch (parsedData.u) {
208
- case UpdateStrategy.BoundedFetchRequest: {
209
- var bitmap_1;
210
- try {
211
- bitmap_1 = parseBitmap(parsedData.d, parsedData.c);
212
- }
213
- catch (e) {
214
- log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['BoundedFetchRequest', e]);
215
- break;
216
- }
217
- forOwn(clients, function (_a) {
218
- var hash64 = _a.hash64, worker = _a.worker;
219
- if (isInBitmap(bitmap_1, hash64.hex)) {
220
- worker.put(parsedData.changeNumber); // fetch mySegments
221
- }
222
- });
223
- return;
198
+ function handleMySegmentsUpdate(parsedData) {
199
+ switch (parsedData.u) {
200
+ case UpdateStrategy.BoundedFetchRequest: {
201
+ var bitmap_1;
202
+ try {
203
+ bitmap_1 = parseBitmap(parsedData.d, parsedData.c);
224
204
  }
225
- case UpdateStrategy.KeyList: {
226
- var keyList = void 0, added_1, removed_1;
227
- try {
228
- keyList = parseKeyList(parsedData.d, parsedData.c);
229
- added_1 = new _Set(keyList.a);
230
- removed_1 = new _Set(keyList.r);
231
- }
232
- catch (e) {
233
- log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['KeyList', e]);
234
- break;
235
- }
236
- forOwn(clients, function (_a) {
237
- var hash64 = _a.hash64, worker = _a.worker;
238
- var add = added_1.has(hash64.dec) ? true : removed_1.has(hash64.dec) ? false : undefined;
239
- if (add !== undefined) {
240
- worker.put(parsedData.changeNumber, {
241
- name: parsedData.segmentName,
242
- add: add
243
- });
244
- }
245
- });
246
- return;
205
+ catch (e) {
206
+ log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['BoundedFetchRequest', e]);
207
+ break;
247
208
  }
248
- case UpdateStrategy.SegmentRemoval:
249
- if (!parsedData.segmentName) {
250
- log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['SegmentRemoval', 'No segment name was provided']);
251
- break;
209
+ forOwn(clients, function (_a, matchingKey) {
210
+ var hash64 = _a.hash64, worker = _a.worker;
211
+ if (isInBitmap(bitmap_1, hash64.hex)) {
212
+ worker.put(parsedData, undefined, getDelay(parsedData, matchingKey));
252
213
  }
253
- forOwn(clients, function (_a) {
254
- var worker = _a.worker;
255
- return worker.put(parsedData.changeNumber, {
256
- name: parsedData.segmentName,
257
- add: false
214
+ });
215
+ return;
216
+ }
217
+ case UpdateStrategy.KeyList: {
218
+ var keyList = void 0, added_1, removed_1;
219
+ try {
220
+ keyList = parseKeyList(parsedData.d, parsedData.c);
221
+ added_1 = new _Set(keyList.a);
222
+ removed_1 = new _Set(keyList.r);
223
+ }
224
+ catch (e) {
225
+ log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['KeyList', e]);
226
+ break;
227
+ }
228
+ if (!parsedData.n || !parsedData.n.length) {
229
+ log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['KeyList', 'No segment name was provided']);
230
+ break;
231
+ }
232
+ forOwn(clients, function (_a) {
233
+ var hash64 = _a.hash64, worker = _a.worker;
234
+ var add = added_1.has(hash64.dec) ? true : removed_1.has(hash64.dec) ? false : undefined;
235
+ if (add !== undefined) {
236
+ worker.put(parsedData, {
237
+ added: add ? [parsedData.n[0]] : [],
238
+ removed: add ? [] : [parsedData.n[0]]
258
239
  });
259
- });
260
- return;
240
+ }
241
+ });
242
+ return;
261
243
  }
262
- // `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
263
- forOwn(clients, function (_a) {
264
- var worker = _a.worker;
265
- worker.put(parsedData.changeNumber);
266
- });
244
+ case UpdateStrategy.SegmentRemoval:
245
+ if (!parsedData.n || !parsedData.n.length) {
246
+ log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['SegmentRemoval', 'No segment name was provided']);
247
+ break;
248
+ }
249
+ forOwn(clients, function (_a) {
250
+ var worker = _a.worker;
251
+ worker.put(parsedData, {
252
+ added: [],
253
+ removed: parsedData.n
254
+ });
255
+ });
256
+ return;
257
+ }
258
+ // `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
259
+ forOwn(clients, function (_a, matchingKey) {
260
+ var worker = _a.worker;
261
+ worker.put(parsedData, undefined, getDelay(parsedData, matchingKey));
267
262
  });
268
263
  }
264
+ if (userKey) {
265
+ pushEmitter.on(MEMBERSHIPS_MS_UPDATE, handleMySegmentsUpdate);
266
+ pushEmitter.on(MEMBERSHIPS_LS_UPDATE, handleMySegmentsUpdate);
267
+ }
269
268
  else {
270
269
  pushEmitter.on(SEGMENT_UPDATE, segmentsUpdateWorker.put);
271
270
  }
@@ -300,7 +299,10 @@ export function pushManagerFactory(params, pollingManager) {
300
299
  var hash = hashUserKey(userKey);
301
300
  if (!userKeyHashes[hash]) {
302
301
  userKeyHashes[hash] = userKey;
303
- clients[userKey] = { hash64: hash64(userKey), worker: MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker) };
302
+ clients[userKey] = {
303
+ hash64: hash64(userKey),
304
+ worker: MySegmentsUpdateWorker(log, storage, mySegmentsSyncTask, telemetryTracker)
305
+ };
304
306
  connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
305
307
  // Reconnects in case of a new client.
306
308
  // Run in next event-loop cycle to save authentication calls
@@ -60,7 +60,7 @@ export var EVENTS = 'ev';
60
60
  export var TELEMETRY = 'te';
61
61
  export var TOKEN = 'to';
62
62
  export var SEGMENT = 'se';
63
- export var MY_SEGMENT = 'ms';
63
+ export var MEMBERSHIPS = 'ms';
64
64
  export var TREATMENT = 't';
65
65
  export var TREATMENTS = 'ts';
66
66
  export var TREATMENT_WITH_CONFIG = 'tc';
@@ -86,6 +86,7 @@ export var NON_REQUESTED = 1;
86
86
  export var DISABLED = 0;
87
87
  export var ENABLED = 1;
88
88
  export var PAUSED = 2;
89
- export var FLAG_SPEC_VERSION = '1.1';
89
+ export var FLAG_SPEC_VERSION = '1.2';
90
90
  // Matcher types
91
91
  export var IN_SEGMENT = 'IN_SEGMENT';
92
+ export var IN_LARGE_SEGMENT = 'IN_LARGE_SEGMENT';
@@ -183,9 +183,10 @@ export function settingsValidation(config, validationParams) {
183
183
  var splitFiltersValidation = validateSplitFilters(log, sync.splitFilters, withDefaults.mode);
184
184
  sync.splitFilters = splitFiltersValidation.validFilters;
185
185
  sync.__splitFiltersValidation = splitFiltersValidation;
186
+ // ensure a valid flag spec version
186
187
  sync.flagSpecVersion = flagSpec ? flagSpec(withDefaults) : FLAG_SPEC_VERSION;
187
188
  // ensure a valid user consent value
188
189
  // @ts-ignore, modify readonly prop
189
- withDefaults.userConsent = consent(withDefaults);
190
+ withDefaults.userConsent = consent ? consent(withDefaults) : undefined;
190
191
  return withDefaults;
191
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.17.0",
3
+ "version": "1.17.1-rc.0",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
package/src/dtos/types.ts CHANGED
@@ -30,6 +30,10 @@ export interface IInSegmentMatcherData {
30
30
  segmentName: string
31
31
  }
32
32
 
33
+ export interface IInLargeSegmentMatcherData {
34
+ largeSegmentName: string
35
+ }
36
+
33
37
  export interface IDependencyMatcherData {
34
38
  split: string,
35
39
  treatments: string[]
@@ -43,6 +47,7 @@ interface ISplitMatcherBase {
43
47
  attribute: string | null
44
48
  }
45
49
  userDefinedSegmentMatcherData?: null | IInSegmentMatcherData
50
+ userDefinedLargeSegmentMatcherData?: null | IInLargeSegmentMatcherData
46
51
  whitelistMatcherData?: null | IWhitelistMatcherData
47
52
  unaryNumericMatcherData?: null | IUnaryNumericMatcherData
48
53
  betweenMatcherData?: null | IBetweenMatcherData
@@ -61,6 +66,11 @@ interface IInSegmentMatcher extends ISplitMatcherBase {
61
66
  userDefinedSegmentMatcherData: IInSegmentMatcherData
62
67
  }
63
68
 
69
+ interface IInLargeSegmentMatcher extends ISplitMatcherBase {
70
+ matcherType: 'IN_LARGE_SEGMENT',
71
+ userDefinedLargeSegmentMatcherData: IInLargeSegmentMatcherData
72
+ }
73
+
64
74
  interface IWhitelistMatcher extends ISplitMatcherBase {
65
75
  matcherType: 'WHITELIST',
66
76
  whitelistMatcherData: IWhitelistMatcherData
@@ -165,7 +175,8 @@ interface IInListSemverMatcher extends ISplitMatcherBase {
165
175
  export type ISplitMatcher = IAllKeysMatcher | IInSegmentMatcher | IWhitelistMatcher | IEqualToMatcher | IGreaterThanOrEqualToMatcher |
166
176
  ILessThanOrEqualToMatcher | IBetweenMatcher | IEqualToSetMatcher | IContainsAnyOfSetMatcher | IContainsAllOfSetMatcher | IPartOfSetMatcher |
167
177
  IStartsWithMatcher | IEndsWithMatcher | IContainsStringMatcher | IInSplitTreatmentMatcher | IEqualToBooleanMatcher | IMatchesStringMatcher |
168
- IEqualToSemverMatcher | IGreaterThanOrEqualToSemverMatcher | ILessThanOrEqualToSemverMatcher | IBetweenSemverMatcher | IInListSemverMatcher
178
+ IEqualToSemverMatcher | IGreaterThanOrEqualToSemverMatcher | ILessThanOrEqualToSemverMatcher | IBetweenSemverMatcher | IInListSemverMatcher |
179
+ IInLargeSegmentMatcher
169
180
 
170
181
  /** Split object */
171
182
  export interface ISplitPartition {
@@ -218,14 +229,17 @@ export interface ISegmentChangesResponse {
218
229
  till: number
219
230
  }
220
231
 
221
- export interface IMySegmentsResponseItem {
222
- id: string,
223
- name: string
232
+ export interface IMySegmentsResponse {
233
+ cn?: number,
234
+ k?: {
235
+ n: string
236
+ }[]
224
237
  }
225
238
 
226
- /** Interface of the parsed JSON response of `/mySegments/{userKey}` */
227
- export interface IMySegmentsResponse {
228
- mySegments: IMySegmentsResponseItem[]
239
+ /** Interface of the parsed JSON response of `/memberships/{userKey}` */
240
+ export interface IMembershipsResponse {
241
+ ms?: IMySegmentsResponse,
242
+ ls?: IMySegmentsResponse
229
243
  }
230
244
 
231
245
  /** Metadata internal type for storages */
@@ -1,5 +1,6 @@
1
1
  import { allMatcherContext } from './all';
2
2
  import { segmentMatcherContext } from './segment';
3
+ import { largeSegmentMatcherContext } from './large_segment';
3
4
  import { whitelistMatcherContext } from './whitelist';
4
5
  import { equalToMatcherContext } from './eq';
5
6
  import { greaterThanEqualMatcherContext } from './gte';
@@ -48,6 +49,7 @@ const matchers = [
48
49
  lessThanEqualToSemverMatcherContext, // LESS_THAN_OR_EQUAL_TO_SEMVER: 20
49
50
  betweenSemverMatcherContext, // BETWEEN_SEMVER: 21
50
51
  inListSemverMatcherContext, // IN_LIST_SEMVER: 22
52
+ largeSegmentMatcherContext, // IN_LARGE_SEGMENT: 23
51
53
  ];
52
54
 
53
55
  /**
@@ -0,0 +1,18 @@
1
+ import { MaybeThenable } from '../../dtos/types';
2
+ import { ISegmentsCacheBase } from '../../storages/types';
3
+ import { thenable } from '../../utils/promise/thenable';
4
+
5
+ export function largeSegmentMatcherContext(largeSegmentName: string, storage: { largeSegments?: ISegmentsCacheBase }) {
6
+
7
+ return function largeSegmentMatcher(key: string): MaybeThenable<boolean> {
8
+ const isInLargeSegment = storage.largeSegments ? storage.largeSegments.isInSegment(largeSegmentName, key) : false;
9
+
10
+ if (thenable(isInLargeSegment)) {
11
+ isInLargeSegment.then(result => {
12
+ return result;
13
+ });
14
+ }
15
+
16
+ return isInLargeSegment;
17
+ };
18
+ }
@@ -22,6 +22,7 @@ export const matcherTypes: Record<string, number> = {
22
22
  LESS_THAN_OR_EQUAL_TO_SEMVER: 20,
23
23
  BETWEEN_SEMVER: 21,
24
24
  IN_LIST_SEMVER: 22,
25
+ IN_LARGE_SEGMENT: 23,
25
26
  };
26
27
 
27
28
  export const matcherDataTypes = {
@@ -4,7 +4,7 @@ import { segmentTransform } from './segment';
4
4
  import { whitelistTransform } from './whitelist';
5
5
  import { numericTransform } from './unaryNumeric';
6
6
  import { zeroSinceHH, zeroSinceSS } from '../convertions';
7
- import { IBetweenMatcherData, IInSegmentMatcherData, ISplitMatcher, IUnaryNumericMatcherData } from '../../dtos/types';
7
+ import { IBetweenMatcherData, IInLargeSegmentMatcherData, IInSegmentMatcherData, ISplitMatcher, IUnaryNumericMatcherData } from '../../dtos/types';
8
8
  import { IMatcherDto } from '../types';
9
9
 
10
10
  /**
@@ -18,6 +18,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
18
18
  negate,
19
19
  keySelector,
20
20
  userDefinedSegmentMatcherData,
21
+ userDefinedLargeSegmentMatcherData,
21
22
  whitelistMatcherData, /* whitelistObject, provided by 'WHITELIST', 'IN_LIST_SEMVER', set and string matchers */
22
23
  unaryNumericMatcherData,
23
24
  betweenMatcherData,
@@ -35,6 +36,8 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
35
36
 
36
37
  if (type === matcherTypes.IN_SEGMENT) {
37
38
  value = segmentTransform(userDefinedSegmentMatcherData as IInSegmentMatcherData);
39
+ } else if (type === matcherTypes.IN_LARGE_SEGMENT) {
40
+ value = segmentTransform(userDefinedLargeSegmentMatcherData as IInLargeSegmentMatcherData);
38
41
  } else if (type === matcherTypes.EQUAL_TO) {
39
42
  value = numericTransform(unaryNumericMatcherData as IUnaryNumericMatcherData);
40
43
  dataType = matcherDataTypes.NUMBER;
@@ -1,8 +1,10 @@
1
- import { IInSegmentMatcherData } from '../../dtos/types';
1
+ import { IInSegmentMatcherData, IInLargeSegmentMatcherData } from '../../dtos/types';
2
2
 
3
3
  /**
4
4
  * Extract segment name as a plain string.
5
5
  */
6
- export function segmentTransform(segment?: IInSegmentMatcherData) {
7
- return segment ? segment.segmentName : undefined;
6
+ export function segmentTransform(segment?: IInSegmentMatcherData | IInLargeSegmentMatcherData) {
7
+ return segment ?
8
+ (segment as IInSegmentMatcherData).segmentName || (segment as IInLargeSegmentMatcherData).largeSegmentName :
9
+ undefined;
8
10
  }
@@ -79,7 +79,7 @@ export const WARN_SPLITS_FILTER_IGNORED = 219;
79
79
  export const WARN_SPLITS_FILTER_INVALID = 220;
80
80
  export const WARN_SPLITS_FILTER_EMPTY = 221;
81
81
  export const WARN_SDK_KEY = 222;
82
- export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
82
+ export const STREAMING_PARSING_MEMBERSHIPS_UPDATE = 223;
83
83
  export const STREAMING_PARSING_SPLIT_UPDATE = 224;
84
84
  export const WARN_INVALID_FLAGSET = 225;
85
85
  export const WARN_LOWERCASE_FLAGSET = 226;
@@ -23,7 +23,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
23
23
  [c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
24
24
  [c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
25
25
  [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'],
26
- [c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and reseting timer.'],
26
+ [c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
27
27
  [c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
28
28
  [c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
29
29
  [c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
@@ -32,7 +32,7 @@ export const codesWarn: [number, string][] = codesError.concat([
32
32
  [c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS + ': feature flag filter configuration must be a non-empty array of filter objects.'],
33
33
  [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'],
34
34
 
35
- [c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
35
+ [c.STREAMING_PARSING_MEMBERSHIPS_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching Memberships due to an error processing %s notification: %s'],
36
36
  [c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing SPLIT_UPDATE notification: %s'],
37
37
  [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.'],
38
38
  [c.WARN_LOWERCASE_FLAGSET, '%s: flag set %s should be all lowercase - converting string to lowercase.'],