@splitsoftware/splitio-commons 2.0.1-rc.0 → 2.0.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 (38) hide show
  1. package/CHANGES.txt +2 -2
  2. package/cjs/readiness/readinessManager.js +18 -16
  3. package/cjs/sdkFactory/index.js +0 -1
  4. package/cjs/storages/inLocalStorage/index.js +18 -3
  5. package/cjs/storages/inMemory/InMemoryStorage.js +9 -1
  6. package/cjs/storages/inMemory/InMemoryStorageCS.js +16 -2
  7. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
  8. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +2 -2
  9. package/cjs/storages/pluggable/SegmentsCachePluggable.js +2 -2
  10. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +2 -2
  11. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
  12. package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +3 -3
  13. package/esm/readiness/readinessManager.js +18 -16
  14. package/esm/sdkFactory/index.js +0 -1
  15. package/esm/storages/inLocalStorage/index.js +18 -3
  16. package/esm/storages/inMemory/InMemoryStorage.js +9 -1
  17. package/esm/storages/inMemory/InMemoryStorageCS.js +16 -2
  18. package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
  19. package/esm/storages/inRedis/SegmentsCacheInRedis.js +2 -2
  20. package/esm/storages/pluggable/SegmentsCachePluggable.js +2 -2
  21. package/esm/sync/polling/updaters/segmentChangesUpdater.js +2 -2
  22. package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
  23. package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +3 -3
  24. package/package.json +1 -1
  25. package/src/readiness/readinessManager.ts +16 -15
  26. package/src/sdkFactory/index.ts +0 -1
  27. package/src/storages/AbstractMySegmentsCacheSync.ts +1 -1
  28. package/src/storages/inLocalStorage/index.ts +17 -3
  29. package/src/storages/inMemory/InMemoryStorage.ts +9 -1
  30. package/src/storages/inMemory/InMemoryStorageCS.ts +16 -2
  31. package/src/storages/inMemory/SegmentsCacheInMemory.ts +1 -1
  32. package/src/storages/inRedis/SegmentsCacheInRedis.ts +2 -2
  33. package/src/storages/pluggable/SegmentsCachePluggable.ts +2 -2
  34. package/src/storages/types.ts +3 -3
  35. package/src/sync/polling/updaters/segmentChangesUpdater.ts +2 -2
  36. package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -1
  37. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +5 -4
  38. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +3 -3
package/CHANGES.txt CHANGED
@@ -1,5 +1,5 @@
1
- 2.0.1 (XXX XX, 2024)
2
- - Updated the factory `init` and `destroy` methods to support re-initialization after destruction, enabling repeated cycles of initialization and cleanup. This update ensures compatibility of the React SDK with React Strict Mode, where the factory's `init` and `destroy` effects are executed an extra time to validate proper resource cleanup.
1
+ 2.0.1 (November 25, 2024)
2
+ - Bugfixing - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification.
3
3
 
4
4
  2.0.0 (November 1, 2024)
5
5
  - Added support for targeting rules based on large segments.
