@splitsoftware/splitio-commons 2.7.9-rc.3 → 2.8.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 (48) hide show
  1. package/CHANGES.txt +5 -1
  2. package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +48 -39
  3. package/cjs/evaluator/fallbackTreatmentsCalculator/index.js +3 -8
  4. package/cjs/sdkFactory/index.js +1 -1
  5. package/cjs/storages/AbstractMySegmentsCacheSync.js +31 -23
  6. package/cjs/storages/AbstractSplitsCacheSync.js +3 -2
  7. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +10 -28
  8. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +22 -33
  9. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +19 -29
  10. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +3 -2
  11. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  12. package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  13. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  14. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +16 -5
  15. package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -3
  16. package/cjs/utils/settingsValidation/index.js +3 -0
  17. package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +44 -38
  18. package/esm/evaluator/fallbackTreatmentsCalculator/index.js +3 -8
  19. package/esm/sdkFactory/index.js +1 -1
  20. package/esm/storages/AbstractMySegmentsCacheSync.js +31 -23
  21. package/esm/storages/AbstractSplitsCacheSync.js +3 -2
  22. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +11 -29
  23. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +22 -33
  24. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +19 -29
  25. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +3 -2
  26. package/esm/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  27. package/esm/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  28. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  29. package/esm/sync/polling/updaters/segmentChangesUpdater.js +16 -5
  30. package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -3
  31. package/esm/utils/settingsValidation/index.js +3 -0
  32. package/package.json +1 -1
  33. package/src/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.ts +50 -44
  34. package/src/evaluator/fallbackTreatmentsCalculator/index.ts +2 -9
  35. package/src/sdkFactory/index.ts +1 -1
  36. package/src/storages/AbstractMySegmentsCacheSync.ts +26 -20
  37. package/src/storages/AbstractSplitsCacheSync.ts +3 -2
  38. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +9 -24
  39. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +18 -27
  40. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +22 -29
  41. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +3 -2
  42. package/src/storages/inRedis/SegmentsCacheInRedis.ts +1 -1
  43. package/src/sync/polling/syncTasks/segmentsSyncTask.ts +2 -0
  44. package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -3
  45. package/src/sync/polling/updaters/segmentChangesUpdater.ts +17 -4
  46. package/src/sync/polling/updaters/splitChangesUpdater.ts +6 -7
  47. package/src/utils/settingsValidation/index.ts +4 -0
  48. package/types/splitio.d.ts +9 -3
package/CHANGES.txt CHANGED
@@ -1,4 +1,8 @@
1
- 2.8.0 (October 28, 2025)
1
+ 2.9.0 (November 26, 2025)
2
+ - Updated the SDK’s initial synchronization in Node.js (server-side) to use the `startup.requestTimeoutBeforeReady` and `startup.retriesOnFailureBeforeReady` options to control the timeout and retry behavior of segment requests.
3
+ - Updated the order of storage operations to prevent inconsistent states when using the `LOCALSTORAGE` storage type and the browser’s `localStorage` fails due to quota limits.
4
+
5
+ 2.8.0 (October 30, 2025)
2
6
  - Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs.
3
7
  - Added `client.getStatus()` method to retrieve the client readiness status properties (`isReady`, `isReadyFromCache`, etc).
4
8
  - Added `client.whenReady()` and `client.whenReadyFromCache()` methods to replace the deprecated `client.ready()` method, which has an issue causing the returned promise to hang when using async/await syntax if it was rejected.
@@ -1,51 +1,60 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FallbacksSanitizer = void 0;
3
+ exports.sanitizeFallbacks = exports.isValidTreatment = exports.isValidFlagName = void 0;
4
4
  var lang_1 = require("../../../utils/lang");
5
5
  var FallbackDiscardReason;
