@splitsoftware/splitio-commons 2.12.0 → 3.0.0-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 (395) hide show
  1. package/CHANGES.txt +19 -1
  2. package/cjs/evaluator/condition/engineUtils.js +2 -2
  3. package/cjs/evaluator/fallbackConfigsCalculator/fallbackSanitizer/index.js +70 -0
  4. package/cjs/evaluator/fallbackConfigsCalculator/index.js +25 -0
  5. package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -8
  6. package/cjs/evaluator/fallbackTreatmentsCalculator/index.js +21 -32
  7. package/cjs/evaluator/index.js +73 -48
  8. package/cjs/evaluator/matchers/whitelist.js +1 -1
  9. package/cjs/integrations/pluggable.js +1 -1
  10. package/cjs/listeners/browser.js +4 -3
  11. package/cjs/listeners/node.js +4 -4
  12. package/cjs/logger/constants.js +15 -21
  13. package/cjs/logger/index.js +1 -1
  14. package/cjs/logger/messages/debug.js +6 -7
  15. package/cjs/logger/messages/error.js +4 -4
  16. package/cjs/logger/messages/info.js +2 -4
  17. package/cjs/logger/messages/warn.js +9 -10
  18. package/cjs/platform/browser.js +15 -0
  19. package/cjs/platform/getEventSource/browser.js +8 -0
  20. package/cjs/platform/getEventSource/eventsource.js +480 -0
  21. package/cjs/platform/getEventSource/node.js +26 -0
  22. package/cjs/platform/getFetch/browser.js +9 -0
  23. package/cjs/platform/getFetch/node.js +27 -0
  24. package/cjs/platform/getOptions/node.js +25 -0
  25. package/cjs/platform/node.js +17 -0
  26. package/cjs/readiness/constants.js +5 -4
  27. package/cjs/readiness/readinessManager.js +22 -22
  28. package/cjs/readiness/sdkReadinessManager.js +0 -16
  29. package/cjs/sdkClient/client.js +9 -35
  30. package/cjs/sdkClient/clientAttributesDecoration.js +3 -3
  31. package/cjs/sdkClient/clientInputValidation.js +38 -81
  32. package/cjs/sdkClient/identity.js +1 -1
  33. package/cjs/sdkClient/sdkClient.js +4 -49
  34. package/cjs/sdkClient/sdkLifecycle.js +71 -0
  35. package/cjs/sdkClient/trackMethod.js +56 -0
  36. package/cjs/sdkFactory/index.js +13 -50
  37. package/cjs/sdkManager/index.js +5 -5
  38. package/cjs/services/authProvider.js +67 -0
  39. package/cjs/services/secureSplitHttpClient.js +39 -0
  40. package/cjs/services/splitApi.js +24 -14
  41. package/cjs/services/splitHttpClient.js +1 -1
  42. package/cjs/storages/AbstractDefinitionsCacheAsync.js +51 -0
  43. package/cjs/storages/AbstractDefinitionsCacheSync.js +76 -0
  44. package/cjs/storages/KeyBuilder.js +16 -16
  45. package/cjs/storages/KeyBuilderCS.js +17 -17
  46. package/cjs/storages/KeyBuilderSS.js +16 -16
  47. package/cjs/storages/getRolloutPlan.js +4 -4
  48. package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +194 -0
  49. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +1 -1
  50. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +10 -10
  51. package/cjs/storages/inLocalStorage/index.js +6 -6
  52. package/cjs/storages/inLocalStorage/validateCache.js +3 -3
  53. package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +121 -0
  54. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
  55. package/cjs/storages/inMemory/InMemoryStorage.js +4 -4
  56. package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -5
  57. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +1 -1
  58. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +5 -5
  59. package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
  60. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +5 -5
  61. package/cjs/storages/inRedis/DefinitionsCacheInRedis.js +243 -0
  62. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  63. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +5 -5
  64. package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +1 -1
  65. package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +1 -1
  66. package/cjs/storages/inRedis/RedisAdapter.js +8 -8
  67. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +7 -7
  68. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +3 -3
  69. package/cjs/storages/inRedis/index.js +2 -2
  70. package/cjs/storages/pluggable/DefinitionsCachePluggable.js +212 -0
  71. package/cjs/storages/pluggable/EventsCachePluggable.js +1 -1
  72. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +6 -6
  73. package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +1 -1
  74. package/cjs/storages/pluggable/TelemetryCachePluggable.js +7 -7
  75. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +2 -2
  76. package/cjs/storages/pluggable/index.js +5 -5
  77. package/cjs/storages/pluggable/wrapperAdapter.js +1 -1
  78. package/cjs/storages/setRolloutPlan.js +6 -6
  79. package/cjs/storages/utils.js +6 -6
  80. package/cjs/sync/offline/splitsParser/parseCondition.js +1 -1
  81. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +5 -5
  82. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  83. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +24 -15
  84. package/cjs/sync/polling/pollingManagerCS.js +18 -18
  85. package/cjs/sync/polling/pollingManagerSS.js +13 -13
  86. package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +12 -0
  87. package/cjs/sync/polling/updaters/{splitChangesUpdater.js → definitionChangesUpdater.js} +72 -64
  88. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -3
  89. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +6 -6
  90. package/cjs/sync/streaming/SSEClient/index.js +2 -2
  91. package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +1 -1
  92. package/cjs/sync/streaming/SSEHandler/index.js +1 -1
  93. package/cjs/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +18 -18
  94. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +2 -2
  95. package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -2
  96. package/cjs/sync/streaming/constants.js +3 -3
  97. package/cjs/sync/streaming/parseUtils.js +12 -12
  98. package/cjs/sync/streaming/pushManager.js +14 -14
  99. package/cjs/sync/submitters/impressionsSubmitter.js +6 -6
  100. package/cjs/sync/submitters/submitter.js +1 -1
  101. package/cjs/sync/submitters/telemetrySubmitter.js +1 -2
  102. package/cjs/sync/syncManagerOnline.js +9 -6
  103. package/cjs/trackers/eventTracker.js +10 -12
  104. package/cjs/trackers/impressionObserver/buildKey.js +1 -1
  105. package/cjs/trackers/impressionsTracker.js +37 -20
  106. package/cjs/trackers/uniqueKeysTracker.js +1 -1
  107. package/cjs/utils/Backoff.js +20 -0
  108. package/cjs/utils/EventEmitter.js +391 -0
  109. package/cjs/utils/filter/bloomFilter.js +37 -0
  110. package/cjs/utils/inputValidation/apiKey.js +1 -1
  111. package/cjs/utils/inputValidation/attribute.js +2 -2
  112. package/cjs/utils/inputValidation/{split.js → definition.js} +10 -10
  113. package/cjs/utils/inputValidation/{splitExistence.js → definitionExistence.js} +6 -6
  114. package/cjs/utils/inputValidation/definitions.js +25 -0
  115. package/cjs/utils/inputValidation/eventProperties.js +1 -1
  116. package/cjs/utils/inputValidation/index.js +7 -7
  117. package/cjs/utils/inputValidation/isOperational.js +4 -4
  118. package/cjs/utils/inputValidation/target.js +22 -0
  119. package/cjs/utils/labels/index.js +3 -3
  120. package/cjs/utils/lang/index.js +1 -1
  121. package/cjs/utils/lang/sets.js +4 -4
  122. package/cjs/utils/promise/timeout.js +1 -1
  123. package/cjs/utils/settingsValidation/splitFilters.js +4 -4
  124. package/cjs/utils/settingsValidation/url.js +6 -6
  125. package/esm/evaluator/condition/engineUtils.js +2 -2
  126. package/esm/evaluator/fallbackConfigsCalculator/fallbackSanitizer/index.js +64 -0
  127. package/esm/evaluator/fallbackConfigsCalculator/index.js +21 -0
  128. package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -8
  129. package/esm/evaluator/fallbackTreatmentsCalculator/index.js +21 -33
  130. package/esm/evaluator/index.js +72 -48
  131. package/esm/evaluator/matchers/whitelist.js +1 -1
  132. package/esm/integrations/pluggable.js +1 -1
  133. package/esm/listeners/browser.js +4 -3
  134. package/esm/listeners/node.js +4 -4
  135. package/esm/logger/constants.js +12 -18
  136. package/esm/logger/index.js +1 -1
  137. package/esm/logger/messages/debug.js +5 -6
  138. package/esm/logger/messages/error.js +3 -3
  139. package/esm/logger/messages/info.js +1 -3
  140. package/esm/logger/messages/warn.js +8 -9
  141. package/esm/platform/browser.js +12 -0
  142. package/esm/platform/getEventSource/browser.js +4 -0
  143. package/esm/platform/getEventSource/eventsource.js +480 -0
  144. package/esm/platform/getEventSource/node.js +20 -0
  145. package/esm/platform/getFetch/browser.js +5 -0
  146. package/esm/platform/getFetch/node.js +22 -0
  147. package/esm/platform/getOptions/node.js +20 -0
  148. package/esm/platform/node.js +14 -0
  149. package/esm/readiness/constants.js +4 -3
  150. package/esm/readiness/readinessManager.js +23 -23
  151. package/esm/readiness/sdkReadinessManager.js +1 -17
  152. package/esm/sdkClient/client.js +10 -37
  153. package/esm/sdkClient/clientAttributesDecoration.js +3 -3
  154. package/esm/sdkClient/clientInputValidation.js +40 -83
  155. package/esm/sdkClient/identity.js +1 -1
  156. package/esm/sdkClient/sdkClient.js +4 -49
  157. package/esm/sdkClient/sdkLifecycle.js +67 -0
  158. package/esm/sdkClient/trackMethod.js +52 -0
  159. package/esm/sdkFactory/index.js +14 -51
  160. package/esm/sdkManager/index.js +6 -6
  161. package/esm/services/authProvider.js +63 -0
  162. package/esm/services/secureSplitHttpClient.js +35 -0
  163. package/esm/services/splitApi.js +24 -14
  164. package/esm/services/splitHttpClient.js +1 -1
  165. package/esm/storages/AbstractDefinitionsCacheAsync.js +48 -0
  166. package/esm/storages/AbstractDefinitionsCacheSync.js +71 -0
  167. package/esm/storages/KeyBuilder.js +16 -16
  168. package/esm/storages/KeyBuilderCS.js +16 -16
  169. package/esm/storages/KeyBuilderSS.js +15 -15
  170. package/esm/storages/getRolloutPlan.js +4 -4
  171. package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +191 -0
  172. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +7 -7
  173. package/esm/storages/inLocalStorage/index.js +6 -6
  174. package/esm/storages/inLocalStorage/validateCache.js +3 -3
  175. package/esm/storages/inMemory/DefinitionsCacheInMemory.js +118 -0
  176. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
  177. package/esm/storages/inMemory/InMemoryStorage.js +4 -4
  178. package/esm/storages/inMemory/InMemoryStorageCS.js +5 -5
  179. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +2 -2
  180. package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
  181. package/esm/storages/inMemory/TelemetryCacheInMemory.js +5 -5
  182. package/esm/storages/inRedis/DefinitionsCacheInRedis.js +240 -0
  183. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  184. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +4 -4
  185. package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +1 -1
  186. package/esm/storages/inRedis/RedisAdapter.js +7 -7
  187. package/esm/storages/inRedis/TelemetryCacheInRedis.js +7 -7
  188. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +1 -1
  189. package/esm/storages/inRedis/index.js +2 -2
  190. package/esm/storages/pluggable/DefinitionsCachePluggable.js +209 -0
  191. package/esm/storages/pluggable/EventsCachePluggable.js +1 -1
  192. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +5 -5
  193. package/esm/storages/pluggable/RBSegmentsCachePluggable.js +1 -1
  194. package/esm/storages/pluggable/TelemetryCachePluggable.js +7 -7
  195. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +1 -1
  196. package/esm/storages/pluggable/index.js +4 -4
  197. package/esm/storages/pluggable/wrapperAdapter.js +1 -1
  198. package/esm/storages/setRolloutPlan.js +6 -6
  199. package/esm/storages/utils.js +6 -6
  200. package/esm/sync/offline/splitsParser/parseCondition.js +1 -1
  201. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
  202. package/esm/sync/polling/fetchers/splitChangesFetcher.js +25 -16
  203. package/esm/sync/polling/pollingManagerCS.js +15 -15
  204. package/esm/sync/polling/pollingManagerSS.js +13 -13
  205. package/esm/sync/polling/syncTasks/definitionsSyncTask.js +8 -0
  206. package/esm/sync/polling/updaters/definitionChangesUpdater.js +200 -0
  207. package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -3
  208. package/esm/sync/polling/updaters/segmentChangesUpdater.js +7 -7
  209. package/esm/sync/streaming/SSEClient/index.js +2 -2
  210. package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +2 -2
  211. package/esm/sync/streaming/SSEHandler/index.js +2 -2
  212. package/esm/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +17 -17
  213. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +2 -2
  214. package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -2
  215. package/esm/sync/streaming/constants.js +2 -2
  216. package/esm/sync/streaming/parseUtils.js +10 -10
  217. package/esm/sync/streaming/pushManager.js +16 -16
  218. package/esm/sync/submitters/impressionsSubmitter.js +6 -6
  219. package/esm/sync/submitters/submitter.js +1 -1
  220. package/esm/sync/submitters/telemetrySubmitter.js +1 -2
  221. package/esm/sync/syncManagerOnline.js +8 -5
  222. package/esm/trackers/eventTracker.js +10 -12
  223. package/esm/trackers/impressionObserver/buildKey.js +1 -1
  224. package/esm/trackers/impressionsTracker.js +38 -21
  225. package/esm/trackers/uniqueKeysTracker.js +1 -1
  226. package/esm/utils/Backoff.js +20 -0
  227. package/esm/utils/EventEmitter.js +387 -0
  228. package/esm/utils/filter/bloomFilter.js +33 -0
  229. package/esm/utils/inputValidation/apiKey.js +1 -1
  230. package/esm/utils/inputValidation/attribute.js +2 -2
  231. package/esm/utils/inputValidation/{split.js → definition.js} +8 -8
  232. package/esm/utils/inputValidation/definitionExistence.js +15 -0
  233. package/esm/utils/inputValidation/definitions.js +21 -0
  234. package/esm/utils/inputValidation/index.js +3 -3
  235. package/esm/utils/inputValidation/isOperational.js +4 -4
  236. package/esm/utils/inputValidation/target.js +18 -0
  237. package/esm/utils/labels/index.js +2 -2
  238. package/esm/utils/lang/sets.js +4 -4
  239. package/esm/utils/promise/timeout.js +1 -1
  240. package/esm/utils/settingsValidation/splitFilters.js +4 -4
  241. package/esm/utils/settingsValidation/url.js +6 -6
  242. package/package.json +15 -6
  243. package/src/dtos/types.ts +65 -70
  244. package/src/evaluator/Engine.ts +4 -4
  245. package/src/evaluator/combiners/and.ts +3 -3
  246. package/src/evaluator/combiners/ifelseif.ts +2 -2
  247. package/src/evaluator/condition/engineUtils.ts +2 -2
  248. package/src/evaluator/condition/index.ts +3 -3
  249. package/src/evaluator/fallbackConfigsCalculator/fallbackSanitizer/index.ts +76 -0
  250. package/src/evaluator/fallbackConfigsCalculator/index.ts +24 -0
  251. package/src/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.ts +5 -7
  252. package/src/evaluator/fallbackTreatmentsCalculator/index.ts +27 -41
  253. package/src/evaluator/index.ts +86 -51
  254. package/src/evaluator/matchers/dependency.ts +2 -2
  255. package/src/evaluator/matchers/matcherTypes.ts +2 -2
  256. package/src/evaluator/matchers/prerequisites.ts +4 -4
  257. package/src/evaluator/matchers/rbsegment.ts +2 -2
  258. package/src/evaluator/matchers/whitelist.ts +2 -2
  259. package/src/evaluator/matchersTransform/index.ts +2 -2
  260. package/src/evaluator/matchersTransform/whitelist.ts +2 -2
  261. package/src/evaluator/parser/index.ts +4 -4
  262. package/src/evaluator/treatments/index.ts +2 -2
  263. package/src/evaluator/types.ts +6 -6
  264. package/src/evaluator/value/index.ts +1 -1
  265. package/src/integrations/pluggable.ts +1 -1
  266. package/src/listeners/browser.ts +11 -8
  267. package/src/listeners/node.ts +2 -5
  268. package/src/logger/constants.ts +12 -18
  269. package/src/logger/messages/debug.ts +5 -6
  270. package/src/logger/messages/error.ts +3 -3
  271. package/src/logger/messages/info.ts +1 -3
  272. package/src/logger/messages/warn.ts +8 -9
  273. package/src/platform/browser.ts +14 -0
  274. package/src/platform/getEventSource/browser.ts +4 -0
  275. package/src/platform/getEventSource/eventsource.js +519 -0
  276. package/src/platform/getEventSource/node.ts +23 -0
  277. package/src/platform/getFetch/browser.ts +5 -0
  278. package/src/platform/getFetch/node.ts +27 -0
  279. package/src/platform/getOptions/node.ts +24 -0
  280. package/src/platform/node.ts +16 -0
  281. package/src/readiness/constants.ts +4 -3
  282. package/src/readiness/readinessManager.ts +22 -22
  283. package/src/readiness/sdkReadinessManager.ts +1 -16
  284. package/src/readiness/types.ts +10 -16
  285. package/src/sdkClient/client.ts +11 -41
  286. package/src/sdkClient/clientInputValidation.ts +44 -84
  287. package/src/sdkClient/sdkClient.ts +5 -54
  288. package/src/sdkClient/sdkLifecycle.ts +79 -0
  289. package/src/sdkClient/trackMethod.ts +59 -0
  290. package/src/sdkFactory/index.ts +15 -59
  291. package/src/sdkFactory/types.ts +19 -16
  292. package/src/sdkManager/index.ts +15 -15
  293. package/src/services/authProvider.ts +86 -0
  294. package/src/services/secureSplitHttpClient.ts +44 -0
  295. package/src/services/splitApi.ts +14 -2
  296. package/src/services/types.ts +7 -2
  297. package/src/storages/AbstractDefinitionsCacheAsync.ts +63 -0
  298. package/src/storages/AbstractDefinitionsCacheSync.ts +95 -0
  299. package/src/storages/KeyBuilder.ts +6 -6
  300. package/src/storages/KeyBuilderCS.ts +6 -6
  301. package/src/storages/KeyBuilderSS.ts +2 -2
  302. package/src/storages/getRolloutPlan.ts +3 -3
  303. package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +234 -0
  304. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +8 -8
  305. package/src/storages/inLocalStorage/index.ts +6 -6
  306. package/src/storages/inLocalStorage/validateCache.ts +4 -4
  307. package/src/storages/inMemory/DefinitionsCacheInMemory.ts +130 -0
  308. package/src/storages/inMemory/InMemoryStorage.ts +4 -4
  309. package/src/storages/inMemory/InMemoryStorageCS.ts +5 -5
  310. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +3 -3
  311. package/src/storages/inMemory/SegmentsCacheInMemory.ts +1 -1
  312. package/src/storages/inMemory/TelemetryCacheInMemory.ts +5 -5
  313. package/src/storages/inRedis/{SplitsCacheInRedis.ts → DefinitionsCacheInRedis.ts} +69 -69
  314. package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +2 -2
  315. package/src/storages/inRedis/index.ts +2 -2
  316. package/src/storages/pluggable/DefinitionsCachePluggable.ts +230 -0
  317. package/src/storages/pluggable/RBSegmentsCachePluggable.ts +2 -2
  318. package/src/storages/pluggable/index.ts +2 -2
  319. package/src/storages/setRolloutPlan.ts +7 -7
  320. package/src/storages/types.ts +34 -29
  321. package/src/sync/offline/splitsParser/parseCondition.ts +2 -2
  322. package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +3 -3
  323. package/src/sync/offline/splitsParser/types.ts +6 -3
  324. package/src/sync/offline/syncManagerOffline.ts +2 -2
  325. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +14 -14
  326. package/src/sync/polling/fetchers/splitChangesFetcher.ts +58 -23
  327. package/src/sync/polling/fetchers/types.ts +5 -3
  328. package/src/sync/polling/pollingManagerCS.ts +16 -14
  329. package/src/sync/polling/pollingManagerSS.ts +15 -13
  330. package/src/sync/polling/syncTasks/{splitsSyncTask.ts → definitionsSyncTask.ts} +11 -12
  331. package/src/sync/polling/types.ts +3 -3
  332. package/src/sync/polling/updaters/definitionChangesUpdater.ts +239 -0
  333. package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -3
  334. package/src/sync/polling/updaters/segmentChangesUpdater.ts +7 -7
  335. package/src/sync/streaming/AuthClient/index.ts +2 -2
  336. package/src/sync/streaming/AuthClient/types.ts +14 -11
  337. package/src/sync/streaming/SSEClient/index.ts +2 -2
  338. package/src/sync/streaming/SSEClient/types.ts +2 -2
  339. package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +2 -2
  340. package/src/sync/streaming/SSEHandler/index.ts +2 -2
  341. package/src/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.ts → DefinitionsUpdateWorker.ts} +21 -21
  342. package/src/sync/streaming/constants.ts +2 -2
  343. package/src/sync/streaming/parseUtils.ts +12 -12
  344. package/src/sync/streaming/pushManager.ts +18 -18
  345. package/src/sync/streaming/types.ts +2 -2
  346. package/src/sync/submitters/impressionsSubmitter.ts +9 -9
  347. package/src/sync/submitters/telemetrySubmitter.ts +1 -2
  348. package/src/sync/submitters/types.ts +3 -0
  349. package/src/sync/syncManagerOnline.ts +9 -5
  350. package/src/trackers/eventTracker.ts +7 -10
  351. package/src/trackers/impressionsTracker.ts +42 -26
  352. package/src/trackers/types.ts +3 -1
  353. package/src/utils/Backoff.ts +21 -0
  354. package/src/utils/EventEmitter.js +444 -0
  355. package/src/utils/filter/bloomFilter.ts +38 -0
  356. package/src/utils/inputValidation/definition.ts +27 -0
  357. package/src/utils/inputValidation/definitionExistence.ts +19 -0
  358. package/src/utils/inputValidation/definitions.ts +21 -0
  359. package/src/utils/inputValidation/index.ts +3 -3
  360. package/src/utils/inputValidation/isOperational.ts +4 -4
  361. package/src/utils/inputValidation/target.ts +21 -0
  362. package/src/utils/inputValidation/trafficTypeExistence.ts +2 -2
  363. package/src/utils/jwt/types.ts +1 -1
  364. package/src/utils/labels/index.ts +2 -2
  365. package/src/utils/lang/sets.ts +4 -2
  366. package/src/utils/settingsValidation/splitFilters.ts +3 -3
  367. package/src/utils/settingsValidation/url.ts +1 -1
  368. package/types/splitio.d.ts +222 -11
  369. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -51
  370. package/cjs/storages/AbstractSplitsCacheSync.js +0 -76
  371. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -194
  372. package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -121
  373. package/cjs/storages/inRedis/SplitsCacheInRedis.js +0 -243
  374. package/cjs/storages/pluggable/SplitsCachePluggable.js +0 -212
  375. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +0 -13
  376. package/cjs/utils/inputValidation/splits.js +0 -25
  377. package/esm/storages/AbstractSplitsCacheAsync.js +0 -48
  378. package/esm/storages/AbstractSplitsCacheSync.js +0 -71
  379. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -191
  380. package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -118
  381. package/esm/storages/inRedis/SplitsCacheInRedis.js +0 -240
  382. package/esm/storages/pluggable/SplitsCachePluggable.js +0 -209
  383. package/esm/sync/polling/syncTasks/splitsSyncTask.js +0 -9
  384. package/esm/sync/polling/updaters/splitChangesUpdater.js +0 -192
  385. package/esm/utils/inputValidation/splitExistence.js +0 -15
  386. package/esm/utils/inputValidation/splits.js +0 -21
  387. package/src/storages/AbstractSplitsCacheAsync.ts +0 -63
  388. package/src/storages/AbstractSplitsCacheSync.ts +0 -95
  389. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -234
  390. package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -130
  391. package/src/storages/pluggable/SplitsCachePluggable.ts +0 -230
  392. package/src/sync/polling/updaters/splitChangesUpdater.ts +0 -230
  393. package/src/utils/inputValidation/split.ts +0 -27
  394. package/src/utils/inputValidation/splitExistence.ts +0 -19
  395. package/src/utils/inputValidation/splits.ts +0 -21
