@splitsoftware/splitio-commons 2.8.1-rc.0 → 2.8.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 (45) hide show
  1. package/CHANGES.txt +2 -3
  2. package/cjs/evaluator/index.js +13 -13
  3. package/cjs/sdkClient/client.js +3 -3
  4. package/cjs/storages/AbstractMySegmentsCacheSync.js +23 -31
  5. package/cjs/storages/AbstractSplitsCacheSync.js +2 -3
  6. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +28 -10
  7. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +33 -22
  8. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +29 -19
  9. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +2 -3
  10. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  11. package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  12. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  13. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +5 -16
  14. package/cjs/sync/polling/updaters/splitChangesUpdater.js +3 -3
  15. package/cjs/utils/inputValidation/eventProperties.js +6 -1
  16. package/esm/evaluator/index.js +13 -13
  17. package/esm/sdkClient/client.js +3 -3
  18. package/esm/storages/AbstractMySegmentsCacheSync.js +23 -31
  19. package/esm/storages/AbstractSplitsCacheSync.js +2 -3
  20. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -11
  21. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +33 -22
  22. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +29 -19
  23. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +2 -3
  24. package/esm/storages/inRedis/SegmentsCacheInRedis.js +1 -1
  25. package/esm/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
  26. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  27. package/esm/sync/polling/updaters/segmentChangesUpdater.js +5 -16
  28. package/esm/sync/polling/updaters/splitChangesUpdater.js +3 -3
  29. package/esm/utils/inputValidation/eventProperties.js +6 -1
  30. package/package.json +1 -1
  31. package/src/evaluator/index.ts +14 -6
  32. package/src/sdkClient/client.ts +3 -3
  33. package/src/storages/AbstractMySegmentsCacheSync.ts +20 -26
  34. package/src/storages/AbstractSplitsCacheSync.ts +2 -3
  35. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +24 -9
  36. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +27 -18
  37. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +29 -22
  38. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +2 -3
  39. package/src/storages/inRedis/SegmentsCacheInRedis.ts +1 -1
  40. package/src/sync/polling/syncTasks/segmentsSyncTask.ts +0 -2
  41. package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -3
  42. package/src/sync/polling/updaters/segmentChangesUpdater.ts +4 -17
  43. package/src/sync/polling/updaters/splitChangesUpdater.ts +7 -6
  44. package/src/utils/inputValidation/eventProperties.ts +6 -1
  45. package/types/splitio.d.ts +10 -0
@@ -30,6 +30,7 @@ export function evaluateFeature(
30
30
  splitName: string,
31
31
  attributes: SplitIO.Attributes | undefined,
32
32
  storage: IStorageSync | IStorageAsync,
33
+ options?: SplitIO.EvaluationOptions
33
34
  ): MaybeThenable<IEvaluationResult> {
34
35
  let parsedSplit;
35
36
 
@@ -47,6 +48,7 @@ export function evaluateFeature(
47
48
  split,
48
49
  attributes,
49
50
  storage,
51
+ options,
50
52
  )).catch(
51
53
  // Exception on async `getSplit` storage. For example, when the storage is redis or
52
54
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
@@ -60,6 +62,7 @@ export function evaluateFeature(
60
62
  parsedSplit,
61
63
  attributes,
62
64
  storage,
65
+ options,
63
66
  );
64
67
  }
65
68
 
@@ -69,6 +72,7 @@ export function evaluateFeatures(
69
72
  splitNames: string[],
70
73
  attributes: SplitIO.Attributes | undefined,
71
74
  storage: IStorageSync | IStorageAsync,
75
+ options?: SplitIO.EvaluationOptions,
72
76
  ): MaybeThenable<Record<string, IEvaluationResult>> {
73
77
  let parsedSplits;
74
78
 
@@ -80,13 +84,13 @@ export function evaluateFeatures(
80
84
  }
81
85
 
82
86
  return thenable(parsedSplits) ?
83
- parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage))
87
+ parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage, options))
84
88
  .catch(() => {
85
89
  // Exception on async `getSplits` storage. For example, when the storage is redis or
86
90
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
87
91
  return treatmentsException(splitNames);
88
92
  }) :