@@ -28,7 +28,7 @@ function segmentsEventEmitterFactory(EventEmitter) {
28
28
  /**
29
29
  * Factory of readiness manager, which handles the ready / update event propagation.
30
30
  */
31
- function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
31
+ function readinessManagerFactory(EventEmitter, settings, splits) {
32
32
  if (splits === void 0) { splits = splitsEventEmitterFactory(EventEmitter); }
33
33
  var readyTimeout = settings.startup.readyTimeout;
34
34
  var segments = segmentsEventEmitterFactory(EventEmitter);
@@ -54,24 +54,22 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
54
54
  syncLastUpdate();
55
55
  gate.emit(constants_1.SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
56
56
  }
57
+ var readyTimeoutId;
58
+ if (readyTimeout > 0) {
59
+ if (splits.hasInit)
60
+ readyTimeoutId = setTimeout(timeout, readyTimeout);
61
+ else
62
+ splits.initCallbacks.push(function () { readyTimeoutId = setTimeout(timeout, readyTimeout); });
63
+ }
57
64
  // emit SDK_READY and SDK_UPDATE
58
65
  var isReady = false;
59
66
  splits.on(constants_1.SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate);
60
67
  segments.on(constants_1.SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
61
68
  var isDestroyed = false;
62
- var readyTimeoutId;
63
- function __init() {
64
- isDestroyed = false;
65
- if (readyTimeout > 0 && !isReady)
66
- readyTimeoutId = setTimeout(timeout, readyTimeout);
67
- }
68
- splits.initCallbacks.push(__init);
69
- if (splits.hasInit)
70
- __init();
71
69
  function checkIsReadyFromCache() {
72
70
  isReadyFromCache = true;
73
71
  // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
74
- if (!isReady && !isDestroyed) {
72
+ if (!isReady) {
75
73
  try {
76
74
  syncLastUpdate();
77
75
  gate.emit(constants_1.SDK_READY_FROM_CACHE);
@@ -83,8 +81,6 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
83
81
  }
84
82
  }
85
83
  function checkIsReadyOrUpdate(diff) {
86
- if (isDestroyed)
87
- return;
88
84
  if (isReady) {
89
85
  try {
90
86
  syncLastUpdate();
@@ -110,12 +106,14 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
110
106
  }
111
107
  }
112
108
  }
109
+ var refCount = 1;
113
110
  return {
114
111
  splits: splits,
115
112
  segments: segments,
116
113
  gate: gate,
117
114
  shared: function () {
118
- return readinessManagerFactory(EventEmitter, settings, splits, true);
115
+ refCount++;
116
+ return readinessManagerFactory(EventEmitter, settings, splits);
119
117
  },
120
118
  // @TODO review/remove next methods when non-recoverable errors are reworked
121
119
  // Called on consumer mode, when storage fails to connect
@@ -132,9 +130,13 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
132
130
  destroy: function () {
133
131
  isDestroyed = true;
134
132
  syncLastUpdate();
133
+ segments.removeAllListeners();
134
+ gate.removeAllListeners();
135
135
  clearTimeout(readyTimeoutId);
136
- if (!isShared)
137
- splits.hasInit = false;
136
+ if (refCount > 0)
137
+ refCount--;
138
+ if (refCount === 0)
139
+ splits.removeAllListeners();
138
140
  },
139
141
  isReady: function () { return isReady; },
140
142
  isReadyFromCache: function () { return isReadyFromCache; },
@@ -102,7 +102,6 @@ function sdkFactory(params) {
102
102
  Logger: (0, sdkLogger_1.createLoggerAPI)(log),
103
103
  settings: settings,
104
104
  destroy: function () {
105
- hasInit = false;
106
105
  return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
107
106
  }
108
107
  }, extraProps && extraProps(ctx), lazyInit ? { init: init } : init());
@@ -9,6 +9,8 @@ var KeyBuilderCS_1 = require("../KeyBuilderCS");
9
9
  var isLocalStorageAvailable_1 = require("../../utils/env/isLocalStorageAvailable");
10
10
  var SplitsCacheInLocal_1 = require("./SplitsCacheInLocal");
11
11
  var MySegmentsCacheInLocal_1 = require("./MySegmentsCacheInLocal");
12
+ var MySegmentsCacheInMemory_1 = require("../inMemory/MySegmentsCacheInMemory");
13
+ var SplitsCacheInMemory_1 = require("../inMemory/SplitsCacheInMemory");
12
14
  var browser_1 = require("../../utils/constants/browser");
13
15
  var InMemoryStorageCS_1 = require("../inMemory/InMemoryStorageCS");
14
16
  var constants_1 = require("./constants");
@@ -28,7 +30,7 @@ function InLocalStorage(options) {
28
30
  params.settings.log.warn(constants_1.LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
29
31
  return (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
30
32
  }
31
- var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, impressionsMode = _a.sync.impressionsMode;
33
+ var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
32
34
  var matchingKey = (0, key_1.getMatching)(settings.core.key);
33
35
  var keys = new KeyBuilderCS_1.KeyBuilderCS(prefix, matchingKey);
34
36
  var expirationTimestamp = Date.now() - browser_1.DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -44,7 +46,16 @@ function InLocalStorage(options) {
44
46
  events: new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize),
45
47
  telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
46
48
  uniqueKeys: impressionsMode === constants_2.NONE ? new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() : undefined,
47
- destroy: function () { },
49
+ destroy: function () {
50
+ var _a;
51
+ this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
52
+ this.segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
53
+ this.largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
54
+ this.impressions.clear();
55
+ this.impressionCounts && this.impressionCounts.clear();
56
+ this.events.clear();
57
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
58
+ },
48
59
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
49
60
  shared: function (matchingKey) {
50
61
  return {
@@ -55,7 +66,11 @@ function InLocalStorage(options) {
55
66
  impressionCounts: this.impressionCounts,
56
67
  events: this.events,
57
68
  telemetry: this.telemetry,
58
- destroy: function () { }
69
+ destroy: function () {
70
+ this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
71
+ this.segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
72
+ this.largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
73
+ }
59
74
  };
60
75
  },
61
76
  };
@@ -26,7 +26,15 @@ function InMemoryStorageFactory(params) {
26
26
  events: new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize),
27
27
  telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
28
28
  uniqueKeys: impressionsMode === constants_1.NONE ? new UniqueKeysCacheInMemory_1.UniqueKeysCacheInMemory() : undefined,
29
- destroy: function () { }
29
+ // When using MEMORY we should clean all the caches to leave them empty
30
+ destroy: function () {
31
+ this.splits.clear();
32
+ this.segments.clear();
33
+ this.impressions.clear();
34
+ this.impressionCounts && this.impressionCounts.clear();
35
+ this.events.clear();
36
+ this.uniqueKeys && this.uniqueKeys.clear();
37
+ }
30
38
  };
31
39
  // @TODO revisit storage logic in localhost mode
32
40
  // No tracking data in localhost mode to avoid memory leaks
@@ -28,7 +28,16 @@ function InMemoryStorageCSFactory(params) {
28
28
  events: new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize),
29
29
  telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
30
30
  uniqueKeys: impressionsMode === constants_1.NONE ? new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() : undefined,
31
- destroy: function () { },
31
+ // When using MEMORY we should clean all the caches to leave them empty
32
+ destroy: function () {
33
+ this.splits.clear();
34
+ this.segments.clear();
35
+ this.largeSegments.clear();
36
+ this.impressions.clear();
37
+ this.impressionCounts && this.impressionCounts.clear();
38
+ this.events.clear();
39
+ this.uniqueKeys && this.uniqueKeys.clear();
40
+ },
32
41
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
33
42
  shared: function () {
34
43
  return {
@@ -39,7 +48,12 @@ function InMemoryStorageCSFactory(params) {
39
48
  impressionCounts: this.impressionCounts,
40
49
  events: this.events,
41
50
  telemetry: this.telemetry,
42
- destroy: function () { }
51
+ // Set a new splits cache to clean it for the client without affecting other clients
52
+ destroy: function () {
53
+ this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
54
+ this.segments.clear();
55
+ this.largeSegments.clear();
56
+ }
43
57
  };
44
58
  },
45
59
  };
@@ -52,7 +52,7 @@ var SegmentsCacheInMemory = /** @class */ (function () {
52
52
  };
53
53
  SegmentsCacheInMemory.prototype.getChangeNumber = function (name) {
54
54
  var value = this.segmentChangeNumber[name];
55
- return (0, lang_1.isIntegerNumber)(value) ? value : -1;
55
+ return (0, lang_1.isIntegerNumber)(value) ? value : undefined;
56
56
  };
57
57
  // No-op. Not used in server-side
58
58
  SegmentsCacheInMemory.prototype.resetSegments = function () { return false; };
@@ -31,10 +31,10 @@ var SegmentsCacheInRedis = /** @class */ (function () {
31
31
  var _this = this;
32
32
  return this.redis.get(this.keys.buildSegmentTillKey(name)).then(function (value) {
33
33
  var i = parseInt(value, 10);
34
- return (0, lang_1.isNaNNumber)(i) ? -1 : i;
34
+ return (0, lang_1.isNaNNumber)(i) ? undefined : i;
35
35
  }).catch(function (e) {
36
36
  _this.log.error(constants_1.LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e);
37
- return -1;
37
+ return undefined;
38
38
  });
39
39
  };
40
40
  SegmentsCacheInRedis.prototype.registerSegments = function (segments) {
@@ -45,10 +45,10 @@ var SegmentsCachePluggable = /** @class */ (function () {
45
45
  var _this = this;
46
46
  return this.wrapper.get(this.keys.buildSegmentTillKey(name)).then(function (value) {
47
47
  var i = parseInt(value, 10);
48
- return (0, lang_1.isNaNNumber)(i) ? -1 : i;
48
+ return (0, lang_1.isNaNNumber)(i) ? undefined : i;
49
49
  }).catch(function (e) {
50
50
  _this.log.error(constants_1.LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e);
51
- return -1;
51
+ return undefined;
52
52
  });
53
53
  };
54
54
  /**
@@ -21,9 +21,9 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
21
21
  var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
22
22
  return sincePromise.then(function (since) {
23
23
  // if fetchOnlyNew flag, avoid processing already fetched segments
24
- return fetchOnlyNew && since !== -1 ?
24
+ return fetchOnlyNew && since !== undefined ?
25
25
  false :
26
- segmentChangesFetcher(since, segmentName, noCache, till).then(function (changes) {
26
+ segmentChangesFetcher(since || -1, segmentName, noCache, till).then(function (changes) {
27
27
  return Promise.all(changes.map(function (x) {
28
28
  log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
29
29
  return segments.update(segmentName, x.added, x.removed, x.till);
@@ -14,7 +14,7 @@ function checkAllSegmentsExist(segments) {
14
14
  var registeredSegments = Promise.resolve(segments.getRegisteredSegments());
15
15
  return registeredSegments.then(function (segmentNames) {
16
16
  return Promise.all(segmentNames.map(function (segmentName) { return segments.getChangeNumber(segmentName); }))
17
- .then(function (changeNumbers) { return changeNumbers.every(function (changeNumber) { return changeNumber !== -1; }); });
17
+ .then(function (changeNumbers) { return changeNumbers.every(function (changeNumber) { return changeNumber !== undefined; }); });
18
18
  });
19
19
  }
20
20
  /**
@@ -16,7 +16,7 @@ function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
16
16
  var backoff = new Backoff_1.Backoff(__handleSegmentUpdateCall, constants_1.FETCH_BACKOFF_BASE, constants_1.FETCH_BACKOFF_MAX_WAIT);
17
17
  function __handleSegmentUpdateCall() {
18
18
  isHandlingEvent = true;
19
- if (maxChangeNumber > segmentsCache.getChangeNumber(segment)) {
19
+ if (maxChangeNumber > (segmentsCache.getChangeNumber(segment) || -1)) {
20
20
  handleNewEvent = false;
21
21
  // fetch segments revalidating data if cached
22
22
  segmentsSyncTask.execute(false, segment, true, cdnBypass ? maxChangeNumber : undefined).then(function () {
@@ -27,7 +27,7 @@ function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
27
27
  }
28
28
  else {
29
29
  var attempts = backoff.attempts + 1;
30
- if (maxChangeNumber <= segmentsCache.getChangeNumber(segment)) {
30
+ if (maxChangeNumber <= (segmentsCache.getChangeNumber(segment) || -1)) {
31
31
  log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
32
32
  isHandlingEvent = false;
33
33
  return;
@@ -54,7 +54,7 @@ function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
54
54
  }
55
55
  return {
56
56
  put: function (changeNumber) {
57
- var currentChangeNumber = segmentsCache.getChangeNumber(segment);
57
+ var currentChangeNumber = segmentsCache.getChangeNumber(segment) || -1;
58
58
  if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
59
59
  return;
60
60
  maxChangeNumber = changeNumber;
@@ -25,7 +25,7 @@ function segmentsEventEmitterFactory(EventEmitter) {
25
25
  /**
26
26
  * Factory of readiness manager, which handles the ready / update event propagation.
27
27
  */
28
- export function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
28
+ export function readinessManagerFactory(EventEmitter, settings, splits) {
29
29
  if (splits === void 0) { splits = splitsEventEmitterFactory(EventEmitter); }
30
30
  var readyTimeout = settings.startup.readyTimeout;
31
31
  var segments = segmentsEventEmitterFactory(EventEmitter);
@@ -51,24 +51,22 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
51
51
  syncLastUpdate();
52
52
  gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
53
53
  }
54
+ var readyTimeoutId;
55
+ if (readyTimeout > 0) {
56
+ if (splits.hasInit)
57
+ readyTimeoutId = setTimeout(timeout, readyTimeout);
58
+ else
59
+ splits.initCallbacks.push(function () { readyTimeoutId = setTimeout(timeout, readyTimeout); });
60
+ }
54
61
  // emit SDK_READY and SDK_UPDATE
55
62
  var isReady = false;
56
63
  splits.on(SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate);
57
64
  segments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
58
65
  var isDestroyed = false;
59
- var readyTimeoutId;
60
- function __init() {
61
- isDestroyed = false;
62
- if (readyTimeout > 0 && !isReady)
63
- readyTimeoutId = setTimeout(timeout, readyTimeout);
64
- }
65
- splits.initCallbacks.push(__init);
66
- if (splits.hasInit)
67
- __init();
68
66
  function checkIsReadyFromCache() {
69
67
  isReadyFromCache = true;
70
68
  // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
71
- if (!isReady && !isDestroyed) {
69
+ if (!isReady) {
72
70
  try {
73
71
  syncLastUpdate();
74
72
  gate.emit(SDK_READY_FROM_CACHE);
@@ -80,8 +78,6 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
80
78
  }
81
79
  }
82
80
  function checkIsReadyOrUpdate(diff) {
83
- if (isDestroyed)
84
- return;
85
81
  if (isReady) {
86
82
  try {
87
83
  syncLastUpdate();
@@ -107,12 +103,14 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
107
103
  }
108
104
  }
109
105
  }
106
+ var refCount = 1;
110
107
  return {
111
108
  splits: splits,
112
109
  segments: segments,
113
110
  gate: gate,
114
111
  shared: function () {
115
- return readinessManagerFactory(EventEmitter, settings, splits, true);
112
+ refCount++;
113
+ return readinessManagerFactory(EventEmitter, settings, splits);
116
114
  },
117
115
  // @TODO review/remove next methods when non-recoverable errors are reworked
118
116
  // Called on consumer mode, when storage fails to connect
@@ -129,9 +127,13 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
129
127
  destroy: function () {
130
128
  isDestroyed = true;
131
129
  syncLastUpdate();
130
+ segments.removeAllListeners();
131
+ gate.removeAllListeners();
132
132
  clearTimeout(readyTimeoutId);
133
- if (!isShared)
134
- splits.hasInit = false;
133
+ if (refCount > 0)
134
+ refCount--;
135
+ if (refCount === 0)
136
+ splits.removeAllListeners();
135
137
  },
136
138
  isReady: function () { return isReady; },
137
139
  isReadyFromCache: function () { return isReadyFromCache; },
@@ -99,7 +99,6 @@ export function sdkFactory(params) {
99
99
  Logger: createLoggerAPI(log),
100
100
  settings: settings,
101
101
  destroy: function () {
102
- hasInit = false;
103
102
  return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
104
103
  }
105
104
  }, extraProps && extraProps(ctx), lazyInit ? { init: init } : init());
@@ -6,6 +6,8 @@ import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
6
6
  import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
7
7
  import { SplitsCacheInLocal } from './SplitsCacheInLocal';
8
8
  import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
9
+ import { MySegmentsCacheInMemory } from '../inMemory/MySegmentsCacheInMemory';
10
+ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
9
11
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
10
12
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
11
13
  import { LOG_PREFIX } from './constants';
@@ -25,7 +27,7 @@ export function InLocalStorage(options) {
25
27
  params.settings.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
26
28
  return InMemoryStorageCSFactory(params);
27
29
  }
28
- var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, impressionsMode = _a.sync.impressionsMode;
30
+ var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
29
31
  var matchingKey = getMatching(settings.core.key);
30
32
  var keys = new KeyBuilderCS(prefix, matchingKey);
31
33
  var expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -41,7 +43,16 @@ export function InLocalStorage(options) {
41
43
  events: new EventsCacheInMemory(eventsQueueSize),
42
44
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
43
45
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
44
- destroy: function () { },
46
+ destroy: function () {
47
+ var _a;
48
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
49
+ this.segments = new MySegmentsCacheInMemory();
50
+ this.largeSegments = new MySegmentsCacheInMemory();
51
+ this.impressions.clear();
52
+ this.impressionCounts && this.impressionCounts.clear();
53
+ this.events.clear();
54
+ (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
55
+ },
45
56
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
46
57
  shared: function (matchingKey) {
47
58
  return {
@@ -52,7 +63,11 @@ export function InLocalStorage(options) {
52
63
  impressionCounts: this.impressionCounts,
53
64
  events: this.events,
54
65
  telemetry: this.telemetry,
55
- destroy: function () { }
66
+ destroy: function () {
67
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
68
+ this.segments = new MySegmentsCacheInMemory();
69
+ this.largeSegments = new MySegmentsCacheInMemory();
70
+ }
56
71
  };
57
72
  },
58
73
  };
@@ -23,7 +23,15 @@ export function InMemoryStorageFactory(params) {
23
23
  events: new EventsCacheInMemory(eventsQueueSize),
24
24
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
25
25
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
26
- destroy: function () { }
26
+ // When using MEMORY we should clean all the caches to leave them empty
27
+ destroy: function () {
28
+ this.splits.clear();
29
+ this.segments.clear();
30
+ this.impressions.clear();
31
+ this.impressionCounts && this.impressionCounts.clear();
32
+ this.events.clear();
33
+ this.uniqueKeys && this.uniqueKeys.clear();
34
+ }
27
35
  };
28
36
  // @TODO revisit storage logic in localhost mode
29
37
  // No tracking data in localhost mode to avoid memory leaks
@@ -25,7 +25,16 @@ export function InMemoryStorageCSFactory(params) {
25
25
  events: new EventsCacheInMemory(eventsQueueSize),
26
26
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
27
27
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
28
- destroy: function () { },
28
+ // When using MEMORY we should clean all the caches to leave them empty
29
+ destroy: function () {
30
+ this.splits.clear();
31
+ this.segments.clear();
32
+ this.largeSegments.clear();
33
+ this.impressions.clear();
34
+ this.impressionCounts && this.impressionCounts.clear();
35
+ this.events.clear();
36
+ this.uniqueKeys && this.uniqueKeys.clear();
37
+ },
29
38
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
30
39
  shared: function () {
31
40
  return {
@@ -36,7 +45,12 @@ export function InMemoryStorageCSFactory(params) {
36
45
  impressionCounts: this.impressionCounts,
37
46
  events: this.events,
38
47
  telemetry: this.telemetry,
39
- destroy: function () { }
48
+ // Set a new splits cache to clean it for the client without affecting other clients
49
+ destroy: function () {
50
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
51
+ this.segments.clear();
52
+ this.largeSegments.clear();
53
+ }
40
54
  };
41
55
  },
42
56
  };
@@ -49,7 +49,7 @@ var SegmentsCacheInMemory = /** @class */ (function () {
49
49
  };
50
50
  SegmentsCacheInMemory.prototype.getChangeNumber = function (name) {
51
51
  var value = this.segmentChangeNumber[name];
52
- return isIntegerNumber(value) ? value : -1;
52
+ return isIntegerNumber(value) ? value : undefined;
53
53
  };
54
54
  // No-op. Not used in server-side
55
55
  SegmentsCacheInMemory.prototype.resetSegments = function () { return false; };
@@ -28,10 +28,10 @@ var SegmentsCacheInRedis = /** @class */ (function () {
28
28
  var _this = this;
29
29
  return this.redis.get(this.keys.buildSegmentTillKey(name)).then(function (value) {
30
30
  var i = parseInt(value, 10);
31
- return isNaNNumber(i) ? -1 : i;
31
+ return isNaNNumber(i) ? undefined : i;
32
32
  }).catch(function (e) {
33
33
  _this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e);
34
- return -1;
34
+ return undefined;
35
35
  });
36
36
  };
37
37
  SegmentsCacheInRedis.prototype.registerSegments = function (segments) {
@@ -42,10 +42,10 @@ var SegmentsCachePluggable = /** @class */ (function () {
42
42
  var _this = this;
43
43
  return this.wrapper.get(this.keys.buildSegmentTillKey(name)).then(function (value) {
44
44
  var i = parseInt(value, 10);
45
- return isNaNNumber(i) ? -1 : i;
45
+ return isNaNNumber(i) ? undefined : i;
46
46
  }).catch(function (e) {
47
47
  _this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e);
48
- return -1;
48
+ return undefined;
49
49
  });
50
50
  };
51
51
  /**
@@ -18,9 +18,9 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
18
18
  var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
19
19
  return sincePromise.then(function (since) {
20
20
  // if fetchOnlyNew flag, avoid processing already fetched segments
21
- return fetchOnlyNew && since !== -1 ?
21
+ return fetchOnlyNew && since !== undefined ?
22
22
  false :
23
- segmentChangesFetcher(since, segmentName, noCache, till).then(function (changes) {
23
+ segmentChangesFetcher(since || -1, segmentName, noCache, till).then(function (changes) {
24
24
  return Promise.all(changes.map(function (x) {
25
25
  log.debug(LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
26
26
  return segments.update(segmentName, x.added, x.removed, x.till);
@@ -11,7 +11,7 @@ function checkAllSegmentsExist(segments) {
11
11
  var registeredSegments = Promise.resolve(segments.getRegisteredSegments());
12
12
  return registeredSegments.then(function (segmentNames) {
13
13
  return Promise.all(segmentNames.map(function (segmentName) { return segments.getChangeNumber(segmentName); }))
14
- .then(function (changeNumbers) { return changeNumbers.every(function (changeNumber) { return changeNumber !== -1; }); });
14
+ .then(function (changeNumbers) { return changeNumbers.every(function (changeNumber) { return changeNumber !== undefined; }); });
15
15
  });
16
16
  }
17
17
  /**
@@ -13,7 +13,7 @@ export function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
13
13
  var backoff = new Backoff(__handleSegmentUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
14
14
  function __handleSegmentUpdateCall() {
15
15
  isHandlingEvent = true;
16
- if (maxChangeNumber > segmentsCache.getChangeNumber(segment)) {
16
+ if (maxChangeNumber > (segmentsCache.getChangeNumber(segment) || -1)) {
17
17
  handleNewEvent = false;
18
18
  // fetch segments revalidating data if cached
19
19
  segmentsSyncTask.execute(false, segment, true, cdnBypass ? maxChangeNumber : undefined).then(function () {
@@ -24,7 +24,7 @@ export function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
24
24
  }
25
25
  else {
26
26
  var attempts = backoff.attempts + 1;
27
- if (maxChangeNumber <= segmentsCache.getChangeNumber(segment)) {
27
+ if (maxChangeNumber <= (segmentsCache.getChangeNumber(segment) || -1)) {
28
28
  log.debug("Refresh completed" + (cdnBypass ? ' bypassing the CDN' : '') + " in " + attempts + " attempts.");
29
29
  isHandlingEvent = false;
30
30
  return;
@@ -51,7 +51,7 @@ export function SegmentsUpdateWorker(log, segmentsSyncTask, segmentsCache) {
51
51
  }
52
52
  return {
53
53
  put: function (changeNumber) {
54
- var currentChangeNumber = segmentsCache.getChangeNumber(segment);
54
+ var currentChangeNumber = segmentsCache.getChangeNumber(segment) || -1;
55
55
  if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber)
56
56
  return;
57
57
  maxChangeNumber = changeNumber;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.0.1-rc.0",
3
+ "version": "2.0.1",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -37,9 +37,7 @@ function segmentsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitt
37
37
  export function readinessManagerFactory(
38
38
  EventEmitter: new () => SplitIO.IEventEmitter,
39
39
  settings: ISettings,
40
- splits: ISplitsEventEmitter = splitsEventEmitterFactory(EventEmitter),
41
- isShared?: boolean
42
- ): IReadinessManager {
40
+ splits: ISplitsEventEmitter = splitsEventEmitterFactory(EventEmitter)): IReadinessManager {
43
41
 
44
42
  const readyTimeout = settings.startup.readyTimeout;
45
43
 
@@ -68,6 +66,11 @@ export function readinessManagerFactory(
68
66
  gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
69
67
  }
70
68
 
69
+ let readyTimeoutId: ReturnType<typeof setTimeout>;
70
+ if (readyTimeout > 0) {
71
+ if (splits.hasInit) readyTimeoutId = setTimeout(timeout, readyTimeout);
72
+ else splits.initCallbacks.push(() => { readyTimeoutId = setTimeout(timeout, readyTimeout); });
73
+ }
71
74
 
72
75
  // emit SDK_READY and SDK_UPDATE
73
76
  let isReady = false;
@@ -75,19 +78,11 @@ export function readinessManagerFactory(
75
78
  segments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
76
79
 
77
80
  let isDestroyed = false;
78
- let readyTimeoutId: ReturnType<typeof setTimeout>;
79
- function __init() {
80
- isDestroyed = false;
81
- if (readyTimeout > 0 && !isReady) readyTimeoutId = setTimeout(timeout, readyTimeout);
82
- }
83
-
84
- splits.initCallbacks.push(__init);
85
- if (splits.hasInit) __init();
86
81
 
87
82
  function checkIsReadyFromCache() {
88
83
  isReadyFromCache = true;
89
84
  // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
90
- if (!isReady && !isDestroyed) {
85
+ if (!isReady) {
91
86
  try {
92
87
  syncLastUpdate();
93
88
  gate.emit(SDK_READY_FROM_CACHE);
@@ -99,7 +94,6 @@ export function readinessManagerFactory(
99
94
  }
100
95
 
101
96
  function checkIsReadyOrUpdate(diff: any) {
102
- if (isDestroyed) return;
103
97
  if (isReady) {
104
98
  try {
105
99
  syncLastUpdate();
@@ -123,13 +117,16 @@ export function readinessManagerFactory(
123
117
  }
124
118
  }
125
119
 
120
+ let refCount = 1;
121
+
126
122
  return {
127
123
  splits,
128
124
  segments,
129
125
  gate,
130
126
 
131
127
  shared() {
132
- return readinessManagerFactory(EventEmitter, settings, splits, true);
128
+ refCount++;
129
+ return readinessManagerFactory(EventEmitter, settings, splits);
133
130
  },
134
131
 
135
132
  // @TODO review/remove next methods when non-recoverable errors are reworked
@@ -148,9 +145,13 @@ export function readinessManagerFactory(
148
145
  destroy() {
149
146
  isDestroyed = true;
150
147
  syncLastUpdate();
148
+
149
+ segments.removeAllListeners();
150
+ gate.removeAllListeners();
151
151
  clearTimeout(readyTimeoutId);
152
152
 
153
- if (!isShared) splits.hasInit = false;
153
+ if (refCount > 0) refCount--;
154
+ if (refCount === 0) splits.removeAllListeners();
154
155
  },
155
156
 
156
157
  isReady() { return isReady; },
@@ -126,7 +126,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
126
126
  settings,
127
127
 
128
128
  destroy() {
129
- hasInit = false;
130
129
  return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => { });
131
130
  }
132
131
  }, extraProps && extraProps(ctx), lazyInit ? { init } : init());
@@ -42,7 +42,7 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
42
42
  // @TODO for client-side it should be the number of clients, but it requires a refactor of MySegments caches to simplify the code.
43
43
  abstract getKeysCount(): number
44
44
 
45
- abstract getChangeNumber(name: string): number
45
+ abstract getChangeNumber(): number
46
46
 
47
47
  /**
48
48
  * For server-side synchronizer: the method is not used.
@@ -7,6 +7,8 @@ import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
7
7
  import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
8
8
  import { SplitsCacheInLocal } from './SplitsCacheInLocal';
9
9
  import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
10
+ import { MySegmentsCacheInMemory } from '../inMemory/MySegmentsCacheInMemory';
11
+ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
10
12
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
11
13
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
12
14
  import { LOG_PREFIX } from './constants';
@@ -34,7 +36,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
34
36
  return InMemoryStorageCSFactory(params);
35
37
  }
36
38
 
37
- const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
39
+ const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
38
40
  const matchingKey = getMatching(settings.core.key);
39
41
  const keys = new KeyBuilderCS(prefix, matchingKey);
40
42
  const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -53,7 +55,15 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
53
55
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
54
56
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
55
57
 
56
- destroy() { },
58
+ destroy() {
59
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
60
+ this.segments = new MySegmentsCacheInMemory();
61
+ this.largeSegments = new MySegmentsCacheInMemory();
62
+ this.impressions.clear();
63
+ this.impressionCounts && this.impressionCounts.clear();
64
+ this.events.clear();
65
+ this.uniqueKeys?.clear();
66
+ },
57
67
 
58
68
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
59
69
  shared(matchingKey: string) {
@@ -67,7 +77,11 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
67
77
  events: this.events,
68
78
  telemetry: this.telemetry,
69
79
 
70
- destroy() { }
80
+ destroy() {
81
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
82
+ this.segments = new MySegmentsCacheInMemory();
83
+ this.largeSegments = new MySegmentsCacheInMemory();
84
+ }
71
85
  };
72
86
  },
73
87
  };
@@ -28,7 +28,15 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
28
28
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
29
29
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
30
30
 
31
- destroy() { }
31
+ // When using MEMORY we should clean all the caches to leave them empty
32
+ destroy() {
33
+ this.splits.clear();
34
+ this.segments.clear();
35
+ this.impressions.clear();
36
+ this.impressionCounts && this.impressionCounts.clear();
37
+ this.events.clear();
38
+ this.uniqueKeys && this.uniqueKeys.clear();
39
+ }
32
40
  };
33
41
 
34
42
  // @TODO revisit storage logic in localhost mode
@@ -30,7 +30,16 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
30
30
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
31
31
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
32
32
 
33
- destroy() { },
33
+ // When using MEMORY we should clean all the caches to leave them empty
34
+ destroy() {
35
+ this.splits.clear();
36
+ this.segments.clear();
37
+ this.largeSegments.clear();
38
+ this.impressions.clear();
39
+ this.impressionCounts && this.impressionCounts.clear();
40
+ this.events.clear();
41
+ this.uniqueKeys && this.uniqueKeys.clear();
42
+ },
34
43
 
35
44
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
36
45
  shared() {
@@ -43,7 +52,12 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
43
52
  events: this.events,
44
53
  telemetry: this.telemetry,
45
54
 
46
- destroy() { }
55
+ // Set a new splits cache to clean it for the client without affecting other clients
56
+ destroy() {
57
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
58
+ this.segments.clear();
59
+ this.largeSegments.clear();
60
+ }
47
61
  };
48
62
  },
49
63
  };
@@ -65,7 +65,7 @@ export class SegmentsCacheInMemory implements ISegmentsCacheSync {
65
65
  getChangeNumber(name: string) {
66
66
  const value = this.segmentChangeNumber[name];
67
67
 
68
- return isIntegerNumber(value) ? value : -1;
68
+ return isIntegerNumber(value) ? value : undefined;
69
69
  }
70
70
 
71
71
  // No-op. Not used in server-side
@@ -44,10 +44,10 @@ export class SegmentsCacheInRedis implements ISegmentsCacheAsync {
44
44
  return this.redis.get(this.keys.buildSegmentTillKey(name)).then((value: string | null) => {
45
45
  const i = parseInt(value as string, 10);
46
46
 
47
- return isNaNNumber(i) ? -1 : i;
47
+ return isNaNNumber(i) ? undefined : i;
48
48
  }).catch((e) => {
49
49
  this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e);
50
- return -1;
50
+ return undefined;
51
51
  });
52
52
  }
53
53
 
@@ -55,10 +55,10 @@ export class SegmentsCachePluggable implements ISegmentsCacheAsync {
55
55
  return this.wrapper.get(this.keys.buildSegmentTillKey(name)).then((value: string | null) => {
56
56
  const i = parseInt(value as string, 10);
57
57
 
58
- return isNaNNumber(i) ? -1 : i;
58
+ return isNaNNumber(i) ? undefined : i;
59
59
  }).catch((e) => {
60
60
  this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e);
61
- return -1;
61
+ return undefined;
62
62
  });
63
63
  }
64
64
 
@@ -237,7 +237,7 @@ export interface ISegmentsCacheBase {
237
237
  isInSegment(name: string, key?: string): MaybeThenable<boolean> // different signature on Server and Client-Side
238
238
  registerSegments(names: string[]): MaybeThenable<boolean | void> // only for Server-Side
239
239
  getRegisteredSegments(): MaybeThenable<string[]> // only for Server-Side
240
- getChangeNumber(name: string): MaybeThenable<number> // only for Server-Side
240
+ getChangeNumber(name: string): MaybeThenable<number | undefined> // only for Server-Side
241
241
  update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): MaybeThenable<boolean> // only for Server-Side
242
242
  clear(): MaybeThenable<boolean | void>
243
243
  }
@@ -248,7 +248,7 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase {
248
248
  registerSegments(names: string[]): boolean
249
249
  getRegisteredSegments(): string[]
250
250
  getKeysCount(): number // only used for telemetry
251
- getChangeNumber(name?: string): number
251
+ getChangeNumber(name?: string): number | undefined
252
252
  update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): boolean // only for Server-Side
253
253
  resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean // only for Sync Client-Side
254
254
  clear(): void
@@ -258,7 +258,7 @@ export interface ISegmentsCacheAsync extends ISegmentsCacheBase {
258
258
  isInSegment(name: string, key: string): Promise<boolean>
259
259
  registerSegments(names: string[]): Promise<boolean | void>
260
260
  getRegisteredSegments(): Promise<string[]>
261
- getChangeNumber(name: string): Promise<number>
261
+ getChangeNumber(name: string): Promise<number | undefined>
262
262
  update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): Promise<boolean>
263
263
  clear(): Promise<boolean | void>
264
264
  }
@@ -33,9 +33,9 @@ export function segmentChangesUpdaterFactory(
33
33
 
34
34
  return sincePromise.then(since => {
35
35
  // if fetchOnlyNew flag, avoid processing already fetched segments
36
- return fetchOnlyNew && since !== -1 ?
36
+ return fetchOnlyNew && since !== undefined ?
37
37
  false :
38
- segmentChangesFetcher(since, segmentName, noCache, till).then((changes) => {
38
+ segmentChangesFetcher(since || -1, segmentName, noCache, till).then((changes) => {
39
39
  return Promise.all(changes.map(x => {
40
40
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
41
41
  return segments.update(segmentName, x.added, x.removed, x.till);
@@ -19,7 +19,7 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
19
19
  let registeredSegments = Promise.resolve(segments.getRegisteredSegments());
20
20
  return registeredSegments.then(segmentNames => {
21
21
  return Promise.all(segmentNames.map(segmentName => segments.getChangeNumber(segmentName)))
22
- .then(changeNumbers => changeNumbers.every(changeNumber => changeNumber !== -1));
22
+ .then(changeNumbers => changeNumbers.every(changeNumber => changeNumber !== undefined));
23
23
  });
24
24
  }
25
25
 
@@ -3,10 +3,11 @@ import { Backoff } from '../../../utils/Backoff';
3
3
  import { IUpdateWorker } from './types';
4
4
  import { ITelemetryTracker } from '../../../trackers/types';
5
5
  import { MEMBERSHIPS } from '../../../utils/constants';
6
- import { ISegmentsCacheSync, IStorageSync } from '../../../storages/types';
6
+ import { IStorageSync } from '../../../storages/types';
7
7
  import { ILogger } from '../../../logger/types';
8
8
  import { FETCH_BACKOFF_MAX_RETRIES } from './constants';
9
9
  import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../constants';
10
+ import { AbstractMySegmentsCacheSync } from '../../../storages/AbstractMySegmentsCacheSync';
10
11
 
11
12
  /**
12
13
  * MySegmentsUpdateWorker factory
@@ -16,7 +17,7 @@ export function MySegmentsUpdateWorker(log: ILogger, storage: Pick<IStorageSync,
16
17
  let _delay: undefined | number;
17
18
  let _delayTimeoutID: any;
18
19
 
19
- function createUpdateWorker(mySegmentsCache: ISegmentsCacheSync) {
20
+ function createUpdateWorker(mySegmentsCache: AbstractMySegmentsCacheSync) {
20
21
 
21
22
  let maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
22
23
  let currentChangeNumber = -1;
@@ -117,8 +118,8 @@ export function MySegmentsUpdateWorker(log: ILogger, storage: Pick<IStorageSync,
117
118
  }
118
119
 
119
120
  const updateWorkers = {
120
- [MEMBERSHIPS_MS_UPDATE]: createUpdateWorker(storage.segments),
121
- [MEMBERSHIPS_LS_UPDATE]: createUpdateWorker(storage.largeSegments!),
121
+ [MEMBERSHIPS_MS_UPDATE]: createUpdateWorker(storage.segments as AbstractMySegmentsCacheSync),
122
+ [MEMBERSHIPS_LS_UPDATE]: createUpdateWorker(storage.largeSegments as AbstractMySegmentsCacheSync),
122
123
  };
123
124
 
124
125
  return {
@@ -21,7 +21,7 @@ export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSy
21
21
 
22
22
  function __handleSegmentUpdateCall() {
23
23
  isHandlingEvent = true;
24
- if (maxChangeNumber > segmentsCache.getChangeNumber(segment)) {
24
+ if (maxChangeNumber > (segmentsCache.getChangeNumber(segment) || -1)) {
25
25
  handleNewEvent = false;
26
26
 
27
27
  // fetch segments revalidating data if cached
@@ -32,7 +32,7 @@ export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSy
32
32
  } else {
33
33
  const attempts = backoff.attempts + 1;
34
34
 
35
- if (maxChangeNumber <= segmentsCache.getChangeNumber(segment)) {
35
+ if (maxChangeNumber <= (segmentsCache.getChangeNumber(segment) || -1)) {
36
36
  log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
37
37
  isHandlingEvent = false;
38
38
  return;
@@ -60,7 +60,7 @@ export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSy
60
60
 
61
61
  return {
62
62
  put(changeNumber: number) {
63
- const currentChangeNumber = segmentsCache.getChangeNumber(segment);
63
+ const currentChangeNumber = segmentsCache.getChangeNumber(segment) || -1;
64
64
 
65
65
  if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
66
66