@@ -0,0 +1,79 @@
1
+ import { releaseApiKey, validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
2
+ import { ISdkFactoryContext } from '../sdkFactory/types';
3
+
4
+ const COOLDOWN_TIME_IN_MILLIS = 1000;
5
+
6
+ /**
7
+ * Creates an Sdk client, i.e., a base client with status, init, flush and destroy interface
8
+ */
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, splitApi } = params;
11
+
12
+ let hasInit = false;
13
+ let lastActionTime = 0;
14
+
15
+ const signalListener = platform.SignalListener && new platform.SignalListener(params);
16
+
17
+ function __cooldown(func: Function, time: number) {
18
+ const now = Date.now();
19
+ //get the actual time elapsed in ms
20
+ const timeElapsed = now - lastActionTime;
21
+ //check if the time elapsed is less than desired cooldown
22
+ if (timeElapsed < time) {
23
+ //if yes, return message with remaining time in seconds
24
+ settings.log.warn(`Flush cooldown, remaining time ${(time - timeElapsed) / 1000} seconds`);
25
+ return Promise.resolve();
26
+ } else {
27
+ //Do the requested action and re-assign the lastActionTime
28
+ lastActionTime = now;
29
+ return func();
30
+ }
31
+ }
32
+
33
+ function __flush() {
34
+ return syncManager ? syncManager.flush() : Promise.resolve();
35
+ }
36
+
37
+ return {
38
+ init() {
39
+ if (hasInit) return;
40
+ hasInit = true;
41
+
42
+ if (!isSharedClient) {
43
+ validateAndTrackApiKey(settings.log, settings.core.authorizationKey);
44
+ sdkReadinessManager.readinessManager.init();
45
+ impressionsTracker.start();
46
+ syncManager && syncManager.start();
47
+ signalListener && signalListener.start();
48
+ }
49
+ },
50
+
51
+ flush() {
52
+ // @TODO define cooldown time
53
+ return __cooldown(__flush, COOLDOWN_TIME_IN_MILLIS);
54
+ },
55
+
56
+ destroy() {
57
+ hasInit = false;
58
+ // Mark the SDK as destroyed immediately
59
+ sdkReadinessManager.readinessManager.destroy();
60
+
61
+ // For main client, cleanup the SDK Key, listeners and scheduled jobs, and record stat before flushing data
62
+ if (!isSharedClient) {
63
+ releaseApiKey(settings.core.authorizationKey);
64
+ telemetryTracker.sessionLength();
65
+ signalListener && signalListener.stop();
66
+ impressionsTracker.stop();
67
+ }
68
+
69
+ // Stop background jobs
70
+ syncManager && syncManager.stop();
71
+ splitApi && splitApi.stop();
72
+
73
+ return __flush().then(() => {
74
+ // Cleanup storage
75
+ return storage.destroy();
76
+ });
77
+ }
78
+ };
79
+ }
@@ -0,0 +1,59 @@
1
+ import { thenable } from '../utils/promise/thenable';
2
+ import { getMatching } from '../utils/key';
3
+ import { validateTrafficTypeExistence } from '../utils/inputValidation/trafficTypeExistence';
4
+ import { validateKey, validateTrafficType, validateEvent, validateEventValue, validateEventProperties, validateIfNotDestroyed } from '../utils/inputValidation';
5
+ import { TRACK, TRACK_FN_LABEL } from '../utils/constants';
6
+ import { isConsumerMode } from '../utils/settingsValidation/mode';
7
+ import SplitIO from '../../types/splitio';
8
+ import { ISdkFactoryContext } from '../sdkFactory/types';
9
+
10
+ /**
11
+ * Creates a standalone `track` function with input validation.
12
+ * Reusable by FF SDK client, Configs SDK, and thin-client SDK.
13
+ */
14
+ export function trackMethodFactory(params: Pick<ISdkFactoryContext, 'settings' | 'eventTracker' | 'telemetryTracker' | 'storage' | 'sdkReadinessManager'>, warnTTExistence = true) {
15
+ const { settings, storage: { definitions }, telemetryTracker, eventTracker, sdkReadinessManager: { readinessManager } } = params;
16
+ const { log, mode } = settings;
17
+ const isAsync = isConsumerMode(mode);
18
+
19
+ return function track(maybeKey: SplitIO.SplitKey, maybeTT: string, maybeEvent: string, maybeEventValue?: number, maybeProperties?: SplitIO.Properties) {
20
+ // Input validation
21
+ const key = validateKey(log, maybeKey, TRACK_FN_LABEL);
22
+ const trafficTypeName = validateTrafficType(log, maybeTT, TRACK_FN_LABEL);
23
+ const eventTypeId = validateEvent(log, maybeEvent, TRACK_FN_LABEL);
24
+ const value = validateEventValue(log, maybeEventValue, TRACK_FN_LABEL);
25
+ const { properties, size } = validateEventProperties(log, maybeProperties, TRACK_FN_LABEL);
26
+ const isNotDestroyed = validateIfNotDestroyed(log, readinessManager, TRACK_FN_LABEL);
27
+
28
+ if (!(isNotDestroyed && key && trafficTypeName && eventTypeId && value !== false && properties !== false)) {
29
+ return isAsync ? Promise.resolve(false) : false;
30
+ }
31
+
32
+ // Core logic
33
+ const stopTelemetryTracker = telemetryTracker.trackEval(TRACK);
34
+
35
+ const eventData: SplitIO.EventData = {
36
+ eventTypeId,
37
+ trafficTypeName,
38
+ value,
39
+ timestamp: Date.now(),
40
+ key: getMatching(key),
41
+ properties: properties as SplitIO.Properties | undefined
42
+ };
43
+
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, definitions, mode, trafficTypeName, TRACK_FN_LABEL);
46
+
47
+ const result = eventTracker.track(eventData, size);
48
+
49
+ if (thenable(result)) {
50
+ return result.then((result) => {
51
+ stopTelemetryTracker();
52
+ return result;
53
+ });
54
+ } else {
55
+ stopTelemetryTracker();
56
+ return result;
57
+ }
58
+ };
59
+ }
@@ -4,16 +4,10 @@ import { impressionsTrackerFactory } from '../trackers/impressionsTracker';
4
4
  import { eventTrackerFactory } from '../trackers/eventTracker';