89
- getEvaluations(log, key, splitNames, parsedSplits, attributes, storage);
93
+ getEvaluations(log, key, splitNames, parsedSplits, attributes, storage, options);
90
94
  }
91
95
 
92
96
  export function evaluateFeaturesByFlagSets(
@@ -96,6 +100,7 @@ export function evaluateFeaturesByFlagSets(
96
100
  attributes: SplitIO.Attributes | undefined,
97
101
  storage: IStorageSync | IStorageAsync,
98
102
  method: string,
103
+ options?: SplitIO.EvaluationOptions,
99
104
  ): MaybeThenable<Record<string, IEvaluationResult>> {
100
105
  let storedFlagNames: MaybeThenable<Set<string>[]>;
101
106
 
@@ -111,7 +116,7 @@ export function evaluateFeaturesByFlagSets(
111
116
  }
112
117
 
113
118
  return featureFlags.size ?
114
- evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage) :
119
+ evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage, options) :
115
120
  {};
116
121
  }
117
122
 
@@ -138,6 +143,7 @@ function getEvaluation(
138
143
  splitJSON: ISplit | null,
139
144
  attributes: SplitIO.Attributes | undefined,
140
145
  storage: IStorageSync | IStorageAsync,
146
+ options?: SplitIO.EvaluationOptions,
141
147
  ): MaybeThenable<IEvaluationResult> {
142
148
  let evaluation: MaybeThenable<IEvaluationResult> = {
143
149
  treatment: CONTROL,
@@ -154,14 +160,14 @@ function getEvaluation(
154
160
  return evaluation.then(result => {
155
161
  result.changeNumber = splitJSON.changeNumber;
156
162
  result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
157
- result.impressionsDisabled = splitJSON.impressionsDisabled;
163
+ result.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
158
164
 
159
165
  return result;
160
166
  });
161
167
  } else {
162
168
  evaluation.changeNumber = splitJSON.changeNumber;
163
169
  evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
164
- evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
170
+ evaluation.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
165
171
  }
166
172
  }
167
173
 
@@ -175,6 +181,7 @@ function getEvaluations(
175
181
  splits: Record<string, ISplit | null>,
176
182
  attributes: SplitIO.Attributes | undefined,
177
183
  storage: IStorageSync | IStorageAsync,
184
+ options?: SplitIO.EvaluationOptions,
178
185
  ): MaybeThenable<Record<string, IEvaluationResult>> {
179
186
  const result: Record<string, IEvaluationResult> = {};
180
187
  const thenables: Promise<void>[] = [];
@@ -184,7 +191,8 @@ function getEvaluations(
184
191
  key,
185
192
  splits[splitName],
186
193
  attributes,
187
- storage
194
+ storage,
195
+ options
188
196
  );
189
197
  if (thenable(evaluation)) {
190
198
  thenables.push(evaluation.then(res => {
@@ -52,7 +52,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
52
52
  };
53
53
 
54
54
  const evaluation = readinessManager.isReadyFromCache() ?
55
- evaluateFeature(log, key, featureFlagName, attributes, storage) :
55
+ evaluateFeature(log, key, featureFlagName, attributes, storage, options) :
56
56
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
57
57
  Promise.resolve(treatmentNotReady) :
58
58
  treatmentNotReady;
@@ -81,7 +81,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
81
81
  };
82
82
 
83
83
  const evaluations = readinessManager.isReadyFromCache() ?
84
- evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
84
+ evaluateFeatures(log, key, featureFlagNames, attributes, storage, options) :
85
85
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
86
86
  Promise.resolve(treatmentsNotReady(featureFlagNames)) :
87
87
  treatmentsNotReady(featureFlagNames);
@@ -110,7 +110,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
110
110
  };
111
111
 
112
112
  const evaluations = readinessManager.isReadyFromCache() ?
113
- evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
113
+ evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName, options) :
114
114
  isAsync ?
115
115
  Promise.resolve({}) :
116
116
  {};
@@ -49,10 +49,12 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
49
49
  * For client-side synchronizer: it resets or updates the cache.
50
50
  */
51
51
  resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
52
- let isDiff = false;
52
+ this.setChangeNumber(segmentsData.cn);
53
+
53
54
  const { added, removed } = segmentsData as MySegmentsData;
54
55
 
55
56
  if (added && removed) {
57
+ let isDiff = false;
56
58
 
57
59
  added.forEach(segment => {
58
60
  isDiff = this.addSegment(segment) || isDiff;
@@ -61,40 +63,32 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync
61
63
  removed.forEach(segment => {
62
64
  isDiff = this.removeSegment(segment) || isDiff;
63
65
  });
64
- } else {
65
66
 
66
- const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
67
- const storedSegmentKeys = this.getRegisteredSegments().sort();
67
+ return isDiff;
68
+ }
68
69
 
69
- // Extreme fast => everything is empty
70
- if (!names.length && !storedSegmentKeys.length) {
71
- isDiff = false;
72
- } else {
70
+ const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
71
+ const storedSegmentKeys = this.getRegisteredSegments().sort();
73
72
 
74
- let index = 0;
73
+ // Extreme fast => everything is empty
74
+ if (!names.length && !storedSegmentKeys.length) return false;
75
75
 
76
- while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
76
+ let index = 0;
77
77
 
78
- // Quick path => no changes
79
- if (index === names.length && index === storedSegmentKeys.length) {
80
- isDiff = false;
81
- } else {
78
+ while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
82
79
 
83
- // Slowest path => add and/or remove segments
84
- for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
85
- this.removeSegment(storedSegmentKeys[removeIndex]);
86
- }
80
+ // Quick path => no changes
81
+ if (index === names.length && index === storedSegmentKeys.length) return false;
87
82
 
88
- for (let addIndex = index; addIndex < names.length; addIndex++) {
89
- this.addSegment(names[addIndex]);
90
- }
83
+ // Slowest path => add and/or remove segments
84
+ for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
85
+ this.removeSegment(storedSegmentKeys[removeIndex]);
86
+ }
91
87
 
92
- isDiff = true;
93
- }
94
- }
88
+ for (let addIndex = index; addIndex < names.length; addIndex++) {
89
+ this.addSegment(names[addIndex]);
95
90
  }
96
91
 
97
- this.setChangeNumber(segmentsData.cn);
98
- return isDiff;
92
+ return true;
99
93
  }
100
94
  }
