@splitsoftware/splitio-commons 2.7.9-rc.0 → 2.7.9-rc.2

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 +9 -3
  2. package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +8 -4
  3. package/cjs/logger/constants.js +2 -2
  4. package/cjs/logger/index.js +0 -3
  5. package/cjs/logger/messages/info.js +1 -1
  6. package/cjs/logger/messages/warn.js +3 -3
  7. package/cjs/readiness/readinessManager.js +3 -5
  8. package/cjs/readiness/sdkReadinessManager.js +46 -10
  9. package/cjs/sdkClient/client.js +4 -4
  10. package/cjs/sdkClient/clientInputValidation.js +1 -1
  11. package/cjs/sdkManager/index.js +3 -3
  12. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +2 -1
  13. package/cjs/storages/pluggable/index.js +2 -1
  14. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -1
  15. package/cjs/sync/streaming/SSEClient/index.js +2 -1
  16. package/cjs/sync/streaming/SSEHandler/index.js +1 -1
  17. package/cjs/sync/streaming/pushManager.js +1 -1
  18. package/cjs/sync/submitters/telemetrySubmitter.js +7 -6
  19. package/cjs/trackers/telemetryTracker.js +6 -6
  20. package/cjs/utils/inputValidation/index.js +2 -1
  21. package/cjs/utils/inputValidation/isOperational.js +9 -4
  22. package/cjs/utils/inputValidation/splitExistence.js +3 -3
  23. package/cjs/utils/key/index.js +5 -1
  24. package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -1
  25. package/esm/logger/constants.js +1 -1
  26. package/esm/logger/index.js +0 -3
  27. package/esm/logger/messages/info.js +1 -1
  28. package/esm/logger/messages/warn.js +3 -3
  29. package/esm/readiness/readinessManager.js +3 -5
  30. package/esm/readiness/sdkReadinessManager.js +46 -10
  31. package/esm/sdkClient/client.js +4 -4
  32. package/esm/sdkClient/clientInputValidation.js +2 -2
  33. package/esm/sdkManager/index.js +4 -4
  34. package/esm/storages/inMemory/TelemetryCacheInMemory.js +2 -1
  35. package/esm/storages/pluggable/index.js +2 -1
  36. package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -1
  37. package/esm/sync/streaming/SSEClient/index.js +2 -1
  38. package/esm/sync/streaming/SSEHandler/index.js +1 -1
  39. package/esm/sync/streaming/pushManager.js +2 -2
  40. package/esm/sync/submitters/telemetrySubmitter.js +7 -6
  41. package/esm/trackers/telemetryTracker.js +6 -6
  42. package/esm/utils/inputValidation/index.js +1 -1
  43. package/esm/utils/inputValidation/isOperational.js +8 -4
  44. package/esm/utils/inputValidation/splitExistence.js +3 -3
  45. package/esm/utils/key/index.js +3 -0
  46. package/package.json +1 -1
  47. package/src/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.ts +4 -1
  48. package/src/logger/constants.ts +1 -1
  49. package/src/logger/index.ts +0 -2
  50. package/src/logger/messages/info.ts +1 -1
  51. package/src/logger/messages/warn.ts +3 -3
  52. package/src/readiness/readinessManager.ts +3 -4
  53. package/src/readiness/sdkReadinessManager.ts +43 -10
  54. package/src/readiness/types.ts +1 -2
  55. package/src/sdkClient/client.ts +4 -4
  56. package/src/sdkClient/clientInputValidation.ts +3 -3
  57. package/src/sdkManager/index.ts +4 -4
  58. package/src/storages/inMemory/TelemetryCacheInMemory.ts +2 -1
  59. package/src/storages/pluggable/index.ts +2 -1
  60. package/src/sync/polling/fetchers/splitChangesFetcher.ts +2 -1
  61. package/src/sync/streaming/SSEClient/index.ts +2 -1
  62. package/src/sync/streaming/SSEHandler/index.ts +1 -1
  63. package/src/sync/streaming/pushManager.ts +2 -2
  64. package/src/sync/submitters/telemetrySubmitter.ts +7 -6
  65. package/src/trackers/telemetryTracker.ts +6 -6
  66. package/src/types.ts +0 -15
  67. package/src/utils/inputValidation/index.ts +1 -1
  68. package/src/utils/inputValidation/isOperational.ts +9 -4
  69. package/src/utils/inputValidation/splitExistence.ts +3 -3
  70. package/src/utils/key/index.ts +5 -0
  71. package/types/splitio.d.ts +77 -10
  72. package/cjs/evaluator/fallbackTreatmentsCalculator/constants.js +0 -8
  73. package/esm/evaluator/fallbackTreatmentsCalculator/constants.js +0 -5
  74. package/src/evaluator/fallbackTreatmentsCalculator/constants.ts +0 -4