5
5
  import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
6
6
  import SplitIO from '../../types/splitio';
7
- import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
8
7
  import { createLoggerAPI } from '../logger/sdkLogger';
9
8
  import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
10
- 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';
11
10
  import { objectAssign } from '../utils/lang/objectAssign';
12
- import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
13
- import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
14
- import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
15
- import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
16
- import { DEBUG, OPTIMIZED } from '../utils/constants';
17
11
  import { setRolloutPlan } from '../storages/setRolloutPlan';
18
12
  import { IStorageSync } from '../storages/types';
19
13
  import { getMatching } from '../utils/key';
@@ -24,24 +18,13 @@ import { FallbackTreatmentsCalculator } from '../evaluator/fallbackTreatmentsCal
24
18
  */
25
19
  export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IAsyncSDK | SplitIO.IBrowserSDK | SplitIO.IBrowserAsyncSDK {
26
20
 
27
- const { settings, platform, storageFactory, splitApiFactory, extraProps,
28
- syncManagerFactory, SignalListener, impressionsObserverFactory,
29
- integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
30
- filterAdapterFactory, lazyInit } = params;
31
- const { log, sync: { impressionsMode }, initialRolloutPlan, core: { key } } = settings;
21
+ const { settings, platform, storageFactory, splitApiFactory, extraProps, syncManagerFactory,
22
+ integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory, lazyInit } = params;
23
+ const { log, initialRolloutPlan, core: { key } } = settings;
32
24
 
33
25
  // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
34
26
  // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
35
27
 
36
- // initialization
37
- let hasInit = false;
38
- const initCallbacks: (() => void)[] = [];
39
-
40
- function whenInit(cb: () => void) {
41
- if (hasInit) cb();
42
- else initCallbacks.push(cb);
43
- }
44
-
45
28
  const sdkReadinessManager = sdkReadinessManagerFactory(platform.EventEmitter, settings);
46
29
  const readiness = sdkReadinessManager.readinessManager;
47
30
 
@@ -53,67 +36,41 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
53
36
  readiness.timeout();
54
37
  return;
55
38
  }