@@ -14,10 +14,9 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
14
14
  protected abstract setChangeNumber(changeNumber: number): boolean | void
15
15
 
16
16
  update(toAdd: ISplit[], toRemove: ISplit[], changeNumber: number): boolean {
17
- let updated = toAdd.map(addedFF => this.addSplit(addedFF)).some(result => result);
18
- updated = toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
19
17
  this.setChangeNumber(changeNumber);
20
- return updated;
18
+ const updated = toAdd.map(addedFF => this.addSplit(addedFF)).some(result => result);
19
+ return toRemove.map(removedFF => this.removeSplit(removedFF.name)).some(result => result) || updated;
21
20
  }
22
21
 
23
22
  abstract getSplit(name: string): ISplit | null
@@ -2,7 +2,7 @@ import { ILogger } from '../../logger/types';
2
2
  import { isNaNNumber } from '../../utils/lang';
3
3
  import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
4
4
  import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
5
- import { DEFINED } from './constants';
5
+ import { LOG_PREFIX, DEFINED } from './constants';
6
6
  import { StorageAdapter } from '../types';
7
7
 
8
8
  export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
@@ -16,22 +16,33 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
16
16
  this.log = log;
17
17
  this.keys = keys;
18
18
  this.storage = storage;
19
+ // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
19
20
  }
