@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
@@ -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
 
@@ -69,6 +72,17 @@ export function sdkReadinessManagerFactory(
69
72
  return promise;
70
73
  }
71
74
 
75
+ function getStatus() {
76
+ return {
77
+ isReady: readinessManager.isReady(),
78
+ isReadyFromCache: readinessManager.isReadyFromCache(),
79
+ isTimedout: readinessManager.isTimedout(),
80
+ hasTimedout: readinessManager.hasTimedout(),
81
+ isDestroyed: readinessManager.isDestroyed(),
82
+ isOperational: readinessManager.isOperational(),
83
+ lastUpdate: readinessManager.lastUpdate(),
84
+ };
85
+ }
72
86
 
73
87
  return {
74
88
  readinessManager,
@@ -93,6 +107,7 @@ export function sdkReadinessManagerFactory(
93
107
  SDK_READY_TIMED_OUT,
94
108
  },
95
109
 
110
+ // @TODO: remove in next major
96
111
  ready() {
97
112
  if (readinessManager.hasTimedout()) {
98
113
  if (!readinessManager.isReady()) {
@@ -104,17 +119,35 @@ export function sdkReadinessManagerFactory(
104
119
  return readyPromise;
105
120
  },
106
121
 
107
- __getStatus() {
108
- return {
109
- isReady: readinessManager.isReady(),
110
- isReadyFromCache: readinessManager.isReadyFromCache(),
111
- isTimedout: readinessManager.isTimedout(),
112
- hasTimedout: readinessManager.hasTimedout(),
113
- isDestroyed: readinessManager.isDestroyed(),
114
- isOperational: readinessManager.isOperational(),
115
- lastUpdate: readinessManager.lastUpdate(),
116
- };
122
+ whenReady() {
123
+ return new Promise<void>((resolve, reject) => {
124
+ if (readinessManager.isReady()) {
125
+ resolve();
126
+ } else if (readinessManager.hasTimedout()) {
127
+ reject(TIMEOUT_ERROR);
128
+ } else {
129
+ readinessManager.gate.once(SDK_READY, resolve);
130
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, () => reject(TIMEOUT_ERROR));
131
+ }
132
+ });
133
+ },
134
+
135
+ whenReadyFromCache() {
136
+ return new Promise<boolean>((resolve, reject) => {
137
+ if (readinessManager.isReadyFromCache()) {
138
+ resolve(readinessManager.isReady());
139
+ } else if (readinessManager.hasTimedout()) {
140
+ reject(TIMEOUT_ERROR);
141
+ } else {
142
+ readinessManager.gate.once(SDK_READY_FROM_CACHE, () => resolve(readinessManager.isReady()));
143
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, () => reject(TIMEOUT_ERROR));
144
+ }
145
+ });
117
146
  },
147
+
148
+ getStatus,
149
+ // @TODO: remove in next major
150
+ __getStatus: getStatus
118
151
  }
119
152
  )
120
153
  };
@@ -1,4 +1,3 @@
1
- import { IStatusInterface } from '../types';
2
1
  import SplitIO from '../../types/splitio';
3
2
 
4
3
  /** Splits data emitter */
