@splitsoftware/splitio-commons 1.12.1-rc.4 → 1.12.1-rc.5

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 (74) hide show
  1. package/CHANGES.txt +1 -1
  2. package/cjs/logger/constants.js +3 -3
  3. package/cjs/logger/messages/warn.js +2 -2
  4. package/cjs/sdkClient/client.js +11 -8
  5. package/cjs/sdkClient/clientInputValidation.js +6 -6
  6. package/cjs/sdkManager/index.js +6 -6
  7. package/cjs/storages/KeyBuilder.js +13 -1
  8. package/cjs/storages/KeyBuilderCS.js +1 -4
  9. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +12 -16
  10. package/cjs/storages/inLocalStorage/index.js +1 -1
  11. package/cjs/storages/inRedis/SplitsCacheInRedis.js +2 -2
  12. package/cjs/storages/inRedis/index.js +1 -1
  13. package/cjs/storages/pluggable/SplitsCachePluggable.js +2 -2
  14. package/cjs/storages/pluggable/index.js +14 -3
  15. package/cjs/trackers/eventTracker.js +4 -4
  16. package/cjs/utils/lang/sets.js +3 -3
  17. package/cjs/utils/settingsValidation/index.js +1 -1
  18. package/cjs/utils/settingsValidation/mode.js +10 -3
  19. package/cjs/utils/settingsValidation/splitFilters.js +20 -19
  20. package/esm/logger/constants.js +2 -2
  21. package/esm/logger/messages/warn.js +2 -2
  22. package/esm/sdkClient/client.js +11 -8
  23. package/esm/sdkClient/clientInputValidation.js +7 -7
  24. package/esm/sdkManager/index.js +6 -6
  25. package/esm/storages/KeyBuilder.js +11 -0
  26. package/esm/storages/KeyBuilderCS.js +1 -4
  27. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +12 -16
  28. package/esm/storages/inLocalStorage/index.js +1 -1
  29. package/esm/storages/inRedis/SplitsCacheInRedis.js +3 -3
  30. package/esm/storages/inRedis/index.js +1 -1
  31. package/esm/storages/pluggable/SplitsCachePluggable.js +3 -3
  32. package/esm/storages/pluggable/index.js +15 -4
  33. package/esm/trackers/eventTracker.js +4 -4
  34. package/esm/utils/lang/sets.js +1 -1
  35. package/esm/utils/settingsValidation/index.js +2 -2
  36. package/esm/utils/settingsValidation/mode.js +7 -1
  37. package/esm/utils/settingsValidation/splitFilters.js +11 -10
  38. package/package.json +1 -1
  39. package/src/logger/constants.ts +2 -2
  40. package/src/logger/messages/warn.ts +2 -2
  41. package/src/sdkClient/client.ts +11 -8
  42. package/src/sdkClient/clientInputValidation.ts +7 -7
  43. package/src/sdkManager/index.ts +6 -6
  44. package/src/storages/KeyBuilder.ts +14 -1
  45. package/src/storages/KeyBuilderCS.ts +1 -5
  46. package/src/storages/KeyBuilderSS.ts +4 -4
  47. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +16 -14
  48. package/src/storages/inLocalStorage/index.ts +1 -1
  49. package/src/storages/inRedis/SplitsCacheInRedis.ts +3 -3
  50. package/src/storages/inRedis/index.ts +1 -1
  51. package/src/storages/pluggable/SplitsCachePluggable.ts +3 -3
  52. package/src/storages/pluggable/index.ts +15 -5
  53. package/src/storages/types.ts +3 -3
  54. package/src/trackers/eventTracker.ts +4 -4
  55. package/src/utils/lang/sets.ts +1 -1
  56. package/src/utils/murmur3/murmur3.ts +0 -1
  57. package/src/utils/settingsValidation/index.ts +2 -2
  58. package/src/utils/settingsValidation/mode.ts +8 -1
  59. package/src/utils/settingsValidation/splitFilters.ts +11 -10
  60. package/types/logger/constants.d.ts +2 -2
  61. package/types/storages/AbstractSplitsCache.d.ts +46 -0
  62. package/types/storages/KeyBuilder.d.ts +8 -1
  63. package/types/storages/KeyBuilderCS.d.ts +0 -1
  64. package/types/storages/KeyBuilderSS.d.ts +4 -4
  65. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +5 -5
  66. package/types/utils/lang/sets.d.ts +1 -1
  67. package/types/utils/settingsValidation/mode.d.ts +5 -1
  68. package/types/utils/settingsValidation/splitFilters.d.ts +1 -1
  69. package/cjs/trackers/impressionObserver/utils.js +0 -11
  70. package/cjs/utils/redis/RedisMock.js +0 -31
  71. package/esm/trackers/impressionObserver/utils.js +0 -7
  72. package/esm/utils/redis/RedisMock.js +0 -28
  73. package/src/trackers/impressionObserver/utils.ts +0 -9
  74. package/src/utils/redis/RedisMock.ts +0 -31