20
21
 
21
22
  protected addSegment(name: string): boolean {
22
23
  const segmentKey = this.keys.buildSegmentNameKey(name);
23
24
 
24
- if (this.storage.getItem(segmentKey) === DEFINED) return false;
25
- this.storage.setItem(segmentKey, DEFINED);
26
- return true;
25
+ try {
26
+ if (this.storage.getItem(segmentKey) === DEFINED) return false;
27
+ this.storage.setItem(segmentKey, DEFINED);
28
+ return true;
29
+ } catch (e) {
30
+ this.log.error(LOG_PREFIX + e);
31
+ return false;
32
+ }
27
33
  }
28
34
 
29
35
  protected removeSegment(name: string): boolean {
30
36
  const segmentKey = this.keys.buildSegmentNameKey(name);
31
37
 
32
- if (this.storage.getItem(segmentKey) !== DEFINED) return false;
33
- this.storage.removeItem(segmentKey);
34
- return true;
38
+ try {
39
+ if (this.storage.getItem(segmentKey) !== DEFINED) return false;
40
+ this.storage.removeItem(segmentKey);
41
+ return true;
42
+ } catch (e) {
43
+ this.log.error(LOG_PREFIX + e);
44
+ return false;
45
+ }
35
46
  }
36
47
 
37
48
  isInSegment(name: string): boolean {
@@ -52,8 +63,12 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
52
63
  }
53
64
 
54
65
  protected setChangeNumber(changeNumber?: number) {
55
- if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
56
- else this.storage.removeItem(this.keys.buildTillKey());
66
+ try {
67
+ if (changeNumber) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
68
+ else this.storage.removeItem(this.keys.buildTillKey());
69
+ } catch (e) {
70
+ this.log.error(e);
71
+ }
57
72
  }
58
73
 
59
74
  getChangeNumber() {
@@ -26,10 +26,9 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
26
26
  }
27
27
 
28
28
  update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
29
- let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
30
- updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
31
29
  this.setChangeNumber(changeNumber);
32
- return updated;
30
+ const updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
31
+ return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
33
32
  }
34
33
 
35
34
  private setChangeNumber(changeNumber: number) {
@@ -49,30 +48,40 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
49
48
  }
50
49
 
51
50
  private add(rbSegment: IRBSegment): boolean {
52
- const name = rbSegment.name;
53
- const rbSegmentKey = this.keys.buildRBSegmentKey(name);
54
- const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
55
- const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
51
+ try {
52
+ const name = rbSegment.name;
53
+ const rbSegmentKey = this.keys.buildRBSegmentKey(name);
54
+ const rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
55
+ const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
56
56
 
57
- this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
57
+ this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
58
58
 
59
- let usesSegmentsDiff = 0;
60
- if (previous && usesSegments(previous)) usesSegmentsDiff--;
61
- if (usesSegments(rbSegment)) usesSegmentsDiff++;
62
- if (usesSegmentsDiff !== 0) this.updateSegmentCount(usesSegmentsDiff);
59
+ let usesSegmentsDiff = 0;
60
+ if (previous && usesSegments(previous)) usesSegmentsDiff--;
61
+ if (usesSegments(rbSegment)) usesSegmentsDiff++;
62
+ if (usesSegmentsDiff !== 0) this.updateSegmentCount(usesSegmentsDiff);
63
63
 
64
- return true;
64
+ return true;
65
+ } catch (e) {
66
+ this.log.error(LOG_PREFIX + e);
67
+ return false;
68
+ }
65
69
  }
66
70
 
67
71
  private remove(name: string): boolean {
68
- const rbSegment = this.get(name);
69
- if (!rbSegment) return false;
72
+ try {
73
+ const rbSegment = this.get(name);
74
+ if (!rbSegment) return false;
70
75
 
71
- this.storage.removeItem(this.keys.buildRBSegmentKey(name));
76
+ this.storage.removeItem(this.keys.buildRBSegmentKey(name));
72
77
 
73
- if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
78
+ if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
74
79
 
75
- return true;
80
+ return true;
81
+ } catch (e) {
82
+ this.log.error(LOG_PREFIX + e);
83
+ return false;
84
+ }
76
85
  }
