@splitsoftware/splitio-commons 3.0.0-rc.0 → 3.0.0-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 (189) hide show
  1. package/CHANGES.txt +8 -3
  2. package/cjs/evaluator/index.js +11 -11
  3. package/cjs/logger/constants.js +4 -4
  4. package/cjs/logger/messages/warn.js +0 -1
  5. package/cjs/readiness/constants.js +4 -4
  6. package/cjs/readiness/readinessManager.js +22 -22
  7. package/cjs/readiness/sdkReadinessManager.js +0 -16
  8. package/cjs/sdkClient/sdkLifecycle.js +2 -1
  9. package/cjs/sdkClient/trackMethod.js +2 -2
  10. package/cjs/sdkFactory/index.js +5 -5
  11. package/cjs/sdkManager/index.js +2 -2
  12. package/cjs/services/authProvider.js +67 -0
  13. package/cjs/services/secureSplitHttpClient.js +39 -0
  14. package/cjs/services/splitApi.js +15 -6
  15. package/cjs/storages/AbstractDefinitionsCacheAsync.js +51 -0
  16. package/cjs/storages/AbstractDefinitionsCacheSync.js +76 -0
  17. package/cjs/storages/KeyBuilder.js +6 -6
  18. package/cjs/storages/KeyBuilderCS.js +5 -5
  19. package/cjs/storages/KeyBuilderSS.js +2 -2
  20. package/cjs/storages/getRolloutPlan.js +3 -3
  21. package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +194 -0
  22. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +9 -9
  23. package/cjs/storages/inLocalStorage/index.js +6 -6
  24. package/cjs/storages/inLocalStorage/validateCache.js +3 -3
  25. package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +121 -0
  26. package/cjs/storages/inMemory/InMemoryStorage.js +4 -4
  27. package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -5
  28. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -4
  29. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
  30. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +3 -3
  31. package/cjs/storages/inRedis/{SplitsCacheInRedis.js → DefinitionsCacheInRedis.js} +77 -77
  32. package/cjs/storages/inRedis/index.js +2 -2
  33. package/cjs/storages/pluggable/DefinitionsCachePluggable.js +212 -0
  34. package/cjs/storages/pluggable/index.js +2 -2
  35. package/cjs/storages/setRolloutPlan.js +5 -5
  36. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +5 -5
  37. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +1 -1
  38. package/cjs/sync/polling/pollingManagerCS.js +7 -7
  39. package/cjs/sync/polling/pollingManagerSS.js +3 -3
  40. package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +1 -1
  41. package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +2 -3
  42. package/cjs/sync/polling/updaters/definitionChangesUpdater.js +8 -8
  43. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  44. package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +1 -1
  45. package/cjs/sync/streaming/SSEHandler/index.js +1 -1
  46. package/cjs/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +15 -15
  47. package/cjs/sync/streaming/constants.js +3 -3
  48. package/cjs/sync/streaming/pushManager.js +13 -13
  49. package/cjs/sync/submitters/telemetrySubmitter.js +1 -2
  50. package/cjs/sync/syncManagerOnline.js +10 -8
  51. package/cjs/utils/Backoff.js +20 -0
  52. package/cjs/utils/lang/sets.js +4 -4
  53. package/cjs/utils/settingsValidation/index.js +2 -0
  54. package/cjs/utils/settingsValidation/url.js +6 -2
  55. package/esm/evaluator/index.js +11 -11
  56. package/esm/logger/constants.js +1 -1
  57. package/esm/logger/messages/warn.js +0 -1
  58. package/esm/readiness/constants.js +3 -3
  59. package/esm/readiness/readinessManager.js +23 -23
  60. package/esm/readiness/sdkReadinessManager.js +1 -17
  61. package/esm/sdkClient/sdkLifecycle.js +2 -1
  62. package/esm/sdkClient/trackMethod.js +2 -2
  63. package/esm/sdkFactory/index.js +6 -6
  64. package/esm/sdkManager/index.js +2 -2
  65. package/esm/services/authProvider.js +63 -0
  66. package/esm/services/secureSplitHttpClient.js +35 -0
  67. package/esm/services/splitApi.js +15 -6
  68. package/esm/storages/AbstractDefinitionsCacheAsync.js +48 -0
  69. package/esm/storages/AbstractDefinitionsCacheSync.js +71 -0
  70. package/esm/storages/KeyBuilder.js +6 -6
  71. package/esm/storages/KeyBuilderCS.js +5 -5
  72. package/esm/storages/KeyBuilderSS.js +2 -2
  73. package/esm/storages/getRolloutPlan.js +3 -3
  74. package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +191 -0
  75. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +6 -6
  76. package/esm/storages/inLocalStorage/index.js +6 -6
  77. package/esm/storages/inLocalStorage/validateCache.js +3 -3
  78. package/esm/storages/inMemory/DefinitionsCacheInMemory.js +118 -0
  79. package/esm/storages/inMemory/InMemoryStorage.js +4 -4
  80. package/esm/storages/inMemory/InMemoryStorageCS.js +5 -5
  81. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +1 -1
  82. package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
  83. package/esm/storages/inMemory/TelemetryCacheInMemory.js +3 -3
  84. package/esm/storages/inRedis/{SplitsCacheInRedis.js → DefinitionsCacheInRedis.js} +76 -76
  85. package/esm/storages/inRedis/index.js +2 -2
  86. package/esm/storages/pluggable/DefinitionsCachePluggable.js +209 -0
  87. package/esm/storages/pluggable/index.js +2 -2
  88. package/esm/storages/setRolloutPlan.js +5 -5
  89. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
  90. package/esm/sync/polling/fetchers/splitChangesFetcher.js +1 -1
  91. package/esm/sync/polling/pollingManagerCS.js +4 -4
  92. package/esm/sync/polling/pollingManagerSS.js +3 -3
  93. package/esm/sync/polling/syncTasks/definitionsSyncTask.js +1 -1
  94. package/esm/sync/polling/syncTasks/segmentsSyncTask.js +2 -3
  95. package/esm/sync/polling/updaters/definitionChangesUpdater.js +9 -9
  96. package/esm/sync/polling/updaters/mySegmentsUpdater.js +1 -1
  97. package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +2 -2
  98. package/esm/sync/streaming/SSEHandler/index.js +2 -2
  99. package/esm/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +14 -14
  100. package/esm/sync/streaming/constants.js +2 -2
  101. package/esm/sync/streaming/pushManager.js +14 -14
  102. package/esm/sync/submitters/telemetrySubmitter.js +1 -2
  103. package/esm/sync/syncManagerOnline.js +9 -7
  104. package/esm/utils/Backoff.js +20 -0
  105. package/esm/utils/lang/sets.js +4 -4
  106. package/esm/utils/settingsValidation/index.js +2 -0
  107. package/esm/utils/settingsValidation/url.js +6 -2
  108. package/package.json +1 -1
  109. package/src/dtos/types.ts +2 -2
  110. package/src/evaluator/index.ts +11 -11
  111. package/src/logger/constants.ts +1 -1
  112. package/src/logger/messages/warn.ts +0 -1
  113. package/src/readiness/constants.ts +3 -3
  114. package/src/readiness/readinessManager.ts +22 -22
  115. package/src/readiness/sdkReadinessManager.ts +1 -16
  116. package/src/readiness/types.ts +10 -16
  117. package/src/sdkClient/sdkLifecycle.ts +2 -1
  118. package/src/sdkClient/trackMethod.ts +2 -2
  119. package/src/sdkFactory/index.ts +5 -5
  120. package/src/sdkManager/index.ts +6 -6
  121. package/src/services/authProvider.ts +86 -0
  122. package/src/services/secureSplitHttpClient.ts +44 -0
  123. package/src/services/splitApi.ts +18 -7
  124. package/src/services/types.ts +6 -1
  125. package/src/storages/AbstractDefinitionsCacheAsync.ts +63 -0
  126. package/src/storages/AbstractDefinitionsCacheSync.ts +95 -0
  127. package/src/storages/KeyBuilder.ts +6 -6
  128. package/src/storages/KeyBuilderCS.ts +6 -6
  129. package/src/storages/KeyBuilderSS.ts +2 -2
  130. package/src/storages/getRolloutPlan.ts +3 -3
  131. package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +234 -0
  132. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +6 -6
  133. package/src/storages/inLocalStorage/index.ts +6 -6
  134. package/src/storages/inLocalStorage/validateCache.ts +4 -4
  135. package/src/storages/inMemory/DefinitionsCacheInMemory.ts +130 -0
  136. package/src/storages/inMemory/InMemoryStorage.ts +4 -4
  137. package/src/storages/inMemory/InMemoryStorageCS.ts +5 -5
  138. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +1 -1
  139. package/src/storages/inMemory/SegmentsCacheInMemory.ts +1 -1
  140. package/src/storages/inMemory/TelemetryCacheInMemory.ts +3 -3
  141. package/src/storages/inRedis/{SplitsCacheInRedis.ts → DefinitionsCacheInRedis.ts} +67 -67
  142. package/src/storages/inRedis/index.ts +2 -2
  143. package/src/storages/pluggable/DefinitionsCachePluggable.ts +230 -0
  144. package/src/storages/pluggable/index.ts +2 -2
  145. package/src/storages/setRolloutPlan.ts +7 -7
  146. package/src/storages/types.ts +19 -19
  147. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +8 -8
  148. package/src/sync/polling/fetchers/splitChangesFetcher.ts +3 -3
  149. package/src/sync/polling/pollingManagerCS.ts +4 -4
  150. package/src/sync/polling/pollingManagerSS.ts +5 -4
  151. package/src/sync/polling/syncTasks/definitionsSyncTask.ts +1 -1
  152. package/src/sync/polling/syncTasks/segmentsSyncTask.ts +3 -4
  153. package/src/sync/polling/types.ts +1 -1
  154. package/src/sync/polling/updaters/definitionChangesUpdater.ts +11 -11
  155. package/src/sync/polling/updaters/mySegmentsUpdater.ts +1 -1
  156. package/src/sync/streaming/AuthClient/index.ts +2 -2
  157. package/src/sync/streaming/AuthClient/types.ts +14 -11
  158. package/src/sync/streaming/SSEClient/index.ts +2 -2
  159. package/src/sync/streaming/SSEClient/types.ts +2 -2
  160. package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +2 -2
  161. package/src/sync/streaming/SSEHandler/index.ts +2 -2
  162. package/src/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.ts → DefinitionsUpdateWorker.ts} +16 -16
  163. package/src/sync/streaming/constants.ts +2 -2
  164. package/src/sync/streaming/pushManager.ts +16 -16
  165. package/src/sync/streaming/types.ts +2 -2
  166. package/src/sync/submitters/telemetrySubmitter.ts +1 -2
  167. package/src/sync/syncManagerOnline.ts +10 -8
  168. package/src/utils/Backoff.ts +21 -0
  169. package/src/utils/inputValidation/trafficTypeExistence.ts +2 -2
  170. package/src/utils/jwt/types.ts +1 -1
  171. package/src/utils/lang/sets.ts +4 -2
  172. package/src/utils/settingsValidation/index.ts +2 -0
  173. package/src/utils/settingsValidation/url.ts +6 -2
  174. package/types/splitio.d.ts +27 -8
  175. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -51
  176. package/cjs/storages/AbstractSplitsCacheSync.js +0 -76
  177. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -194
  178. package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -121
  179. package/cjs/storages/pluggable/SplitsCachePluggable.js +0 -212
  180. package/esm/storages/AbstractSplitsCacheAsync.js +0 -48
  181. package/esm/storages/AbstractSplitsCacheSync.js +0 -71
  182. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -191
  183. package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -118
  184. package/esm/storages/pluggable/SplitsCachePluggable.js +0 -209
  185. package/src/storages/AbstractSplitsCacheAsync.ts +0 -63
  186. package/src/storages/AbstractSplitsCacheSync.ts +0 -95
  187. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -234
  188. package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -130
  189. package/src/storages/pluggable/SplitsCachePluggable.ts +0 -230