@@ -19,7 +19,7 @@ export var codesInfo = codesWarn.concat([
19
19
  [c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
20
20
  [c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
21
21
  [c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
22
- [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'],
22
+ [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying fetch of feature flags (attempt #%s). Reason: %s'],
23
23
  [c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
24
24
  [c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
25
25
  [c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'],
@@ -5,7 +5,7 @@ export var codesWarn = codesError.concat([
5
5
  [c.ENGINE_VALUE_INVALID, c.LOG_PREFIX_ENGINE_VALUE + 'Value %s doesn\'t match with expected type.'],
6
6
  [c.ENGINE_VALUE_NO_ATTRIBUTES, c.LOG_PREFIX_ENGINE_VALUE + 'Defined attribute `%s`. No attributes received.'],
7
7
  // synchronizer
8
- [c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying download of segments #%s. Reason: %s'],
8
+ [c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying fetch of memberships (attempt #%s). Reason: %s'],
9
9
  [c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of feature flags. %s'],
10
10
  [c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
11
11
  [c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
@@ -13,8 +13,8 @@ export var codesWarn = codesError.concat([
13
13
  [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Dropping %s after retry. Reason: %s.'],
14
14
  [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s, keeping data to retry on next iteration. Reason: %s.'],
15
15
  // client status
16
- [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect%s. Make sure to wait for SDK readiness before using this method.'],
17
- [c.CLIENT_NO_LISTENER, 'No listeners for SDK Readiness detected. Incorrect control treatments could have been logged if you called getTreatment/s while the SDK was not yet ready.'],
16
+ [c.CLIENT_NOT_READY_FROM_CACHE, '%s: the SDK is not ready to evaluate. Results may be incorrect%s. Make sure to wait for SDK readiness before using this method.'],
17
+ [c.CLIENT_NO_LISTENER, 'No listeners for SDK_READY event detected. Incorrect control treatments could have been logged if you called getTreatment/s while the SDK was not yet synchronized with the backend.'],
18
18
  // input validation
19
19
  [c.WARN_SETTING_NULL, '%s: Property "%s" is of invalid type. Setting value to null.'],
20
20
  [c.WARN_TRIMMING_PROPERTIES, '%s: more than 300 properties were provided. Some of them will be trimmed when processed.'],
@@ -1,6 +1,5 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
3
- import { STORAGE_LOCALSTORAGE } from '../utils/constants';
4
3
  function splitsEventEmitterFactory(EventEmitter) {
5
4
  var splitsEventEmitter = objectAssign(new EventEmitter(), {
6
5
  splitsArrived: false,
@@ -72,7 +71,7 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
72
71
  if (!isReady && !isDestroyed) {
73
72
  try {
74
73
  syncLastUpdate();
75
- gate.emit(SDK_READY_FROM_CACHE);
74
+ gate.emit(SDK_READY_FROM_CACHE, isReady);
76
75
  }
77
76
  catch (e) {
78
77
  // throws user callback exceptions in next tick
@@ -81,7 +80,6 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
81
80
  }
82
81
  }
83
82
  function checkIsReadyOrUpdate(diff) {
84
- var _a;
85
83
  if (isDestroyed)
86
84
  return;
87
85
  if (isReady) {
@@ -100,9 +98,9 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
100
98
  isReady = true;
101
99
  try {
102
100
  syncLastUpdate();
103
- if (!isReadyFromCache && ((_a = settings.storage) === null || _a === void 0 ? void 0 : _a.type) === STORAGE_LOCALSTORAGE) {
101
+ if (!isReadyFromCache) {
104
102
  isReadyFromCache = true;
105
- gate.emit(SDK_READY_FROM_CACHE);
103
+ gate.emit(SDK_READY_FROM_CACHE, isReady);
106
104
  }
107
105
  gate.emit(SDK_READY);
108
106
  }
@@ -5,6 +5,7 @@ import { SDK_READY, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE } from
5
5
  import { ERROR_CLIENT_LISTENER, CLIENT_READY_FROM_CACHE, CLIENT_READY, CLIENT_NO_LISTENER } from '../logger/constants';
6
6
  var NEW_LISTENER_EVENT = 'newListener';
7
7
  var REMOVE_LISTENER_EVENT = 'removeListener';
8
+ var TIMEOUT_ERROR = new Error(SDK_READY_TIMED_OUT);
8
9
  /**
9
10
  * SdkReadinessManager factory, which provides the public status API of SDK clients and manager: ready promise, readiness event emitter and constants (SDK_READY, etc).
10
11
  * It also updates logs related warnings and errors.
@@ -31,6 +32,9 @@ export function sdkReadinessManagerFactory(EventEmitter, settings, readinessMana
31
32
  readyCbCount++;
32
33
  }
33
34
  }
35
+ else if (event === SDK_READY_FROM_CACHE && readinessManager.isReadyFromCache()) {
36
+ log.error(ERROR_CLIENT_LISTENER, ['SDK_READY_FROM_CACHE']);
37
+ }
34
38
  });
35
39
  /** Ready promise */
36
40
  var readyPromise = generateReadyPromise();
@@ -55,6 +59,17 @@ export function sdkReadinessManagerFactory(EventEmitter, settings, readinessMana
55
59
  }), defaultOnRejected);
56
60
  return promise;
57
61
  }
62
+ function getStatus() {
63
+ return {
64
+ isReady: readinessManager.isReady(),
65
+ isReadyFromCache: readinessManager.isReadyFromCache(),
66
+ isTimedout: readinessManager.isTimedout(),
67
+ hasTimedout: readinessManager.hasTimedout(),
68
+ isDestroyed: readinessManager.isDestroyed(),
69
+ isOperational: readinessManager.isOperational(),
70
+ lastUpdate: readinessManager.lastUpdate(),
71
+ };
72
+ }
58
73
  return {
59
74
  readinessManager: readinessManager,
60
75
  shared: function () {
@@ -73,6 +88,7 @@ export function sdkReadinessManagerFactory(EventEmitter, settings, readinessMana
73
88
  SDK_UPDATE: SDK_UPDATE,
74
89
  SDK_READY_TIMED_OUT: SDK_READY_TIMED_OUT,
75
90
  },
91
+ // @TODO: remove in next major
76
92
  ready: function () {
77
93
  if (readinessManager.hasTimedout()) {
78
94
  if (!readinessManager.isReady()) {
@@ -84,17 +100,37 @@ export function sdkReadinessManagerFactory(EventEmitter, settings, readinessMana
84
100
  }
85
101
  return readyPromise;
86
102
  },
87
- __getStatus: function () {
88
- return {
89
- isReady: readinessManager.isReady(),
90
- isReadyFromCache: readinessManager.isReadyFromCache(),
91
- isTimedout: readinessManager.isTimedout(),
92
- hasTimedout: readinessManager.hasTimedout(),
93
- isDestroyed: readinessManager.isDestroyed(),
94
- isOperational: readinessManager.isOperational(),
95
- lastUpdate: readinessManager.lastUpdate(),
96
- };
103
+ whenReady: function () {
104
+ return new Promise(function (resolve, reject) {
105
+ if (readinessManager.isReady()) {
106
+ resolve();
107
+ }
108
+ else if (readinessManager.hasTimedout()) {
109
+ reject(TIMEOUT_ERROR);
110
+ }
111
+ else {
112
+ readinessManager.gate.once(SDK_READY, resolve);
113
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, function () { return reject(TIMEOUT_ERROR); });
114
+ }
115
+ });
116
+ },
117
+ whenReadyFromCache: function () {
118
+ return new Promise(function (resolve, reject) {
119
+ if (readinessManager.isReadyFromCache()) {
120
+ resolve(readinessManager.isReady());
121
+ }
122
+ else if (readinessManager.hasTimedout()) {
123
+ reject(TIMEOUT_ERROR);
124
+ }
125
+ else {
126
+ readinessManager.gate.once(SDK_READY_FROM_CACHE, function () { return resolve(readinessManager.isReady()); });
127
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, function () { return reject(TIMEOUT_ERROR); });
128
+ }
129
+ });
97
130
  },
131
+ getStatus: getStatus,
132
+ // @TODO: remove in next major
133
+ __getStatus: getStatus
98
134
  })
99
135
  };
100
136
  }
@@ -41,7 +41,7 @@ export function clientFactory(params) {
41
41
  stopTelemetryTracker(queue[0] && queue[0].imp.label);
42
42
  return treatment;
43
43
  };
44
- var evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
44
+ var evaluation = readinessManager.isReadyFromCache() ?
45
45
  evaluateFeature(log, key, featureFlagName, attributes, storage) :
46
46
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
47
47
  Promise.resolve(treatmentNotReady) :
@@ -66,7 +66,7 @@ export function clientFactory(params) {
66
66
  stopTelemetryTracker(queue[0] && queue[0].imp.label);
67
67
  return treatments;
68
68
  };
69
- var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
69
+ var evaluations = readinessManager.isReadyFromCache() ?
70
70
  evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
71
71
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
72
72
  Promise.resolve(treatmentsNotReady(featureFlagNames)) :
@@ -92,7 +92,7 @@ export function clientFactory(params) {
92
92
  stopTelemetryTracker(queue[0] && queue[0].imp.label);
93
93
  return treatments;
94
94
  };
95
- var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
95
+ var evaluations = readinessManager.isReadyFromCache() ?
96
96
  evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
97
97
  isAsync ?
98
98
  Promise.resolve({}) :
@@ -117,7 +117,7 @@ export function clientFactory(params) {
117
117
  if (treatment === CONTROL) {
118
118
  var fallbackTreatment = fallbackTreatmentsCalculator.resolve(featureFlagName, label);
119
119
  treatment = fallbackTreatment.treatment;
120
- label = fallbackTreatment.label ? fallbackTreatment.label : label;
120
+ label = fallbackTreatment.label;
121
121
  config = fallbackTreatment.config;
122
122
  }
123
123
  log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
@@ -1,4 +1,4 @@
1
- import { validateAttributes, validateEvent, validateEventValue, validateEventProperties, validateKey, validateSplit, validateSplits, validateTrafficType, validateIfNotDestroyed, validateIfOperational, validateEvaluationOptions } from '../utils/inputValidation';
1
+ import { validateAttributes, validateEvent, validateEventValue, validateEventProperties, validateKey, validateSplit, validateSplits, validateTrafficType, validateIfNotDestroyed, validateIfReadyFromCache, validateEvaluationOptions } from '../utils/inputValidation';
2
2
  import { startsWith } from '../utils/lang';
3
3
  import { 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';
4
4
  import { isConsumerMode } from '../utils/settingsValidation/mode';
@@ -23,7 +23,7 @@ export function clientInputValidationDecorator(settings, client, readinessManage
23
23
  var attributes = validateAttributes(log, maybeAttributes, methodName);
24
24
  var isNotDestroyed = validateIfNotDestroyed(log, readinessManager, methodName);
25
25
  var options = validateEvaluationOptions(log, maybeOptions, methodName);
26
- validateIfOperational(log, readinessManager, methodName, nameOrNames);
26
+ validateIfReadyFromCache(log, readinessManager, methodName, nameOrNames);
27
27
  var valid = isNotDestroyed && key && nameOrNames && attributes !== false;
28
28
  return {
29
29
  valid: valid,
@@ -1,7 +1,7 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { find } from '../utils/lang';
4
- import { validateSplit, validateSplitExistence, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
4
+ import { validateSplit, validateSplitExistence, validateIfOperational } from '../utils/inputValidation';
5
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) {
@@ -51,7 +51,7 @@ export function sdkManagerFactory(settings, splits, _a) {
51
51
  */
52
52
  split: function (featureFlagName) {
53
53
  var splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
54
- if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
54
+ if (!validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
55
55
  return isAsync ? Promise.resolve(null) : null;
56
56
  }
57
57
  var split = splits.getSplit(splitName);
@@ -68,7 +68,7 @@ export function sdkManagerFactory(settings, splits, _a) {
68
68
  * Get the feature flag objects present on the factory storage
69
69
  */
70
70
  splits: function () {
71
- if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
71
+ if (!validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
72
72
  return isAsync ? Promise.resolve([]) : [];
73
73
  }
74
74
  var currentSplits = splits.getAll();
@@ -80,7 +80,7 @@ export function sdkManagerFactory(settings, splits, _a) {
80
80
  * Get the feature flag names present on the factory storage
81
81
  */
82
82
  names: function () {
83
- if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
83
+ if (!validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
84
84
  return isAsync ? Promise.resolve([]) : [];
85
85
  }
86
86
  var splitNames = splits.getSplitNames();
@@ -1,4 +1,5 @@
1
1
  import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
2
+ import { checkIfServerSide } from '../../utils/key';
2
3
  import { findLatencyIndex } from '../findLatencyIndex';
3
4
  var MAX_STREAMING_EVENTS = 20;
4
5
  var MAX_TAGS = 10;
@@ -15,7 +16,7 @@ var ACCEPTANCE_RANGE = 0.001;
15
16
  */
16
17
  export function shouldRecordTelemetry(_a) {
17
18
  var settings = _a.settings;
18
- return settings.mode !== LOCALHOST_MODE && (settings.core.key === undefined || Math.random() <= ACCEPTANCE_RANGE);
19
+ return settings.mode !== LOCALHOST_MODE && (checkIfServerSide(settings) || Math.random() <= ACCEPTANCE_RANGE);
19
20
  }
20
21
  var TelemetryCacheInMemory = /** @class */ (function () {
21
22
  function TelemetryCacheInMemory(splits, segments, largeSegments) {
@@ -20,6 +20,7 @@ import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS
20
20
  import { metadataBuilder } from '../utils';
21
21
  import { LOG_PREFIX } from '../pluggable/constants';
22
22
  import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
23
+ import { checkIfServerSide } from '../../utils/key';
23
24
  var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
24
25
  var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
25
26
  /**
@@ -67,7 +68,7 @@ export function PluggableStorage(options) {
67
68
  new ImpressionCountsCacheInMemory() :
68
69
  new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper);
69
70
  var uniqueKeysCache = isPartialConsumer ?
70
- settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
71
+ checkIfServerSide(settings) ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
71
72
  new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper);
72
73
  // Connects to wrapper and emits SDK_READY event on main client
73
74
  var connectPromise = wrapper.connect().then(function () {
@@ -1,6 +1,7 @@
1
1
  import { FLAG_SPEC_VERSION } from '../../../utils/constants';
2
2
  import { base } from '../../../utils/settingsValidation';
3
3
  import { LOG_PREFIX_SYNC_SPLITS } from '../../../logger/constants';
4
+ import { checkIfServerSide } from '../../../utils/key';
4
5
  var PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
5
6
  var PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
6
7
  function sdkEndpointOverridden(settings) {
@@ -13,7 +14,7 @@ function sdkEndpointOverridden(settings) {
13
14
  // @TODO breaking: drop support for Split Proxy below v5.10.0 and simplify the implementation
14
15
  export function splitChangesFetcherFactory(fetchSplitChanges, settings, storage) {
15
16
  var log = settings.log;
16
- var PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
17
+ var PROXY_CHECK_INTERVAL_MILLIS = checkIfServerSide(settings) ? PROXY_CHECK_INTERVAL_MILLIS_SS : PROXY_CHECK_INTERVAL_MILLIS_CS;
17
18
  var lastProxyCheckTimestamp;
18
19
  return function splitChangesFetcher(since, noCache, till, rbSince,
19
20
  // Optional decorator for `fetchSplitChanges` promise, such as timeout or time tracker
@@ -1,4 +1,5 @@
1
1
  import { decorateHeaders } from '../../../services/decorateHeaders';
2
+ import { checkIfServerSide } from '../../../utils/key';
2
3
  import { isString } from '../../../utils/lang';
3
4
  import { objectAssign } from '../../../utils/lang/objectAssign';
4
5
  var ABLY_API_VERSION = '1.1';
@@ -57,7 +58,7 @@ var SSEClient = /** @class */ (function () {
57
58
  return encodeURIComponent(params + channel);
58
59
  }).join(',');
59
60
  var url = this.settings.urls.streaming + "/sse?channels=" + channelsQueryParam + "&accessToken=" + authToken.token + "&v=" + ABLY_API_VERSION + "&heartbeats=true"; // same results using `&heartbeats=false`
60
- var isServerSide = !this.settings.core.key;
61
+ var isServerSide = checkIfServerSide(this.settings);
61
62
  this.connection = new this.eventSource(
62
63
  // For client-side SDKs, metadata is passed as query param to avoid CORS issues and because native EventSource implementations in browsers do not support headers
63
64
  isServerSide ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
@@ -16,7 +16,7 @@ export function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
16
16
  // Ably error
17
17
  var code = error.parsedData.code;
18
18
  telemetryTracker.streamingEvent(ABLY_ERROR, code);
19
- // 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
19
+ // 401 errors due to invalid or expired token (e.g., if refresh token couldn't be executed)
20
20
  if (40140 <= code && code <= 40149)
21
21
  return true;
22
22
  // Others 4XX errors (e.g., bad request from the SDK)
@@ -7,7 +7,7 @@ import { SplitsUpdateWorker } from './UpdateWorkers/SplitsUpdateWorker';
7
7
  import { authenticateFactory, hashUserKey } from './AuthClient';
8
8
  import { forOwn } from '../../utils/lang';
9
9
  import { SSEClient } from './SSEClient';
10
- import { getMatching } from '../../utils/key';
10
+ import { checkIfServerSide, getMatching } from '../../utils/key';
11
11
  import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
12
12
  import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE } from '../../logger/constants';
13
13
  import { UpdateStrategy } from './SSEHandler/types';
@@ -23,7 +23,7 @@ export function pushManagerFactory(params, pollingManager) {
23
23
  var settings = params.settings, storage = params.storage, splitApi = params.splitApi, readiness = params.readiness, platform = params.platform, telemetryTracker = params.telemetryTracker;
24
24
  // `userKey` is the matching key of main client in client-side SDK.
25
25
  // It can be used to check if running on client-side or server-side SDK.
26
- var userKey = settings.core.key ? getMatching(settings.core.key) : undefined;
26
+ var userKey = checkIfServerSide(settings) ? undefined : getMatching(settings.core.key);
27
27
  var log = settings.log;
28
28
  var sseClient;
29
29
  try {
@@ -6,6 +6,7 @@ import { base } from '../../utils/settingsValidation';
6
6
  import { usedKeysMap } from '../../utils/inputValidation/apiKey';
7
7
  import { timer } from '../../utils/timeTracker/timer';
8
8
  import { objectAssign } from '../../utils/lang/objectAssign';
9
+ import { checkIfServerSide } from '../../utils/key';
9
10
  var OPERATION_MODE_MAP = (_a = {},
10
11
  _a[STANDALONE_MODE] = STANDALONE_ENUM,
11
12
  _a[CONSUMER_MODE] = CONSUMER_ENUM,
@@ -58,14 +59,14 @@ export function telemetryCacheConfigAdapter(telemetry, settings) {
58
59
  clear: function () { },
59
60
  pop: function () {
60
61
  var urls = settings.urls, scheduler = settings.scheduler;
61
- var isClientSide = settings.core.key !== undefined;
62
+ var isServerSide = checkIfServerSide(settings);
62
63
  var _a = getTelemetryFlagSetsStats(settings.sync.__splitFiltersValidation), flagSetsTotal = _a.flagSetsTotal, flagSetsIgnored = _a.flagSetsIgnored;
63
64
  return objectAssign(getTelemetryConfigStats(settings.mode, settings.storage.type), {
64
65
  sE: settings.streamingEnabled,
65
66
  rR: {
66
67
  sp: scheduler.featuresRefreshRate / 1000,
67
- se: isClientSide ? undefined : scheduler.segmentsRefreshRate / 1000,
68
- ms: isClientSide ? scheduler.segmentsRefreshRate / 1000 : undefined,
68
+ se: isServerSide ? scheduler.segmentsRefreshRate / 1000 : undefined,
69
+ ms: isServerSide ? undefined : scheduler.segmentsRefreshRate / 1000,
69
70
  im: scheduler.impressionsRefreshRate / 1000,
70
71
  ev: scheduler.eventsPushRate / 1000,
71
72
  te: scheduler.telemetryRefreshRate / 1000,
@@ -102,14 +103,14 @@ export function telemetrySubmitterFactory(params) {
102
103
  if (!telemetry || !now)
103
104
  return; // No submitter created if telemetry cache is not defined
104
105
  var settings = params.settings, _a = params.settings, log = _a.log, telemetryRefreshRate = _a.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
105
- var startTime = timer(now);
106
+ var stopTimer = timer(now);
106
107
  var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate, undefined, 0, true), telemetryRefreshRate);
107
108
  readiness.gate.once(SDK_READY_FROM_CACHE, function () {
108
- telemetry.recordTimeUntilReadyFromCache(startTime());
109
+ telemetry.recordTimeUntilReadyFromCache(stopTimer());
109
110
  });
110
111
  sdkReadinessManager.incInternalReadyCbCount();
111
112
  readiness.gate.once(SDK_READY, function () {
112
- telemetry.recordTimeUntilReady(startTime());
113
+ telemetry.recordTimeUntilReady(stopTimer());
113
114
  // Post config data when the SDK is ready and if the telemetry submitter was started
114
115
  if (submitter.isRunning()) {
115
116
  var postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, undefined, 0, true);
@@ -3,10 +3,10 @@ import { timer } from '../utils/timeTracker/timer';
3
3
  import { TOKEN_REFRESH, AUTH_REJECTION } from '../utils/constants';
4
4
  export function telemetryTrackerFactory(telemetryCache, now) {
5
5
  if (telemetryCache && now) {
6
- var startTime_1 = timer(now);
6
+ var sessionTimer_1 = timer(now);
7
7
  return {
8
8
  trackEval: function (method) {
9
- var evalTime = timer(now);
9
+ var evalTimer = timer(now);
10
10
  return function (label) {
11
11
  switch (label) {
12
12
  case EXCEPTION:
@@ -16,13 +16,13 @@ export function telemetryTrackerFactory(telemetryCache, now) {
16
16
  if (telemetryCache.recordNonReadyUsage)
17
17
  telemetryCache.recordNonReadyUsage();
18
18
  }
19
- telemetryCache.recordLatency(method, evalTime());
19
+ telemetryCache.recordLatency(method, evalTimer());
20
20
  };
21
21
  },
22
22
  trackHttp: function (operation) {
23
- var httpTime = timer(now);
23
+ var httpTimer = timer(now);
24
24
  return function (error) {
25
- telemetryCache.recordHttpLatency(operation, httpTime());
25
+ telemetryCache.recordHttpLatency(operation, httpTimer());
26
26
  if (error && error.statusCode)
27
27
  telemetryCache.recordHttpError(operation, error.statusCode);
28
28
  else
@@ -31,7 +31,7 @@ export function telemetryTrackerFactory(telemetryCache, now) {
31
31
  },
32
32
  sessionLength: function () {
33
33
  if (telemetryCache.recordSessionLength)
34
- telemetryCache.recordSessionLength(startTime_1());
34
+ telemetryCache.recordSessionLength(sessionTimer_1());
35
35
  },
36
36
  streamingEvent: function (e, d) {
37
37
  if (e === AUTH_REJECTION) {
@@ -7,7 +7,7 @@ export { validateKey } from './key';
7
7
  export { validateSplit } from './split';
8
8
  export { validateSplits } from './splits';
9
9
  export { validateTrafficType } from './trafficType';
10
- export { validateIfNotDestroyed, validateIfOperational } from './isOperational';
10
+ export { validateIfNotDestroyed, validateIfReadyFromCache, validateIfOperational } from './isOperational';
11
11
  export { validateSplitExistence } from './splitExistence';
12
12
  export { validateTrafficTypeExistence } from './trafficTypeExistence';
13
13
  export { validateEvaluationOptions } from './eventProperties';
@@ -1,13 +1,17 @@
1
- import { ERROR_CLIENT_DESTROYED, CLIENT_NOT_READY } from '../../logger/constants';
1
+ import { ERROR_CLIENT_DESTROYED, CLIENT_NOT_READY_FROM_CACHE } from '../../logger/constants';
2
2
  export function validateIfNotDestroyed(log, readinessManager, method) {
3
3
  if (!readinessManager.isDestroyed())
4
4
  return true;
5
5
  log.error(ERROR_CLIENT_DESTROYED, [method]);
6
6
  return false;
7
7
  }
8
- export function validateIfOperational(log, readinessManager, method, featureFlagNameOrNames) {
9
- if (readinessManager.isReady() || readinessManager.isReadyFromCache())
8
+ export function validateIfReadyFromCache(log, readinessManager, method, featureFlagNameOrNames) {
9
+ if (readinessManager.isReadyFromCache())
10
10
  return true;
11
- log.warn(CLIENT_NOT_READY, [method, featureFlagNameOrNames ? " for feature flag " + featureFlagNameOrNames.toString() : '']);
11
+ log.warn(CLIENT_NOT_READY_FROM_CACHE, [method, featureFlagNameOrNames ? " for feature flag " + featureFlagNameOrNames.toString() : '']);
12
12
  return false;
13
13
  }
14
+ // Operational means that the SDK is ready to evaluate (not destroyed and ready from cache)
15
+ export function validateIfOperational(log, readinessManager, method, featureFlagNameOrNames) {
16
+ return validateIfNotDestroyed(log, readinessManager, method) && validateIfReadyFromCache(log, readinessManager, method, featureFlagNameOrNames);
17
+ }
@@ -2,11 +2,11 @@ import { FALLBACK_SPLIT_NOT_FOUND, SPLIT_NOT_FOUND } from '../labels';
2
2
  import { WARN_NOT_EXISTENT_SPLIT } from '../../logger/constants';
3
3
  /**
4
4
  * This is defined here and in this format mostly because of the logger and the fact that it's considered a validation at product level.
5
- * But it's not going to run on the input validation layer. In any case, the most compeling reason to use it as we do is to avoid going to Redis and get a split twice.
5
+ * But it's not going to run on the input validation layer. In any case, the most compelling reason to use it as we do is to avoid going to Redis and get a split twice.
6
6
  */
7
7
  export function validateSplitExistence(log, readinessManager, splitName, labelOrSplitObj, method) {
8
- if (readinessManager.isReady()) { // Only if it's ready we validate this, otherwise it may just be that the SDK is not ready yet.
9
- if (labelOrSplitObj === SPLIT_NOT_FOUND || labelOrSplitObj == null || labelOrSplitObj === FALLBACK_SPLIT_NOT_FOUND) {
8
+ if (readinessManager.isReady()) { // Only if it's ready (synced with BE) we validate this, otherwise it may just be that the SDK is still syncing
9
+ if (labelOrSplitObj === SPLIT_NOT_FOUND || labelOrSplitObj === FALLBACK_SPLIT_NOT_FOUND || labelOrSplitObj == null) {
10
10
  log.warn(WARN_NOT_EXISTENT_SPLIT, [method, splitName]);
11
11
  return false;
12
12
  }
@@ -28,3 +28,6 @@ export function keyParser(key) {
28
28
  };
29
29
  }
30
30
  }
31
+ export function checkIfServerSide(settings) {
32
+ return !settings.core.key;
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.7.9-rc.0",
3
+ "version": "2.7.9-rc.2",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -1,8 +1,11 @@
1
1
  import { Treatment, TreatmentWithConfig } from '../../../../types/splitio';
2
2
  import { ILogger } from '../../../logger/types';
3
3
  import { isObject, isString } from '../../../utils/lang';
4
- import { FallbackDiscardReason } from '../constants';
5
4
 
5
+ enum FallbackDiscardReason {
6
+ FlagName = 'Invalid flag name (max 100 chars, no spaces)',
7
+ Treatment = 'Invalid treatment (max 100 chars and must match pattern)',
8
+ }
6
9
 
7
10
  export class FallbacksSanitizer {
8
11
 
@@ -60,7 +60,7 @@ export const SUBMITTERS_PUSH_PAGE_HIDDEN = 125;
60
60
  export const ENGINE_VALUE_INVALID = 200;
61
61
  export const ENGINE_VALUE_NO_ATTRIBUTES = 201;
62
62
  export const CLIENT_NO_LISTENER = 202;
63
- export const CLIENT_NOT_READY = 203;
63
+ export const CLIENT_NOT_READY_FROM_CACHE = 203;
64
64
  export const SYNC_MYSEGMENTS_FETCH_RETRY = 204;
65
65
  export const SYNC_SPLITS_FETCH_FAILS = 205;
66
66
  export const STREAMING_PARSING_ERROR_FAILS = 206;
@@ -72,8 +72,6 @@ export class Logger implements ILogger {
72
72
  if (logger) {
73
73
  if (isLogger(logger)) {
74
74
  this.logger = logger;
75
- // If custom logger is set, all logs are either enabled or disabled
76
- if (this.logLevel !== LogLevelIndexes.NONE) this.setLogLevel(LogLevels.DEBUG);
77
75
  return;
78
76
  } else {
79
77
  this.error('Invalid `logger` instance. It must be an object with `debug`, `info`, `warn` and `error` methods. Defaulting to `console.log`');
@@ -22,7 +22,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
22
22
  [c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
23
23
  [c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
24
24
  [c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
25
- [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'],
25
+ [c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying fetch of feature flags (attempt #%s). Reason: %s'],
26
26
  [c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
27
27
  [c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
28
28
  [c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'],
@@ -6,7 +6,7 @@ export const codesWarn: [number, string][] = codesError.concat([
6
6
  [c.ENGINE_VALUE_INVALID, c.LOG_PREFIX_ENGINE_VALUE + 'Value %s doesn\'t match with expected type.'],
7
7
  [c.ENGINE_VALUE_NO_ATTRIBUTES, c.LOG_PREFIX_ENGINE_VALUE + 'Defined attribute `%s`. No attributes received.'],
8
8
  // synchronizer
9
- [c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying download of segments #%s. Reason: %s'],
9
+ [c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying fetch of memberships (attempt #%s). Reason: %s'],
10
10
  [c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of feature flags. %s'],
11
11
  [c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
12
12
  [c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
@@ -14,8 +14,8 @@ export const codesWarn: [number, string][] = codesError.concat([
14
14
  [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Dropping %s after retry. Reason: %s.'],
15
15
  [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s, keeping data to retry on next iteration. Reason: %s.'],
16
16
  // client status
17
- [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect%s. Make sure to wait for SDK readiness before using this method.'],
18
- [c.CLIENT_NO_LISTENER, 'No listeners for SDK Readiness detected. Incorrect control treatments could have been logged if you called getTreatment/s while the SDK was not yet ready.'],
17
+ [c.CLIENT_NOT_READY_FROM_CACHE, '%s: the SDK is not ready to evaluate. Results may be incorrect%s. Make sure to wait for SDK readiness before using this method.'],
18
+ [c.CLIENT_NO_LISTENER, 'No listeners for SDK_READY event detected. Incorrect control treatments could have been logged if you called getTreatment/s while the SDK was not yet synchronized with the backend.'],
19
19
  // input validation
20
20
  [c.WARN_SETTING_NULL, '%s: Property "%s" is of invalid type. Setting value to null.'],
21
21
  [c.WARN_TRIMMING_PROPERTIES, '%s: more than 300 properties were provided. Some of them will be trimmed when processed.'],
@@ -3,7 +3,6 @@ import { ISettings } from '../types';
3
3
  import SplitIO from '../../types/splitio';
4
4
  import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
5
5
  import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';
6
- import { STORAGE_LOCALSTORAGE } from '../utils/constants';
7
6
 
8
7
  function splitsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): ISplitsEventEmitter {
9
8
  const splitsEventEmitter = objectAssign(new EventEmitter(), {
@@ -91,7 +90,7 @@ export function readinessManagerFactory(
91
90
  if (!isReady && !isDestroyed) {
92
91
  try {
93
92
  syncLastUpdate();
94
- gate.emit(SDK_READY_FROM_CACHE);
93
+ gate.emit(SDK_READY_FROM_CACHE, isReady);
95
94
  } catch (e) {
96
95
  // throws user callback exceptions in next tick
97
96
  setTimeout(() => { throw e; }, 0);
@@ -115,9 +114,9 @@ export function readinessManagerFactory(
115
114
  isReady = true;
116
115
  try {
117
116
  syncLastUpdate();
118
- if (!isReadyFromCache && settings.storage?.type === STORAGE_LOCALSTORAGE) {
117
+ if (!isReadyFromCache) {
119
118
  isReadyFromCache = true;
120
- gate.emit(SDK_READY_FROM_CACHE);
119
+ gate.emit(SDK_READY_FROM_CACHE, isReady);
121
120
  }
122
121
  gate.emit(SDK_READY);
123
122
  } catch (e) {