@@ -6,7 +6,7 @@ import { validateTrafficTypeExistence } from '../utils/inputValidation/trafficTy
6
6
  import { SDK_NOT_READY } from '../utils/labels';
7
7
  import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, TREATMENTS_BY_FLAGSETS, TREATMENTS_BY_FLAGSET, TREATMENTS_WITH_CONFIG_BY_FLAGSET, GET_TREATMENTS_WITH_CONFIG, GET_TREATMENTS_BY_FLAG_SETS, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, GET_TREATMENTS_BY_FLAG_SET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, GET_TREATMENT_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, TRACK_FN_LABEL } from '../utils/constants';
8
8
  import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
9
- import { isStorageSync } from '../trackers/impressionObserver/utils';
9
+ import { isConsumerMode } from '../utils/settingsValidation/mode';
10
10
  var treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
11
11
  function treatmentsNotReady(featureFlagNames) {
12
12
  var evaluations = {};
@@ -21,6 +21,7 @@ function treatmentsNotReady(featureFlagNames) {
21
21
  export function clientFactory(params) {
22
22
  var readinessManager = params.sdkReadinessManager.readinessManager, storage = params.storage, settings = params.settings, impressionsTracker = params.impressionsTracker, eventTracker = params.eventTracker, telemetryTracker = params.telemetryTracker;
23
23
  var log = settings.log, mode = settings.mode;
24
+ var isAsync = isConsumerMode(mode);
24
25
  function getTreatment(key, featureFlagName, attributes, withConfig, methodName) {
25
26
  if (withConfig === void 0) { withConfig = false; }
26
27
  if (methodName === void 0) { methodName = GET_TREATMENT; }
@@ -34,9 +35,9 @@ export function clientFactory(params) {
34
35
  };
35
36
  var evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
36
37
  evaluateFeature(log, key, featureFlagName, attributes, storage) :
37
- isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
38
- treatmentNotReady :
39
- Promise.resolve(treatmentNotReady); // Promisify if async
38
+ isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
39
+ Promise.resolve(treatmentNotReady) :
40
+ treatmentNotReady;
40
41
  return thenable(evaluation) ? evaluation.then(function (res) { return wrapUp(res); }) : wrapUp(evaluation);
41
42
  }
42
43
  function getTreatmentWithConfig(key, featureFlagName, attributes) {
@@ -58,9 +59,9 @@ export function clientFactory(params) {
58
59
  };
59
60
  var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
60
61
  evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
61
- isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
62
- treatmentsNotReady(featureFlagNames) :
63
- Promise.resolve(treatmentsNotReady(featureFlagNames)); // Promisify if async
62
+ isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
63
+ Promise.resolve(treatmentsNotReady(featureFlagNames)) :
64
+ treatmentsNotReady(featureFlagNames);
64
65
  return thenable(evaluations) ? evaluations.then(function (res) { return wrapUp(res); }) : wrapUp(evaluations);
65
66
  }
66
67
  function getTreatmentsWithConfig(key, featureFlagNames, attributes) {
@@ -84,7 +85,9 @@ export function clientFactory(params) {
84
85
  };
85
86
  var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
86
87
  evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
87
- isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async
88
+ isAsync ?
89
+ Promise.resolve({}) :
90
+ {};
88
91
  return thenable(evaluations) ? evaluations.then(function (res) { return wrapUp(res); }) : wrapUp(evaluations);
89
92
  }
90
93
  function getTreatmentsWithConfigByFlagSets(key, flagSetNames, attributes) {
@@ -2,15 +2,15 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { validateAttributes, validateEvent, validateEventValue, validateEventProperties, validateKey, validateSplit, validateSplits, validateTrafficType, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
3
3
  import { startsWith } from '../utils/lang';
4
4
  import { CONTROL, CONTROL_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, GET_TREATMENTS_BY_FLAG_SET, GET_TREATMENTS_BY_FLAG_SETS, GET_TREATMENTS_WITH_CONFIG, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, GET_TREATMENT_WITH_CONFIG, TRACK_FN_LABEL } from '../utils/constants';
5
- import { isStorageSync } from '../trackers/impressionObserver/utils';
6
- import { flagSetsAreValid } from '../utils/settingsValidation/splitFilters';
5
+ import { isConsumerMode } from '../utils/settingsValidation/mode';
6
+ import { validateFlagSets } from '../utils/settingsValidation/splitFilters';
7
7
  /**
8
8
  * Decorator that validates the input before actually executing the client methods.
9
9
  * We should "guard" the client here, while not polluting the "real" implementation of those methods.
10
10
  */
11
11
  export function clientInputValidationDecorator(settings, client, readinessManager) {
12
- var log = settings.log;
13
- var isSync = isStorageSync(settings);
12
+ var log = settings.log, mode = settings.mode;
13
+ var isAsync = isConsumerMode(mode);
14
14
  /**
15
15
  * Avoid repeating this validations code
16
16
  */
@@ -25,7 +25,7 @@ export function clientInputValidationDecorator(settings, client, readinessManage
25
25
  var attributes = validateAttributes(log, maybeAttributes, methodName);
26
26
  var isNotDestroyed = validateIfNotDestroyed(log, readinessManager, methodName);
27
27
  if (maybeFlagSetNameOrNames) {
28
- flagSetOrFlagSets = flagSetsAreValid(log, methodName, maybeFlagSetNameOrNames, settings.sync.__splitFiltersValidation.groupedFilters.bySet);
28
+ flagSetOrFlagSets = validateFlagSets(log, methodName, maybeFlagSetNameOrNames, settings.sync.__splitFiltersValidation.groupedFilters.bySet);
29
29
  }
30
30
  validateIfOperational(log, readinessManager, methodName, splitOrSplits);
31
31
  var valid = isNotDestroyed && key && (splitOrSplits || flagSetOrFlagSets.length > 0) && attributes !== false;
@@ -38,7 +38,7 @@ export function clientInputValidationDecorator(settings, client, readinessManage
38
38
  };
39
39
  }
40
40
  function wrapResult(value) {
41
- return isSync ? value : Promise.resolve(value);
41
+ return isAsync ? Promise.resolve(value) : value;
42
42
  }
43
43
  function getTreatment(maybeKey, maybeFeatureFlagName, maybeAttributes) {
44
44
  var params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, GET_TREATMENT);
@@ -129,7 +129,7 @@ export function clientInputValidationDecorator(settings, client, readinessManage
129
129
  return client.track(key, tt, event, eventValue, properties, size);
130
130
  }
131
131
  else {
132
- return isSync ? false : Promise.resolve(false);
132
+ return isAsync ? Promise.resolve(false) : false;
133
133
  }
134
134
  }
135
135
  return {
@@ -2,7 +2,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { find } from '../utils/lang';
4
4
  import { validateSplit, validateSplitExistence, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
5
- import { isStorageSync } from '../trackers/impressionObserver/utils';
5
+ import { isConsumerMode } from '../utils/settingsValidation/mode';
6
6
  import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';
7
7
  function collectTreatments(splitObject) {
8
8
  var conditions = splitObject.conditions;
@@ -39,8 +39,8 @@ function objectsToViews(splitObjects) {
39
39
  }
40
40
  export function sdkManagerFactory(settings, splits, _a) {
41
41
  var readinessManager = _a.readinessManager, sdkStatus = _a.sdkStatus;
42
- var log = settings.log;
43
- var isSync = isStorageSync(settings);
42
+ var log = settings.log, mode = settings.mode;
43
+ var isAsync = isConsumerMode(mode);
44
44
  return objectAssign(
45
45
  // Proto-linkage of the readiness Event Emitter
46
46
  Object.create(sdkStatus), {
@@ -50,7 +50,7 @@ export function sdkManagerFactory(settings, splits, _a) {
50
50
  split: function (featureFlagName) {
51
51
  var splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
52
52
  if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
53
- return isSync ? null : Promise.resolve(null);
53
+ return isAsync ? Promise.resolve(null) : null;
54
54
  }
55
55
  var split = splits.getSplit(splitName);
56
56
  if (thenable(split)) {
@@ -67,7 +67,7 @@ export function sdkManagerFactory(settings, splits, _a) {
67
67
  */
68
68
  splits: function () {
69
69
  if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
70
- return isSync ? [] : Promise.resolve([]);
70
+ return isAsync ? Promise.resolve([]) : [];
71
71
  }
72
72
  var currentSplits = splits.getAll();
73
73
  return thenable(currentSplits) ?
@@ -79,7 +79,7 @@ export function sdkManagerFactory(settings, splits, _a) {
79
79
  */
80
80
  names: function () {
81
81
  if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
82
- return isSync ? [] : Promise.resolve([]);
82
+ return isAsync ? Promise.resolve([]) : [];
83
83
  }
84
84
  var splitNames = splits.getSplitNames();
85
85
  return thenable(splitNames) ?
@@ -1,4 +1,5 @@
1
1
  import { startsWith } from '../utils/lang';
2
+ import { hash } from '../utils/murmur3/murmur3';
2
3
  var everythingAtTheEnd = /[^.]+$/;
3
4
  var DEFAULT_PREFIX = 'SPLITIO';
4
5
  export function validatePrefix(prefix) {
@@ -54,6 +55,16 @@ var KeyBuilder = /** @class */ (function () {
54
55
  throw new Error('Invalid latency key provided');
55
56
  }
56
57
  };
58
+ KeyBuilder.prototype.buildHashKey = function () {
59
+ return this.prefix + ".hash";
60
+ };
57
61
  return KeyBuilder;
58
62
  }());
59
63
  export { KeyBuilder };
64
+ /**
65
+ * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
66
+ * The hash is in hexadecimal format (8 characters max, 32 bits).
67
+ */
68
+ export function getStorageHash(settings) {
69
+ return hash(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString).toString(16);
70
+ }
@@ -6,7 +6,7 @@ var KeyBuilderCS = /** @class */ (function (_super) {
6
6
  function KeyBuilderCS(prefix, matchingKey) {
7
7
  var _this = _super.call(this, prefix) || this;
8
8
  _this.matchingKey = matchingKey;
9
- _this.regexSplitsCacheKey = new RegExp("^" + prefix + "\\.(splits?|trafficType)\\.");
9
+ _this.regexSplitsCacheKey = new RegExp("^" + prefix + "\\.");
10
10
  return _this;
11
11
  }
12
12
  /**
@@ -36,9 +36,6 @@ var KeyBuilderCS = /** @class */ (function (_super) {
36
36
  KeyBuilderCS.prototype.isSplitsCacheKey = function (key) {
37
37
  return this.regexSplitsCacheKey.test(key);
38
38
  };
39
- KeyBuilderCS.prototype.buildSplitsFilterQueryKey = function () {
40
- return this.prefix + ".splits.filterQuery";
41
- };
42
39
  return KeyBuilderCS;
43
40
  }(KeyBuilder));
44
41
  export { KeyBuilderCS };
@@ -3,6 +3,7 @@ import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSyn
3
3
  import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
4
4
  import { LOG_PREFIX } from './constants';
5
5
  import { _Set, setToArray } from '../../utils/lang/sets';
6
+ import { getStorageHash } from '../KeyBuilder';
6
7
  /**
7
8
  * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
8
9
  */
@@ -13,13 +14,12 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
13
14
  * @param {number | undefined} expirationTimestamp
14
15
  * @param {ISplitFiltersValidation} splitFiltersValidation
15
16
  */
16
- function SplitsCacheInLocal(log, keys, expirationTimestamp, splitFiltersValidation) {
17
- if (splitFiltersValidation === void 0) { splitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }; }
17
+ function SplitsCacheInLocal(settings, keys, expirationTimestamp) {
18
18
  var _this = _super.call(this) || this;
19
- _this.log = log;
20
19
  _this.keys = keys;
21
- _this.splitFiltersValidation = splitFiltersValidation;
22
- _this.flagSetsFilter = _this.splitFiltersValidation.groupedFilters.bySet;
20
+ _this.log = settings.log;
21
+ _this.storageHash = getStorageHash(settings);
22
+ _this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
23
23
  _this._checkExpiration(expirationTimestamp);
24
24
  _this._checkFilterQuery();
25
25
  return _this;
@@ -123,14 +123,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
123
123
  SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
124
124
  // when using a new split query, we must update it at the store
125
125
  if (this.updateNewFilter) {
126
- this.log.info(LOG_PREFIX + 'Split filter query was modified. Updating cache.');
127
- var queryKey = this.keys.buildSplitsFilterQueryKey();
128
- var queryString = this.splitFiltersValidation.queryString;
126
+ this.log.info(LOG_PREFIX + 'SDK key or feature flag filter criteria was modified. Updating cache.');
127
+ var storageHashKey = this.keys.buildHashKey();
129
128
  try {
130
- if (queryString)
131
- localStorage.setItem(queryKey, queryString);
132
- else
133
- localStorage.removeItem(queryKey);
129
+ localStorage.setItem(storageHashKey, this.storageHash);
134
130
  }
135
131
  catch (e) {
136
132
  this.log.error(LOG_PREFIX + e);
@@ -208,11 +204,11 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
208
204
  this.clear();
209
205
  }
210
206
  };
207
+ // @TODO eventually remove `_checkFilterQuery`. Cache should be cleared at the storage level, reusing same logic than PluggableStorage
211
208
  SplitsCacheInLocal.prototype._checkFilterQuery = function () {
212
- var queryString = this.splitFiltersValidation.queryString;
213
- var queryKey = this.keys.buildSplitsFilterQueryKey();
214
- var currentQueryString = localStorage.getItem(queryKey);
215
- if (currentQueryString !== queryString) {
209
+ var storageHashKey = this.keys.buildHashKey();
210
+ var storageHash = localStorage.getItem(storageHashKey);
211
+ if (storageHash !== this.storageHash) {
216
212
  try {
217
213
  // mark cache to update the new query filter on first successful splits fetch
218
214
  this.updateNewFilter = true;
@@ -31,7 +31,7 @@ export function InLocalStorage(options) {
31
31
  var matchingKey = getMatching(settings.core.key);
32
32
  var keys = new KeyBuilderCS(prefix, matchingKey);
33
33
  var expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
34
- var splits = new SplitsCacheInLocal(log, keys, expirationTimestamp, __splitFiltersValidation);
34
+ var splits = new SplitsCacheInLocal(settings, keys, expirationTimestamp);
35
35
  var segments = new MySegmentsCacheInLocal(log, keys);
36
36
  return {
37
37
  splits: splits,
@@ -2,7 +2,7 @@ import { __extends, __spreadArray } from "tslib";
2
2
  import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
3
3
  import { LOG_PREFIX } from './constants';
4
4
  import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
5
- import { _Set, returnListDifference } from '../../utils/lang/sets';
5
+ import { _Set, returnDifference } from '../../utils/lang/sets';
6
6
  /**
7
7
  * Discard errors for an answer of multiple operations.
8
8
  */
@@ -49,8 +49,8 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
49
49
  };
50
50
  SplitsCacheInRedis.prototype._updateFlagSets = function (featureFlagName, flagSetsOfRemovedFlag, flagSetsOfAddedFlag) {
51
51
  var _this = this;
52
- var removeFromFlagSets = returnListDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
53
- var addToFlagSets = returnListDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
52
+ var removeFromFlagSets = returnDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
53
+ var addToFlagSets = returnDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
54
54
  if (this.flagSetsFilter.length > 0) {
55
55
  addToFlagSets = addToFlagSets.filter(function (flagSet) {
56
56
  return _this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === flagSet; });
@@ -36,7 +36,7 @@ export function InRedisStorage(options) {
36
36
  telemetry.recordConfig();
37
37
  });
38
38
  return {
39
- splits: new SplitsCacheInRedis(log, keys, redisClient),
39
+ splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
40
40
  segments: new SegmentsCacheInRedis(log, keys, redisClient),
41
41
  impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
42
42
  impressionCounts: impressionCountsCache,
@@ -2,7 +2,7 @@ import { __extends, __spreadArray } from "tslib";
2
2
  import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
3
3
  import { LOG_PREFIX } from './constants';
4
4
  import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
5
- import { _Set, returnListDifference } from '../../utils/lang/sets';
5
+ import { _Set, returnDifference } from '../../utils/lang/sets';
6
6
  /**
7
7
  * ISplitsCacheAsync implementation for pluggable storages.
8
8
  */
@@ -36,8 +36,8 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
36
36
  };
37
37
  SplitsCachePluggable.prototype._updateFlagSets = function (featureFlagName, flagSetsOfRemovedFlag, flagSetsOfAddedFlag) {
38
38
  var _this = this;
39
- var removeFromFlagSets = returnListDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
40
- var addToFlagSets = returnListDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
39
+ var removeFromFlagSets = returnDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
40
+ var addToFlagSets = returnDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
41
41
  if (this.flagSetsFilter.length > 0) {
42
42
  addToFlagSets = addToFlagSets.filter(function (flagSet) {
43
43
  return _this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === flagSet; });
@@ -6,7 +6,7 @@ import { ImpressionsCachePluggable } from './ImpressionsCachePluggable';
6
6
  import { EventsCachePluggable } from './EventsCachePluggable';
7
7
  import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
8
8
  import { isObject } from '../../utils/lang';
9
- import { validatePrefix } from '../KeyBuilder';
9
+ import { getStorageHash, validatePrefix } from '../KeyBuilder';
10
10
  import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
11
11
  import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
12
12
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
@@ -73,15 +73,26 @@ export function PluggableStorage(options) {
73
73
  undefined;
74
74
  // Connects to wrapper and emits SDK_READY event on main client
75
75
  var connectPromise = wrapper.connect().then(function () {
76
- onReadyCb();
77
- // Start periodic flush of async storages if not running synchronizer (producer mode)
78
- if (!isSyncronizer) {
76
+ if (isSyncronizer) {
77
+ // In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
78
+ return wrapper.get(keys.buildHashKey()).then(function (hash) {
79
+ var currentHash = getStorageHash(settings);
80
+ if (hash !== currentHash) {
81
+ return wrapper.getKeysByPrefix(keys.prefix + ".").then(function (storageKeys) {
82
+ return Promise.all(storageKeys.map(function (storageKey) { return wrapper.del(storageKey); }));
83
+ }).then(function () { return wrapper.set(keys.buildHashKey(), currentHash); });
84
+ }
85
+ }).then(onReadyCb);
86
+ }
87
+ else {
88
+ // Start periodic flush of async storages if not running synchronizer (producer mode)
79
89
  if (impressionCountsCache && impressionCountsCache.start)
80
90
  impressionCountsCache.start();
81
91
  if (uniqueKeysCache && uniqueKeysCache.start)
82
92
  uniqueKeysCache.start();
83
93
  if (telemetry && telemetry.recordConfig)
84
94
  telemetry.recordConfig();
95
+ onReadyCb();
85
96
  }
86
97
  }).catch(function (e) {
87
98
  e = e || new Error('Error connecting wrapper');
@@ -2,7 +2,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constants';
4
4
  import { CONSENT_DECLINED, DROPPED, QUEUED } from '../utils/constants';
5
- import { isStorageSync } from './impressionObserver/utils';
5
+ import { isConsumerMode } from '../utils/settingsValidation/mode';
6
6
  /**
7
7
  * Event tracker stores events in cache and pass them to the integrations manager if provided.
8
8
  *
@@ -10,8 +10,8 @@ import { isStorageSync } from './impressionObserver/utils';
10
10
  * @param integrationsManager optional event handler used for integrations
11
11
  */
12
12
  export function eventTrackerFactory(settings, eventsCache, integrationsManager, telemetryCache) {
13
- var log = settings.log;
14
- var isSync = isStorageSync(settings);
13
+ var log = settings.log, mode = settings.mode;
14
+ var isAsync = isConsumerMode(mode);
15
15
  function queueEventsCallback(eventData, tracked) {
16
16
  var eventTypeId = eventData.eventTypeId, trafficTypeName = eventData.trafficTypeName, key = eventData.key, value = eventData.value, timestamp = eventData.timestamp, properties = eventData.properties;
17
17
  // Logging every prop would be too much.
@@ -38,7 +38,7 @@ export function eventTrackerFactory(settings, eventsCache, integrationsManager,
38
38
  return {
39
39
  track: function (eventData, size) {
40
40
  if (settings.userConsent === CONSENT_DECLINED) {
41
- return isSync ? false : Promise.resolve(false);
41
+ return isAsync ? Promise.resolve(false) : false;
42
42
  }
43
43
  var tracked = eventsCache.track(eventData, size);
44
44
  if (thenable(tracked)) {
@@ -102,7 +102,7 @@ export function returnSetsUnion(set, set2) {
102
102
  });
103
103
  return result;
104
104
  }
105
- export function returnListDifference(list, list2) {
105
+ export function returnDifference(list, list2) {
106
106
  if (list === void 0) { list = []; }
107
107
  if (list2 === void 0) { list2 = []; }
108
108
  var result = new _Set(list);
@@ -1,5 +1,5 @@
1
1
  import { merge, get } from '../lang';
2
- import { mode } from './mode';
2
+ import { validateMode } from './mode';
3
3
  import { validateSplitFilters } from './splitFilters';
4
4
  import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE, DEBUG } from '../constants';
5
5
  import { validImpressionsMode } from './impressionsMode';
@@ -123,7 +123,7 @@ export function settingsValidation(config, validationParams) {
123
123
  startup.eventsFirstPushWindow = fromSecondsToMillis(startup.eventsFirstPushWindow);
124
124
  // ensure a valid SDK mode
125
125
  // @ts-ignore, modify readonly prop
126
- withDefaults.mode = mode(withDefaults.core.authorizationKey, withDefaults.mode);
126
+ withDefaults.mode = validateMode(withDefaults.core.authorizationKey, withDefaults.mode);
127
127
  // ensure a valid Storage based on mode defined.
128
128
  // @ts-ignore, modify readonly prop
129
129
  if (storage)
@@ -1,5 +1,5 @@
1
1
  import { LOCALHOST_MODE, STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';
2
- export function mode(key, mode) {
2
+ export function validateMode(key, mode) {
3
3
  // Leaving the comparison as is, in case we change the mode name but not the setting.
4
4
  if (key === 'localhost')
5
5
  return LOCALHOST_MODE;
@@ -7,3 +7,9 @@ export function mode(key, mode) {
7
7
  throw Error('Invalid mode provided');
8
8
  return mode;
9
9
  }
10
+ /**
11
+ * Storage is async if mode is consumer or partial consumer
12
+ */
13
+ export function isConsumerMode(mode) {
14
+ return CONSUMER_MODE === mode || CONSUMER_PARTIAL_MODE === mode;
15
+ }
@@ -1,8 +1,8 @@
1
- import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';
2
1
  import { validateSplits } from '../inputValidation/splits';
3
- import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE, WARN_SPLITS_FILTER_LOWERCASE_SET, WARN_SPLITS_FILTER_INVALID_SET, WARN_FLAGSET_NOT_CONFIGURED } from '../../logger/constants';
2
+ import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE, WARN_LOWERCASE_FLAGSET, WARN_INVALID_FLAGSET, WARN_FLAGSET_NOT_CONFIGURED } from '../../logger/constants';
4
3
  import { objectAssign } from '../lang/objectAssign';
5
4
  import { find, uniq } from '../lang';
5
+ import { isConsumerMode } from './mode';
6
6
  // Split filters metadata.
7
7
  // Ordered according to their precedency when forming the filter query string: `&names=<values>&prefixes=<values>`
8
8
  var FILTERS_METADATA = [
@@ -45,7 +45,7 @@ function validateSplitFilter(log, type, values, maxLength) {
45
45
  var result = validateSplits(log, values, LOG_PREFIX_SETTINGS, type + " filter", type + " filter value");
46
46
  if (result) {
47
47
  if (type === 'bySet') {
48
- result = sanitizeFlagSets(log, result);
48
+ result = sanitizeFlagSets(log, result, LOG_PREFIX_SETTINGS);
49
49
  }
50
50
  // check max length
51
51
  if (result.length > maxLength)
@@ -78,7 +78,7 @@ function queryStringBuilder(groupedFilters) {
78
78
  return queryParams.length > 0 ? '&' + queryParams.join('&') : null;
79
79
  }
80
80
  /**
81
- * Sanitizes set names list taking in account:
81
+ * Sanitizes set names list taking into account:
82
82
  * - It should be lowercase
83
83
  * - Must adhere the following regular expression /^[a-z0-9][_a-z0-9]{0,49}$/ that means
84
84
  * - must start with a letter or number
@@ -88,20 +88,21 @@ function queryStringBuilder(groupedFilters) {
88
88
  *
89
89
  * @param {ILogger} log
90
90
  * @param {string[]} flagSets
91
+ * @param {string} method
91
92
  * @returns sanitized list of set names
92
93
  */
93
- function sanitizeFlagSets(log, flagSets) {
94
+ function sanitizeFlagSets(log, flagSets, method) {
94
95
  var sanitizedSets = flagSets
95
96
  .map(function (flagSet) {
96
97
  if (CAPITAL_LETTERS_REGEX.test(flagSet)) {
97
- log.warn(WARN_SPLITS_FILTER_LOWERCASE_SET, [flagSet]);
98
+ log.warn(WARN_LOWERCASE_FLAGSET, [method, flagSet]);
98
99
  flagSet = flagSet.toLowerCase();
99
100
  }
100
101
  return flagSet;
101
102
  })
102
103
  .filter(function (flagSet) {
103
104
  if (!VALID_FLAGSET_REGEX.test(flagSet)) {
104
- log.warn(WARN_SPLITS_FILTER_INVALID_SET, [flagSet, VALID_FLAGSET_REGEX, flagSet]);
105
+ log.warn(WARN_INVALID_FLAGSET, [method, flagSet, VALID_FLAGSET_REGEX, flagSet]);
105
106
  return false;
106
107
  }
107
108
  if (typeof flagSet !== 'string')
@@ -137,7 +138,7 @@ export function validateSplitFilters(log, maybeSplitFilters, mode) {
137
138
  if (!maybeSplitFilters)
138
139
  return res;
139
140
  // Warn depending on the mode
140
- if (mode === CONSUMER_MODE || mode === CONSUMER_PARTIAL_MODE) {
141
+ if (isConsumerMode(mode)) {
141
142
  log.warn(WARN_SPLITS_FILTER_IGNORED);
142
143
  return res;
143
144
  }
@@ -175,9 +176,9 @@ export function validateSplitFilters(log, maybeSplitFilters, mode) {
175
176
  log.debug(SETTINGS_SPLITS_FILTER, [res.queryString]);
176
177
  return res;
177
178
  }
178
- export function flagSetsAreValid(log, method, flagSets, flagSetsInConfig) {
179
+ export function validateFlagSets(log, method, flagSets, flagSetsInConfig) {
179
180
  var sets = validateSplits(log, flagSets, method, 'flag sets', 'flag set');
180
- var toReturn = sets ? sanitizeFlagSets(log, sets) : [];
181
+ var toReturn = sets ? sanitizeFlagSets(log, sets, method) : [];
181
182
  if (flagSetsInConfig.length > 0) {
182
183
  toReturn = toReturn.filter(function (flagSet) {
183
184
  if (flagSetsInConfig.indexOf(flagSet) > -1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.12.1-rc.4",
3
+ "version": "1.12.1-rc.5",
4
4
  "description": "Split Javascript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -97,8 +97,8 @@ export const WARN_SPLITS_FILTER_EMPTY = 221;
97
97
  export const WARN_SDK_KEY = 222;
98
98
  export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
99
99
  export const STREAMING_PARSING_SPLIT_UPDATE = 224;
100
- export const WARN_SPLITS_FILTER_INVALID_SET = 225;
101
- export const WARN_SPLITS_FILTER_LOWERCASE_SET = 226;
100
+ export const WARN_INVALID_FLAGSET = 225;
101
+ export const WARN_LOWERCASE_FLAGSET = 226;
102
102
  export const WARN_FLAGSET_NOT_CONFIGURED = 227;
103
103
  export const WARN_FLAGSET_WITHOUT_FLAGS = 228;
104
104
 
@@ -34,7 +34,7 @@ export const codesWarn: [number, string][] = codesError.concat([
34
34
 
35
35
  [c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
36
36
  [c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing SPLIT_UPDATE notification: %s'],
37
- [c.WARN_SPLITS_FILTER_INVALID_SET, c.LOG_PREFIX_SETTINGS + ': you passed %s, flag set must adhere to the regular expressions %s. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. %s was discarded.'],
38
- [c.WARN_SPLITS_FILTER_LOWERCASE_SET, c.LOG_PREFIX_SETTINGS + ': flag set %s should be all lowercase - converting string to lowercase.'],
37
+ [c.WARN_INVALID_FLAGSET, '%s: you passed %s, flag set must adhere to the regular expressions %s. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. %s was discarded.'],
38
+ [c.WARN_LOWERCASE_FLAGSET, '%s: flag set %s should be all lowercase - converting string to lowercase.'],
39
39
  [c.WARN_FLAGSET_WITHOUT_FLAGS, '%s: you passed %s flag set that does not contain cached feature flag names. Please double check what flag sets are in use in the Split user interface.'],
40
40
  ]);
@@ -9,7 +9,7 @@ import { IEvaluationResult } from '../evaluator/types';
9
9
  import { SplitIO, ImpressionDTO } from '../types';
10
10
  import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
11
11
  import { ISdkFactoryContext } from '../sdkFactory/types';
12
- import { isStorageSync } from '../trackers/impressionObserver/utils';
12
+ import { isConsumerMode } from '../utils/settingsValidation/mode';
13
13
  import { Method } from '../sync/submitters/types';
14
14
 
15
15
  const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
@@ -28,6 +28,7 @@ function treatmentsNotReady(featureFlagNames: string[]) {
28
28
  export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
29
29
  const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker, telemetryTracker } = params;
30
30
  const { log, mode } = settings;
31
+ const isAsync = isConsumerMode(mode);
31
32
 
32
33
  function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined, withConfig = false, methodName = GET_TREATMENT) {
33
34
  const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
@@ -43,9 +44,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
43
44
 
44
45
  const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
45
46
  evaluateFeature(log, key, featureFlagName, attributes, storage) :
46
- isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
47
- treatmentNotReady :
48
- Promise.resolve(treatmentNotReady); // Promisify if async
47
+ isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
48
+ Promise.resolve(treatmentNotReady) :
49
+ treatmentNotReady;
49
50
 
50
51
  return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
51
52
  }
@@ -71,9 +72,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
71
72
 
72
73
  const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
73
74
  evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
74
- isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
75
- treatmentsNotReady(featureFlagNames) :
76
- Promise.resolve(treatmentsNotReady(featureFlagNames)); // Promisify if async
75
+ isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
76
+ Promise.resolve(treatmentsNotReady(featureFlagNames)) :
77
+ treatmentsNotReady(featureFlagNames);
77
78
 
78
79
  return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
79
80
  }
@@ -100,7 +101,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
100
101
 
101
102
  const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
102
103
  evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
103
- isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async
104
+ isAsync ?
105
+ Promise.resolve({}) :
106
+ {};
104
107
 
105
108
  return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
106
109
  }