77
86
 
78
87
  private getNames(): string[] {
@@ -80,34 +80,44 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
80
80
  }
81
81
 
82
82
  addSplit(split: ISplit) {
83
- const name = split.name;
84
- const splitKey = this.keys.buildSplitKey(name);
85
- const splitFromStorage = this.storage.getItem(splitKey);
86
- const previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
87
-
88
- if (previousSplit) {
89
- this._decrementCounts(previousSplit);
90
- this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
91
- }
83
+ try {
84
+ const name = split.name;
85
+ const splitKey = this.keys.buildSplitKey(name);
86
+ const splitFromStorage = this.storage.getItem(splitKey);
87
+ const previousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : null;
88
+
89
+ if (previousSplit) {
90
+ this._decrementCounts(previousSplit);
91
+ this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
92
+ }
92
93
 
93
- this.storage.setItem(splitKey, JSON.stringify(split));
94
+ this.storage.setItem(splitKey, JSON.stringify(split));
94
95
 
95
- this._incrementCounts(split);
96
- this.addToFlagSets(split);
96
+ this._incrementCounts(split);
97
+ this.addToFlagSets(split);
97
98
 
98
- return true;
99
+ return true;
100
+ } catch (e) {
101
+ this.log.error(LOG_PREFIX + e);
102
+ return false;
103
+ }
99
104
  }
100
105
 
101
106
  removeSplit(name: string): boolean {
102
- const split = this.getSplit(name);
103
- if (!split) return false;
107
+ try {
108
+ const split = this.getSplit(name);
109
+ if (!split) return false;
104
110
 
105
- this.storage.removeItem(this.keys.buildSplitKey(name));
111
+ this.storage.removeItem(this.keys.buildSplitKey(name));
106
112
 
107
- this._decrementCounts(split);
108
- this.removeFromFlagSets(split.name, split.sets);
113
+ this._decrementCounts(split);
114
+ this.removeFromFlagSets(split.name, split.sets);
109
115
 
110
- return true;
116
+ return true;
117
+ } catch (e) {
118
+ this.log.error(LOG_PREFIX + e);
119
+ return false;
120
+ }
111
121
  }
112
122
 
113
123
  getSplit(name: string): ISplit | null {
@@ -196,9 +206,6 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
196
206
  const flagSetFromStorage = this.storage.getItem(flagSetKey);
197
207
 
198
208
  const flagSetCache = new Set(flagSetFromStorage ? JSON.parse(flagSetFromStorage) : []);
199
-
200
- if (flagSetCache.has(featureFlag.name)) return;
201
-
202
209
  flagSetCache.add(featureFlag.name);
203
210
 
204
211
  this.storage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
@@ -16,10 +16,9 @@ export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
16
16
  }
17
17
 
18
18
  update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
19
- let updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
20
- updated = toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
21
19
  this.changeNumber = changeNumber;
22
- return updated;
20
+ const updated = toAdd.map(toAdd => this.add(toAdd)).some(result => result);
21
+ return toRemove.map(toRemove => this.remove(toRemove.name)).some(result => result) || updated;
23
22
  }
24
23
 
