@splitsoftware/splitio-commons 2.7.9-rc.0 → 2.7.9-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 +8 -3
  2. package/cjs/logger/constants.js +2 -2
  3. package/cjs/logger/index.js +0 -3
  4. package/cjs/logger/messages/warn.js +2 -2
  5. package/cjs/readiness/readinessManager.js +3 -5
  6. package/cjs/readiness/sdkReadinessManager.js +33 -0
  7. package/cjs/sdkClient/client.js +4 -4
  8. package/cjs/sdkClient/clientInputValidation.js +1 -1
  9. package/cjs/sdkManager/index.js +3 -3
  10. package/cjs/sync/streaming/SSEHandler/index.js +1 -1
  11. package/cjs/sync/submitters/telemetrySubmitter.js +3 -3
  12. package/cjs/trackers/telemetryTracker.js +6 -6
  13. package/cjs/utils/inputValidation/index.js +2 -1
  14. package/cjs/utils/inputValidation/isOperational.js +9 -4
  15. package/cjs/utils/inputValidation/splitExistence.js +3 -3
  16. package/esm/logger/constants.js +1 -1
  17. package/esm/logger/index.js +0 -3
  18. package/esm/logger/messages/warn.js +2 -2
  19. package/esm/readiness/readinessManager.js +3 -5
  20. package/esm/readiness/sdkReadinessManager.js +33 -0
  21. package/esm/sdkClient/client.js +4 -4
  22. package/esm/sdkClient/clientInputValidation.js +2 -2
  23. package/esm/sdkManager/index.js +4 -4
  24. package/esm/sync/streaming/SSEHandler/index.js +1 -1
  25. package/esm/sync/submitters/telemetrySubmitter.js +3 -3
  26. package/esm/trackers/telemetryTracker.js +6 -6
  27. package/esm/utils/inputValidation/index.js +1 -1
  28. package/esm/utils/inputValidation/isOperational.js +8 -4
  29. package/esm/utils/inputValidation/splitExistence.js +3 -3
  30. package/package.json +1 -1
  31. package/src/logger/constants.ts +1 -1
  32. package/src/logger/index.ts +0 -2
  33. package/src/logger/messages/warn.ts +2 -2
  34. package/src/readiness/readinessManager.ts +3 -4
  35. package/src/readiness/sdkReadinessManager.ts +30 -0
  36. package/src/sdkClient/client.ts +4 -4
  37. package/src/sdkClient/clientInputValidation.ts +3 -3
  38. package/src/sdkManager/index.ts +4 -4
  39. package/src/sync/streaming/SSEHandler/index.ts +1 -1
  40. package/src/sync/submitters/telemetrySubmitter.ts +3 -3
  41. package/src/trackers/telemetryTracker.ts +6 -6
  42. package/src/utils/inputValidation/index.ts +1 -1
  43. package/src/utils/inputValidation/isOperational.ts +9 -4
  44. package/src/utils/inputValidation/splitExistence.ts +3 -3
  45. package/types/splitio.d.ts +25 -10
package/CHANGES.txt CHANGED
@@ -1,8 +1,13 @@
1
- 2.8.0 (October XX, 2025)
2
- - Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs.
1
+ 2.8.0 (October 28, 2025)
2
+ - Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs.
3
+ - Added `client.whenReady()` and `client.whenReadyFromCache()` methods to replace the deprecated `client.ready()` method, which has an issue causing the returned promise to hang when using async/await syntax if it was rejected.
4
+ - Updated the SDK_READY_FROM_CACHE event to be emitted alongside the SDK_READY event if it hasn’t already been emitted.
5
+
6
+ 2.7.1 (October 8, 2025)
7
+ - Bugfix - Update `debug` option to support log levels when `logger` option is used.
3
8
 
4
9
  2.7.0 (October 7, 2025)
5
- - Added support for custom loggers: added `logger` configuration option and `LoggerAPI.setLogger` method to allow the SDK to use a custom logger.
10
+ - Added support for custom loggers: added `logger` configuration option and `factory.Logger.setLogger` method to allow the SDK to use a custom logger.
6
11
 
7
12
  2.6.0 (September 18, 2025)