6
6
  (function (FallbackDiscardReason) {
7
7
  FallbackDiscardReason["FlagName"] = "Invalid flag name (max 100 chars, no spaces)";
8
8
  FallbackDiscardReason["Treatment"] = "Invalid treatment (max 100 chars and must match pattern)";
9
9
  })(FallbackDiscardReason || (FallbackDiscardReason = {}));
10
- var FallbacksSanitizer = /** @class */ (function () {
11
- function FallbacksSanitizer() {
10
+ var TREATMENT_PATTERN = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
11
+ function isValidFlagName(name) {
12
+ return name.length <= 100 && !name.includes(' ');
13
+ }
14
+ exports.isValidFlagName = isValidFlagName;
15
+ function isValidTreatment(t) {
16
+ var treatment = (0, lang_1.isObject)(t) ? t.treatment : t;
17
+ if (!(0, lang_1.isString)(treatment) || treatment.length > 100) {
18
+ return false;
12
19
  }
13
- FallbacksSanitizer.isValidFlagName = function (name) {
14
- return name.length <= 100 && !name.includes(' ');
15
- };
16
- FallbacksSanitizer.isValidTreatment = function (t) {
17
- var treatment = (0, lang_1.isObject)(t) ? t.treatment : t;
18
- if (!(0, lang_1.isString)(treatment) || treatment.length > 100) {
19
- return false;
20
+ return TREATMENT_PATTERN.test(treatment);
21
+ }
22
+ exports.isValidTreatment = isValidTreatment;
23
+ function sanitizeGlobal(logger, treatment) {
24
+ if (treatment === undefined)
25
+ return undefined;
26
+ if (!isValidTreatment(treatment)) {
27
+ logger.error("Fallback treatments - Discarded fallback: " + FallbackDiscardReason.Treatment);
28
+ return undefined;
29
+ }
30
+ return treatment;
31
+ }
32
+ function sanitizeByFlag(logger, byFlagFallbacks) {
33
+ var sanitizedByFlag = {};
34
+ if (!(0, lang_1.isObject)(byFlagFallbacks))
35
+ return sanitizedByFlag;
36
+ Object.keys(byFlagFallbacks).forEach(function (flag) {
37
+ var t = byFlagFallbacks[flag];
38
+ if (!isValidFlagName(flag)) {
39
+ logger.error("Fallback treatments - Discarded flag '" + flag + "': " + FallbackDiscardReason.FlagName);
40
+ return;
20
41
  }
21
- return FallbacksSanitizer.pattern.test(treatment);
22
- };
23
- FallbacksSanitizer.sanitizeGlobal = function (logger, treatment) {
24
- if (!this.isValidTreatment(treatment)) {
25
- logger.error("Fallback treatments - Discarded fallback: " + FallbackDiscardReason.Treatment);
26
- return undefined;
42
+ if (!isValidTreatment(t)) {
43
+ logger.error("Fallback treatments - Discarded treatment for flag '" + flag + "': " + FallbackDiscardReason.Treatment);
44
+ return;
27
45
  }
28
- return treatment;
29
- };
30
- FallbacksSanitizer.sanitizeByFlag = function (logger, byFlagFallbacks) {
31
- var _this = this;
32
- var sanitizedByFlag = {};
33
- var entries = Object.keys(byFlagFallbacks);
34
- entries.forEach(function (flag) {
35
- var t = byFlagFallbacks[flag];
36
- if (!_this.isValidFlagName(flag)) {
37
- logger.error("Fallback treatments - Discarded flag '" + flag + "': " + FallbackDiscardReason.FlagName);
38
- return;
39
- }
40
- if (!_this.isValidTreatment(t)) {
41
- logger.error("Fallback treatments - Discarded treatment for flag '" + flag + "': " + FallbackDiscardReason.Treatment);
42
- return;
43
- }
44
- sanitizedByFlag[flag] = t;
45
- });
46
- return sanitizedByFlag;
46
+ sanitizedByFlag[flag] = t;
47
+ });
48
+ return sanitizedByFlag;
49
+ }
50
+ function sanitizeFallbacks(logger, fallbacks) {
51
+ if (!(0, lang_1.isObject)(fallbacks)) {
52
+ logger.error('Fallback treatments - Discarded configuration: it must be an object with optional `global` and `byFlag` properties');
53
+ return;
54
+ }
55
+ return {
56
+ global: sanitizeGlobal(logger, fallbacks.global),
57
+ byFlag: sanitizeByFlag(logger, fallbacks.byFlag)
47
58
  };
48
- FallbacksSanitizer.pattern = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
49
- return FallbacksSanitizer;
50
- }());
51
- exports.FallbacksSanitizer = FallbacksSanitizer;
59
+ }
60
+ exports.sanitizeFallbacks = sanitizeFallbacks;
@@ -1,18 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FallbackTreatmentsCalculator = exports.FALLBACK_PREFIX = void 0;
4
- var fallbackSanitizer_1 = require("./fallbackSanitizer");
5
4
  var constants_1 = require("../../utils/constants");
6
5
  var lang_1 = require("../../utils/lang");
7
6
  exports.FALLBACK_PREFIX = 'fallback - ';
8
7
  var FallbackTreatmentsCalculator = /** @class */ (function () {
9
- function FallbackTreatmentsCalculator(logger, fallbacks) {
10
- var sanitizedGlobal = (fallbacks === null || fallbacks === void 0 ? void 0 : fallbacks.global) ? fallbackSanitizer_1.FallbacksSanitizer.sanitizeGlobal(logger, fallbacks.global) : undefined;
11
- var sanitizedByFlag = (fallbacks === null || fallbacks === void 0 ? void 0 : fallbacks.byFlag) ? fallbackSanitizer_1.FallbacksSanitizer.sanitizeByFlag(logger, fallbacks.byFlag) : {};
12
- this.fallbacks = {
13
- global: sanitizedGlobal,
14
- byFlag: sanitizedByFlag
15
- };
8
+ function FallbackTreatmentsCalculator(fallbacks) {
9
+ if (fallbacks === void 0) { fallbacks = {}; }
10
+ this.fallbacks = fallbacks;
16
11
  }
17
12
  FallbackTreatmentsCalculator.prototype.resolve = function (flagName, label) {
18
13
  var _a;
@@ -52,7 +52,7 @@ function sdkFactory(params) {
52
52
  readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
53
53
  }
54
54
  });
55
- var fallbackTreatmentsCalculator = new fallbackTreatmentsCalculator_1.FallbackTreatmentsCalculator(settings.log, settings.fallbackTreatments);
55
+ var fallbackTreatmentsCalculator = new fallbackTreatmentsCalculator_1.FallbackTreatmentsCalculator(settings.fallbackTreatments);
56
56
  if (initialRolloutPlan) {
57
57
  (0, setRolloutPlan_1.setRolloutPlan)(log, initialRolloutPlan, storage, key && (0, key_1.getMatching)(key));
58
58
  if (storage.splits.getChangeNumber() > -1)
@@ -23,37 +23,45 @@ var AbstractMySegmentsCacheSync = /** @class */ (function () {
23
23
  */
24
24
  AbstractMySegmentsCacheSync.prototype.resetSegments = function (segmentsData) {
25
25
  var _this = this;
26
- this.setChangeNumber(segmentsData.cn);
26
+ var isDiff = false;
27
27
  var _a = segmentsData, added = _a.added, removed = _a.removed;
28
28
  if (added && removed) {
29
- var isDiff_1 = false;
30
29
  added.forEach(function (segment) {
31
- isDiff_1 = _this.addSegment(segment) || isDiff_1;
30
+ isDiff = _this.addSegment(segment) || isDiff;
32
31
  });
33
32
  removed.forEach(function (segment) {
34
- isDiff_1 = _this.removeSegment(segment) || isDiff_1;
33
+ isDiff = _this.removeSegment(segment) || isDiff;
35
34
  });
36
- return isDiff_1;
37
- }
38
- var names = (segmentsData.k || []).map(function (s) { return s.n; }).sort();
39
- var storedSegmentKeys = this.getRegisteredSegments().sort();
40
- // Extreme fast => everything is empty
41
- if (!names.length && !storedSegmentKeys.length)
42
- return false;
43
- var index = 0;
44
- while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index])
45
- index++;
46
- // Quick path => no changes
47
- if (index === names.length && index === storedSegmentKeys.length)
48
- return false;
49
- // Slowest path => add and/or remove segments
50
- for (var removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
51
- this.removeSegment(storedSegmentKeys[removeIndex]);
52
35
  }
53
- for (var addIndex = index; addIndex < names.length; addIndex++) {
54
- this.addSegment(names[addIndex]);
36
+ else {
37
+ var names = (segmentsData.k || []).map(function (s) { return s.n; }).sort();
38
+ var storedSegmentKeys = this.getRegisteredSegments().sort();
39
+ // Extreme fast => everything is empty
40
+ if (!names.length && !storedSegmentKeys.length) {
41
+ isDiff = false;
42
+ }
43
+ else {
44
+ var index = 0;
45
+ while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index])
46
+ index++;
47
+ // Quick path => no changes
48
+ if (index === names.length && index === storedSegmentKeys.length) {
49
+ isDiff = false;
50
+ }
51
+ else {
52
+ // Slowest path => add and/or remove segments
53
+ for (var removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
54
+ this.removeSegment(storedSegmentKeys[removeIndex]);
55
+ }
56
+ for (var addIndex = index; addIndex < names.length; addIndex++) {
57
+ this.addSegment(names[addIndex]);
58
+ }
59
+ isDiff = true;
60
+ }
61
+ }
55
62
  }
56
- return true;
63
+ this.setChangeNumber(segmentsData.cn);
64
+ return isDiff;
57
65
  };
58
66
  return AbstractMySegmentsCacheSync;
59
67
  }());
@@ -12,9 +12,10 @@ var AbstractSplitsCacheSync = /** @class */ (function () {
12
12
  }
13
13
  AbstractSplitsCacheSync.prototype.update = function (toAdd, toRemove, changeNumber) {
14
14
  var _this = this;
15
- this.setChangeNumber(changeNumber);
16
15
  var updated = toAdd.map(function (addedFF) { return _this.addSplit(addedFF); }).some(function (result) { return result; });
17
- return toRemove.map(function (removedFF) { return _this.removeSplit(removedFF.name); }).some(function (result) { return result; }) || updated;
16
+ updated = toRemove.map(function (removedFF) { return _this.removeSplit(removedFF.name); }).some(function (result) { return result; }) || updated;
17
+ this.setChangeNumber(changeNumber);
18
+ return updated;
18
19
  };
19
20
  AbstractSplitsCacheSync.prototype.getSplits = function (names) {
20
21
  var _this = this;
@@ -13,33 +13,20 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
13
13
  _this.keys = keys;
14
14
  _this.storage = storage;
15
15
  return _this;
16
- // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
17
16
  }
18
17
  MySegmentsCacheInLocal.prototype.addSegment = function (name) {
19
18
  var segmentKey = this.keys.buildSegmentNameKey(name);
20
- try {
21
- if (this.storage.getItem(segmentKey) === constants_1.DEFINED)
22
- return false;
23
- this.storage.setItem(segmentKey, constants_1.DEFINED);
24
- return true;
25
- }
26
- catch (e) {
27
- this.log.error(constants_1.LOG_PREFIX + e);
19
+ if (this.storage.getItem(segmentKey) === constants_1.DEFINED)
28
20
  return false;
29
- }
21
+ this.storage.setItem(segmentKey, constants_1.DEFINED);
22
+ return true;
30
23
  };
31
24
  MySegmentsCacheInLocal.prototype.removeSegment = function (name) {
32
25
  var segmentKey = this.keys.buildSegmentNameKey(name);
33
- try {
34
- if (this.storage.getItem(segmentKey) !== constants_1.DEFINED)
35
- return false;
36
- this.storage.removeItem(segmentKey);
37
- return true;
38
- }
39
- catch (e) {
40
- this.log.error(constants_1.LOG_PREFIX + e);
26
+ if (this.storage.getItem(segmentKey) !== constants_1.DEFINED)
41
27
  return false;
42
- }
28
+ this.storage.removeItem(segmentKey);
29
+ return true;
43
30
  };
44
31
  MySegmentsCacheInLocal.prototype.isInSegment = function (name) {
45
32
  return this.storage.getItem(this.keys.buildSegmentNameKey(name)) === constants_1.DEFINED;
@@ -57,15 +44,10 @@ var MySegmentsCacheInLocal = /** @class */ (function (_super) {
57
44
  return 1;
58
45
  };
59
46
  MySegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
60
- try {
61
- if (changeNumber)
62
- this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
63
- else
64
- this.storage.removeItem(this.keys.buildTillKey());
65
- }
66
- catch (e) {
67
- this.log.error(e);
68
- }
47
+ if (changeNumber)
48
+ this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
49
+ else
50
+ this.storage.removeItem(this.keys.buildTillKey());
69
51
  };
70
52
  MySegmentsCacheInLocal.prototype.getChangeNumber = function () {
71
53
  var n = -1;
@@ -18,9 +18,10 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
18
18
  };
19
19
  RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
20
20
  var _this = this;
21
- this.setChangeNumber(changeNumber);
22
21
  var updated = toAdd.map(function (toAdd) { return _this.add(toAdd); }).some(function (result) { return result; });
23
- return toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
22
+ updated = toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
23
+ this.setChangeNumber(changeNumber);
24
+ return updated;
24
25
  };
25
26
  RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
26
27
  try {
@@ -40,40 +41,28 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
40
41
  this.storage.removeItem(segmentsCountKey);
41
42
  };
42
43
  RBSegmentsCacheInLocal.prototype.add = function (rbSegment) {
43
- try {
44
- var name_1 = rbSegment.name;
45
- var rbSegmentKey = this.keys.buildRBSegmentKey(name_1);
46
- var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
47
- var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
48
- this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
49
- var usesSegmentsDiff = 0;
50
- if (previous && (0, AbstractSplitsCacheSync_1.usesSegments)(previous))
51
- usesSegmentsDiff--;
52
- if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
53
- usesSegmentsDiff++;
54
- if (usesSegmentsDiff !== 0)
55
- this.updateSegmentCount(usesSegmentsDiff);
56
- return true;
57
- }
58
- catch (e) {
59
- this.log.error(constants_1.LOG_PREFIX + e);
60
- return false;
61
- }
44
+ var name = rbSegment.name;
45
+ var rbSegmentKey = this.keys.buildRBSegmentKey(name);
46
+ var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
47
+ var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
48
+ this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
49
+ var usesSegmentsDiff = 0;
50
+ if (previous && (0, AbstractSplitsCacheSync_1.usesSegments)(previous))
51
+ usesSegmentsDiff--;
52
+ if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
53
+ usesSegmentsDiff++;
54
+ if (usesSegmentsDiff !== 0)
55
+ this.updateSegmentCount(usesSegmentsDiff);
56
+ return true;
62
57
  };
63
58
  RBSegmentsCacheInLocal.prototype.remove = function (name) {
64
- try {
65
- var rbSegment = this.get(name);
66
- if (!rbSegment)
67
- return false;
68
- this.storage.removeItem(this.keys.buildRBSegmentKey(name));
69
- if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
70
- this.updateSegmentCount(-1);
71
- return true;
72
- }
73
- catch (e) {
74
- this.log.error(constants_1.LOG_PREFIX + e);
59
+ var rbSegment = this.get(name);
60
+ if (!rbSegment)
75
61
  return false;
76
- }
62
+ this.storage.removeItem(this.keys.buildRBSegmentKey(name));
63
+ if ((0, AbstractSplitsCacheSync_1.usesSegments)(rbSegment))
64
+ this.updateSegmentCount(-1);
65
+ return true;
77
66
  };
78
67
  RBSegmentsCacheInLocal.prototype.getNames = function () {
79
68
  var len = this.storage.length;
@@ -70,39 +70,27 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
70
70
  this.hasSync = false;
71
71
  };
72
72
  SplitsCacheInLocal.prototype.addSplit = function (split) {
73
- try {
74
- var name_1 = split.name;
75
- var splitKey = this.keys.buildSplitKey(name_1);
76
- var splitFromStorage = this.storage.getItem(splitKey);
77
- var previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
78
- if (previousSplit) {
79
- this._decrementCounts(previousSplit);
80
- this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
81
- }
82
- this.storage.setItem(splitKey, JSON.stringify(split));
83
- this._incrementCounts(split);
84
- this.addToFlagSets(split);
85
- return true;
86
- }
87
- catch (e) {
88
- this.log.error(constants_1.LOG_PREFIX + e);
89
- return false;
73
+ var name = split.name;
74
+ var splitKey = this.keys.buildSplitKey(name);
75
+ var splitFromStorage = this.storage.getItem(splitKey);
76
+ var previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
77
+ if (previousSplit) {
78
+ this._decrementCounts(previousSplit);
79
+ this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
90
80
  }
81
+ this.storage.setItem(splitKey, JSON.stringify(split));
82
+ this._incrementCounts(split);
83
+ this.addToFlagSets(split);
84
+ return true;
91
85
  };
92
86
  SplitsCacheInLocal.prototype.removeSplit = function (name) {
93
- try {
94
- var split = this.getSplit(name);
95
- if (!split)
96
- return false;
97
- this.storage.removeItem(this.keys.buildSplitKey(name));
98
- this._decrementCounts(split);
99
- this.removeFromFlagSets(split.name, split.sets);
100
- return true;
101
- }
102
- catch (e) {
103
- this.log.error(constants_1.LOG_PREFIX + e);
87
+ var split = this.getSplit(name);
88
+ if (!split)
104
89
  return false;
105
- }
90
+ this.storage.removeItem(this.keys.buildSplitKey(name));
91
+ this._decrementCounts(split);
92
+ this.removeFromFlagSets(split.name, split.sets);
93
+ return true;
106
94
  };
107
95
  SplitsCacheInLocal.prototype.getSplit = function (name) {
108
96
  var item = this.storage.getItem(this.keys.buildSplitKey(name));
@@ -174,6 +162,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
174
162
  var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
175
163
  var flagSetFromStorage = _this.storage.getItem(flagSetKey);
176
164
  var flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
165
+ if (flagSetCache.has(featureFlag.name))
166
+ return;
177
167
  flagSetCache.add(featureFlag.name);
178
168
  _this.storage.setItem(flagSetKey, JSON.stringify((0, sets_1.setToArray)(flagSetCache)));
179
169
  });
@@ -16,9 +16,10 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
16
16
  };
17
17
  RBSegmentsCacheInMemory.prototype.update = function (toAdd, toRemove, changeNumber) {
18
18
  var _this = this;
19
- this.changeNumber = changeNumber;
20
19
  var updated = toAdd.map(function (toAdd) { return _this.add(toAdd); }).some(function (result) { return result; });
21
- return toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
20
+ updated = toRemove.map(function (toRemove) { return _this.remove(toRemove.name); }).some(function (result) { return result; }) || updated;
21
+ this.changeNumber = changeNumber;
22
+ return updated;
22
23
  };
23
24
  RBSegmentsCacheInMemory.prototype.add = function (rbSegment) {
24
25
  var name = rbSegment.name;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SegmentsCacheInRedis = void 0;
4
4
  var lang_1 = require("../../utils/lang");
5
- var constants_1 = require("../inLocalStorage/constants");
5
+ var constants_1 = require("./constants");
6
6
  var SegmentsCacheInRedis = /** @class */ (function () {
7
7
  function SegmentsCacheInRedis(log, keys, redis) {
8
8
  this.log = log;
@@ -8,6 +8,6 @@ var segmentChangesUpdater_1 = require("../updaters/segmentChangesUpdater");
8
8
  * Creates a sync task that periodically executes a `segmentChangesUpdater` task
9
9
  */
10
10
  function segmentsSyncTaskFactory(fetchSegmentChanges, storage, readiness, settings) {
11
- return (0, syncTask_1.syncTaskFactory)(settings.log, (0, segmentChangesUpdater_1.segmentChangesUpdaterFactory)(settings.log, (0, segmentChangesFetcher_1.segmentChangesFetcherFactory)(fetchSegmentChanges), storage.segments, readiness), settings.scheduler.segmentsRefreshRate, 'segmentChangesUpdater');
11
+ return (0, syncTask_1.syncTaskFactory)(settings.log, (0, segmentChangesUpdater_1.segmentChangesUpdaterFactory)(settings.log, (0, segmentChangesFetcher_1.segmentChangesFetcherFactory)(fetchSegmentChanges), storage.segments, readiness, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady), settings.scheduler.segmentsRefreshRate, 'segmentChangesUpdater');
12
12
  }
13
13
  exports.segmentsSyncTaskFactory = segmentsSyncTaskFactory;
@@ -48,9 +48,9 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
48
48
  new Promise(function (res) { updateSegments(segmentsData); res(true); }) :
49
49
  // If not provided, fetch mySegments
50
50
  mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(function (segments) {
51
- // Only when we have downloaded segments completely, we should not keep retrying anymore
52
- startingUp = false;
53
51
  updateSegments(segments);
52
+ // Only when we have downloaded and stored segments completely, we should not keep retrying anymore
53
+ startingUp = false;
54
54
  return true;
55
55
  });
56
56
  return updaterPromise.catch(function (error) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.segmentChangesUpdaterFactory = void 0;
4
4
  var constants_1 = require("../../../readiness/constants");
5
5
  var constants_2 = require("../../../logger/constants");
6
+ var timeout_1 = require("../../../utils/promise/timeout");
6
7
  /**
7
8
  * Factory of SegmentChanges updater, a task that:
8
9
  * - fetches segment changes using `segmentChangesFetcher`
@@ -14,22 +15,33 @@ var constants_2 = require("../../../logger/constants");
14
15
  * @param segments - segments storage, with sync or async methods
15
16
  * @param readiness - optional readiness manager. Not required for synchronizer or producer mode.
16
17
  */
17
- function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness) {
18
+ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, readiness, requestTimeoutBeforeReady, retriesOnFailureBeforeReady) {
18
19
  var readyOnAlreadyExistentState = true;
19
- function updateSegment(segmentName, noCache, till, fetchOnlyNew) {
20
+ function _promiseDecorator(promise) {
21
+ if (readyOnAlreadyExistentState && requestTimeoutBeforeReady)
22
+ promise = (0, timeout_1.timeout)(requestTimeoutBeforeReady, promise);
23
+ return promise;
24
+ }
25
+ function updateSegment(segmentName, noCache, till, fetchOnlyNew, retries) {
20
26
  log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing segment " + segmentName);
21
27
  var sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
22
28
  return sincePromise.then(function (since) {
23
29
  // if fetchOnlyNew flag, avoid processing already fetched segments
24
30
  return fetchOnlyNew && since !== undefined ?
25
31
  false :
26
- segmentChangesFetcher(since || -1, segmentName, noCache, till).then(function (changes) {
32
+ segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then(function (changes) {
27
33
  return Promise.all(changes.map(function (x) {
28
34
  log.debug(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Processing " + segmentName + " with till = " + x.till + ". Added: " + x.added.length + ". Removed: " + x.removed.length);
29
35
  return segments.update(segmentName, x.added, x.removed, x.till);
30
36
  })).then(function (updates) {
31
37
  return updates.some(function (update) { return update; });
32
38
  });
39
+ }).catch(function (error) {
40
+ if (retries) {
41
+ log.warn(constants_2.LOG_PREFIX_SYNC_SEGMENTS + "Retrying fetch of segment " + segmentName + " (attempt #" + retries + "). Reason: " + error);
42
+ return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
43
+ }
44
+ throw error;
33
45
  });
34
46
  });
35
47
  }
@@ -49,8 +61,7 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
49
61
  // If not a segment name provided, read list of available segments names to be updated.
50
62
  var segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
51
63
  return segmentsPromise.then(function (segmentNames) {
52
- // Async fetchers
53
- var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew); });
64
+ var updaters = segmentNames.map(function (segmentName) { return updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0); });
54
65
  return Promise.all(updaters).then(function (shouldUpdateFlags) {
55
66
  // if at least one segment fetch succeeded, mark segments ready
56
67
  if (shouldUpdateFlags.some(function (update) { return update; }) || readyOnAlreadyExistentState) {
@@ -135,7 +135,6 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
135
135
  { rbs: { d: [instantUpdate.payload], t: instantUpdate.changeNumber } } :
136
136
  splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator))
137
137
  .then(function (splitChanges) {
138
- startingUp = false;
139
138
  var usedSegments = new Set();
140
139
  var ffUpdate = false;
141
140
  if (splitChanges.ff) {
@@ -156,6 +155,7 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
156
155
  var ffChanged = _a[0], rbsChanged = _a[1];
157
156
  if (storage.save)
158
157
  storage.save();
158
+ startingUp = false;
159
159
  if (splitsEventEmitter) {
160
160
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
161
161
  return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
@@ -171,14 +171,14 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
171
171
  });
172
172
  })
173
173
  .catch(function (error) {
174
- log.warn(constants_2.SYNC_SPLITS_FETCH_FAILS, [error]);
175
174
  if (startingUp && retriesOnFailureBeforeReady > retry) {
176
175
  retry += 1;
177
- log.info(constants_2.SYNC_SPLITS_FETCH_RETRY, [retry, error]);
176
+ log.warn(constants_2.SYNC_SPLITS_FETCH_RETRY, [retry, error]);
178
177
  return _splitChangesUpdater(sinces, retry);
179
178
  }
180
179
  else {
181
180
  startingUp = false;
181
+ log.warn(constants_2.SYNC_SPLITS_FETCH_FAILS, [error]);
182
182
  }
183
183
  return false;
184
184
  });
@@ -9,6 +9,7 @@ var impressionsMode_1 = require("./impressionsMode");
9
9
  var key_1 = require("../inputValidation/key");
10
10
  var constants_2 = require("../../logger/constants");
11
11
  var setRolloutPlan_1 = require("../../storages/setRolloutPlan");
12
+ var fallbackSanitizer_1 = require("../../evaluator/fallbackTreatmentsCalculator/fallbackSanitizer");
12
13
  // Exported for telemetry
13
14
  exports.base = {
14
15
  // Define which kind of object you want to retrieve from SplitFactory
@@ -183,6 +184,8 @@ function settingsValidation(config, validationParams) {
183
184
  // ensure a valid user consent value
184
185
  // @ts-ignore, modify readonly prop
185
186
  withDefaults.userConsent = consent ? consent(withDefaults) : undefined;
187
+ // @ts-ignore, modify readonly prop
188
+ withDefaults.fallbackTreatments = withDefaults.fallbackTreatments ? (0, fallbackSanitizer_1.sanitizeFallbacks)(log, withDefaults.fallbackTreatments) : undefined;
186
189
  return withDefaults;
187
190
  }
188
191
  exports.settingsValidation = settingsValidation;