@splitsoftware/splitio-commons 1.16.0 → 1.16.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 (148) hide show
  1. package/CHANGES.txt +3 -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 +1 -1
  6. package/cjs/logger/constants.js +4 -4
  7. package/cjs/logger/messages/info.js +0 -1
  8. package/cjs/readiness/readinessManager.js +14 -10
  9. package/cjs/readiness/sdkReadinessManager.js +5 -6
  10. package/cjs/sdkClient/sdkClientMethodCS.js +3 -4
  11. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +4 -5
  12. package/cjs/sdkFactory/index.js +1 -1
  13. package/cjs/services/splitApi.js +4 -0
  14. package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
  15. package/cjs/storages/AbstractSplitsCacheSync.js +5 -5
  16. package/cjs/storages/KeyBuilder.js +3 -0
  17. package/cjs/storages/KeyBuilderCS.js +17 -5
  18. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +16 -4
  19. package/cjs/storages/inLocalStorage/index.js +6 -2
  20. package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -0
  21. package/cjs/storages/inMemory/SplitsCacheInMemory.js +20 -11
  22. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +7 -10
  23. package/cjs/storages/pluggable/inMemoryWrapper.js +1 -1
  24. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +5 -1
  25. package/cjs/sync/polling/pollingManagerCS.js +51 -33
  26. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  27. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +5 -6
  28. package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -1
  29. package/cjs/sync/streaming/SSEHandler/index.js +1 -0
  30. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -5
  31. package/cjs/sync/streaming/constants.js +2 -1
  32. package/cjs/sync/streaming/pushManager.js +95 -64
  33. package/cjs/sync/submitters/telemetrySubmitter.js +2 -0
  34. package/cjs/sync/syncManagerOnline.js +24 -14
  35. package/cjs/utils/constants/index.js +5 -1
  36. package/cjs/utils/settingsValidation/index.js +9 -4
  37. package/esm/evaluator/matchers/index.js +3 -1
  38. package/esm/evaluator/matchers/large_segment.js +12 -0
  39. package/esm/evaluator/matchers/matcherTypes.js +1 -0
  40. package/esm/evaluator/matchersTransform/index.js +1 -1
  41. package/esm/logger/constants.js +1 -1
  42. package/esm/logger/messages/info.js +0 -1
  43. package/esm/readiness/readinessManager.js +14 -10
  44. package/esm/readiness/sdkReadinessManager.js +5 -6
  45. package/esm/sdkClient/sdkClientMethodCS.js +4 -5
  46. package/esm/sdkClient/sdkClientMethodCSWithTT.js +5 -6
  47. package/esm/sdkFactory/index.js +1 -1
  48. package/esm/services/splitApi.js +5 -1
  49. package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
  50. package/esm/storages/AbstractSplitsCacheSync.js +3 -3
  51. package/esm/storages/KeyBuilder.js +3 -0
  52. package/esm/storages/KeyBuilderCS.js +15 -4
  53. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +17 -5
  54. package/esm/storages/inLocalStorage/index.js +7 -3
  55. package/esm/storages/inMemory/InMemoryStorageCS.js +5 -0
  56. package/esm/storages/inMemory/SplitsCacheInMemory.js +21 -12
  57. package/esm/storages/inMemory/TelemetryCacheInMemory.js +7 -10
  58. package/esm/storages/pluggable/inMemoryWrapper.js +1 -1
  59. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +5 -1
  60. package/esm/sync/polling/pollingManagerCS.js +52 -34
  61. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  62. package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -4
  63. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -1
  64. package/esm/sync/streaming/SSEHandler/index.js +2 -1
  65. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -5
  66. package/esm/sync/streaming/constants.js +1 -0
  67. package/esm/sync/streaming/pushManager.js +95 -65
  68. package/esm/sync/submitters/telemetrySubmitter.js +2 -0
  69. package/esm/sync/syncManagerOnline.js +25 -15
  70. package/esm/utils/constants/index.js +4 -0
  71. package/esm/utils/settingsValidation/index.js +10 -5
  72. package/package.json +1 -1
  73. package/src/dtos/types.ts +17 -7
  74. package/src/evaluator/matchers/index.ts +2 -0
  75. package/src/evaluator/matchers/large_segment.ts +18 -0
  76. package/src/evaluator/matchers/matcherTypes.ts +1 -0
  77. package/src/evaluator/matchersTransform/index.ts +1 -1
  78. package/src/logger/constants.ts +1 -1
  79. package/src/logger/messages/info.ts +0 -1
  80. package/src/readiness/readinessManager.ts +13 -9
  81. package/src/readiness/sdkReadinessManager.ts +7 -7
  82. package/src/readiness/types.ts +3 -2
  83. package/src/sdkClient/sdkClientMethodCS.ts +4 -6
  84. package/src/sdkClient/sdkClientMethodCSWithTT.ts +5 -7
  85. package/src/sdkFactory/index.ts +1 -1
  86. package/src/services/splitApi.ts +6 -1
  87. package/src/services/types.ts +1 -0
  88. package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
  89. package/src/storages/AbstractSplitsCacheSync.ts +4 -4
  90. package/src/storages/KeyBuilder.ts +3 -0
  91. package/src/storages/KeyBuilderCS.ts +25 -5
  92. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +3 -3
  93. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +20 -5
  94. package/src/storages/inLocalStorage/index.ts +8 -4
  95. package/src/storages/inMemory/InMemoryStorageCS.ts +5 -0
  96. package/src/storages/inMemory/SplitsCacheInMemory.ts +15 -10
  97. package/src/storages/inMemory/TelemetryCacheInMemory.ts +7 -11
  98. package/src/storages/pluggable/inMemoryWrapper.ts +1 -1
  99. package/src/storages/types.ts +7 -5
  100. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +6 -2
  101. package/src/sync/polling/pollingManagerCS.ts +61 -29
  102. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +10 -10
  103. package/src/sync/polling/types.ts +3 -2
  104. package/src/sync/polling/updaters/mySegmentsUpdater.ts +5 -8
  105. package/src/sync/polling/updaters/splitChangesUpdater.ts +4 -3
  106. package/src/sync/streaming/SSEHandler/index.ts +2 -1
  107. package/src/sync/streaming/SSEHandler/types.ts +14 -2
  108. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +17 -5
  109. package/src/sync/streaming/constants.ts +1 -0
  110. package/src/sync/streaming/pushManager.ts +100 -63
  111. package/src/sync/streaming/types.ts +5 -3
  112. package/src/sync/submitters/telemetrySubmitter.ts +2 -0
  113. package/src/sync/submitters/types.ts +10 -4
  114. package/src/sync/syncManagerOnline.ts +19 -11
  115. package/src/types.ts +26 -1
  116. package/src/utils/constants/index.ts +5 -0
  117. package/src/utils/settingsValidation/index.ts +11 -6
  118. package/src/utils/settingsValidation/types.ts +1 -1
  119. package/types/dtos/types.d.ts +14 -6
  120. package/types/evaluator/matchers/large_segment.d.ts +5 -0
  121. package/types/logger/constants.d.ts +1 -1
  122. package/types/readiness/readinessManager.d.ts +2 -2
  123. package/types/readiness/sdkReadinessManager.d.ts +2 -3
  124. package/types/readiness/types.d.ts +3 -2
  125. package/types/services/types.d.ts +1 -0
  126. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  127. package/types/storages/AbstractSplitsCacheSync.d.ts +3 -3
  128. package/types/storages/KeyBuilder.d.ts +1 -0
  129. package/types/storages/KeyBuilderCS.d.ts +7 -2
  130. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -2
  131. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  132. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
  133. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +4 -6
  134. package/types/storages/pluggable/inMemoryWrapper.d.ts +1 -1
  135. package/types/storages/types.d.ts +4 -3
  136. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -3
  137. package/types/sync/polling/types.d.ts +9 -2
  138. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +4 -4
  139. package/types/sync/streaming/SSEHandler/types.d.ts +13 -2
  140. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -1
  141. package/types/sync/streaming/constants.d.ts +1 -0
  142. package/types/sync/streaming/pushManager.d.ts +2 -0
  143. package/types/sync/streaming/types.d.ts +5 -4
  144. package/types/sync/submitters/types.d.ts +9 -3
  145. package/types/types.d.ts +25 -0
  146. package/types/utils/constants/index.d.ts +3 -0
  147. package/types/utils/settingsValidation/index.d.ts +2 -0
  148. package/types/utils/settingsValidation/types.d.ts +1 -1