8
13
  - Added `storage.wrapper` configuration option to allow the SDK to use a custom storage wrapper for the storage type `LOCALSTORAGE`. Default value is `window.localStorage`.
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SUBMITTERS_PUSH_PAGE_HIDDEN = exports.USER_CONSENT_INITIAL = exports.USER_CONSENT_NOT_UPDATED = exports.USER_CONSENT_UPDATED = exports.IMPRESSIONS_TRACKER_SUCCESS = exports.EVENTS_TRACKER_SUCCESS = exports.SYNC_STOP_POLLING = exports.SYNC_CONTINUE_POLLING = exports.SYNC_START_POLLING = exports.SUBMITTERS_PUSH = exports.SUBMITTERS_PUSH_FULL_QUEUE = exports.STREAMING_DISCONNECTING = exports.STREAMING_DISABLED = exports.STREAMING_CONNECTING = exports.STREAMING_RECONNECT = exports.STREAMING_REFRESH_TOKEN = exports.SYNC_SPLITS_FETCH_RETRY = exports.POLLING_STOP = exports.POLLING_START = exports.POLLING_SMART_PAUSING = exports.NEW_FACTORY = exports.NEW_SHARED_CLIENT = exports.IMPRESSION_QUEUEING = exports.IMPRESSION = exports.CLIENT_READY = exports.CLIENT_READY_FROM_CACHE = exports.ENGINE_DEFAULT = exports.ENGINE_MATCHER_RESULT = exports.SETTINGS_SPLITS_FILTER = exports.SYNC_TASK_STOP = exports.SYNC_TASK_EXECUTE = exports.SYNC_TASK_START = exports.STREAMING_NEW_MESSAGE = exports.SYNC_RBS_UPDATE = exports.SYNC_SPLITS_UPDATE = exports.SYNC_SPLITS_FETCH = exports.SYNC_OFFLINE_DATA = exports.RETRIEVE_MANAGER = exports.RETRIEVE_CLIENT_EXISTING = exports.RETRIEVE_CLIENT_DEFAULT = exports.CLEANUP_DEREGISTERING = exports.CLEANUP_REGISTERING = exports.ENGINE_SANITIZE = exports.ENGINE_VALUE = exports.ENGINE_MATCHER_DEPENDENCY_PRE = exports.ENGINE_MATCHER_DEPENDENCY = exports.ENGINE_BUCKET = exports.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT = exports.ENGINE_COMBINER_IFELSEIF = exports.ENGINE_COMBINER_AND = void 0;
4
- exports.ERROR_EMPTY_ARRAY = exports.ERROR_EMPTY = exports.ERROR_INVALID = exports.ERROR_INVALID_KEY_OBJECT = exports.ERROR_TOO_LONG = exports.ERROR_NULL = exports.ERROR_CLIENT_DESTROYED = exports.ERROR_NOT_FINITE = exports.ERROR_SIZE_EXCEEDED = exports.ERROR_NOT_PLAIN_OBJECT = exports.ERROR_EVENT_TYPE_FORMAT = exports.ERROR_EVENTS_TRACKER = exports.ERROR_IMPRESSIONS_LISTENER = exports.ERROR_IMPRESSIONS_TRACKER = exports.ERROR_STREAMING_AUTH = exports.ERROR_STREAMING_SSE = exports.ERROR_SYNC_OFFLINE_LOADING = exports.ERROR_CLIENT_CANNOT_GET_READY = exports.ERROR_CLIENT_LISTENER = exports.ERROR_LOGLEVEL_INVALID = exports.ERROR_ENGINE_COMBINER_IFELSEIF = exports.WARN_FLAGSET_WITHOUT_FLAGS = exports.WARN_FLAGSET_NOT_CONFIGURED = exports.WARN_LOWERCASE_FLAGSET = exports.WARN_INVALID_FLAGSET = exports.STREAMING_PARSING_SPLIT_UPDATE = exports.STREAMING_PARSING_MEMBERSHIPS_UPDATE = exports.WARN_SDK_KEY = exports.WARN_SPLITS_FILTER_EMPTY = exports.WARN_SPLITS_FILTER_INVALID = exports.WARN_SPLITS_FILTER_IGNORED = exports.WARN_INTEGRATION_INVALID = exports.WARN_NOT_EXISTENT_TT = exports.WARN_LOWERCASE_TRAFFIC_TYPE = exports.WARN_NOT_EXISTENT_SPLIT = exports.WARN_TRIMMING = exports.WARN_CONVERTING = exports.WARN_TRIMMING_PROPERTIES = exports.WARN_SETTING_NULL = exports.SUBMITTERS_PUSH_RETRY = exports.SUBMITTERS_PUSH_FAILS = exports.STREAMING_FALLBACK = exports.STREAMING_PARSING_MESSAGE_FAILS = exports.STREAMING_PARSING_ERROR_FAILS = exports.SYNC_SPLITS_FETCH_FAILS = exports.SYNC_MYSEGMENTS_FETCH_RETRY = exports.CLIENT_NOT_READY = exports.CLIENT_NO_LISTENER = exports.ENGINE_VALUE_NO_ATTRIBUTES = exports.ENGINE_VALUE_INVALID = void 0;
4
+ exports.ERROR_EMPTY_ARRAY = exports.ERROR_EMPTY = exports.ERROR_INVALID = exports.ERROR_INVALID_KEY_OBJECT = exports.ERROR_TOO_LONG = exports.ERROR_NULL = exports.ERROR_CLIENT_DESTROYED = exports.ERROR_NOT_FINITE = exports.ERROR_SIZE_EXCEEDED = exports.ERROR_NOT_PLAIN_OBJECT = exports.ERROR_EVENT_TYPE_FORMAT = exports.ERROR_EVENTS_TRACKER = exports.ERROR_IMPRESSIONS_LISTENER = exports.ERROR_IMPRESSIONS_TRACKER = exports.ERROR_STREAMING_AUTH = exports.ERROR_STREAMING_SSE = exports.ERROR_SYNC_OFFLINE_LOADING = exports.ERROR_CLIENT_CANNOT_GET_READY = exports.ERROR_CLIENT_LISTENER = exports.ERROR_LOGLEVEL_INVALID = exports.ERROR_ENGINE_COMBINER_IFELSEIF = exports.WARN_FLAGSET_WITHOUT_FLAGS = exports.WARN_FLAGSET_NOT_CONFIGURED = exports.WARN_LOWERCASE_FLAGSET = exports.WARN_INVALID_FLAGSET = exports.STREAMING_PARSING_SPLIT_UPDATE = exports.STREAMING_PARSING_MEMBERSHIPS_UPDATE = exports.WARN_SDK_KEY = exports.WARN_SPLITS_FILTER_EMPTY = exports.WARN_SPLITS_FILTER_INVALID = exports.WARN_SPLITS_FILTER_IGNORED = exports.WARN_INTEGRATION_INVALID = exports.WARN_NOT_EXISTENT_TT = exports.WARN_LOWERCASE_TRAFFIC_TYPE = exports.WARN_NOT_EXISTENT_SPLIT = exports.WARN_TRIMMING = exports.WARN_CONVERTING = exports.WARN_TRIMMING_PROPERTIES = exports.WARN_SETTING_NULL = exports.SUBMITTERS_PUSH_RETRY = exports.SUBMITTERS_PUSH_FAILS = exports.STREAMING_FALLBACK = exports.STREAMING_PARSING_MESSAGE_FAILS = exports.STREAMING_PARSING_ERROR_FAILS = exports.SYNC_SPLITS_FETCH_FAILS = exports.SYNC_MYSEGMENTS_FETCH_RETRY = exports.CLIENT_NOT_READY_FROM_CACHE = exports.CLIENT_NO_LISTENER = exports.ENGINE_VALUE_NO_ATTRIBUTES = exports.ENGINE_VALUE_INVALID = void 0;
5
5
  exports.LOG_PREFIX_CLEANUP = exports.LOG_PREFIX_UNIQUE_KEYS_TRACKER = exports.LOG_PREFIX_EVENTS_TRACKER = exports.LOG_PREFIX_IMPRESSIONS_TRACKER = exports.LOG_PREFIX_SYNC_SUBMITTERS = exports.LOG_PREFIX_SYNC_POLLING = exports.LOG_PREFIX_SYNC_MYSEGMENTS = exports.LOG_PREFIX_SYNC_SEGMENTS = exports.LOG_PREFIX_SYNC_SPLITS = exports.LOG_PREFIX_SYNC_STREAMING = exports.LOG_PREFIX_SYNC_OFFLINE = exports.LOG_PREFIX_SYNC_MANAGER = exports.LOG_PREFIX_SYNC = exports.LOG_PREFIX_ENGINE_VALUE = exports.LOG_PREFIX_ENGINE_MATCHER = exports.LOG_PREFIX_ENGINE_COMBINER = exports.LOG_PREFIX_ENGINE = exports.LOG_PREFIX_CLIENT_INSTANTIATION = exports.LOG_PREFIX_INSTANTIATION = exports.LOG_PREFIX_SETTINGS = exports.ENGINE_MATCHER_ERROR = exports.ERROR_SETS_FILTER_EXCLUSIVE = exports.ERROR_TOO_MANY_SETS = exports.ERROR_MIN_CONFIG_PARAM = exports.ERROR_NOT_BOOLEAN = exports.ERROR_STORAGE_INVALID = exports.ERROR_HTTP = exports.ERROR_INVALID_CONFIG_PARAM = void 0;