@@ -72,7 +71,7 @@ export interface IReadinessManager {
72
71
 
73
72
  export interface ISdkReadinessManager {
74
73
  readinessManager: IReadinessManager
75
- sdkStatus: IStatusInterface
74
+ sdkStatus: SplitIO.IStatusInterface
76
75
 
77
76
  /**
78
77
  * Increment internalReadyCbCount, an offset value of SDK_READY listeners that are added/removed internally
@@ -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();
@@ -1,5 +1,6 @@
1
1
  import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies, StreamingEvent, Method, OperationType, MethodExceptions, MethodLatencies, TelemetryUsageStatsPayload, UpdatesFromSSEEnum, UpdatesFromSSE } from '../../sync/submitters/types';
2
2
  import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
3
+ import { checkIfServerSide } from '../../utils/key';
3
4
  import { findLatencyIndex } from '../findLatencyIndex';
4
5
  import { ISegmentsCacheSync, ISplitsCacheSync, IStorageFactoryParams, ITelemetryCacheSync } from '../types';
5
6
 
@@ -20,7 +21,7 @@ const ACCEPTANCE_RANGE = 0.001;
20
21
  * All factory instances track telemetry on server-side, and 0.1% on client-side.
21
22
  */
22
23
  export function shouldRecordTelemetry({ settings }: IStorageFactoryParams) {
23
- return settings.mode !== LOCALHOST_MODE && (settings.core.key === undefined || Math.random() <= ACCEPTANCE_RANGE);
24
+ return settings.mode !== LOCALHOST_MODE && (checkIfServerSide(settings) || Math.random() <= ACCEPTANCE_RANGE);
24
25
  }
25
26
 
26
27
  export class TelemetryCacheInMemory implements ITelemetryCacheSync {
@@ -21,6 +21,7 @@ import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS
21
21
  import { metadataBuilder } from '../utils';
22
22
  import { LOG_PREFIX } from '../pluggable/constants';
23
23
  import { RBSegmentsCachePluggable } from './RBSegmentsCachePluggable';
24
+ import { checkIfServerSide } from '../../utils/key';
24
25
 
25
26
  const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
26
27
  const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
@@ -83,7 +84,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
83
84
  new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper);
84
85
 
85
86
  const uniqueKeysCache = isPartialConsumer ?
86
- settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
87
+ checkIfServerSide(settings) ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
87
88
  new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper);
88
89
 
89
90
  // Connects to wrapper and emits SDK_READY event on main client
@@ -6,6 +6,7 @@ import { FLAG_SPEC_VERSION } from '../../../utils/constants';
6
6
  import { base } from '../../../utils/settingsValidation';
7
7
  import { ISplitChangesFetcher } from './types';
8
8
  import { LOG_PREFIX_SYNC_SPLITS } from '../../../logger/constants';
9
+ import { checkIfServerSide } from '../../../utils/key';
9
10
 
10
11
  const PROXY_CHECK_INTERVAL_MILLIS_CS = 60 * 60 * 1000; // 1 hour in Client Side
11
12
  const PROXY_CHECK_INTERVAL_MILLIS_SS = 24 * PROXY_CHECK_INTERVAL_MILLIS_CS; // 24 hours in Server Side
@@ -22,7 +23,7 @@ function sdkEndpointOverridden(settings: ISettings) {
22
23
  export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges, settings: ISettings, storage: Pick<IStorageBase, 'splits' | 'rbSegments'>): ISplitChangesFetcher {
23
24
 
24
25
  const log = settings.log;
25
- const PROXY_CHECK_INTERVAL_MILLIS = settings.core.key !== undefined ? PROXY_CHECK_INTERVAL_MILLIS_CS : PROXY_CHECK_INTERVAL_MILLIS_SS;
26
+ const PROXY_CHECK_INTERVAL_MILLIS = checkIfServerSide(settings) ? PROXY_CHECK_INTERVAL_MILLIS_SS : PROXY_CHECK_INTERVAL_MILLIS_CS;
26
27
  let lastProxyCheckTimestamp: number | undefined;
27
28
 
28
29
  return function splitChangesFetcher(
@@ -2,6 +2,7 @@ import { IPlatform } from '../../../sdkFactory/types';
2
2
  import { decorateHeaders } from '../../../services/decorateHeaders';
3
3
  import { IEventSourceConstructor } from '../../../services/types';
4
4
  import { ISettings } from '../../../types';
5
+ import { checkIfServerSide } from '../../../utils/key';
5
6
  import { isString } from '../../../utils/lang';
6
7
  import { objectAssign } from '../../../utils/lang/objectAssign';
7
8
  import { IAuthTokenPushEnabled } from '../AuthClient/types';
@@ -73,7 +74,7 @@ export class SSEClient implements ISSEClient {
73
74
  return encodeURIComponent(params + channel);
74
75
  }).join(',');
75
76
  const url = `${this.settings.urls.streaming}/sse?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${ABLY_API_VERSION}&heartbeats=true`; // same results using `&heartbeats=false`
76
- const isServerSide = !this.settings.core.key;
77
+ const isServerSide = checkIfServerSide(this.settings);
77
78
 
78
79
  this.connection = new this.eventSource!(
79
80
  // 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
@@ -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;
@@ -10,7 +10,7 @@ import { SplitsUpdateWorker } from './UpdateWorkers/SplitsUpdateWorker';
10
10
  import { authenticateFactory, hashUserKey } from './AuthClient';
11
11
  import { forOwn } from '../../utils/lang';
12
12
  import { SSEClient } from './SSEClient';
13
- import { getMatching } from '../../utils/key';
13
+ import { checkIfServerSide, getMatching } from '../../utils/key';
14
14
  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';
15
15
  import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE } from '../../logger/constants';
16
16
  import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types';
@@ -34,7 +34,7 @@ export function pushManagerFactory(
34
34
 
35
35
  // `userKey` is the matching key of main client in client-side SDK.
36
36
  // It can be used to check if running on client-side or server-side SDK.
37
- const userKey = settings.core.key ? getMatching(settings.core.key) : undefined;
37
+ const userKey = checkIfServerSide(settings) ? undefined : getMatching(settings.core.key);
38
38
  const log = settings.log;
39
39
 
40
40
  let sseClient: ISSEClient;
@@ -11,6 +11,7 @@ import { timer } from '../../utils/timeTracker/timer';
11
11
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
12
12
  import { objectAssign } from '../../utils/lang/objectAssign';
13
13
  import { ISplitFiltersValidation } from '../../dtos/types';
14
+ import { checkIfServerSide } from '../../utils/key';
14
15
 
15
16
  const OPERATION_MODE_MAP = {
16
17
  [STANDALONE_MODE]: STANDALONE_ENUM,
@@ -72,7 +73,7 @@ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, sett
72
73
 
73
74
  pop(): TelemetryConfigStatsPayload {
74
75
  const { urls, scheduler } = settings;
75
- const isClientSide = settings.core.key !== undefined;
76
+ const isServerSide = checkIfServerSide(settings);
76
77
 
77
78
  const { flagSetsTotal, flagSetsIgnored } = getTelemetryFlagSetsStats(settings.sync.__splitFiltersValidation);
78
79
 
@@ -80,8 +81,8 @@ export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, sett
80
81
  sE: settings.streamingEnabled,
81
82
  rR: {
82
83
  sp: scheduler.featuresRefreshRate / 1000,
83
- se: isClientSide ? undefined : scheduler.segmentsRefreshRate / 1000,
84
- ms: isClientSide ? scheduler.segmentsRefreshRate / 1000 : undefined,
84
+ se: isServerSide ? scheduler.segmentsRefreshRate / 1000 : undefined,
85
+ ms: isServerSide ? undefined : scheduler.segmentsRefreshRate / 1000,
85
86
  im: scheduler.impressionsRefreshRate / 1000,
86
87
  ev: scheduler.eventsPushRate / 1000,
87
88
  te: scheduler.telemetryRefreshRate / 1000,
@@ -119,7 +120,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
119
120
  if (!telemetry || !now) return; // No submitter created if telemetry cache is not defined
120
121
 
121
122
  const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, splitApi, readiness, sdkReadinessManager } = params;
122
- const startTime = timer(now);
123
+ const stopTimer = timer(now);
123
124
 
124
125
  const submitter = firstPushWindowDecorator(
125
126
  submitterFactory(
@@ -131,12 +132,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
131
132
  );
132
133
 
133
134
  readiness.gate.once(SDK_READY_FROM_CACHE, () => {
134
- telemetry.recordTimeUntilReadyFromCache(startTime());
135
+ telemetry.recordTimeUntilReadyFromCache(stopTimer());
135
136
  });
136
137
 
137
138
  sdkReadinessManager.incInternalReadyCbCount();
138
139
  readiness.gate.once(SDK_READY, () => {
139
- telemetry.recordTimeUntilReady(startTime());
140
+ telemetry.recordTimeUntilReady(stopTimer());
140
141
 
141
142
  // Post config data when the SDK is ready and if the telemetry submitter was started
142
143
  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) {
package/src/types.ts CHANGED
@@ -14,21 +14,6 @@ export interface ISettings extends SplitIO.ISettings {
14
14
  readonly initialRolloutPlan?: RolloutPlan;
15
15
  }
16
16
 
17
- /**
18
- * SplitIO.IStatusInterface interface extended with private properties for internal use
19
- */
20
- export interface IStatusInterface extends SplitIO.IStatusInterface {
21
- // Expose status for internal purposes only. Not considered part of the public API, and might be updated eventually.
22
- __getStatus(): {
23
- isReady: boolean;
24
- isReadyFromCache: boolean;
25
- isTimedout: boolean;
26
- hasTimedout: boolean;
27
- isDestroyed: boolean;
28
- isOperational: boolean;
29
- lastUpdate: number;
30
- };
31
- }
32
17
  /**
33
18
  * SplitIO.IBasicClient interface extended with private properties for internal use
34
19
  */
@@ -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
  }
@@ -1,4 +1,5 @@
1
1
  import SplitIO from '../../../types/splitio';
2
+ import { ISettings } from '../../types';
2
3
  import { isObject } from '../lang';
3
4
 
4
5
  // function isSplitKeyObject(key: any): key is SplitIO.SplitKeyObject {
@@ -32,3 +33,7 @@ export function keyParser(key: SplitIO.SplitKey): SplitIO.SplitKeyObject {
32
33
  };
33
34
  }
34
35
  }
36
+
37
+ export function checkIfServerSide(settings: ISettings) {
38
+ return !settings.core.key;
39
+ }
@@ -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
  };
@@ -698,6 +695,52 @@ declare namespace SplitIO {
698
695
  [status in ConsentStatus]: ConsentStatus;
699
696
  };
700
697
  }
698
+ /**
699
+ * Readiness Status interface. It represents the readiness state of an SDK client.
700
+ */
701
+ interface ReadinessStatus {
702
+
703
+ /**
704
+ * `isReady` indicates if the client has triggered an `SDK_READY` event and
705
+ * thus is ready to evaluate with cached data synchronized with the backend.
706
+ */
707
+ isReady: boolean;
708
+
709
+ /**
710
+ * `isReadyFromCache` indicates if the client has triggered an `SDK_READY_FROM_CACHE` event and
711
+ * thus is ready to evaluate with cached data, although the data in cache might be stale, not synchronized with the backend.
712
+ */
713
+ isReadyFromCache: boolean;
714
+
715
+ /**
716
+ * `isTimedout` indicates if the client has triggered an `SDK_READY_TIMED_OUT` event and is not ready to evaluate.
717
+ * In other words, `isTimedout` is equivalent to `hasTimedout && !isReady`.
718
+ */
719
+ isTimedout: boolean;
720
+
721
+ /**
722
+ * `hasTimedout` indicates if the client has ever triggered an `SDK_READY_TIMED_OUT` event.
723
+ * It's meant to keep a reference that the SDK emitted a timeout at some point, not the current state.
724
+ */
725
+ hasTimedout: boolean;
726
+
727
+ /**
728
+ * `isDestroyed` indicates if the client has been destroyed, i.e., `destroy` method has been called.
729
+ */
730
+ isDestroyed: boolean;
731
+
732
+ /**
733
+ * `isOperational` indicates if the client can evaluate feature flags.
734
+ * In this state, `getTreatment` calls will not return `CONTROL` due to the SDK being unready or destroyed.
735
+ * It's equivalent to `isReadyFromCache && !isDestroyed`.
736
+ */
737
+ isOperational: boolean;
738
+
739
+ /**
740
+ * `lastUpdate` indicates the timestamp of the most recent status event.
741
+ */
742
+ lastUpdate: number;
743
+ }
701
744
  /**
702
745
  * Common API for entities that expose status handlers.
703
746
  */
@@ -707,7 +750,13 @@ declare namespace SplitIO {
707
750
  */
708
751
  Event: EventConsts;
709
752
  /**
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).
753
+ * Gets the readiness status.
754
+ *
755
+ * @returns The current readiness status.
756
+ */
757
+ getStatus(): ReadinessStatus;
758
+ /**
759
+ * 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
760
  * 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
761
  *
713
762
  * 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 +771,26 @@ declare namespace SplitIO {
722
771
  * ```
723
772
  *
724
773
  * @returns A promise that resolves once the SDK is ready or rejects if the SDK has timedout.
774
+ * @deprecated Use `whenReady` instead.
725
775
  */
726
776
  ready(): Promise<void>;
777
+ /**
778
+ * 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).
779
+ * 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.
780
+ * 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.
781
+ *
782
+ * @returns A promise that resolves once the SDK_READY event is emitted or rejects if the SDK has timedout.
783
+ */
784
+ whenReady(): Promise<void>;
785
+ /**
786
+ * 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).
787
+ * 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.
788
+ * 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.
789
+ *
790
+ * @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
791
+ * 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.
792
+ */
793
+ whenReadyFromCache(): Promise<boolean>;
727
794
  }
728
795
  /**
729
796
  * Common definitions between clients for different environments interface.
@@ -1666,7 +1733,7 @@ declare namespace SplitIO {
1666
1733
  * Wait for the SDK client to be ready before calling this method.
1667
1734
  *
1668
1735
  * ```js
1669
- * await factory.client().ready();
1736
+ * await factory.client().whenReady();
1670
1737
  * const rolloutPlan = factory.getRolloutPlan();
1671
1738
  * ```
1672
1739
  *
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FallbackDiscardReason = void 0;
4
- var FallbackDiscardReason;
5
- (function (FallbackDiscardReason) {
6
- FallbackDiscardReason["FlagName"] = "Invalid flag name (max 100 chars, no spaces)";
7
- FallbackDiscardReason["Treatment"] = "Invalid treatment (max 100 chars and must match pattern)";
8
- })(FallbackDiscardReason = exports.FallbackDiscardReason || (exports.FallbackDiscardReason = {}));