56
- readiness.splits.emit(SDK_SPLITS_ARRIVED);
39
+ readiness.definitions.emit(SDK_DEFINITIONS_ARRIVED);
57
40
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
58
41
  },
59
42
  onReadyFromCacheCb() {
60
- readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
43
+ readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED);
61
44
  }
62
45
  });
63
46
 
64
- const fallbackTreatmentsCalculator = new FallbackTreatmentsCalculator(settings.fallbackTreatments);
47
+ const fallbackCalculator = FallbackTreatmentsCalculator(settings.fallbackTreatments);
65
48
 
66
49
  if (initialRolloutPlan) {
67
50
  setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
68
- 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 */ });
69
52
  }
70
53
 
71
- const clients: Record<string, SplitIO.IBasicClient> = {};
54
+ const clients: Record<string, SplitIO.IBasicClient & { init: () => void }> = {};
72
55
  const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
73
56
  const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
74
57
 
75
- const observer = impressionsObserverFactory();
76
- const uniqueKeysTracker = uniqueKeysTrackerFactory(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory());
77
-
78
- const noneStrategy = strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker);
79
- const strategy = impressionsMode === OPTIMIZED ?
80
- strategyOptimizedFactory(observer, storage.impressionCounts) :
81
- impressionsMode === DEBUG ?
82
- strategyDebugFactory(observer) :
83
- noneStrategy;
84
-
85
- const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, noneStrategy, strategy, whenInit, integrationsManager, storage.telemetry);
86
- const eventTracker = eventTrackerFactory(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
58
+ const impressionsTracker = impressionsTrackerFactory(params, storage, integrationsManager);
59
+ const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
87
60
 
88
61
  // splitApi is used by SyncManager and Browser signal listener
89
62
  const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
90
63
 
91
- const ctx: ISdkFactoryContext = { clients, splitApi, eventTracker, impressionsTracker, telemetryTracker, uniqueKeysTracker, sdkReadinessManager, readiness, settings, storage, platform, fallbackTreatmentsCalculator };
92
-
93
- const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
94
- ctx.syncManager = syncManager;
95
-
96
- const signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
97
- ctx.signalListener = signalListener;
64
+ const ctx: ISdkFactoryContext = { clients, splitApi, eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, readiness, settings, storage, platform, fallbackCalculator };
65
+ ctx.syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
98
66
 
99
67
  // SDK client and manager
100
68
  const clientMethod = sdkClientMethodFactory(ctx);
101
- const managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
69
+ const managerInstance = sdkManagerFactory(settings, storage.definitions, sdkReadinessManager);
102
70
 
103
71
 
104
72
  function init() {
105
- if (hasInit) return;
106
- hasInit = true;
107
-
108
- // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
109
- validateAndTrackApiKey(log, settings.core.authorizationKey);
110
- readiness.init();
111
- uniqueKeysTracker.start();
112
- syncManager && syncManager.start();
113
- signalListener && signalListener.start();
114
-
115
- initCallbacks.forEach((cb) => cb());
116
- initCallbacks.length = 0;
73
+ Object.keys(clients).map(key => clients[key].init());
117
74
  }
118
75
 
119
76
  log.info(NEW_FACTORY, [settings.version]);
@@ -135,7 +92,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
135
92
  settings,
136
93
 
137
94
  destroy() {
138
- hasInit = false;
139
95
  return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => { });
140
96
  }