25
24
  private add(rbSegment: IRBSegment): boolean {
@@ -1,6 +1,6 @@
1
1
  import { ILogger } from '../../logger/types';
2
2
  import { isNaNNumber } from '../../utils/lang';
3
- import { LOG_PREFIX } from './constants';
3
+ import { LOG_PREFIX } from '../inLocalStorage/constants';
4
4
  import { KeyBuilderSS } from '../KeyBuilderSS';
5
5
  import { ISegmentsCacheAsync } from '../types';
6
6
  import type { RedisAdapter } from './RedisAdapter';
@@ -23,8 +23,6 @@ export function segmentsSyncTaskFactory(
23
23
  segmentChangesFetcherFactory(fetchSegmentChanges),
24
24
  storage.segments,
25
25
  readiness,
26
- settings.startup.requestTimeoutBeforeReady,
27
- settings.startup.retriesOnFailureBeforeReady,
28
26
  ),
29
27
  settings.scheduler.segmentsRefreshRate,
30
28
  'segmentChangesUpdater'
@@ -66,10 +66,10 @@ export function mySegmentsUpdaterFactory(
66
66
  new Promise((res) => { updateSegments(segmentsData); res(true); }) :
67
67
  // If not provided, fetch mySegments
68
68
  mySegmentsFetcher(matchingKey, noCache, till, _promiseDecorator).then(segments => {
69
- updateSegments(segments);
70
-
71
- // Only when we have downloaded and stored segments completely, we should not keep retrying anymore
69
+ // Only when we have downloaded segments completely, we should not keep retrying anymore
72
70
  startingUp = false;
71
+
72
+ updateSegments(segments);
73
73
  return true;
74
74
  });
75
75
 
@@ -4,7 +4,6 @@ import { IReadinessManager } from '../../../readiness/types';
4
4
  import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
5
5
  import { ILogger } from '../../../logger/types';
6
6
  import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
7
- import { timeout } from '../../../utils/promise/timeout';
8
7
 
9
8
  type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>
10
9
 
@@ -24,18 +23,11 @@ export function segmentChangesUpdaterFactory(
24
23
  segmentChangesFetcher: ISegmentChangesFetcher,
25
24
  segments: ISegmentsCacheBase,
26
25
  readiness?: IReadinessManager,
27
- requestTimeoutBeforeReady?: number,
28
- retriesOnFailureBeforeReady?: number,
29
26
  ): ISegmentChangesUpdater {
30
27
 
31
28
  let readyOnAlreadyExistentState = true;
32
29
 
33
- function _promiseDecorator<T>(promise: Promise<T>) {
34
- if (readyOnAlreadyExistentState && requestTimeoutBeforeReady) promise = timeout(requestTimeoutBeforeReady, promise);
35
- return promise;
36
- }
37
-
38
- function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean, retries?: number): Promise<boolean> {
30
+ function updateSegment(segmentName: string, noCache?: boolean, till?: number, fetchOnlyNew?: boolean): Promise<boolean> {
39
31
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing segment ${segmentName}`);
40
32
  let sincePromise = Promise.resolve(segments.getChangeNumber(segmentName));
41
33
 
@@ -43,19 +35,13 @@ export function segmentChangesUpdaterFactory(
43
35
  // if fetchOnlyNew flag, avoid processing already fetched segments
44
36
  return fetchOnlyNew && since !== undefined ?
45
37
  false :
46
- segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then((changes) => {
38
+ segmentChangesFetcher(since || -1, segmentName, noCache, till).then((changes) => {
47
39
  return Promise.all(changes.map(x => {
48
40
  log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
49
41
  return segments.update(segmentName, x.added, x.removed, x.till);
50
42
  })).then((updates) => {
51
43
  return updates.some(update => update);
52
44
  });
53
- }).catch(error => {
54
- if (retries) {
55
- log.warn(`${LOG_PREFIX_SYNC_SEGMENTS}Retrying fetch of segment ${segmentName} (attempt #${retries}). Reason: ${error}`);
56
- return updateSegment(segmentName, noCache, till, fetchOnlyNew, retries - 1);
57
- }
58
- throw error;
59
45
  });
60
46
  });
61
47
  }
@@ -77,7 +63,8 @@ export function segmentChangesUpdaterFactory(
77
63
  let segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments());
78
64
 
79
65
  return segmentsPromise.then(segmentNames => {
80
- const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0));
66
+ // Async fetchers
67
+ const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew));
81
68
 