6
6
  /**
7
7
  * Message codes used to trim string log messages from commons and client-side API modules,
@@ -63,7 +63,7 @@ exports.SUBMITTERS_PUSH_PAGE_HIDDEN = 125;
63
63
  exports.ENGINE_VALUE_INVALID = 200;
64
64
  exports.ENGINE_VALUE_NO_ATTRIBUTES = 201;
65
65
  exports.CLIENT_NO_LISTENER = 202;
66
- exports.CLIENT_NOT_READY = 203;
66
+ exports.CLIENT_NOT_READY_FROM_CACHE = 203;
67
67
  exports.SYNC_MYSEGMENTS_FETCH_RETRY = 204;
68
68
  exports.SYNC_SPLITS_FETCH_FAILS = 205;
69
69
  exports.STREAMING_PARSING_ERROR_FAILS = 206;
@@ -63,9 +63,6 @@ var Logger = /** @class */ (function () {
63
63
  if (logger) {
64
64
  if ((0, commons_1.isLogger)(logger)) {
65
65
  this.logger = logger;
66
- // If custom logger is set, all logs are either enabled or disabled
67
- if (this.logLevel !== LogLevelIndexes.NONE)
68
- this.setLogLevel(exports.LogLevels.DEBUG);
69
66
  return;
70
67
  }
71
68
  else {
@@ -17,8 +17,8 @@ exports.codesWarn = error_1.codesError.concat([
17
17
  [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Dropping %s after retry. Reason: %s.'],
18
18
  [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s, keeping data to retry on next iteration. Reason: %s.'],
19
19
  // client status
20
- [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.'],
21
- [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.'],
20
+ [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.'],
21
+ [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.'],
22
22
  // input validation
23
23
  [c.WARN_SETTING_NULL, '%s: Property "%s" is of invalid type. Setting value to null.'],
24
24
  [c.WARN_TRIMMING_PROPERTIES, '%s: more than 300 properties were provided. Some of them will be trimmed when processed.'],
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.readinessManagerFactory = void 0;
4
4
  var objectAssign_1 = require("../utils/lang/objectAssign");
5
5
  var constants_1 = require("./constants");
6
- var constants_2 = require("../utils/constants");
7
6
  function splitsEventEmitterFactory(EventEmitter) {
8
7
  var splitsEventEmitter = (0, objectAssign_1.objectAssign)(new EventEmitter(), {
9
8
  splitsArrived: false,
@@ -75,7 +74,7 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
75
74
  if (!isReady && !isDestroyed) {
76
75
  try {
77
76
  syncLastUpdate();
78
- gate.emit(constants_1.SDK_READY_FROM_CACHE);
77
+ gate.emit(constants_1.SDK_READY_FROM_CACHE, isReady);
79
78
  }
80
79
  catch (e) {
81
80
  // throws user callback exceptions in next tick
@@ -84,7 +83,6 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
84
83
  }
85
84
  }
86
85
  function checkIsReadyOrUpdate(diff) {
87
- var _a;
88
86
  if (isDestroyed)
89
87
  return;
90
88
  if (isReady) {
@@ -103,9 +101,9 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
103
101
  isReady = true;
104
102
  try {
105
103
  syncLastUpdate();
106
- if (!isReadyFromCache && ((_a = settings.storage) === null || _a === void 0 ? void 0 : _a.type) === constants_2.STORAGE_LOCALSTORAGE) {
104
+ if (!isReadyFromCache) {
107
105
  isReadyFromCache = true;
108
- gate.emit(constants_1.SDK_READY_FROM_CACHE);
106
+ gate.emit(constants_1.SDK_READY_FROM_CACHE, isReady);
109
107
  }
110
108
  gate.emit(constants_1.SDK_READY);
111
109
  }
@@ -8,6 +8,7 @@ var constants_1 = require("./constants");
8
8
  var constants_2 = require("../logger/constants");
9
9
  var NEW_LISTENER_EVENT = 'newListener';
10
10
  var REMOVE_LISTENER_EVENT = 'removeListener';
11
+ var TIMEOUT_ERROR = new Error(constants_1.SDK_READY_TIMED_OUT);
11
12
  /**
12
13
  * SdkReadinessManager factory, which provides the public status API of SDK clients and manager: ready promise, readiness event emitter and constants (SDK_READY, etc).
13
14
  * It also updates logs related warnings and errors.
@@ -34,6 +35,9 @@ function sdkReadinessManagerFactory(EventEmitter, settings, readinessManager) {
34
35
  readyCbCount++;
35
36
  }
36
37
  }
38
+ else if (event === constants_1.SDK_READY_FROM_CACHE && readinessManager.isReadyFromCache()) {
39
+ log.error(constants_2.ERROR_CLIENT_LISTENER, ['SDK_READY_FROM_CACHE']);
40
+ }
37
41
  });
38
42
  /** Ready promise */
39
43
  var readyPromise = generateReadyPromise();
@@ -76,6 +80,7 @@ function sdkReadinessManagerFactory(EventEmitter, settings, readinessManager) {
76
80
  SDK_UPDATE: constants_1.SDK_UPDATE,
77
81
  SDK_READY_TIMED_OUT: constants_1.SDK_READY_TIMED_OUT,
78
82
  },
83
+ // @TODO: remove in next major
79
84
  ready: function () {
80
85
  if (readinessManager.hasTimedout()) {
81
86
  if (!readinessManager.isReady()) {
@@ -87,6 +92,34 @@ function sdkReadinessManagerFactory(EventEmitter, settings, readinessManager) {
87
92
  }
88
93
  return readyPromise;
89
94
  },
95
+ whenReady: function () {
96
+ return new Promise(function (resolve, reject) {
97
+ if (readinessManager.isReady()) {
98
+ resolve();
99
+ }
100
+ else if (readinessManager.hasTimedout()) {
101
+ reject(TIMEOUT_ERROR);
102
+ }
103
+ else {
104
+ readinessManager.gate.once(constants_1.SDK_READY, resolve);
105
+ readinessManager.gate.once(constants_1.SDK_READY_TIMED_OUT, function () { return reject(TIMEOUT_ERROR); });
106
+ }
107
+ });
108
+ },
109
+ whenReadyFromCache: function () {
110
+ return new Promise(function (resolve, reject) {
111
+ if (readinessManager.isReadyFromCache()) {
112
+ resolve(readinessManager.isReady());
113
+ }
114
+ else if (readinessManager.hasTimedout()) {
115
+ reject(TIMEOUT_ERROR);
116
+ }
117
+ else {
118
+ readinessManager.gate.once(constants_1.SDK_READY_FROM_CACHE, function () { return resolve(readinessManager.isReady()); });
119
+ readinessManager.gate.once(constants_1.SDK_READY_TIMED_OUT, function () { return reject(TIMEOUT_ERROR); });
120
+ }
121
+ });
122
+ },
90
123
  __getStatus: function () {
91
124
  return {
92
125
  isReady: readinessManager.isReady(),
@@ -44,7 +44,7 @@ function clientFactory(params) {
44
44
  stopTelemetryTracker(queue[0] && queue[0].imp.label);
45
45
  return treatment;
46
46
  };
47
- var evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
47
+ var evaluation = readinessManager.isReadyFromCache() ?
48
48
  (0, evaluator_1.evaluateFeature)(log, key, featureFlagName, attributes, storage) :
49
49
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
50
50
  Promise.resolve(treatmentNotReady) :
@@ -69,7 +69,7 @@ function clientFactory(params) {
69
69
  stopTelemetryTracker(queue[0] && queue[0].imp.label);
70
70
  return treatments;
71
71
  };
72
- var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
72
+ var evaluations = readinessManager.isReadyFromCache() ?
73
73
  (0, evaluator_1.evaluateFeatures)(log, key, featureFlagNames, attributes, storage) :
74
74
  isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
75
75
  Promise.resolve(treatmentsNotReady(featureFlagNames)) :
@@ -95,7 +95,7 @@ function clientFactory(params) {
95
95
  stopTelemetryTracker(queue[0] && queue[0].imp.label);
96
96
  return treatments;
97
97
  };
98
- var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
98
+ var evaluations = readinessManager.isReadyFromCache() ?
99
99
  (0, evaluator_1.evaluateFeaturesByFlagSets)(log, key, flagSetNames, attributes, storage, methodName) :
100
100
  isAsync ?
101
101
  Promise.resolve({}) :
@@ -120,7 +120,7 @@ function clientFactory(params) {
120
120
  if (treatment === constants_1.CONTROL) {
121
121
  var fallbackTreatment = fallbackTreatmentsCalculator.resolve(featureFlagName, label);
122
122
  treatment = fallbackTreatment.treatment;
123
- label = fallbackTreatment.label ? fallbackTreatment.label : label;
123
+ label = fallbackTreatment.label;
124
124
  config = fallbackTreatment.config;
125
125
  }
126
126
  log.info(constants_2.IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
@@ -26,7 +26,7 @@ function clientInputValidationDecorator(settings, client, readinessManager, fall
26
26
  var attributes = (0, inputValidation_1.validateAttributes)(log, maybeAttributes, methodName);
27
27
  var isNotDestroyed = (0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, methodName);
28
28
  var options = (0, inputValidation_1.validateEvaluationOptions)(log, maybeOptions, methodName);
29
- (0, inputValidation_1.validateIfOperational)(log, readinessManager, methodName, nameOrNames);
29
+ (0, inputValidation_1.validateIfReadyFromCache)(log, readinessManager, methodName, nameOrNames);
30
30
  var valid = isNotDestroyed && key && nameOrNames && attributes !== false;
31
31
  return {
32
32
  valid: valid,
@@ -54,7 +54,7 @@ function sdkManagerFactory(settings, splits, _a) {
54
54
  */
55
55
  split: function (featureFlagName) {
56
56
  var splitName = (0, inputValidation_1.validateSplit)(log, featureFlagName, constants_1.SPLIT_FN_LABEL);
57
- if (!(0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, constants_1.SPLIT_FN_LABEL) || !(0, inputValidation_1.validateIfOperational)(log, readinessManager, constants_1.SPLIT_FN_LABEL) || !splitName) {
57
+ if (!(0, inputValidation_1.validateIfOperational)(log, readinessManager, constants_1.SPLIT_FN_LABEL) || !splitName) {
58
58
  return isAsync ? Promise.resolve(null) : null;
59
59
  }
60
60
  var split = splits.getSplit(splitName);
@@ -71,7 +71,7 @@ function sdkManagerFactory(settings, splits, _a) {
71
71
  * Get the feature flag objects present on the factory storage
72
72
  */
73
73
  splits: function () {
74
- if (!(0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, constants_1.SPLITS_FN_LABEL) || !(0, inputValidation_1.validateIfOperational)(log, readinessManager, constants_1.SPLITS_FN_LABEL)) {
74
+ if (!(0, inputValidation_1.validateIfOperational)(log, readinessManager, constants_1.SPLITS_FN_LABEL)) {
75
75
  return isAsync ? Promise.resolve([]) : [];
76
76
  }
77
77
  var currentSplits = splits.getAll();
@@ -83,7 +83,7 @@ function sdkManagerFactory(settings, splits, _a) {
83
83
  * Get the feature flag names present on the factory storage
84
84
  */
85
85
  names: function () {
86
- if (!(0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, constants_1.NAMES_FN_LABEL) || !(0, inputValidation_1.validateIfOperational)(log, readinessManager, constants_1.NAMES_FN_LABEL)) {
86
+ if (!(0, inputValidation_1.validateIfOperational)(log, readinessManager, constants_1.NAMES_FN_LABEL)) {
87
87
  return isAsync ? Promise.resolve([]) : [];
88
88
  }
89
89
  var splitNames = splits.getSplitNames();
@@ -19,7 +19,7 @@ function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
19
19
  // Ably error
20
20
  var code = error.parsedData.code;
21
21
  telemetryTracker.streamingEvent(constants_3.ABLY_ERROR, code);
22
- // 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
22
+ // 401 errors due to invalid or expired token (e.g., if refresh token couldn't be executed)
23
23
  if (40140 <= code && code <= 40149)
24
24
  return true;
25
25
  // Others 4XX errors (e.g., bad request from the SDK)
@@ -107,14 +107,14 @@ function telemetrySubmitterFactory(params) {
107
107
  if (!telemetry || !now)
108
108
  return; // No submitter created if telemetry cache is not defined
109
109
  var settings = params.settings, _a = params.settings, log = _a.log, telemetryRefreshRate = _a.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
110
- var startTime = (0, timer_1.timer)(now);
110
+ var stopTimer = (0, timer_1.timer)(now);
111
111
  var submitter = (0, submitter_1.firstPushWindowDecorator)((0, submitter_1.submitterFactory)(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate, undefined, 0, true), telemetryRefreshRate);
112
112
  readiness.gate.once(constants_2.SDK_READY_FROM_CACHE, function () {
113
- telemetry.recordTimeUntilReadyFromCache(startTime());
113
+ telemetry.recordTimeUntilReadyFromCache(stopTimer());
114
114
  });
115
115
  sdkReadinessManager.incInternalReadyCbCount();
116
116
  readiness.gate.once(constants_2.SDK_READY, function () {
117
- telemetry.recordTimeUntilReady(startTime());
117
+ telemetry.recordTimeUntilReady(stopTimer());
118
118
  // Post config data when the SDK is ready and if the telemetry submitter was started
119
119
  if (submitter.isRunning()) {
120
120
  var postMetricsConfigTask = (0, submitter_1.submitterFactory)(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, undefined, 0, true);
@@ -6,10 +6,10 @@ var timer_1 = require("../utils/timeTracker/timer");
6
6
  var constants_1 = require("../utils/constants");
7
7
  function telemetryTrackerFactory(telemetryCache, now) {
8
8
  if (telemetryCache && now) {
9
- var startTime_1 = (0, timer_1.timer)(now);
9
+ var sessionTimer_1 = (0, timer_1.timer)(now);
10
10
  return {
11
11
  trackEval: function (method) {
12
- var evalTime = (0, timer_1.timer)(now);
12
+ var evalTimer = (0, timer_1.timer)(now);
13
13
  return function (label) {
14
14
  switch (label) {
15
15
  case labels_1.EXCEPTION:
@@ -19,13 +19,13 @@ function telemetryTrackerFactory(telemetryCache, now) {
19
19
  if (telemetryCache.recordNonReadyUsage)
20
20
  telemetryCache.recordNonReadyUsage();
21
21
  }
22
- telemetryCache.recordLatency(method, evalTime());
22
+ telemetryCache.recordLatency(method, evalTimer());
23
23
  };
24
24
  },
25
25
  trackHttp: function (operation) {
26
- var httpTime = (0, timer_1.timer)(now);
26
+ var httpTimer = (0, timer_1.timer)(now);
27
27
  return function (error) {
28
- telemetryCache.recordHttpLatency(operation, httpTime());
28
+ telemetryCache.recordHttpLatency(operation, httpTimer());
29
29
  if (error && error.statusCode)
30
30
  telemetryCache.recordHttpError(operation, error.statusCode);
31
31
  else
@@ -34,7 +34,7 @@ function telemetryTrackerFactory(telemetryCache, now) {
34
34
  },
35
35
  sessionLength: function () {
36
36
  if (telemetryCache.recordSessionLength)
37
- telemetryCache.recordSessionLength(startTime_1());
37
+ telemetryCache.recordSessionLength(sessionTimer_1());
38
38
  },
39
39
  streamingEvent: function (e, d) {
40
40
  if (e === constants_1.AUTH_REJECTION) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateEvaluationOptions = exports.validateTrafficTypeExistence = exports.validateSplitExistence = exports.validateIfOperational = exports.validateIfNotDestroyed = exports.validateTrafficType = exports.validateSplits = exports.validateSplit = exports.validateKey = exports.validateEventProperties = exports.validateEventValue = exports.validateEvent = exports.validateAttributes = exports.releaseApiKey = exports.validateAndTrackApiKey = exports.validateApiKey = void 0;
3
+ exports.validateEvaluationOptions = exports.validateTrafficTypeExistence = exports.validateSplitExistence = exports.validateIfOperational = exports.validateIfReadyFromCache = exports.validateIfNotDestroyed = exports.validateTrafficType = exports.validateSplits = exports.validateSplit = exports.validateKey = exports.validateEventProperties = exports.validateEventValue = exports.validateEvent = exports.validateAttributes = exports.releaseApiKey = exports.validateAndTrackApiKey = exports.validateApiKey = void 0;
4
4
  var apiKey_1 = require("./apiKey");
5
5
  Object.defineProperty(exports, "validateApiKey", { enumerable: true, get: function () { return apiKey_1.validateApiKey; } });
6
6
  Object.defineProperty(exports, "validateAndTrackApiKey", { enumerable: true, get: function () { return apiKey_1.validateAndTrackApiKey; } });
@@ -23,6 +23,7 @@ var trafficType_1 = require("./trafficType");
23
23
  Object.defineProperty(exports, "validateTrafficType", { enumerable: true, get: function () { return trafficType_1.validateTrafficType; } });
24
24
  var isOperational_1 = require("./isOperational");
25
25
  Object.defineProperty(exports, "validateIfNotDestroyed", { enumerable: true, get: function () { return isOperational_1.validateIfNotDestroyed; } });
26
+ Object.defineProperty(exports, "validateIfReadyFromCache", { enumerable: true, get: function () { return isOperational_1.validateIfReadyFromCache; } });
26
27
  Object.defineProperty(exports, "validateIfOperational", { enumerable: true, get: function () { return isOperational_1.validateIfOperational; } });
27
28
  var splitExistence_1 = require("./splitExistence");
28
29
  Object.defineProperty(exports, "validateSplitExistence", { enumerable: true, get: function () { return splitExistence_1.validateSplitExistence; } });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateIfOperational = exports.validateIfNotDestroyed = void 0;
3
+ exports.validateIfOperational = exports.validateIfReadyFromCache = exports.validateIfNotDestroyed = void 0;
4
4
  var constants_1 = require("../../logger/constants");
5
5
  function validateIfNotDestroyed(log, readinessManager, method) {
6
6
  if (!readinessManager.isDestroyed())
@@ -9,10 +9,15 @@ function validateIfNotDestroyed(log, readinessManager, method) {
9
9
  return false;
10
10
  }
11
11
  exports.validateIfNotDestroyed = validateIfNotDestroyed;
12
- function validateIfOperational(log, readinessManager, method, featureFlagNameOrNames) {
13
- if (readinessManager.isReady() || readinessManager.isReadyFromCache())
12
+ function validateIfReadyFromCache(log, readinessManager, method, featureFlagNameOrNames) {
13
+ if (readinessManager.isReadyFromCache())
14
14
  return true;
15
- log.warn(constants_1.CLIENT_NOT_READY, [method, featureFlagNameOrNames ? " for feature flag " + featureFlagNameOrNames.toString() : '']);
15
+ log.warn(constants_1.CLIENT_NOT_READY_FROM_CACHE, [method, featureFlagNameOrNames ? " for feature flag " + featureFlagNameOrNames.toString() : '']);
16
16
  return false;
17
17
  }
18
+ exports.validateIfReadyFromCache = validateIfReadyFromCache;
19
+ // Operational means that the SDK is ready to evaluate (not destroyed and ready from cache)
20
+ function validateIfOperational(log, readinessManager, method, featureFlagNameOrNames) {
21
+ return validateIfNotDestroyed(log, readinessManager, method) && validateIfReadyFromCache(log, readinessManager, method, featureFlagNameOrNames);
22
+ }
18
23
  exports.validateIfOperational = validateIfOperational;
@@ -5,11 +5,11 @@ var labels_1 = require("../labels");
5
5
  var constants_1 = require("../../logger/constants");
6
6
  /**
7
7
  * 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.
8
- * 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.
8
+ * 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.
9
9
  */
10
10
  function validateSplitExistence(log, readinessManager, splitName, labelOrSplitObj, method) {
11
- if (readinessManager.isReady()) { // Only if it's ready we validate this, otherwise it may just be that the SDK is not ready yet.
12
- if (labelOrSplitObj === labels_1.SPLIT_NOT_FOUND || labelOrSplitObj == null || labelOrSplitObj === labels_1.FALLBACK_SPLIT_NOT_FOUND) {
11
+ 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
12
+ if (labelOrSplitObj === labels_1.SPLIT_NOT_FOUND || labelOrSplitObj === labels_1.FALLBACK_SPLIT_NOT_FOUND || labelOrSplitObj == null) {
13
13
  log.warn(constants_1.WARN_NOT_EXISTENT_SPLIT, [method, splitName]);
14
14
  return false;
15
15
  }
@@ -58,7 +58,7 @@ export var SUBMITTERS_PUSH_PAGE_HIDDEN = 125;
58
58
  export var ENGINE_VALUE_INVALID = 200;
59
59
  export var ENGINE_VALUE_NO_ATTRIBUTES = 201;
60
60
  export var CLIENT_NO_LISTENER = 202;
61
- export var CLIENT_NOT_READY = 203;
61
+ export var CLIENT_NOT_READY_FROM_CACHE = 203;
62
62
  export var SYNC_MYSEGMENTS_FETCH_RETRY = 204;
63
63
  export var SYNC_SPLITS_FETCH_FAILS = 205;
64
64
  export var STREAMING_PARSING_ERROR_FAILS = 206;
@@ -58,9 +58,6 @@ var Logger = /** @class */ (function () {
58
58
  if (logger) {
59
59
  if (isLogger(logger)) {
60
60
  this.logger = logger;
61
- // If custom logger is set, all logs are either enabled or disabled
62
- if (this.logLevel !== LogLevelIndexes.NONE)
63
- this.setLogLevel(LogLevels.DEBUG);
64
61
  return;
65
62
  }
66
63
  else {
@@ -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();
@@ -73,6 +77,7 @@ export function sdkReadinessManagerFactory(EventEmitter, settings, readinessMana
73
77
  SDK_UPDATE: SDK_UPDATE,
74
78
  SDK_READY_TIMED_OUT: SDK_READY_TIMED_OUT,
75
79
  },
80
+ // @TODO: remove in next major
76
81
  ready: function () {
77
82
  if (readinessManager.hasTimedout()) {
78
83
  if (!readinessManager.isReady()) {
@@ -84,6 +89,34 @@ export function sdkReadinessManagerFactory(EventEmitter, settings, readinessMana
84
89
  }
85
90
  return readyPromise;
86
91
  },
92
+ whenReady: function () {
93
+ return new Promise(function (resolve, reject) {
94
+ if (readinessManager.isReady()) {
95
+ resolve();
96
+ }
97
+ else if (readinessManager.hasTimedout()) {
98
+ reject(TIMEOUT_ERROR);
99
+ }
100
+ else {
101
+ readinessManager.gate.once(SDK_READY, resolve);
102
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, function () { return reject(TIMEOUT_ERROR); });
103
+ }
104
+ });
105
+ },
106
+ whenReadyFromCache: function () {
107
+ return new Promise(function (resolve, reject) {
108
+ if (readinessManager.isReadyFromCache()) {
109
+ resolve(readinessManager.isReady());
110
+ }
111
+ else if (readinessManager.hasTimedout()) {
112
+ reject(TIMEOUT_ERROR);
113
+ }
114
+ else {
115
+ readinessManager.gate.once(SDK_READY_FROM_CACHE, function () { return resolve(readinessManager.isReady()); });
116
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, function () { return reject(TIMEOUT_ERROR); });
117
+ }
118
+ });
119
+ },
87
120
  __getStatus: function () {
88
121
  return {
89
122
  isReady: readinessManager.isReady(),
@@ -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();
@@ -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)
@@ -102,14 +102,14 @@ export function telemetrySubmitterFactory(params) {
102
102
  if (!telemetry || !now)
103
103
  return; // No submitter created if telemetry cache is not defined
104
104
  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);
105
+ var stopTimer = timer(now);
106
106
  var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate, undefined, 0, true), telemetryRefreshRate);
107
107
  readiness.gate.once(SDK_READY_FROM_CACHE, function () {
108
- telemetry.recordTimeUntilReadyFromCache(startTime());
108
+ telemetry.recordTimeUntilReadyFromCache(stopTimer());
109
109
  });
110
110
  sdkReadinessManager.incInternalReadyCbCount();
111
111
  readiness.gate.once(SDK_READY, function () {
112
- telemetry.recordTimeUntilReady(startTime());
112
+ telemetry.recordTimeUntilReady(stopTimer());
113
113
  // Post config data when the SDK is ready and if the telemetry submitter was started
114
114
  if (submitter.isRunning()) {
115
115
  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
  }
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.1",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -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`');
@@ -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) {
@@ -9,6 +9,7 @@ import { ERROR_CLIENT_LISTENER, CLIENT_READY_FROM_CACHE, CLIENT_READY, CLIENT_NO
9
9
 
10
10
  const NEW_LISTENER_EVENT = 'newListener';
11
11
  const REMOVE_LISTENER_EVENT = 'removeListener';
12
+ const TIMEOUT_ERROR = new Error(SDK_READY_TIMED_OUT);
12
13
 
13
14
  /**
14
15
  * SdkReadinessManager factory, which provides the public status API of SDK clients and manager: ready promise, readiness event emitter and constants (SDK_READY, etc).
@@ -38,6 +39,8 @@ export function sdkReadinessManagerFactory(
38
39
  } else if (event === SDK_READY) {
39
40
  readyCbCount++;
40
41
  }
42
+ } else if (event === SDK_READY_FROM_CACHE && readinessManager.isReadyFromCache()) {
43
+ log.error(ERROR_CLIENT_LISTENER, ['SDK_READY_FROM_CACHE']);
41
44
  }
42
45
  });
43
46
 
@@ -93,6 +96,7 @@ export function sdkReadinessManagerFactory(
93
96
  SDK_READY_TIMED_OUT,
94
97
  },
95
98
 
99
+ // @TODO: remove in next major
96
100
  ready() {
97
101
  if (readinessManager.hasTimedout()) {
98
102
  if (!readinessManager.isReady()) {
@@ -104,6 +108,32 @@ export function sdkReadinessManagerFactory(
104
108
  return readyPromise;
105
109
  },
106
110
 
111
+ whenReady() {
112
+ return new Promise<void>((resolve, reject) => {
113
+ if (readinessManager.isReady()) {
114
+ resolve();
115
+ } else if (readinessManager.hasTimedout()) {
116
+ reject(TIMEOUT_ERROR);
117
+ } else {
118
+ readinessManager.gate.once(SDK_READY, resolve);
119
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, () => reject(TIMEOUT_ERROR));
120
+ }
121
+ });
122
+ },
123
+
124
+ whenReadyFromCache() {
125
+ return new Promise<boolean>((resolve, reject) => {
126
+ if (readinessManager.isReadyFromCache()) {
127
+ resolve(readinessManager.isReady());
128
+ } else if (readinessManager.hasTimedout()) {
129
+ reject(TIMEOUT_ERROR);
130
+ } else {
131
+ readinessManager.gate.once(SDK_READY_FROM_CACHE, () => resolve(readinessManager.isReady()));
132
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, () => reject(TIMEOUT_ERROR));
133
+ }
134
+ });
135
+ },
136
+
107
137
  __getStatus() {
108
138
  return {
109
139
  isReady: readinessManager.isReady(),
@@ -51,7 +51,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
51
51
  return treatment;
52
52
  };
53
53
 
54
- const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
54
+ const evaluation = readinessManager.isReadyFromCache() ?
55
55
  evaluateFeature(log, key, featureFlagName, attributes, storage) :
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) :
@@ -80,7 +80,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
80
80
  return treatments;
81
81
  };
82
82
 
83
- const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
83
+ const evaluations = readinessManager.isReadyFromCache() ?
84
84
  evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
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)) :
@@ -109,7 +109,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
109
109
  return treatments;
110
110
  };
111
111
 
112
- const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
112
+ const evaluations = readinessManager.isReadyFromCache() ?
113
113
  evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
114
114
  isAsync ?
115
115
  Promise.resolve({}) :
@@ -149,7 +149,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
149
149
  if (treatment === CONTROL) {
150
150
  const fallbackTreatment = fallbackTreatmentsCalculator.resolve(featureFlagName, label);
151
151
  treatment = fallbackTreatment.treatment;
152
- label = fallbackTreatment.label ? fallbackTreatment.label : label;
152
+ label = fallbackTreatment.label;
153
153
  config = fallbackTreatment.config;
154
154
  }
155
155
 
@@ -8,7 +8,7 @@ import {
8
8
  validateSplits,
9
9
  validateTrafficType,
10
10
  validateIfNotDestroyed,
11
- validateIfOperational,
11
+ validateIfReadyFromCache,
12
12
  validateEvaluationOptions
13
13
  } from '../utils/inputValidation';
14
14
  import { startsWith } from '../utils/lang';
@@ -46,7 +46,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
46
46
  const isNotDestroyed = validateIfNotDestroyed(log, readinessManager, methodName);
47
47
  const options = validateEvaluationOptions(log, maybeOptions, methodName);
48
48
 
49
- validateIfOperational(log, readinessManager, methodName, nameOrNames);
49
+ validateIfReadyFromCache(log, readinessManager, methodName, nameOrNames);
50
50
 
51
51
  const valid = isNotDestroyed && key && nameOrNames && attributes !== false;
52
52
 
@@ -60,7 +60,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
60
60
  }
61
61
 
62
62
  function evaluateFallBackTreatment(featureFlagName: string, withConfig: boolean): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
63
- const {treatment, config} = fallbackTreatmentsCalculator.resolve(featureFlagName, '');
63
+ const { treatment, config } = fallbackTreatmentsCalculator.resolve(featureFlagName, '');
64
64
 
65
65
  if (withConfig) {
66
66
  return {
@@ -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 { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types';
6
6
  import { ISdkReadinessManager } from '../readiness/types';
7
7
  import { ISplit } from '../dtos/types';
@@ -66,7 +66,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
66
66
  */
67
67
  split(featureFlagName: string) {
68
68
  const splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
69
- if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
69
+ if (!validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
70
70
  return isAsync ? Promise.resolve(null) : null;
71
71
  }
72
72
 
@@ -87,7 +87,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
87
87
  * Get the feature flag objects present on the factory storage
88
88
  */
89
89
  splits() {
90
- if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
90
+ if (!validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
91
91
  return isAsync ? Promise.resolve([]) : [];
92
92
  }
93
93
  const currentSplits = splits.getAll();
@@ -100,7 +100,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
100
100
  * Get the feature flag names present on the factory storage
101
101
  */
102
102
  names() {
103
- if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
103
+ if (!validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
104
104
  return isAsync ? Promise.resolve([]) : [];
105
105
  }
106
106
  const splitNames = splits.getSplitNames();
@@ -25,7 +25,7 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
25
25
  const code = error.parsedData.code;
26
26
  telemetryTracker.streamingEvent(ABLY_ERROR, code);
27
27
 
28
- // 401 errors due to invalid or expired token (e.g., if refresh token coudn't be executed)
28
+ // 401 errors due to invalid or expired token (e.g., if refresh token couldn't be executed)
29
29
  if (40140 <= code && code <= 40149) return true;
30
30
  // Others 4XX errors (e.g., bad request from the SDK)
31
31
  if (40000 <= code && code <= 49999) return false;
@@ -119,7 +119,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
119
119
  if (!telemetry || !now) return; // No submitter created if telemetry cache is not defined
120
120
 
121
121
  const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, splitApi, readiness, sdkReadinessManager } = params;
122
- const startTime = timer(now);
122
+ const stopTimer = timer(now);
123
123
 
124
124
  const submitter = firstPushWindowDecorator(
125
125
  submitterFactory(
@@ -131,12 +131,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
131
131
  );
132
132
 
133
133
  readiness.gate.once(SDK_READY_FROM_CACHE, () => {
134
- telemetry.recordTimeUntilReadyFromCache(startTime());
134
+ telemetry.recordTimeUntilReadyFromCache(stopTimer());
135
135
  });
136
136
 
137
137
  sdkReadinessManager.incInternalReadyCbCount();
138
138
  readiness.gate.once(SDK_READY, () => {
139
- telemetry.recordTimeUntilReady(startTime());
139
+ telemetry.recordTimeUntilReady(stopTimer());
140
140
 
141
141
  // Post config data when the SDK is ready and if the telemetry submitter was started
142
142
  if (submitter.isRunning()) {
@@ -11,11 +11,11 @@ export function telemetryTrackerFactory(
11
11
  ): ITelemetryTracker {
12
12
 
13
13
  if (telemetryCache && now) {
14
- const startTime = timer(now);
14
+ const sessionTimer = timer(now);
15
15
 
16
16
  return {
17
17
  trackEval(method) {
18
- const evalTime = timer(now);
18
+ const evalTimer = timer(now);
19
19
 
20
20
  return (label) => {
21
21
  switch (label) {
@@ -25,20 +25,20 @@ export function telemetryTrackerFactory(
25
25
  case SDK_NOT_READY: // @ts-ignore ITelemetryCacheAsync doesn't implement the method
26
26
  if (telemetryCache.recordNonReadyUsage) telemetryCache.recordNonReadyUsage();
27
27
  }
28
- telemetryCache.recordLatency(method, evalTime());
28
+ telemetryCache.recordLatency(method, evalTimer());
29
29
  };
30
30
  },
31
31
  trackHttp(operation) {
32
- const httpTime = timer(now);
32
+ const httpTimer = timer(now);
33
33
 
34
34
  return (error) => {
35
- (telemetryCache as ITelemetryCacheSync).recordHttpLatency(operation, httpTime());
35
+ (telemetryCache as ITelemetryCacheSync).recordHttpLatency(operation, httpTimer());
36
36
  if (error && error.statusCode) (telemetryCache as ITelemetryCacheSync).recordHttpError(operation, error.statusCode);
37
37
  else (telemetryCache as ITelemetryCacheSync).recordSuccessfulSync(operation, Date.now());
38
38
  };
39
39
  },
40
40
  sessionLength() { // @ts-ignore ITelemetryCacheAsync doesn't implement the method
41
- if (telemetryCache.recordSessionLength) telemetryCache.recordSessionLength(startTime());
41
+ if (telemetryCache.recordSessionLength) telemetryCache.recordSessionLength(sessionTimer());
42
42
  },
43
43
  streamingEvent(e, d) {
44
44
  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,4 +1,4 @@
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
  import { ILogger } from '../../logger/types';
3
3
  import { IReadinessManager } from '../../readiness/types';
4
4
 
@@ -9,9 +9,14 @@ export function validateIfNotDestroyed(log: ILogger, readinessManager: IReadines
9
9
  return false;
10
10
  }
11
11
 
12
- export function validateIfOperational(log: ILogger, readinessManager: IReadinessManager, method: string, featureFlagNameOrNames?: string | string[] | false) {
13
- if (readinessManager.isReady() || readinessManager.isReadyFromCache()) return true;
12
+ export function validateIfReadyFromCache(log: ILogger, readinessManager: IReadinessManager, method: string, featureFlagNameOrNames?: string | string[] | false) {
13
+ if (readinessManager.isReadyFromCache()) return true;
14
14
 
15
- log.warn(CLIENT_NOT_READY, [method, featureFlagNameOrNames ? ` for feature flag ${featureFlagNameOrNames.toString()}` : '']);
15
+ log.warn(CLIENT_NOT_READY_FROM_CACHE, [method, featureFlagNameOrNames ? ` for feature flag ${featureFlagNameOrNames.toString()}` : '']);
16
16
  return false;
17
17
  }
18
+
19
+ // Operational means that the SDK is ready to evaluate (not destroyed and ready from cache)
20
+ export function validateIfOperational(log: ILogger, readinessManager: IReadinessManager, method: string, featureFlagNameOrNames?: string | string[] | false) {
21
+ return validateIfNotDestroyed(log, readinessManager, method) && validateIfReadyFromCache(log, readinessManager, method, featureFlagNameOrNames);
22
+ }
@@ -5,11 +5,11 @@ import { WARN_NOT_EXISTENT_SPLIT } from '../../logger/constants';
5
5
 
6
6
  /**
7
7
  * 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.
8
- * 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.
8
+ * 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.
9
9
  */
10
10
  export function validateSplitExistence(log: ILogger, readinessManager: IReadinessManager, splitName: string, labelOrSplitObj: any, method: string): boolean {
11
- if (readinessManager.isReady()) { // Only if it's ready we validate this, otherwise it may just be that the SDK is not ready yet.
12
- if (labelOrSplitObj === SPLIT_NOT_FOUND || labelOrSplitObj == null || labelOrSplitObj === FALLBACK_SPLIT_NOT_FOUND) {
11
+ 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
12
+ if (labelOrSplitObj === SPLIT_NOT_FOUND || labelOrSplitObj === FALLBACK_SPLIT_NOT_FOUND || labelOrSplitObj == null) {
13
13
  log.warn(WARN_NOT_EXISTENT_SPLIT, [method, splitName]);
14
14
  return false;
15
15
  }
@@ -93,6 +93,7 @@ interface ISharedSettings {
93
93
  urls?: SplitIO.UrlSettings;
94
94
  /**
95
95
  * Custom logger object. If not provided, the SDK will use the default `console.log` method for all log levels.
96
+ * Set together with `debug` option to `true` or a log level string to enable logging.
96
97
  */
97
98
  logger?: SplitIO.Logger;
98
99
  }
@@ -145,8 +146,6 @@ interface IPluggableSharedSettings {
145
146
  * config.debug = ErrorLogger()
146
147
  * ```
147
148
  *
148
- * When combined with the `logger` option, any log level other than `NONE` (false) will be set to `DEBUG` (true), delegating log level control to the custom logger.
149
- *
150
149
  * @defaultValue `false`
151
150
  */
152
151
  debug?: boolean | SplitIO.LogLevel | SplitIO.ILogger;
@@ -170,8 +169,6 @@ interface INonPluggableSharedSettings {
170
169
  * config.debug = 'WARN'
171
170
  * ```
172
171
  *
173
- * When combined with the `logger` option, any log level other than `NONE` (false) will be set to `DEBUG` (true), delegating log level control to the custom logger.
174
- *
175
172
  * @defaultValue `false`
176
173
  */
177
174
  debug?: boolean | SplitIO.LogLevel;
@@ -528,19 +525,19 @@ declare namespace SplitIO {
528
525
  */
529
526
  type EventConsts = {
530
527
  /**
531
- * The ready event.
528
+ * The ready event emitted once the SDK is ready to evaluate feature flags with cache synchronized with the backend.
532
529
  */
533
530
  SDK_READY: 'init::ready';
534
531
  /**
535
- * The ready event when fired with cached data.
532
+ * The ready event emitted once the SDK is ready to evaluate feature flags with cache that could be stale. Use SDK_READY if you want to be sure the cache is in sync with the backend.
536
533
  */
537
534
  SDK_READY_FROM_CACHE: 'init::cache-ready';
538
535
  /**
539
- * The timeout event.
536
+ * The timeout event emitted after `startup.readyTimeout` seconds if the SDK_READY event was not emitted.
540
537
  */
541
538
  SDK_READY_TIMED_OUT: 'init::timeout';
542
539
  /**
543
- * The update event.
540
+ * The update event emitted when the SDK cache is updated with new data from the backend.
544
541
  */
545
542
  SDK_UPDATE: 'state::update';
546
543
  };
@@ -707,7 +704,7 @@ declare namespace SplitIO {
707
704
  */
708
705
  Event: EventConsts;
709
706
  /**
710
- * Returns a promise that resolves once the SDK has finished loading (`SDK_READY` event emitted) or rejected if the SDK has timedout (`SDK_READY_TIMED_OUT` event emitted).
707
+ * Returns a promise that resolves when the SDK has finished initial synchronization with the backend (`SDK_READY` event emitted), or rejected if the SDK has timedout (`SDK_READY_TIMED_OUT` event emitted).
711
708
  * As it's meant to provide similar flexibility to the event approach, given that the SDK might be eventually ready after a timeout event, the `ready` method will return a resolved promise once the SDK is ready.
712
709
  *
713
710
  * Caveats: the method was designed to avoid an unhandled Promise rejection if the rejection case is not handled, so that `onRejected` handler is optional when using promises.
@@ -722,8 +719,26 @@ declare namespace SplitIO {
722
719
  * ```
723
720
  *
724
721
  * @returns A promise that resolves once the SDK is ready or rejects if the SDK has timedout.
722
+ * @deprecated Use `whenReady` instead.
725
723
  */
726
724
  ready(): Promise<void>;
725
+ /**
726
+ * Returns a promise that resolves when the SDK has finished initial synchronization with the backend (`SDK_READY` event emitted), or rejected if the SDK has timedout (`SDK_READY_TIMED_OUT` event emitted).
727
+ * As it's meant to provide similar flexibility than event listeners, given that the SDK might be ready after a timeout event, the `whenReady` method will return a resolved promise once the SDK is ready.
728
+ * You must handle the promise rejection to avoid an unhandled promise rejection error, or set the `startup.readyTimeout` configuration option to 0 to avoid the timeout and thus the rejection.
729
+ *
730
+ * @returns A promise that resolves once the SDK_READY event is emitted or rejects if the SDK has timedout.
731
+ */
732
+ whenReady(): Promise<void>;
733
+ /**
734
+ * Returns a promise that resolves when the SDK is ready for evaluations using cached data, which might not yet be synchronized with the backend (`SDK_READY_FROM_CACHE` event emitted), or rejected if the SDK has timedout (`SDK_READY_TIMED_OUT` event emitted).
735
+ * As it's meant to provide similar flexibility than event listeners, given that the SDK might be ready from cache after a timeout event, the `whenReadyFromCache` method will return a resolved promise once the SDK is ready from cache.
736
+ * You must handle the promise rejection to avoid an unhandled promise rejection error, or set the `startup.readyTimeout` configuration option to 0 to avoid the timeout and thus the rejection.
737
+ *
738
+ * @returns A promise that resolves once the SDK_READY_FROM_CACHE event is emitted or rejects if the SDK has timedout. The promise resolves with a boolean value that
739
+ * indicates whether the SDK_READY_FROM_CACHE event was emitted together with the SDK_READY event (i.e., the SDK is ready and synchronized with the backend) or not.
740
+ */
741
+ whenReadyFromCache(): Promise<boolean>;
727
742
  }
728
743
  /**
729
744
  * Common definitions between clients for different environments interface.
@@ -1666,7 +1681,7 @@ declare namespace SplitIO {
1666
1681
  * Wait for the SDK client to be ready before calling this method.
1667
1682
  *
1668
1683
  * ```js
1669
- * await factory.client().ready();
1684
+ * await factory.client().whenReady();
1670
1685
  * const rolloutPlan = factory.getRolloutPlan();
1671
1686
  * ```
1672
1687
  *