141
97
  }, extraProps && extraProps(ctx), lazyInit ? { init } : init());
@@ -3,12 +3,12 @@ import { ISignalListener } from '../listeners/types';
3
3
  import { IReadinessManager, ISdkReadinessManager } from '../readiness/types';
4
4
  import type { sdkManagerFactory } from '../sdkManager';
5
5
  import type { splitApiFactory } from '../services/splitApi';
6
- import type { IFallbackTreatmentsCalculator } from '../evaluator/fallbackTreatmentsCalculator';
6
+ import type { IFallbackCalculator } from '../evaluator/fallbackTreatmentsCalculator';
7
7
  import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
8
8
  import { IStorageAsync, IStorageSync, IStorageFactoryParams } from '../storages/types';
9
9
  import { ISyncManager } from '../sync/types';
10
10
  import { IImpressionObserver } from '../trackers/impressionObserver/types';
11
- import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter, IUniqueKeysTracker } from '../trackers/types';
11
+ import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter } from '../trackers/types';
12
12
  import { ISettings } from '../types';
13
13
  import SplitIO from '../../types/splitio';
14
14
 
@@ -35,9 +35,17 @@ export interface IPlatform {
35
35
  /**
36
36
  * Function used to track latencies for telemetry.
37
37
  */
38
- now?: () => number
38
+ now?: () => number,
39
+ /**
40
+ * Optional signal listener constructor. Used to listen and handle runtime environment states, like server shutdown, app paused or resumed.
41
+ */
42
+ // eslint-disable-next-line no-use-before-define
43
+ SignalListener?: new (params: ISdkFactoryContext) => ISignalListener, // Used by BrowserSignalListener
39
44
  }