@@ -15,7 +15,6 @@ export const codesWarn: [number, string][] = codesError.concat([
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
17
  [c.CLIENT_NOT_READY_FROM_CACHE, '%s: the SDK is not ready to evaluate. Results may be incorrect. 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
18
  // input validation
20
19
  [c.WARN_SETTING_NULL, '%s: Property "%s" is of invalid type. Setting value to null.'],
21
20
  [c.WARN_TRIMMING_PROPERTIES, '%s: more than 300 properties were provided. Some of them will be trimmed when processed.'],
@@ -1,6 +1,6 @@
1
- // Splits events:
2
- export const SDK_SPLITS_ARRIVED = 'state::splits-arrived';
3
- export const SDK_SPLITS_CACHE_LOADED = 'state::splits-cache-loaded';
1
+ // Definitions events:
2
+ export const SDK_DEFINITIONS_ARRIVED = 'state::splits-arrived';
3
+ export const SDK_DEFINITIONS_CACHE_LOADED = 'state::splits-cache-loaded';
4
4
 
5
5
  // Segments events:
6
6
  export const SDK_SEGMENTS_ARRIVED = 'state::segments-arrived';
@@ -1,13 +1,13 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { ISettings } from '../types';
3
3
  import SplitIO, { SdkReadyMetadata } from '../../types/splitio';
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
- import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';
4
+ import { SDK_DEFINITIONS_ARRIVED, SDK_DEFINITIONS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
5
+ import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, IDefinitionsEventEmitter } from './types';
6
6
 
7
- function splitsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): ISplitsEventEmitter {
8
- const splitsEventEmitter = objectAssign(new EventEmitter(), {
9
- splitsArrived: false,
10
- splitsCacheLoaded: false,
7
+ function definitionsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): IDefinitionsEventEmitter {
8
+ const definitionsEventEmitter = objectAssign(new EventEmitter(), {
9
+ definitionsArrived: false,
10
+ definitionsCacheLoaded: false,
11
11
  hasInit: false,
12
12
  initCallbacks: []
13
13
  });
@@ -15,10 +15,10 @@ function splitsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter
15
15
  // `isSplitKill` condition avoids an edge-case of wrongly emitting SDK_READY if:
16
16
  // - `/memberships` fetch and SPLIT_KILL occurs before `/splitChanges` fetch, and
17
17
  // - storage has cached splits (for which case `splitsStorage.killLocally` can return true)
18
- splitsEventEmitter.on(SDK_SPLITS_ARRIVED, (metadata: SplitIO.SdkUpdateMetadata, isSplitKill: boolean) => { if (!isSplitKill) splitsEventEmitter.splitsArrived = true; });
19
- splitsEventEmitter.once(SDK_SPLITS_CACHE_LOADED, () => { splitsEventEmitter.splitsCacheLoaded = true; });
18
+ definitionsEventEmitter.on(SDK_DEFINITIONS_ARRIVED, (metadata: SplitIO.SdkUpdateMetadata, isSplitKill: boolean) => { if (!isSplitKill) definitionsEventEmitter.definitionsArrived = true; });
19
+ definitionsEventEmitter.once(SDK_DEFINITIONS_CACHE_LOADED, () => { definitionsEventEmitter.definitionsCacheLoaded = true; });
20
20
 
21
- return splitsEventEmitter;
21
+ return definitionsEventEmitter;
22
22
  }
23
23
 
24
24
  function segmentsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): ISegmentsEventEmitter {
@@ -37,7 +37,7 @@ function segmentsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitt
37
37
  export function readinessManagerFactory(
38
38
  EventEmitter: new () => SplitIO.IEventEmitter,
39
39
  settings: ISettings,
40
- splits: ISplitsEventEmitter = splitsEventEmitterFactory(EventEmitter),
40
+ definitions: IDefinitionsEventEmitter = definitionsEventEmitterFactory(EventEmitter),
41
41
  isShared?: boolean
42
42
  ): IReadinessManager {
43
43
 
@@ -59,8 +59,8 @@ export function readinessManagerFactory(
59
59
 
60
60
  // emit SDK_READY_FROM_CACHE
61
61
  let isReadyFromCache = false;
62
- if (splits.splitsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
63
- else splits.once(SDK_SPLITS_CACHE_LOADED, checkIsReadyFromCache);
62
+ if (definitions.definitionsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
63
+ else definitions.once(SDK_DEFINITIONS_CACHE_LOADED, checkIsReadyFromCache);
64
64
 
65
65
  // emit SDK_READY_TIMED_OUT
66
66
  let hasTimedout = false;
@@ -75,7 +75,7 @@ export function readinessManagerFactory(
75
75
 
76
76
  // emit SDK_READY and SDK_UPDATE
77
77
  let isReady = false;
78
- splits.on(SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate);
78
+ definitions.on(SDK_DEFINITIONS_ARRIVED, checkIsReadyOrUpdate);
79
79
  segments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
80
80
 
81
81
  let isDestroyed = false;
@@ -85,8 +85,8 @@ export function readinessManagerFactory(
85
85
  if (readyTimeout > 0 && !isReady) readyTimeoutId = setTimeout(timeout, readyTimeout);
86
86
  }
87
87
 
88
- splits.initCallbacks.push(__init);
89
- if (splits.hasInit) __init();
88
+ definitions.initCallbacks.push(__init);
89
+ if (definitions.hasInit) __init();
90
90
 
91
91
  function checkIsReadyFromCache(cacheMetadata: SdkReadyMetadata) {
92
92
  metadataReady = cacheMetadata;
@@ -114,7 +114,7 @@ export function readinessManagerFactory(
114
114
  setTimeout(() => { throw e; }, 0);
115
115
  }
116
116
  } else {
117
- if (splits.splitsArrived && segments.segmentsArrived) {
117
+ if (definitions.definitionsArrived && segments.segmentsArrived) {
118
118
  clearTimeout(readyTimeoutId);
119
119
  isReady = true;
120
120
  try {
@@ -137,12 +137,12 @@ export function readinessManagerFactory(
137
137
  }
138
138
 
139
139
  return {
140
- splits,
140
+ definitions,
141
141
  segments,
142
142
  gate,
143
143
 
144
144
  shared() {
145
- return readinessManagerFactory(EventEmitter, settings, splits, true);
145
+ return readinessManagerFactory(EventEmitter, settings, definitions, true);
146
146
  },
147
147
 
148
148
  // @TODO review/remove next methods when non-recoverable errors are reworked
@@ -153,9 +153,9 @@ export function readinessManagerFactory(
153
153
  setDestroyed() { isDestroyed = true; },
154
154
 
155
155
  init() {
156
- if (splits.hasInit) return;
157
- splits.hasInit = true;
158
- splits.initCallbacks.forEach(cb => cb());
156
+ if (definitions.hasInit) return;
157
+ definitions.hasInit = true;
158
+ definitions.initCallbacks.forEach(cb => cb());
159
159
  },
160
160
 
161
161
  destroy() {
@@ -163,7 +163,7 @@ export function readinessManagerFactory(
163
163
  syncLastUpdate();
164
164
  clearTimeout(readyTimeoutId);
165
165
 
166
- if (!isShared) splits.hasInit = false;
166
+ if (!isShared) definitions.hasInit = false;
167
167
  },
168
168
 
169
169
  isReady() { return isReady; },
@@ -5,10 +5,9 @@ import { ISdkReadinessManager } from './types';
5
5
  import { ISettings } from '../types';
6
6
  import SplitIO from '../../types/splitio';
7
7
  import { SDK_READY, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE } from './constants';
8
- import { ERROR_CLIENT_LISTENER, CLIENT_READY_FROM_CACHE, CLIENT_READY, CLIENT_NO_LISTENER } from '../logger/constants';
8
+ import { ERROR_CLIENT_LISTENER, CLIENT_READY_FROM_CACHE, CLIENT_READY } from '../logger/constants';
9
9
 
10
10
  const NEW_LISTENER_EVENT = 'newListener';
11
- const REMOVE_LISTENER_EVENT = 'removeListener';
12
11
  const TIMEOUT_ERROR = new Error(SDK_READY_TIMED_OUT);
13
12
 
14
13
  /**
@@ -25,19 +24,10 @@ export function sdkReadinessManagerFactory(
25
24
 
26
25
  const log = settings.log;
27
26
 
28
- /** Ready callback warning */
29
- let internalReadyCbCount = 0;
30
- let readyCbCount = 0;
31
- readinessManager.gate.on(REMOVE_LISTENER_EVENT, (event: any) => {
32
- if (event === SDK_READY) readyCbCount--;
33
- });
34
-
35
27
  readinessManager.gate.on(NEW_LISTENER_EVENT, (event: any) => {
36
28
  if (event === SDK_READY || event === SDK_READY_TIMED_OUT) {
37
29
  if (readinessManager.isReady()) {
38
30
  log.error(ERROR_CLIENT_LISTENER, [event === SDK_READY ? 'SDK_READY' : 'SDK_READY_TIMED_OUT']);
39
- } else if (event === SDK_READY) {
40
- readyCbCount++;
41
31
  }
42
32
  } else if (event === SDK_READY_FROM_CACHE && readinessManager.isReadyFromCache()) {
43
33
  log.error(ERROR_CLIENT_LISTENER, ['SDK_READY_FROM_CACHE']);
@@ -61,7 +51,6 @@ export function sdkReadinessManagerFactory(
61
51
  readinessManager.gate.once(SDK_READY, () => {
62
52
  log.info(CLIENT_READY);
63
53
 
64
- if (readyCbCount === internalReadyCbCount && !promise.hasOnFulfilled()) log.warn(CLIENT_NO_LISTENER);
65
54
  resolve();
66
55
  });
67
56
  readinessManager.gate.once(SDK_READY_TIMED_OUT, (message: string) => {
@@ -91,10 +80,6 @@ export function sdkReadinessManagerFactory(
91
80
  return sdkReadinessManagerFactory(EventEmitter, settings, readinessManager.shared());
92
81
  },
93
82
 
94
- incInternalReadyCbCount() {
95
- internalReadyCbCount++;
96
- },
97
-
98
83
  sdkStatus: objectAssign(
99
84
  // Expose Event Emitter functionality
100
85
  Object.create(readinessManager.gate),
@@ -16,17 +16,17 @@ export interface IReadinessEventEmitter extends SplitIO.IEventEmitter {
16
16
 
17
17
  /** Splits data emitter */
18
18
 
19
- type SDK_SPLITS_ARRIVED = 'state::splits-arrived'
20
- type SDK_SPLITS_CACHE_LOADED = 'state::splits-cache-loaded'
21
- type ISplitsEvent = SDK_SPLITS_ARRIVED | SDK_SPLITS_CACHE_LOADED
19
+ type SDK_DEFINITIONS_ARRIVED = 'state::splits-arrived'
20
+ type SDK_DEFINITIONS_CACHE_LOADED = 'state::splits-cache-loaded'
21
+ type IDefinitionsEvent = SDK_DEFINITIONS_ARRIVED | SDK_DEFINITIONS_CACHE_LOADED
22
22
 
23
- export interface ISplitsEventEmitter extends SplitIO.IEventEmitter {
24
- emit(event: ISplitsEvent, ...args: any[]): boolean
25
- on(event: ISplitsEvent, listener: (...args: any[]) => void): this;
23
+ export interface IDefinitionsEventEmitter extends SplitIO.IEventEmitter {
24
+ emit(event: IDefinitionsEvent, ...args: any[]): boolean
25
+ on(event: IDefinitionsEvent, listener: (...args: any[]) => void): this;
26
26
  on(event: SDK_UPDATE, listener: (metadata: SplitIO.SdkUpdateMetadata) => void): this;
27
- once(event: ISplitsEvent, listener: (...args: any[]) => void): this;
28
- splitsArrived: boolean
29
- splitsCacheLoaded: boolean
27
+ once(event: IDefinitionsEvent, listener: (...args: any[]) => void): this;
28
+ definitionsArrived: boolean
29
+ definitionsCacheLoaded: boolean
30
30
  hasInit: boolean,
31
31
  initCallbacks: (() => void)[]
32
32
  }
@@ -48,7 +48,7 @@ export interface ISegmentsEventEmitter extends SplitIO.IEventEmitter {
48
48
 
49
49
  export interface IReadinessManager {
50
50
  /** Event emitters */
51
- splits: ISplitsEventEmitter,
51
+ definitions: IDefinitionsEventEmitter,
52
52
  segments: ISegmentsEventEmitter,
53
53
  gate: IReadinessEventEmitter,
54
54
 
@@ -77,12 +77,6 @@ export interface ISdkReadinessManager {
77
77
  readinessManager: IReadinessManager
78
78
  sdkStatus: SplitIO.IStatusInterface
79
79
 
80
- /**
81
- * Increment internalReadyCbCount, an offset value of SDK_READY listeners that are added/removed internally
82
- * by the SDK. It is required to properly log the warning 'No listeners for SDK Readiness detected'
83
- */
84
- incInternalReadyCbCount(): void
85
-
86
80
  /** for client-side */
87
81
  shared(): ISdkReadinessManager
88
82
  }
@@ -7,7 +7,7 @@ const COOLDOWN_TIME_IN_MILLIS = 1000;
7
7
  * Creates an Sdk client, i.e., a base client with status, init, flush and destroy interface
8
8
  */
9
9
  export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?: boolean): { init(): void; flush(): Promise<void>; destroy(): Promise<void> } {
10
- const { sdkReadinessManager, syncManager, storage, settings, telemetryTracker, impressionsTracker, platform } = params;
10
+ const { sdkReadinessManager, syncManager, storage, settings, telemetryTracker, impressionsTracker, platform, splitApi } = params;
11
11
 
12
12
  let hasInit = false;
13
13
  let lastActionTime = 0;
@@ -68,6 +68,7 @@ export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?:
68
68
 
69
69
  // Stop background jobs
70
70
  syncManager && syncManager.stop();
71
+ splitApi && splitApi.stop();
71
72
 
72
73
  return __flush().then(() => {
73
74
  // Cleanup storage
@@ -12,7 +12,7 @@ import { ISdkFactoryContext } from '../sdkFactory/types';
12
12
  * Reusable by FF SDK client, Configs SDK, and thin-client SDK.
13
13
  */
14
14
  export function trackMethodFactory(params: Pick<ISdkFactoryContext, 'settings' | 'eventTracker' | 'telemetryTracker' | 'storage' | 'sdkReadinessManager'>, warnTTExistence = true) {
15
- const { settings, storage: { splits }, telemetryTracker, eventTracker, sdkReadinessManager: { readinessManager } } = params;
15
+ const { settings, storage: { definitions }, telemetryTracker, eventTracker, sdkReadinessManager: { readinessManager } } = params;
16
16
  const { log, mode } = settings;
17
17
  const isAsync = isConsumerMode(mode);
18
18
 
@@ -42,7 +42,7 @@ export function trackMethodFactory(params: Pick<ISdkFactoryContext, 'settings' |
42
42
  };
43
43
 
44
44
  // This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event.
45
- if (warnTTExistence) validateTrafficTypeExistence(log, readinessManager, splits, mode, trafficTypeName, TRACK_FN_LABEL);
45
+ if (warnTTExistence) validateTrafficTypeExistence(log, readinessManager, definitions, mode, trafficTypeName, TRACK_FN_LABEL);
46
46
 
47
47
  const result = eventTracker.track(eventData, size);
48
48
 
@@ -6,7 +6,7 @@ import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
6
6
  import SplitIO from '../../types/splitio';
7
7
  import { createLoggerAPI } from '../logger/sdkLogger';
8
8
  import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
9
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
9
+ import { SDK_DEFINITIONS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_DEFINITIONS_CACHE_LOADED } from '../readiness/constants';
10
10
  import { objectAssign } from '../utils/lang/objectAssign';
11
11
  import { setRolloutPlan } from '../storages/setRolloutPlan';
12
12
  import { IStorageSync } from '../storages/types';
@@ -36,11 +36,11 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
36
36
  readiness.timeout();
37
37
  return;
38
38
  }
39
- readiness.splits.emit(SDK_SPLITS_ARRIVED);
39
+ readiness.definitions.emit(SDK_DEFINITIONS_ARRIVED);
40
40
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
41
41
  },
42
42
  onReadyFromCacheCb() {
43
- readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
43
+ readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED);
44
44
  }
45
45
  });
46
46
 
@@ -48,7 +48,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
48
48
 
49
49
  if (initialRolloutPlan) {
50
50
  setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
51
- if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
51
+ if ((storage as IStorageSync).definitions.getChangeNumber() > -1) readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
52
52
  }
53
53
 
54
54
  const clients: Record<string, SplitIO.IBasicClient & { init: () => void }> = {};
@@ -66,7 +66,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
66
66
 
67
67
  // SDK client and manager
68
68
  const clientMethod = sdkClientMethodFactory(ctx);
69
- const managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
69
+ const managerInstance = sdkManagerFactory(settings, storage.definitions, sdkReadinessManager);
70
70
 
71
71
 
72
72
  function init() {
@@ -2,7 +2,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { find } from '../utils/lang';
4
4
  import { validateDefinition, validateDefinitionExistence, validateIfOperational } from '../utils/inputValidation';
5
- import { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types';
5
+ import { IDefinitionsCacheAsync, IDefinitionsCacheSync } from '../storages/types';
6
6
  import { ISdkReadinessManager } from '../readiness/types';
7
7
  import { IDefinition } from '../dtos/types';
8
8
  import { ISettings } from '../types';
@@ -48,11 +48,11 @@ function objectsToViews(splitObjects: IDefinition[]) {
48
48
  return views;
49
49
  }
50
50
 
51
- export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplitsCacheAsync>(
51
+ export function sdkManagerFactory<TDefinitionsCache extends IDefinitionsCacheSync | IDefinitionsCacheAsync>(
52
52
  settings: Pick<ISettings, 'log' | 'mode'>,
53
- splits: TSplitCache,
53
+ splits: TDefinitionsCache,
54
54
  { readinessManager, sdkStatus }: ISdkReadinessManager,
55
- ): TSplitCache extends ISplitsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {
55
+ ): TDefinitionsCache extends IDefinitionsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {
56
56
 
57
57
  const { log, mode } = settings;
58
58
  const isAsync = isConsumerMode(mode);
@@ -70,7 +70,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
70
70
  return isAsync ? Promise.resolve(null) : null;
71
71
  }
72
72
 
73
- const split = splits.getSplit(splitName);
73
+ const split = splits.get(splitName);
74
74
 
75
75
  if (thenable(split)) {
76
76
  return split.catch(() => null).then(result => { // handle possible rejections when using pluggable storage
@@ -103,7 +103,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
103
103
  if (!validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
104
104
  return isAsync ? Promise.resolve([]) : [];
105
105
  }
106
- const splitNames = splits.getSplitNames();
106
+ const splitNames = splits.getNames();
107
107
 
108
108
  return thenable(splitNames) ?
109
109
  splitNames.catch(() => []) : // handle possible rejections when using pluggable storage
@@ -0,0 +1,86 @@
1
+ import { ISplitHttpClient, NetworkError } from './types';
2
+ import { IJwtCredentialV3 } from '../sync/streaming/AuthClient/types';
3
+ import { authenticateFactory } from '../sync/streaming/AuthClient';
4
+ import { Backoff } from '../utils/Backoff';
5
+ import { LOG_PREFIX_SYNC_AUTH } from '../logger/constants';
6
+ import { ISettings } from '../types';
7
+ import { TOKEN } from '../utils/constants';
8
+ import { ITelemetryTracker } from '../trackers/types';
9
+
10
+ const SKEW_SECONDS = 30;
11
+
12
+ function isExpired(credential: IJwtCredentialV3): boolean {
13
+ return Date.now() / 1000 + SKEW_SECONDS >= credential.decodedToken.exp;
14
+ }
15
+
16
+ export interface IAuthProvider {
17
+ credential(): Promise<IJwtCredentialV3>;
18
+ invalidate(): void;
19
+ stop(): void;
20
+ }
21
+
22
+ /**
23
+ * Factory of AuthProvider, which provides JWT credentials for authenticated HTTP requests.
24
+ * Credentials are fetched lazily on demand, cached in memory, and retried with backoff on failure.
25
+ */
26
+ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplitHttpClient, telemetryTracker: ITelemetryTracker): IAuthProvider {
27
+
28
+ const { urls, log } = settings;
29
+
30
+ function fetchAuth() {
31
+ let url = `${urls.auth}/v3/auth?capabilities=config`;
32
+ return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
33
+ }
34
+
35
+ const authenticate = authenticateFactory(fetchAuth);
36
+ const backoff = new Backoff(fetchCredential);
37
+
38
+ let cachedCredential: IJwtCredentialV3 | undefined;
39
+ let inFlightPromise: Promise<IJwtCredentialV3> | undefined;
40
+ let stopped = false;
41
+
42
+ function fetchCredential(): Promise<IJwtCredentialV3> {
43
+ return authenticate().then((credential: IJwtCredentialV3) => {
44
+ log.info(LOG_PREFIX_SYNC_AUTH + 'credential fetched successfully');
45
+ cachedCredential = credential;
46
+ inFlightPromise = undefined;
47
+ backoff.reset();
48
+ return credential;
49
+ }).catch((error: NetworkError) => {
50
+ // Avoid rejected promises and unnecessary retries after stop()
51
+ if (stopped) return cachedCredential!;
52
+
53
+ if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
54
+ log.error(LOG_PREFIX_SYNC_AUTH + 'non-retryable error fetching credential (status ' + error.statusCode + '): ' + error.message);
55
+ inFlightPromise = undefined;
56
+ throw error;
57
+ }
58
+
59
+ log.warn(LOG_PREFIX_SYNC_AUTH + 'credential fetch failed (attempt ' + (backoff.attempts + 1) + '). Error: ' + error.message);
60
+ return backoff.scheduleCallAsync();
61
+ });
62
+ }
63
+
64
+ return {
65
+ credential(): Promise<IJwtCredentialV3> {
66
+ if (cachedCredential && !isExpired(cachedCredential)) {
67
+ return Promise.resolve(cachedCredential);
68
+ }
69
+
70
+ if (cachedCredential) log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
71
+
72
+ return inFlightPromise || (inFlightPromise = fetchCredential());
73
+ },
74
+
75
+ invalidate() {
76
+ cachedCredential = undefined;
77
+ },
78
+
79
+ stop() {
80
+ stopped = true;
81
+ cachedCredential = undefined;
82
+ inFlightPromise = undefined;
83
+ backoff.reset();
84
+ }
85
+ };
86
+ }
@@ -0,0 +1,44 @@
1
+ import { IRequestOptions, IResponse, ISecureSplitHttpClient, NetworkError } from './types';
2
+ import { ISettings } from '../types';
3
+ import { IPlatform } from '../sdkFactory/types';
4
+ import { splitHttpClientFactory } from './splitHttpClient';
5
+ import { authProviderFactory } from './authProvider';
6
+ import { ITelemetryTracker } from '../trackers/types';
7
+
8
+ /**
9
+ * Factory of Secure HTTP client, which authenticates requests using a JWT token.
10
+ * On 401 responses, invalidates the cached credential and retries once with a fresh token.
11
+ *
12
+ * @param settings - SDK settings
13
+ * @param platform - object containing environment-specific dependencies
14
+ * @param fetchAuth - function to fetch auth credentials from the /v2/auth endpoint
15
+ */
16
+ export function secureSplitHttpClientFactory(settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker): ISecureSplitHttpClient {
17
+
18
+ const splitHttpClient = splitHttpClientFactory(settings, platform);
19
+ const authProvider = authProviderFactory(settings, splitHttpClient, telemetryTracker);
20
+
21
+ function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker: ((error?: NetworkError) => void) | undefined, logErrorsAsInfo: boolean | undefined, token: string): Promise<IResponse> {
22
+ return splitHttpClient(url, { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } }, latencyTracker, logErrorsAsInfo);
23
+ }
24
+
25
+ const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean): Promise<IResponse> {
26
+ return authProvider.credential().then(credential => {
27
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
28
+ .catch((error: NetworkError) => {
29
+ if (error.statusCode === 401) {
30
+ // retry once for 401, in case the token has just expired
31
+ authProvider.invalidate();
32
+ return authProvider.credential().then(newCredential => {
33
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
34
+ });
35
+ }
36
+ throw error;
37
+ });
38
+ });
39
+ };
40
+
41
+ httpClient.stop = () => authProvider.stop();
42
+
43
+ return httpClient;
44
+ }
@@ -1,7 +1,7 @@
1
1
  import { IPlatform } from '../sdkFactory/types';
2
2
  import { ISettings } from '../types';
3
3
  import { splitHttpClientFactory } from './splitHttpClient';
4
- import { ISplitApi } from './types';
4
+ import { ISecureSplitHttpClient, ISplitApi } from './types';
5
5
  import { objectAssign } from '../utils/lang/objectAssign';
6
6
  import { ITelemetryTracker } from '../trackers/types';
7
7
  import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
@@ -23,13 +23,15 @@ function userKeyToQueryParam(userKey: string) {
23
23
  export function splitApiFactory(
24
24
  settings: ISettings,
25
25
  platform: Pick<IPlatform, 'getOptions' | 'getFetch'>,
26
- telemetryTracker: ITelemetryTracker
26
+ telemetryTracker: ITelemetryTracker,
27
+ secureSplitHttpClientFactory?: (settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker) => ISecureSplitHttpClient,
27
28
  ): ISplitApi {
28
29
 
29
30
  const urls = settings.urls;
30
31
  const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
31
32
  const SplitSDKImpressionsMode = settings.sync.impressionsMode;
32
33
  const splitHttpClient = splitHttpClientFactory(settings, platform);
34
+ const secureSplitHttpClient = secureSplitHttpClientFactory ? secureSplitHttpClientFactory(settings, platform, telemetryTracker) : undefined;
33
35
 
34
36
  return {
35
37
  // @TODO throw errors if health check requests fail, to log them in the Synchronizer
@@ -61,15 +63,20 @@ export function splitApiFactory(
61
63
  });
62
64
  },
63
65
 
66
+ fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
67
+ const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
68
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
69
+ },
70
+
64
71
  // @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
65
72
  fetchConfigs(since: number, noCache?: boolean, till?: number) {
66
- const url = `${urls.sdk}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
67
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
73
+ const url = `${urls.configs}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
74
+ return (secureSplitHttpClient || splitHttpClient)(url, noCache ? noCacheHeaderOptions : undefined);
68
75
  },
69
76
 
70
- fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
71
- const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
72
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
77
+ fetchConfigsSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
78
+ const url = `${urls.configs}/v1/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
79
+ return (secureSplitHttpClient || splitHttpClient)(url, noCache ? noCacheHeaderOptions : undefined);
73
80
  },
74
81
 
75
82
  fetchMemberships(userMatchingKey: string, noCache?: boolean, till?: number) {
@@ -149,6 +156,10 @@ export function splitApiFactory(
149
156
  postMetricsUsage(body: string, headers?: Record<string, string>) {
150
157
  const url = `${urls.telemetry}/v1/metrics/usage`;
151
158
  return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
159
+ },
160
+
161
+ stop() {
162
+ if (secureSplitHttpClient) secureSplitHttpClient.stop();
152
163
  }
153
164
  };
154
165
  }
@@ -33,6 +33,8 @@ export type IHealthCheckAPI = () => Promise<boolean>
33
33
 
34
34
  export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean) => Promise<IResponse>
35
35
 
36
+ export type ISecureSplitHttpClient = ISplitHttpClient & { stop(): void }
37
+
36
38
  export type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>
37
39
 
38
40
  export type IFetchDefinitionChanges = (since: number, noCache?: boolean, till?: number, rbSince?: number) => Promise<IResponse>
@@ -60,8 +62,9 @@ export interface ISplitApi {
60
62
  getEventsAPIHealthCheck: IHealthCheckAPI
61
63
  fetchAuth: IFetchAuth
62
64
  fetchSplitChanges: IFetchDefinitionChanges
63
- fetchConfigs: IFetchDefinitionChanges
64
65
  fetchSegmentChanges: IFetchSegmentChanges
66
+ fetchConfigs: IFetchDefinitionChanges
67
+ fetchConfigsSegmentChanges: IFetchSegmentChanges
65
68
  fetchMemberships: IFetchMemberships
66
69
  postEventsBulk: IPostEventsBulk
67
70
  postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
@@ -70,6 +73,8 @@ export interface ISplitApi {
70
73
  postTestImpressionsCount: IPostTestImpressionsCount
71
74
  postMetricsConfig: IPostMetricsConfig
72
75
  postMetricsUsage: IPostMetricsUsage
76
+ // lifecycle
77
+ stop(): void
73
78
  }
74
79
 
75
80
  // Minimal version of EventSource API used by the SDK
@@ -0,0 +1,63 @@
1
+ import { IDefinitionsCacheAsync } from './types';
2
+ import { IDefinition } from '../dtos/types';
3
+ import { objectAssign } from '../utils/lang/objectAssign';
4
+
5
+ /**
6
+ * This class provides a skeletal implementation of the IDefinitionsCacheAsync interface
7
+ * to minimize the effort required to implement this interface.
8
+ */
9
+ export abstract class AbstractDefinitionsCacheAsync implements IDefinitionsCacheAsync {
10
+
11
+ protected abstract add(definition: IDefinition): Promise<boolean>
12
+ protected abstract remove(name: string): Promise<boolean>
13
+ protected abstract setChangeNumber(changeNumber: number): Promise<boolean | void>
14
+
15
+ update(toAdd: IDefinition[], toRemove: string[], changeNumber: number): Promise<boolean> {
16
+ return Promise.all([
17
+ this.setChangeNumber(changeNumber),
18
+ Promise.all(toAdd.map(addedFF => this.add(addedFF))),
19
+ Promise.all(toRemove.map(removedFF => this.remove(removedFF)))
20
+ ]).then(([, added, removed]) => {
21
+ return added.some(result => result) || removed.some(result => result);
22
+ });
23
+ }
24
+
25
+ abstract get(name: string): Promise<IDefinition | null>
26
+ abstract getMany(names: string[]): Promise<Record<string, IDefinition | null>>
27
+ abstract getChangeNumber(): Promise<number>
28
+ abstract getAll(): Promise<IDefinition[]>
29
+ abstract getNames(): Promise<string[]>
30
+ abstract getNamesBySets(sets: string[]): Promise<Set<string>[]>
31
+ abstract trafficTypeExists(trafficType: string): Promise<boolean>
32
+ abstract clear(): Promise<boolean | void>
33
+
34
+ // @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
35
+ // noop, just keeping the interface. This is used by standalone client-side API only, and so only implemented by InMemory and InLocalStorage.
36
+ usesSegments(): Promise<boolean> {
37
+ return Promise.resolve(true);
38
+ }
39
+
40
+ /**
41
+ * Kill `name` definition and set `defaultTreatment` and `changeNumber`.
42
+ * Used for SPLIT_KILL push notifications.
43
+ *
44
+ * @returns a promise that is resolved once the definition kill operation is performed. The fulfillment value is a boolean: `true` if the operation succeeded updating the definition or `false` if no definition is updated,
45
+ * for instance, if the `changeNumber` is old, or if the definition is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
46
+ * The promise will never be rejected.
47
+ */
48
+ killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean> {
49
+ return this.get(name).then(definition => {
50
+
51
+ if (definition && (!definition.changeNumber || definition.changeNumber < changeNumber)) {
52
+ const newDefinition = objectAssign({}, definition);
53
+ newDefinition.killed = true;
54
+ newDefinition.defaultTreatment = defaultTreatment;
55
+ newDefinition.changeNumber = changeNumber;
56
+
57
+ return this.add(newDefinition);
58
+ }
59
+ return false;
60
+ }).catch(() => false);
61
+ }
62
+
63
+ }