82
69
  return Promise.all(updaters).then(shouldUpdateFlags => {
83
70
  // if at least one segment fetch succeeded, mark segments ready
@@ -120,8 +120,8 @@ export function splitChangesUpdaterFactory(
120
120
  storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments' | 'save'>,
121
121
  splitFiltersValidation: ISplitFiltersValidation,
122
122
  splitsEventEmitter?: ISplitsEventEmitter,
123
- requestTimeoutBeforeReady = 0,
124
- retriesOnFailureBeforeReady = 0,
123
+ requestTimeoutBeforeReady: number = 0,
124
+ retriesOnFailureBeforeReady: number = 0,
125
125
  isClientSide?: boolean
126
126
  ): SplitChangesUpdater {
127
127
  const { splits, rbSegments, segments } = storage;
@@ -163,6 +163,8 @@ export function splitChangesUpdaterFactory(
163
163
  splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
164
164
  )
165
165
  .then((splitChanges: ISplitChangesResponse) => {
166
+ startingUp = false;
167
+
166
168
  const usedSegments = new Set<string>();
167
169
 
168
170
  let ffUpdate: MaybeThenable<boolean> = false;
@@ -185,8 +187,6 @@ export function splitChangesUpdaterFactory(
185
187
  ]).then(([ffChanged, rbsChanged]) => {
186
188
  if (storage.save) storage.save();
187
189
 
188
- startingUp = false;
189
-
190
190
  if (splitsEventEmitter) {
191
191
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
192
192
  return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
@@ -201,13 +201,14 @@ export function splitChangesUpdaterFactory(
201
201
  });
202
202
  })
203
203
  .catch(error => {
204
+ log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
205
+
204
206
  if (startingUp && retriesOnFailureBeforeReady > retry) {
205
207
  retry += 1;
206
- log.warn(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
208
+ log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
207
209
  return _splitChangesUpdater(sinces, retry);
208
210
  } else {
209
211
  startingUp = false;
210
- log.warn(SYNC_SPLITS_FETCH_FAILS, [error]);
211
212
  }
212
213
  return false;
213
214
  });
@@ -70,7 +70,12 @@ export function validateEventProperties(log: ILogger, maybeProperties: any, meth
70
70
  export function validateEvaluationOptions(log: ILogger, maybeOptions: any, method: string): SplitIO.EvaluationOptions | undefined {
71
71
  if (isObject(maybeOptions)) {
72
72
  const properties = validateEventProperties(log, maybeOptions.properties, method).properties;
73
- return properties && Object.keys(properties).length > 0 ? { properties } : undefined;
73
+ let options = properties && Object.keys(properties).length > 0 ? { properties } : undefined;
74
+
75
+ const impressionsDisabled = maybeOptions.impressionsDisabled;
76
+ if (!impressionsDisabled) return options;
77
+
78
+ return options ? { ...options, impressionsDisabled } : { impressionsDisabled };
74
79
  } else if (maybeOptions) {
75
80
  log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']);
76
81
  }
@@ -918,8 +918,16 @@ declare namespace SplitIO {
918
918
  * Evaluation options object for getTreatment methods.
919
919
  */
920
920
  type EvaluationOptions = {
921
+ /**
922
+ * Whether the evaluation/s will track impressions or not.
923
+ *
924
+ * @defaultValue `false`
925
+ */
926
+ impressionsDisabled?: boolean;
921
927
  /**
922
928
  * Optional properties to append to the generated impression object sent to Split backend.
929
+ *
930
+ * @defaultValue `undefined`
923
931
  */
924
932
  properties?: Properties;
925
933
  }
@@ -1335,6 +1343,8 @@ declare namespace SplitIO {
1335
1343
  /**
1336
1344
  * Defines the factory function to instantiate the storage. If not provided, the default in-memory storage is used.
1337
1345
  *
1346
+ * NOTE: Currently, there is no persistent storage option available for the React Native SDK; only `InLocalStorage` for the Browser SDK.
1347
+ *
1338
1348
  * Example:
1339
1349
  * ```
1340
1350
  * SplitFactory({