@@ -8,13 +8,19 @@ 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';
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, MY_LARGE_SEGMENTS_UPDATE } from './constants';
12
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';
13
13
  import { UpdateStrategy } from './SSEHandler/types';
14
14
  import { isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
15
15
  import { _Set } from '../../utils/lang/sets';
16
+ import { hash } from '../../utils/murmur3/murmur3';
16
17
  import { hash64 } from '../../utils/murmur3/murmur3_64';
17
- import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
18
+ import { TOKEN_REFRESH, AUTH_REJECTION, MY_LARGE_SEGMENT, MY_SEGMENT } from '../../utils/constants';
19
+ export function getDelay(parsedData, matchingKey) {
20
+ var interval = parsedData.i || 60000;
21
+ var seed = parsedData.s || 0;
22
+ return hash(matchingKey, seed) % interval;
23
+ }
18
24
  /**
19
25
  * PushManager factory:
20
26
  * - for server-side if key is not provided in settings.
@@ -140,8 +146,9 @@ export function pushManagerFactory(params, pollingManager) {
140
146
  splitsUpdateWorker.stop();
141
147
  if (userKey)
142
148
  forOwn(clients, function (_a) {
143
- var worker = _a.worker;
144
- return worker.stop();
149
+ var worker = _a.worker, workerLarge = _a.workerLarge;
150
+ worker.stop();
151
+ workerLarge && workerLarge.stop();
145
152
  });
146
153
  else
147
154
  segmentsUpdateWorker.stop();
@@ -195,77 +202,96 @@ export function pushManagerFactory(params, pollingManager) {
195
202
  }
196
203
  splitsUpdateWorker.put(parsedData);
197
204
  });
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;
205
+ function handleMySegmentsUpdate(parsedData) {
206
+ var isLS = parsedData.type === MY_LARGE_SEGMENTS_UPDATE;
207
+ switch (parsedData.u) {
208
+ case UpdateStrategy.BoundedFetchRequest: {
209
+ var bitmap_1;
210
+ try {
211
+ bitmap_1 = parseBitmap(parsedData.d, parsedData.c);
224
212
  }
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;
213
+ catch (e) {
214
+ log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['BoundedFetchRequest', e]);
215
+ break;
216
+ }
217
+ forOwn(clients, function (_a, matchingKey) {
218
+ var hash64 = _a.hash64, worker = _a.worker, workerLarge = _a.workerLarge;
219
+ if (isInBitmap(bitmap_1, hash64.hex)) {
220
+ isLS ?
221
+ workerLarge && workerLarge.put(parsedData.changeNumber, undefined, getDelay(parsedData, matchingKey)) :
222
+ worker.put(parsedData.changeNumber);
235
223
  }
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) {
224
+ });
225
+ return;
226
+ }
227
+ case UpdateStrategy.KeyList: {
228
+ var keyList = void 0, added_1, removed_1;
229
+ try {
230
+ keyList = parseKeyList(parsedData.d, parsedData.c);
231
+ added_1 = new _Set(keyList.a);
232
+ removed_1 = new _Set(keyList.r);
233
+ }
234
+ catch (e) {
235
+ log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['KeyList', e]);
236
+ break;
237
+ }
238
+ forOwn(clients, function (_a) {
239
+ var hash64 = _a.hash64, worker = _a.worker, workerLarge = _a.workerLarge;
240
+ var add = added_1.has(hash64.dec) ? true : removed_1.has(hash64.dec) ? false : undefined;
241
+ if (add !== undefined) {
242
+ isLS ?
243
+ workerLarge && workerLarge.put(parsedData.changeNumber, {
244
+ name: parsedData.largeSegments[0],
245
+ add: add
246
+ }) :
240
247
  worker.put(parsedData.changeNumber, {
241
248
  name: parsedData.segmentName,
242
249
  add: add
243
250
  });
244
- }
245
- });
246
- return;
247
- }
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;
252
251
  }
253
- forOwn(clients, function (_a) {
254
- var worker = _a.worker;
255
- return worker.put(parsedData.changeNumber, {
252
+ });
253
+ return;
254
+ }
255
+ case UpdateStrategy.SegmentRemoval:
256
+ if ((isLS && parsedData.largeSegments.length === 0) || (!isLS && !parsedData.segmentName)) {
257
+ log.warn(STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, ['SegmentRemoval', 'No segment name was provided']);
258
+ break;
259
+ }
260
+ forOwn(clients, function (_a) {
261
+ var worker = _a.worker, workerLarge = _a.workerLarge;
262
+ isLS ?
263
+ workerLarge && parsedData.largeSegments.forEach(function (largeSegment) {
264
+ workerLarge.put(parsedData.changeNumber, {
265
+ name: largeSegment,
266
+ add: false
267
+ });
268
+ }) :
269
+ worker.put(parsedData.changeNumber, {
256
270
  name: parsedData.segmentName,
257
271
  add: false
258
272
  });
259
- });
260
- return;
261
- }
262
- // `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
263
- forOwn(clients, function (_a) {
264
- var worker = _a.worker;
273
+ });
274
+ return;
275
+ }
276
+ // `UpdateStrategy.UnboundedFetchRequest` and fallbacks of other cases
277
+ forOwn(clients, function (_a, matchingKey) {
278
+ var worker = _a.worker, workerLarge = _a.workerLarge;
279
+ isLS ?
280
+ workerLarge && workerLarge.put(parsedData.changeNumber, undefined, getDelay(parsedData, matchingKey)) :
265
281
  worker.put(parsedData.changeNumber);
266
- });
267
282
  });
268
283
  }
284
+ if (userKey) {
285
+ pushEmitter.on(MY_SEGMENTS_UPDATE, function handleMySegmentsUpdate(parsedData, channel) {
286
+ var userKeyHash = channel.split('_')[2];
287
+ var userKey = userKeyHashes[userKeyHash];
288
+ if (userKey && clients[userKey]) { // check existence since it can be undefined if client has been destroyed
289
+ clients[userKey].worker.put(parsedData.changeNumber, parsedData.includesPayload ? parsedData.segmentList ? parsedData.segmentList : [] : undefined);
290
+ }
291
+ });
292
+ pushEmitter.on(MY_SEGMENTS_UPDATE_V2, handleMySegmentsUpdate);
293
+ pushEmitter.on(MY_LARGE_SEGMENTS_UPDATE, handleMySegmentsUpdate);
294
+ }
269
295
  else {
270
296
  pushEmitter.on(SEGMENT_UPDATE, segmentsUpdateWorker.put);
271
297
  }
@@ -287,7 +313,7 @@ export function pushManagerFactory(params, pollingManager) {
287
313
  return;
288
314
  disconnected = false;
289
315
  if (userKey)
290
- this.add(userKey, pollingManager.segmentsSyncTask); // client-side
316
+ this.add(userKey, pollingManager.segmentsSyncTask, pollingManager.largeSegmentsSyncTask); // client-side
291
317
  else
292
318
  setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
293
319
  },
@@ -296,11 +322,15 @@ export function pushManagerFactory(params, pollingManager) {
296
322
  return disconnected === false;
297
323
  },
298
324
  // [Only for client-side]
299
- add: function (userKey, mySegmentsSyncTask) {
325
+ add: function (userKey, mySegmentsSyncTask, myLargeSegmentsSyncTask) {
300
326
  var hash = hashUserKey(userKey);
301
327
  if (!userKeyHashes[hash]) {
302
328
  userKeyHashes[hash] = userKey;
303
- clients[userKey] = { hash64: hash64(userKey), worker: MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker) };
329
+ clients[userKey] = {
330
+ hash64: hash64(userKey),
331
+ worker: MySegmentsUpdateWorker(mySegmentsSyncTask, telemetryTracker, MY_SEGMENT),
332
+ workerLarge: myLargeSegmentsSyncTask ? MySegmentsUpdateWorker(myLargeSegmentsSyncTask, telemetryTracker, MY_LARGE_SEGMENT) : undefined
333
+ };
304
334
  connectForNewClient = true; // we must reconnect on start, to listen the channel for the new user key
305
335
  // Reconnects in case of a new client.
306
336
  // Run in next event-loop cycle to save authentication calls
@@ -61,10 +61,12 @@ export function telemetryCacheConfigAdapter(telemetry, settings) {
61
61
  var _a = getTelemetryFlagSetsStats(settings.sync.__splitFiltersValidation), flagSetsTotal = _a.flagSetsTotal, flagSetsIgnored = _a.flagSetsIgnored;
62
62
  return objectAssign(getTelemetryConfigStats(settings.mode, settings.storage.type), {
63
63
  sE: settings.streamingEnabled,
64
+ lE: isClientSide ? settings.sync.largeSegmentsEnabled : undefined,
64
65
  rR: {
65
66
  sp: scheduler.featuresRefreshRate / 1000,
66
67
  se: isClientSide ? undefined : scheduler.segmentsRefreshRate / 1000,
67
68
  ms: isClientSide ? scheduler.segmentsRefreshRate / 1000 : undefined,
69
+ mls: isClientSide && settings.sync.largeSegmentsEnabled ? scheduler.largeSegmentsRefreshRate / 1000 : undefined,
68
70
  im: scheduler.impressionsRefreshRate / 1000,
69
71
  ev: scheduler.eventsPushRate / 1000,
70
72
  te: scheduler.telemetryRefreshRate / 1000,
@@ -2,7 +2,7 @@ import { submitterManagerFactory } from './submitters/submitterManager';
2
2
  import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
3
3
  import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
4
4
  import { isConsentGranted } from '../consent';
5
- import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
5
+ import { IN_LARGE_SEGMENT, IN_SEGMENT, POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
6
6
  /**
7
7
  * Online SyncManager factory.
8
8
  * Can be used for server-side API, and client-side API with or without multiple clients.
@@ -114,43 +114,53 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
114
114
  shared: function (matchingKey, readinessManager, storage) {
115
115
  if (!pollingManager)
116
116
  return;
117
- var mySegmentsSyncTask = pollingManager.add(matchingKey, readinessManager, storage);
117
+ var _a = pollingManager.add(matchingKey, readinessManager, storage), msSyncTask = _a.msSyncTask, mlsSyncTask = _a.mlsSyncTask;
118
118
  return {
119
- isRunning: mySegmentsSyncTask.isRunning,
119
+ isRunning: msSyncTask.isRunning,
120
120
  start: function () {
121
121
  if (syncEnabled) {
122
122
  if (pushManager) {
123
123
  if (pollingManager.isRunning()) {
124
124
  // if doing polling, we must start the periodic fetch of data
125
- if (storage.splits.usesSegments())
126
- mySegmentsSyncTask.start();
125
+ if (storage.splits.usesMatcher(IN_SEGMENT))
126
+ msSyncTask.start();
127
+ if (mlsSyncTask && storage.splits.usesMatcher(IN_LARGE_SEGMENT))
128
+ mlsSyncTask.start();
127
129
  }
128
130
  else {
129
131
  // if not polling, we must execute the sync task for the initial fetch
130
132
  // of segments since `syncAll` was already executed when starting the main client
131
- mySegmentsSyncTask.execute();
133
+ msSyncTask.execute();
134
+ mlsSyncTask && mlsSyncTask.execute();
132
135
  }
133
- pushManager.add(matchingKey, mySegmentsSyncTask);
136
+ pushManager.add(matchingKey, msSyncTask, mlsSyncTask);
134
137
  }
135
138
  else {
136
- if (storage.splits.usesSegments())
137
- mySegmentsSyncTask.start();
139
+ if (storage.splits.usesMatcher(IN_SEGMENT))
140
+ msSyncTask.start();
141
+ if (mlsSyncTask && storage.splits.usesMatcher(IN_LARGE_SEGMENT))
142
+ mlsSyncTask.start();
138
143
  }
139
144
  }
140
145
  else {
141
- if (!readinessManager.isReady())
142
- mySegmentsSyncTask.execute();
146
+ if (!readinessManager.isReady()) {
147
+ msSyncTask.execute();
148
+ mlsSyncTask && mlsSyncTask.execute();
149
+ }
143
150
  }
144
151
  },
145
152
  stop: function () {
146
153
  // check in case `client.destroy()` has been invoked more than once for the same client
147
- var mySegmentsSyncTask = pollingManager.get(matchingKey);
148
- if (mySegmentsSyncTask) {
154
+ var syncTasks = pollingManager.get(matchingKey);
155
+ if (syncTasks) {
156
+ var msSyncTask_1 = syncTasks.msSyncTask, mlsSyncTask_1 = syncTasks.mlsSyncTask;
149
157
  // stop syncing
150
158
  if (pushManager)
151
159
  pushManager.remove(matchingKey);
152
- if (mySegmentsSyncTask.isRunning())
153
- mySegmentsSyncTask.stop();
160
+ if (msSyncTask_1.isRunning())
161
+ msSyncTask_1.stop();
162
+ if (mlsSyncTask_1 && mlsSyncTask_1.isRunning())
163
+ mlsSyncTask_1.stop();
154
164
  pollingManager.remove(matchingKey);
155
165
  }
156
166
  },
@@ -61,6 +61,7 @@ export var TELEMETRY = 'te';
61
61
  export var TOKEN = 'to';
62
62
  export var SEGMENT = 'se';
63
63
  export var MY_SEGMENT = 'ms';
64
+ export var MY_LARGE_SEGMENT = 'mls';
64
65
  export var TREATMENT = 't';
65
66
  export var TREATMENTS = 'ts';
66
67
  export var TREATMENT_WITH_CONFIG = 'tc';
@@ -87,3 +88,6 @@ export var DISABLED = 0;
87
88
  export var ENABLED = 1;
88
89
  export var PAUSED = 2;
89
90
  export var FLAG_SPEC_VERSION = '1.1';
91
+ // Matcher types
92
+ export var IN_SEGMENT = 'IN_SEGMENT';
93
+ export var IN_LARGE_SEGMENT = 'IN_LARGE_SEGMENT';
@@ -5,7 +5,7 @@ import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE, DEBUG, FLAG_SPEC_VERSION }
5
5
  import { validImpressionsMode } from './impressionsMode';
6
6
  import { validateKey } from '../inputValidation/key';
7
7
  import { validateTrafficType } from '../inputValidation/trafficType';
8
- import { ERROR_MIN_CONFIG_PARAM } from '../../logger/constants';
8
+ import { ERROR_MIN_CONFIG_PARAM, LOG_PREFIX_CLIENT_INSTANTIATION } from '../../logger/constants';
9
9
  // Exported for telemetry
10
10
  export var base = {
11
11
  // Define which kind of object you want to retrieve from SplitFactory
@@ -27,6 +27,8 @@ export var base = {
27
27
  featuresRefreshRate: 60,
28
28
  // fetch segments updates each 60 sec
29
29
  segmentsRefreshRate: 60,
30
+ // fetch large segments updates each 60 sec
31
+ largeSegmentsRefreshRate: 60,
30
32
  // publish telemetry stats each 3600 secs (1 hour)
31
33
  telemetryRefreshRate: 3600,
32
34
  // publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
@@ -72,7 +74,8 @@ export var base = {
72
74
  impressionsMode: OPTIMIZED,
73
75
  localhostMode: undefined,
74
76
  enabled: true,
75
- flagSpecVersion: FLAG_SPEC_VERSION
77
+ flagSpecVersion: FLAG_SPEC_VERSION,
78
+ largeSegmentsEnabled: false
76
79
  },
77
80
  // Logger
78
81
  log: undefined
@@ -111,6 +114,7 @@ export function settingsValidation(config, validationParams) {
111
114
  var scheduler = withDefaults.scheduler, startup = withDefaults.startup;
112
115
  scheduler.featuresRefreshRate = fromSecondsToMillis(scheduler.featuresRefreshRate);
113
116
  scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
117
+ scheduler.largeSegmentsRefreshRate = fromSecondsToMillis(scheduler.largeSegmentsRefreshRate);
114
118
  scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
115
119
  scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
116
120
  scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
@@ -143,12 +147,12 @@ export function settingsValidation(config, validationParams) {
143
147
  // Keeping same behaviour than JS SDK: if settings key or TT are invalid,
144
148
  // `false` value is used as bound key/TT of the default client, which leads to some issues.
145
149
  // @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
146
- withDefaults.core.key = validateKey(log, maybeKey, 'Client instantiation');
150
+ withDefaults.core.key = validateKey(log, maybeKey, LOG_PREFIX_CLIENT_INSTANTIATION);
147
151
  }
148
152
  if (validationParams.acceptTT) {
149
153
  var maybeTT = withDefaults.core.trafficType;
150
154
  if (maybeTT !== undefined) { // @ts-ignore
151
- withDefaults.core.trafficType = validateTrafficType(log, maybeTT, 'Client instantiation');
155
+ withDefaults.core.trafficType = validateTrafficType(log, maybeTT, LOG_PREFIX_CLIENT_INSTANTIATION);
152
156
  }
153
157
  }
154
158
  }
@@ -183,9 +187,10 @@ export function settingsValidation(config, validationParams) {
183
187
  var splitFiltersValidation = validateSplitFilters(log, sync.splitFilters, withDefaults.mode);
184
188
  sync.splitFilters = splitFiltersValidation.validFilters;
185
189
  sync.__splitFiltersValidation = splitFiltersValidation;
190
+ // ensure a valid flag spec version
186
191
  sync.flagSpecVersion = flagSpec ? flagSpec(withDefaults) : FLAG_SPEC_VERSION;
187
192
  // ensure a valid user consent value
188
193
  // @ts-ignore, modify readonly prop
189
- withDefaults.userConsent = consent(withDefaults);
194
+ withDefaults.userConsent = consent ? consent(withDefaults) : undefined;
190
195
  return withDefaults;
191
196
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.16.0",
3
+ "version": "1.16.1-rc.1",
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
@@ -61,6 +61,11 @@ interface IInSegmentMatcher extends ISplitMatcherBase {
61
61
  userDefinedSegmentMatcherData: IInSegmentMatcherData
62
62
  }
63
63
 
64
+ interface IInLargeSegmentMatcher extends ISplitMatcherBase {
65
+ matcherType: 'IN_LARGE_SEGMENT',
66
+ userDefinedSegmentMatcherData: IInSegmentMatcherData
67
+ }
68
+
64
69
  interface IWhitelistMatcher extends ISplitMatcherBase {
65
70
  matcherType: 'WHITELIST',
66
71
  whitelistMatcherData: IWhitelistMatcherData
@@ -165,7 +170,8 @@ interface IInListSemverMatcher extends ISplitMatcherBase {
165
170
  export type ISplitMatcher = IAllKeysMatcher | IInSegmentMatcher | IWhitelistMatcher | IEqualToMatcher | IGreaterThanOrEqualToMatcher |
166
171
  ILessThanOrEqualToMatcher | IBetweenMatcher | IEqualToSetMatcher | IContainsAnyOfSetMatcher | IContainsAllOfSetMatcher | IPartOfSetMatcher |
167
172
  IStartsWithMatcher | IEndsWithMatcher | IContainsStringMatcher | IInSplitTreatmentMatcher | IEqualToBooleanMatcher | IMatchesStringMatcher |
168
- IEqualToSemverMatcher | IGreaterThanOrEqualToSemverMatcher | ILessThanOrEqualToSemverMatcher | IBetweenSemverMatcher | IInListSemverMatcher
173
+ IEqualToSemverMatcher | IGreaterThanOrEqualToSemverMatcher | ILessThanOrEqualToSemverMatcher | IBetweenSemverMatcher | IInListSemverMatcher |
174
+ IInLargeSegmentMatcher
169
175
 
170
176
  /** Split object */
171
177
  export interface ISplitPartition {
@@ -218,14 +224,18 @@ export interface ISegmentChangesResponse {
218
224
  till: number
219
225
  }
220
226
 
221
- export interface IMySegmentsResponseItem {
222
- id: string,
223
- name: string
224
- }
225
-
226
227
  /** Interface of the parsed JSON response of `/mySegments/{userKey}` */
227
228
  export interface IMySegmentsResponse {
228
- mySegments: IMySegmentsResponseItem[]
229
+ mySegments: {
230
+ id: string,
231
+ name: string
232
+ }[]
233
+ }
234
+
235
+ /** Interface of the parsed JSON response of `/myLargeSegments/{userKey}` */
236
+ export interface IMyLargeSegmentsResponse {
237
+ myLargeSegments: string[],
238
+ changeNumber: number
229
239
  }
230
240
 
231
241
  /** 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 = {
@@ -33,7 +33,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
33
33
  let dataType = matcherDataTypes.STRING;
34
34
  let value = undefined;
35
35
 
36
- if (type === matcherTypes.IN_SEGMENT) {
36
+ if (type === matcherTypes.IN_SEGMENT || type === matcherTypes.IN_LARGE_SEGMENT) {
37
37
  value = segmentTransform(userDefinedSegmentMatcherData as IInSegmentMatcherData);
38
38
  } else if (type === matcherTypes.EQUAL_TO) {
39
39
  value = numericTransform(unaryNumericMatcherData as IUnaryNumericMatcherData);
@@ -36,7 +36,6 @@ export const IMPRESSION = 102;
36
36
  export const IMPRESSION_QUEUEING = 103;
37
37
  export const NEW_SHARED_CLIENT = 104;
38
38
  export const NEW_FACTORY = 105;
39
- export const POLLING_SMART_PAUSING = 106;
40
39
  export const POLLING_START = 107;
41
40
  export const POLLING_STOP = 108;
42
41
  export const SYNC_SPLITS_FETCH_RETRY = 109;
@@ -120,6 +119,7 @@ export const ENGINE_MATCHER_ERROR = 329;
120
119
  // Log prefixes (a.k.a. tags or categories)
121
120
  export const LOG_PREFIX_SETTINGS = 'settings';
122
121
  export const LOG_PREFIX_INSTANTIATION = 'Factory instantiation';
122
+ export const LOG_PREFIX_CLIENT_INSTANTIATION = 'Client instantiation';
123
123
  export const LOG_PREFIX_ENGINE = 'engine';
124
124
  export const LOG_PREFIX_ENGINE_COMBINER = LOG_PREFIX_ENGINE + ':combiner: ';
125
125
  export const LOG_PREFIX_ENGINE_MATCHER = LOG_PREFIX_ENGINE + ':matcher: ';
@@ -19,7 +19,6 @@ export const codesInfo: [number, string][] = codesWarn.concat([
19
19
  [c.USER_CONSENT_INITIAL, 'Starting the SDK with %s user consent. No data will be sent.'],
20
20
 
21
21
  // synchronizer
22
- [c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
23
22
  [c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
24
23
  [c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
25
24
  [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'],
@@ -1,5 +1,5 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
- import { IEventEmitter } from '../types';
2
+ import { IEventEmitter, ISettings } from '../types';
3
3
  import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
4
4
  import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';
5
5
 
@@ -18,10 +18,8 @@ function splitsEventEmitterFactory(EventEmitter: new () => IEventEmitter): ISpli
18
18
  return splitsEventEmitter;
19
19
  }
20
20
 
21
- function segmentsEventEmitterFactory(EventEmitter: new () => IEventEmitter): ISegmentsEventEmitter {
22
- const segmentsEventEmitter = objectAssign(new EventEmitter(), {
23
- segmentsArrived: false
24
- });
21
+ function segmentsEventEmitterFactory(EventEmitter: new () => IEventEmitter, segmentsArrived = false): ISegmentsEventEmitter {
22
+ const segmentsEventEmitter = objectAssign(new EventEmitter(), { segmentsArrived });
25
23
 
26
24
  segmentsEventEmitter.once(SDK_SEGMENTS_ARRIVED, () => { segmentsEventEmitter.segmentsArrived = true; });
27
25
 
@@ -33,10 +31,13 @@ function segmentsEventEmitterFactory(EventEmitter: new () => IEventEmitter): ISe
33
31
  */
34
32
  export function readinessManagerFactory(
35
33
  EventEmitter: new () => IEventEmitter,
36
- readyTimeout = 0,
34
+ settings: ISettings,
37
35
  splits: ISplitsEventEmitter = splitsEventEmitterFactory(EventEmitter)): IReadinessManager {
38
36
 
37
+ const { startup: { readyTimeout, waitForLargeSegments }, sync: { largeSegmentsEnabled } } = settings;
38
+
39
39
  const segments: ISegmentsEventEmitter = segmentsEventEmitterFactory(EventEmitter);
40
+ const largeSegments = largeSegmentsEnabled ? segmentsEventEmitterFactory(EventEmitter, !waitForLargeSegments) : undefined;
40
41
  const gate: IReadinessEventEmitter = new EventEmitter();
41
42
 
42
43
  // emit SDK_READY_FROM_CACHE
@@ -62,6 +63,7 @@ export function readinessManagerFactory(
62
63
  let isReady = false;
63
64
  splits.on(SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate);
64
65
  segments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
66
+ if (largeSegments) largeSegments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
65
67
 
66
68
  let isDestroyed = false;
67
69
 
@@ -87,7 +89,7 @@ export function readinessManagerFactory(
87
89
  setTimeout(() => { throw e; }, 0);
88
90
  }
89
91
  } else {
90
- if (splits.splitsArrived && segments.segmentsArrived) {
92
+ if (splits.splitsArrived && segments.segmentsArrived && (!largeSegments || largeSegments.segmentsArrived)) {
91
93
  clearTimeout(readyTimeoutId);
92
94
  isReady = true;
93
95
  try {
@@ -105,11 +107,12 @@ export function readinessManagerFactory(
105
107
  return {
106
108
  splits,
107
109
  segments,
110
+ largeSegments,
108
111
  gate,
109
112
 
110
- shared(readyTimeout = 0) {
113
+ shared() {
111
114
  refCount++;
112
- return readinessManagerFactory(EventEmitter, readyTimeout, splits);
115
+ return readinessManagerFactory(EventEmitter, settings, splits);
113
116
  },
114
117
 
115
118
  // @TODO review/remove next methods when non-recoverable errors are reworked
@@ -123,6 +126,7 @@ export function readinessManagerFactory(
123
126
  isDestroyed = true;
124
127
 
125
128
  segments.removeAllListeners();
129
+ if (largeSegments) largeSegments.removeAllListeners();
126
130
  gate.removeAllListeners();
127
131
  clearTimeout(readyTimeoutId);
128
132
 
@@ -2,9 +2,8 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { promiseWrapper } from '../utils/promise/wrapper';
3
3
  import { readinessManagerFactory } from './readinessManager';
4
4
  import { ISdkReadinessManager } from './types';
5
- import { IEventEmitter } from '../types';
5
+ import { IEventEmitter, ISettings } from '../types';
6
6
  import { SDK_READY, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE } from './constants';
7
- import { ILogger } from '../logger/types';
8
7
  import { ERROR_CLIENT_LISTENER, CLIENT_READY_FROM_CACHE, CLIENT_READY, CLIENT_NO_LISTENER } from '../logger/constants';
9
8
 
10
9
  const NEW_LISTENER_EVENT = 'newListener';
@@ -18,10 +17,11 @@ const REMOVE_LISTENER_EVENT = 'removeListener';
18
17
  * @param readinessManager optional readinessManager to use. only used internally for `shared` method
19
18
  */
20
19
  export function sdkReadinessManagerFactory(
21
- log: ILogger,
22
20
  EventEmitter: new () => IEventEmitter,
23
- readyTimeout = 0,
24
- readinessManager = readinessManagerFactory(EventEmitter, readyTimeout)): ISdkReadinessManager {
21
+ settings: ISettings,
22
+ readinessManager = readinessManagerFactory(EventEmitter, settings)): ISdkReadinessManager {
23
+
24
+ const log = settings.log;
25
25
 
26
26
  /** Ready callback warning */
27
27
  let internalReadyCbCount = 0;
@@ -72,8 +72,8 @@ export function sdkReadinessManagerFactory(
72
72
  return {
73
73
  readinessManager,
74
74
 
75
- shared(readyTimeout = 0) {
76
- return sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, readinessManager.shared(readyTimeout));
75
+ shared() {
76
+ return sdkReadinessManagerFactory(EventEmitter, settings, readinessManager.shared());
77
77
  },
78
78
 
79
79
  incInternalReadyCbCount() {