40
45
 
46
+ // Definition type
47
+ export type EntityType = 'config' | 'flag';
48
+
41
49
  export interface ISdkFactoryContext {
42
50
  platform: IPlatform,
43
51
  sdkReadinessManager: ISdkReadinessManager,
@@ -47,12 +55,11 @@ export interface ISdkFactoryContext {
47
55
  eventTracker: IEventTracker,
48
56
  telemetryTracker: ITelemetryTracker,
49
57
  storage: IStorageSync | IStorageAsync,
50
- uniqueKeysTracker: IUniqueKeysTracker,
51
- signalListener?: ISignalListener
52
- splitApi?: ISplitApi
58
+ splitApi?: ISplitApi,
53
59
  syncManager?: ISyncManager,
54
60
  clients: Record<string, SplitIO.IBasicClient>,
55
- fallbackTreatmentsCalculator: IFallbackTreatmentsCalculator
61
+ fallbackCalculator: IFallbackCalculator,
62
+ entityType?: EntityType
56
63
  }
57
64
 
58
65
  export interface ISdkFactoryContextSync extends ISdkFactoryContext {
@@ -98,21 +105,17 @@ export interface ISdkFactoryParams {
98
105
 
99
106
  // Sdk client method factory.
100
107
  // It Allows to distinguish SDK clients with the client-side API (`IBrowserSDK` and `IBrowserAsyncSDK`) or server-side API (`ISDK` and `IAsyncSDK`).
101
- sdkClientMethodFactory: (params: ISdkFactoryContext) => ({ (): SplitIO.IBrowserClient; (key: SplitIO.SplitKey): SplitIO.IBrowserClient; } | (() => SplitIO.IClient) | (() => SplitIO.IAsyncClient))
108
+ sdkClientMethodFactory: (params: ISdkFactoryContext) => (
109
+ { (): SplitIO.IBrowserClient & { init(): void }; (key: SplitIO.SplitKey): SplitIO.IBrowserClient & { init(): void }; } |
110
+ (() => SplitIO.IClient & { init(): void }) |
111
+ (() => SplitIO.IAsyncClient & { init(): void })
112
+ )
102
113
 
103
114
  // Impression observer factory.
104
115
  impressionsObserverFactory: () => IImpressionObserver
105
116
 
106
117
  filterAdapterFactory?: () => IFilterAdapter
107
118
 
108
- // Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed.
109
- // Pass only if `syncManager` (used by NodeSignalListener) and `splitApi` (used by Browser listener) are passed.
110
- SignalListener?: new (
111
- syncManager: ISyncManager | undefined, // Used by NodeSignalListener to flush data, and by BrowserSignalListener to close streaming connection.
112
- settings: ISettings, // Used by BrowserSignalListener
113
- storage: IStorageSync | IStorageAsync, // Used by BrowserSignalListener
114
- serviceApi: ISplitApi | undefined) => ISignalListener, // Used by BrowserSignalListener
115
-
116
119
  // @TODO review impressionListener and integrations interfaces. What about handling impressionListener as an integration ?
117
120
  integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined,
118
121
 
@@ -1,16 +1,16 @@
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, validateIfOperational } from '../utils/inputValidation';
5
- import { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types';
4
+ import { validateDefinition, validateDefinitionExistence, validateIfOperational } from '../utils/inputValidation';
5
+ import { IDefinitionsCacheAsync, IDefinitionsCacheSync } from '../storages/types';
6
6
  import { ISdkReadinessManager } from '../readiness/types';
7
- import { ISplit } from '../dtos/types';
7
+ import { IDefinition } from '../dtos/types';
8
8
  import { ISettings } from '../types';
9
9
  import SplitIO from '../../types/splitio';
10
10
  import { isConsumerMode } from '../utils/settingsValidation/mode';
11
11
  import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';
12
12
 
13
- function collectTreatments(splitObject: ISplit) {
13
+ function collectTreatments(splitObject: IDefinition) {
14
14
  const conditions = splitObject.conditions;
15
15
  // Rollout conditions are supposed to have the entire partitions list, so we find the first one.
16
16
  let allTreatmentsCondition = find(conditions, (cond) => cond.conditionType === 'ROLLOUT');
@@ -20,7 +20,7 @@ function collectTreatments(splitObject: ISplit) {
20
20
  return allTreatmentsCondition ? allTreatmentsCondition.partitions!.map(v => v.treatment) : [];
21
21
  }
22
22
 
23
- function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
23
+ function objectToView(splitObject: IDefinition | null): SplitIO.SplitView | null {
24
24
  if (!splitObject) return null;
25
25
 
26
26
  return {
@@ -29,7 +29,7 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
29
29
  killed: splitObject.killed,
30
30
  changeNumber: splitObject.changeNumber || 0,
31
31
  treatments: collectTreatments(splitObject),
32
- configs: splitObject.configurations || {},
32
+ configs: splitObject.configurations as SplitIO.SplitView['configs'] || {},
33
33
  sets: splitObject.sets || [],
34
34
  defaultTreatment: splitObject.defaultTreatment,
35
35
  impressionsDisabled: splitObject.impressionsDisabled === true,
@@ -37,7 +37,7 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
37
37
  };
38
38
  }
39
39
 
40
- function objectsToViews(splitObjects: ISplit[]) {
40
+ function objectsToViews(splitObjects: IDefinition[]) {
41
41
  let views: SplitIO.SplitView[] = [];
42
42
 
43
43
  splitObjects.forEach(split => {
@@ -48,11 +48,11 @@ function objectsToViews(splitObjects: ISplit[]) {
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);
@@ -65,21 +65,21 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
65
65
  * Get the feature flag object corresponding to the given feature flag name if valid
66
66
  */
67
67
  split(featureFlagName: string) {
68
- const splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
68
+ const splitName = validateDefinition(log, featureFlagName, SPLIT_FN_LABEL);
69
69
  if (!validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
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
77
- validateSplitExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
77
+ validateDefinitionExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
78
78
  return objectToView(result);
79
79
  });
80
80
  }
81
81
 
82
- validateSplitExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
82
+ validateDefinitionExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
83
83
 
84
84
  return objectToView(split);
85
85
  },
@@ -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,6 +63,12 @@ export function splitApiFactory(
61
63
  });
62
64
  },
63
65
 
66
+ // @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
67
+ fetchConfigs(since: number, noCache?: boolean, till?: number) {
68
+ const url = `${urls.sdk}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
69
+ return (secureSplitHttpClient || splitHttpClient)(url, noCache ? noCacheHeaderOptions : undefined);
70
+ },
71
+
64
72
  fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
65
73
  const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
66
74
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
@@ -143,6 +151,10 @@ export function splitApiFactory(
143
151
  postMetricsUsage(body: string, headers?: Record<string, string>) {
144
152
  const url = `${urls.telemetry}/v1/metrics/usage`;
145
153
  return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
154
+ },
155
+
156
+ stop() {
157
+ if (secureSplitHttpClient) secureSplitHttpClient.stop();
146
158
  }
147
159
  };
148
160
  }
@@ -33,9 +33,11 @@ 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
- export type IFetchSplitChanges = (since: number, noCache?: boolean, till?: number, rbSince?: number) => Promise<IResponse>
40
+ export type IFetchDefinitionChanges = (since: number, noCache?: boolean, till?: number, rbSince?: number) => Promise<IResponse>
39
41
 
40
42
  export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>
41
43
 
@@ -59,7 +61,8 @@ export interface ISplitApi {
59
61
  getSdkAPIHealthCheck: IHealthCheckAPI
60
62
  getEventsAPIHealthCheck: IHealthCheckAPI
61
63
  fetchAuth: IFetchAuth
62
- fetchSplitChanges: IFetchSplitChanges
64
+ fetchSplitChanges: IFetchDefinitionChanges
65
+ fetchConfigs: IFetchDefinitionChanges
63
66
  fetchSegmentChanges: IFetchSegmentChanges
64
67
  fetchMemberships: IFetchMemberships
65
68
  postEventsBulk: IPostEventsBulk
@@ -69,6 +72,8 @@ export interface ISplitApi {
69
72
  postTestImpressionsCount: IPostTestImpressionsCount
70
73
  postMetricsConfig: IPostMetricsConfig
71
74
  postMetricsUsage: IPostMetricsUsage
75
+ // lifecycle
76
+ stop(): void
72
77
  }
73
78
 
74
79
  // Minimal version of EventSource API used by the SDK